diff --git a/.github/config.yml b/.github/config.yml
new file mode 100644
index 0000000..fa7be91
--- /dev/null
+++ b/.github/config.yml
@@ -0,0 +1,19 @@
+# Configuration for new-issue-welcome - https://github.com/behaviorbot/new-issue-welcome
+
+# Comment to be posted to on first time issues
+newIssueWelcomeComment: |
+ 👋 Thanks for opening your first issue here! If you're reporting a 🐞 bug, please make sure you include steps to reproduce it. We get a lot of issues on this repo, so please be patient and we will get back to you as soon as we can.
+
+ To help make it easier for us to investigate your issue, please follow the [contributing instructions](https://github.com/whipper-team/whipper#bug-reports--feature-requests).
+
+# Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome
+
+# Comment to be posted to on PRs from first time contributors in your repository
+newPRWelcomeComment: >
+ 💖 Thanks for opening your first pull request here! 💖
+
+# Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge
+
+# Comment to be posted to on pull requests merged by a first time user
+firstPRMergeComment: >
+ Congrats on merging your first pull request! 🎉🎉🎉
diff --git a/.github/stale.yml b/.github/stale.yml
new file mode 100644
index 0000000..bf20bee
--- /dev/null
+++ b/.github/stale.yml
@@ -0,0 +1,42 @@
+# Configuration for probot-stale - https://github.com/probot/stale
+
+# Number of days of inactivity before an Issue or Pull Request becomes stale
+daysUntilStale: 30
+
+# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
+# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
+daysUntilClose: 7
+
+# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
+exemptLabels:
+ - Accepted
+
+# Set to true to ignore issues in a project (defaults to false)
+exemptProjects: false
+
+# Set to true to ignore issues in a milestone (defaults to false)
+exemptMilestones:
+ - "2.0"
+ - backlog
+
+# Label to use when marking as stale
+staleLabel: "Status: stale"
+
+# Comment to post when marking as stale. Set to `false` to disable
+markComment: >
+ This issue/pull request has been automatically marked as stale because it has not had
+ recent activity. It will be closed in 7 days if no further activity occurs. Thank you
+ for your contributions.
+
+# Comment to post when removing the stale label.
+unmarkComment: >
+ Thank you for updating this issue. It is no longer marked as stale.
+
+# Comment to post when closing a stale Issue or Pull Request.
+closeComment: |
+ This issue/pull request has been closed due to prolonged inactivity.
+
+ If you think this is an error, please leave a comment and we will gladly reopen it.
+
+# Limit the number of actions per hour, from 1-30. Default is 30
+limitPerRun: 30
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ef0e599..63d66a7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,23 +2,59 @@
## [Unreleased](https://github.com/whipper-team/whipper/tree/HEAD)
-[Full Changelog](https://github.com/whipper-team/whipper/compare/v0.7.2...HEAD)
+[Full Changelog](https://github.com/whipper-team/whipper/compare/v0.7.3...HEAD)
+
+## [v0.7.3](https://github.com/whipper-team/whipper/tree/v0.7.3) (2018-12-14)
+[Full Changelog](https://github.com/whipper-team/whipper/compare/v0.7.2...v0.7.3)
+
+**Fixed bugs:**
+
+- Error when parsing log file due to left pad track number [\#340](https://github.com/whipper-team/whipper/issues/340)
+- Failing AccurateRipResponse tests [\#333](https://github.com/whipper-team/whipper/issues/333)
+- CRITICAL:whipper.command.cd:output directory is a finished rip output directory [\#287](https://github.com/whipper-team/whipper/issues/287)
+- Possible HTOA error [\#281](https://github.com/whipper-team/whipper/issues/281)
+- Disc template KeyError [\#279](https://github.com/whipper-team/whipper/issues/279)
+- Enhanced CD causes computer to freeze. [\#256](https://github.com/whipper-team/whipper/issues/256)
+- pycdio & libcdio issues [\#238](https://github.com/whipper-team/whipper/issues/238)
+- Unicode issues [\#215](https://github.com/whipper-team/whipper/issues/215)
+- whipper offset find exception [\#208](https://github.com/whipper-team/whipper/issues/208)
+- ZeroDivisionError: float division by zero [\#202](https://github.com/whipper-team/whipper/issues/202)
+- Allow plugins from system directories [\#135](https://github.com/whipper-team/whipper/issues/135)
+
+**Closed issues:**
+
+- On Ubuntu 18.10 cd-paranoia binary is called cdparanoia [\#347](https://github.com/whipper-team/whipper/issues/347)
+- WARNING:whipper.common.program:network error: NetworkError\(\) [\#338](https://github.com/whipper-team/whipper/issues/338)
+- Can not install [\#314](https://github.com/whipper-team/whipper/issues/314)
+- Write musicbrainz\_discid tag when disc is unknown [\#280](https://github.com/whipper-team/whipper/issues/280)
+- Write .toc files in addition to .cue files to support cdrdao and non-compliant .cue sheets [\#214](https://github.com/whipper-team/whipper/issues/214)
+
+**Merged pull requests:**
+
+- Discover plugins in system directories too [\#348](https://github.com/whipper-team/whipper/pull/348) ([JoeLametta](https://github.com/JoeLametta))
+- Avoid zero padding in logger track numbers [\#341](https://github.com/whipper-team/whipper/pull/341) ([itismadness](https://github.com/itismadness))
+- Update failing AccurateRipResponse tests [\#334](https://github.com/whipper-team/whipper/pull/334) ([JoeLametta](https://github.com/JoeLametta))
+- Replace sys.std{out,err} statements with logger/print calls [\#331](https://github.com/whipper-team/whipper/pull/331) ([JoeLametta](https://github.com/JoeLametta))
+- Add Probot apps to improve workflow [\#329](https://github.com/whipper-team/whipper/pull/329) ([JoeLametta](https://github.com/JoeLametta))
+- Raise exception when cdparanoia can't read any frames [\#328](https://github.com/whipper-team/whipper/pull/328) ([JoeLametta](https://github.com/JoeLametta))
+- Prevent exception in offset find [\#327](https://github.com/whipper-team/whipper/pull/327) ([JoeLametta](https://github.com/JoeLametta))
+- Fix template validation error [\#325](https://github.com/whipper-team/whipper/pull/325) ([JoeLametta](https://github.com/JoeLametta))
+- Fix UnicodeEncodeError with non ASCII MusicBrainz's catalog numbers [\#323](https://github.com/whipper-team/whipper/pull/323) ([JoeLametta](https://github.com/JoeLametta))
+- Raise exception if template has invalid variables [\#322](https://github.com/whipper-team/whipper/pull/322) ([JoeLametta](https://github.com/JoeLametta))
+- Preserve ToC file generated by cdrdao [\#321](https://github.com/whipper-team/whipper/pull/321) ([JoeLametta](https://github.com/JoeLametta))
## [v0.7.2](https://github.com/whipper-team/whipper/tree/v0.7.2) (2018-10-31)
[Full Changelog](https://github.com/whipper-team/whipper/compare/v0.7.1...v0.7.2)
-**Implemented enhancements:**
-
-- Add whipper to Hydrogen Audio wiki's "Comparison of CD rippers" [\#317](https://github.com/whipper-team/whipper/issues/317)
-- automatically build Docker images [\#301](https://github.com/whipper-team/whipper/issues/301)
-
**Fixed bugs:**
- UnicodeEncodeError: 'ascii' codec can't encode characters in position 17-18: ordinal not in range\(128\) [\#315](https://github.com/whipper-team/whipper/issues/315)
**Closed issues:**
+- Add whipper to Hydrogen Audio wiki's "Comparison of CD rippers" [\#317](https://github.com/whipper-team/whipper/issues/317)
- Make 0.7.1 release \(before GCI 😅\) [\#312](https://github.com/whipper-team/whipper/issues/312)
+- automatically build Docker images [\#301](https://github.com/whipper-team/whipper/issues/301)
**Merged pull requests:**
@@ -28,24 +64,28 @@
## [v0.7.1](https://github.com/whipper-team/whipper/tree/v0.7.1) (2018-10-23)
[Full Changelog](https://github.com/whipper-team/whipper/compare/v0.7.0...v0.7.1)
-**Implemented enhancements:**
-
-- Transfer repository ownership to GitHub organization [\#306](https://github.com/whipper-team/whipper/issues/306)
-- Add cdparanoia version to log file [\#267](https://github.com/whipper-team/whipper/issues/267)
-- Remove whipper's retag feature [\#262](https://github.com/whipper-team/whipper/issues/262)
-- Add a requirements.txt file [\#221](https://github.com/whipper-team/whipper/issues/221)
-- Limit length of filenames [\#197](https://github.com/whipper-team/whipper/issues/197)
-- Loggers [\#117](https://github.com/whipper-team/whipper/issues/117)
-
**Fixed bugs:**
- TypeError on whipper offset find [\#263](https://github.com/whipper-team/whipper/issues/263)
+- Remove whipper's retag feature [\#262](https://github.com/whipper-team/whipper/issues/262)
+- ImportError: libcdio.so.16: cannot open shared object file: No such file or directory [\#229](https://github.com/whipper-team/whipper/issues/229)
- Catch DNS error [\#206](https://github.com/whipper-team/whipper/issues/206)
+- Limit length of filenames [\#197](https://github.com/whipper-team/whipper/issues/197)
+- Loggers [\#117](https://github.com/whipper-team/whipper/issues/117)
**Closed issues:**
- Disable eject button when ripping [\#308](https://github.com/whipper-team/whipper/issues/308)
+- Transfer repository ownership to GitHub organization [\#306](https://github.com/whipper-team/whipper/issues/306)
+- Variable offset detected [\#295](https://github.com/whipper-team/whipper/issues/295)
- Github repo [\#293](https://github.com/whipper-team/whipper/issues/293)
+- yaml logger [\#292](https://github.com/whipper-team/whipper/issues/292)
+- Add replaygain processing [\#285](https://github.com/whipper-team/whipper/issues/285)
+- pre emphasis documentation [\#275](https://github.com/whipper-team/whipper/issues/275)
+- Add cdparanoia version to log file [\#267](https://github.com/whipper-team/whipper/issues/267)
+- whipper sometimes creates invalid cue sheets [\#265](https://github.com/whipper-team/whipper/issues/265)
+- Make .cue and .m3u writing optional [\#259](https://github.com/whipper-team/whipper/issues/259)
+- Add a requirements.txt file [\#221](https://github.com/whipper-team/whipper/issues/221)
**Merged pull requests:**
@@ -66,60 +106,49 @@
## [v0.7.0](https://github.com/whipper-team/whipper/tree/v0.7.0) (2018-04-09)
[Full Changelog](https://github.com/whipper-team/whipper/compare/v0.6.0...v0.7.0)
-**Implemented enhancements:**
-
-- Various ripping issues [\#179](https://github.com/whipper-team/whipper/issues/179)
-- Simple message while reading TOC [\#257](https://github.com/whipper-team/whipper/issues/257)
-- Small readme cleanups [\#250](https://github.com/whipper-team/whipper/pull/250) ([RecursiveForest](https://github.com/RecursiveForest))
-- Remove debug commands, add mblookup command [\#249](https://github.com/whipper-team/whipper/pull/249) ([RecursiveForest](https://github.com/RecursiveForest))
-- remove -T/--toc-pickle [\#245](https://github.com/whipper-team/whipper/pull/245) ([RecursiveForest](https://github.com/RecursiveForest))
-- credit four major developers by line count [\#243](https://github.com/whipper-team/whipper/pull/243) ([RecursiveForest](https://github.com/RecursiveForest))
-- Removed reference to unused "profile = flac" config option \(issue \#99\) [\#231](https://github.com/whipper-team/whipper/pull/231) ([calumchisholm](https://github.com/calumchisholm))
-
**Fixed bugs:**
-- whipper offset find exception [\#208](https://github.com/whipper-team/whipper/issues/208)
- cd rip is not able to rip the last track [\#203](https://github.com/whipper-team/whipper/issues/203)
+- CD-ROM powers off during rip command. [\#189](https://github.com/whipper-team/whipper/issues/189)
+- Various ripping issues [\#179](https://github.com/whipper-team/whipper/issues/179)
+- "whipper image verify" abends on FLAC having ID3 tags \("TypeError: %x format: a number is required, not NoneType"\) [\#176](https://github.com/whipper-team/whipper/issues/176)
- whipper not picking up all settings in whipper.conf [\#99](https://github.com/whipper-team/whipper/issues/99)
-- ImportError: libcdio.so.16: cannot open shared object file: No such file or directory [\#229](https://github.com/whipper-team/whipper/issues/229)
-- fix CI build error with latest pycdio [\#233](https://github.com/whipper-team/whipper/pull/233) ([thomas-mc-work](https://github.com/thomas-mc-work))
**Closed issues:**
+- Simple message while reading TOC [\#257](https://github.com/whipper-team/whipper/issues/257)
+- Statement to your "only flac" decision [\#247](https://github.com/whipper-team/whipper/issues/247)
+- How to choose device \(if there are more\)? [\#241](https://github.com/whipper-team/whipper/issues/241)
+- Import Error No Module Named gobject Fedora 26 and 27 [\#228](https://github.com/whipper-team/whipper/issues/228)
- Make a 0.6.0 release [\#219](https://github.com/whipper-team/whipper/issues/219)
-- CD-ROM powers off during rip command. [\#189](https://github.com/whipper-team/whipper/issues/189)
+- flac settings [\#184](https://github.com/whipper-team/whipper/issues/184)
- Remove connection to parent fork. [\#79](https://github.com/whipper-team/whipper/issues/79)
- GUI frontend for whipper [\#40](https://github.com/whipper-team/whipper/issues/40)
**Merged pull requests:**
+- Small readme cleanups [\#250](https://github.com/whipper-team/whipper/pull/250) ([RecursiveForest](https://github.com/RecursiveForest))
+- Remove debug commands, add mblookup command [\#249](https://github.com/whipper-team/whipper/pull/249) ([RecursiveForest](https://github.com/RecursiveForest))
- Remove reference to Copr repository [\#248](https://github.com/whipper-team/whipper/pull/248) ([mruszczyk](https://github.com/mruszczyk))
- Revert "Convert docstrings to reStructuredText" [\#246](https://github.com/whipper-team/whipper/pull/246) ([RecursiveForest](https://github.com/RecursiveForest))
+- remove -T/--toc-pickle [\#245](https://github.com/whipper-team/whipper/pull/245) ([RecursiveForest](https://github.com/RecursiveForest))
+- credit four major developers by line count [\#243](https://github.com/whipper-team/whipper/pull/243) ([RecursiveForest](https://github.com/RecursiveForest))
- remove radon reports [\#242](https://github.com/whipper-team/whipper/pull/242) ([RecursiveForest](https://github.com/RecursiveForest))
- read command parameters from config sections [\#240](https://github.com/whipper-team/whipper/pull/240) ([RecursiveForest](https://github.com/RecursiveForest))
+- fix CI build error with latest pycdio [\#233](https://github.com/whipper-team/whipper/pull/233) ([thomas-mc-work](https://github.com/thomas-mc-work))
+- Removed reference to unused "profile = flac" config option \(issue \#99\) [\#231](https://github.com/whipper-team/whipper/pull/231) ([calumchisholm](https://github.com/calumchisholm))
## [v0.6.0](https://github.com/whipper-team/whipper/tree/v0.6.0) (2018-02-02)
[Full Changelog](https://github.com/whipper-team/whipper/compare/v0.5.1...v0.6.0)
-**Implemented enhancements:**
-
-- Error: NotFoundException message displayed while ripping an unknown disc [\#198](https://github.com/whipper-team/whipper/issues/198)
-- rename milestone 101010 to backlog [\#190](https://github.com/whipper-team/whipper/issues/190)
-- Use 'Artist as credited' in filename instead of 'Artist in MusicBrainz' \(e.g. to solve \[unknown\]\) [\#155](https://github.com/whipper-team/whipper/issues/155)
-- Declare supported Python version [\#152](https://github.com/whipper-team/whipper/issues/152)
-- Update doc/release or remove it [\#149](https://github.com/whipper-team/whipper/issues/149)
-- Test HTOA peak value against 0 \(integer equality\) [\#143](https://github.com/whipper-team/whipper/issues/143)
-- Identify media type in log file \(ie CD vs CD-R\) [\#137](https://github.com/whipper-team/whipper/issues/137)
-- Rename the Python module [\#100](https://github.com/whipper-team/whipper/issues/100)
-- libcdio-paranoia instead of cdparanoia [\#87](https://github.com/whipper-team/whipper/issues/87)
-- Support both AccurateRip V1 and AccurateRip V2 at the same time [\#18](https://github.com/whipper-team/whipper/issues/18)
-- Test HTOA peak value against 0 \(integer comparison\) [\#224](https://github.com/whipper-team/whipper/pull/224) ([JoeLametta](https://github.com/JoeLametta))
-
**Fixed bugs:**
+- Error: NotFoundException message displayed while ripping an unknown disc [\#198](https://github.com/whipper-team/whipper/issues/198)
- whipper doesn't name files .flac, which leads to it not being able to find ripped files [\#194](https://github.com/whipper-team/whipper/issues/194)
- Issues with finding offset [\#182](https://github.com/whipper-team/whipper/issues/182)
- failing unittests in systemd-nspawn container [\#157](https://github.com/whipper-team/whipper/issues/157)
+- Update doc/release or remove it [\#149](https://github.com/whipper-team/whipper/issues/149)
+- Test HTOA peak value against 0 \(integer equality\) [\#143](https://github.com/whipper-team/whipper/issues/143)
- Regression: Unable to resume a failed rip [\#136](https://github.com/whipper-team/whipper/issues/136)
- "Catalog Number" incorrectly appended to "artist" instead of the Album name. [\#127](https://github.com/whipper-team/whipper/issues/127)
- Track "can't be ripped" but EAC can :\) [\#116](https://github.com/whipper-team/whipper/issues/116)
@@ -128,11 +157,26 @@
**Closed issues:**
+- TRACK and FILE order in cue file [\#212](https://github.com/whipper-team/whipper/issues/212)
+- ImportError - CDDB on Solus. [\#209](https://github.com/whipper-team/whipper/issues/209)
+- rename milestone 101010 to backlog [\#190](https://github.com/whipper-team/whipper/issues/190)
+- AttributeError: RipResult instance has no attribute 'profileName' [\#181](https://github.com/whipper-team/whipper/issues/181)
+- .log, .cue, and .m3u file names [\#180](https://github.com/whipper-team/whipper/issues/180)
+- Accurip verification step failure [\#178](https://github.com/whipper-team/whipper/issues/178)
+- Whipper offset find failing [\#177](https://github.com/whipper-team/whipper/issues/177)
- using your own MusicBrainz server [\#172](https://github.com/whipper-team/whipper/issues/172)
-- cdda2wav from cdrtools instead of cdparanoia [\#38](https://github.com/whipper-team/whipper/issues/38)
+- Use 'Artist as credited' in filename instead of 'Artist in MusicBrainz' \(e.g. to solve \[unknown\]\) [\#155](https://github.com/whipper-team/whipper/issues/155)
+- Declare supported Python version [\#152](https://github.com/whipper-team/whipper/issues/152)
+- Identify media type in log file \(ie CD vs CD-R\) [\#137](https://github.com/whipper-team/whipper/issues/137)
+- Accurate rip failures still exit 0 [\#126](https://github.com/whipper-team/whipper/issues/126)
+- Rename the Python module [\#100](https://github.com/whipper-team/whipper/issues/100)
+- libcdio-paranoia instead of cdparanoia [\#87](https://github.com/whipper-team/whipper/issues/87)
+- Release, Tags, NEWS? [\#63](https://github.com/whipper-team/whipper/issues/63)
+- Support both AccurateRip V1 and AccurateRip V2 at the same time [\#18](https://github.com/whipper-team/whipper/issues/18)
**Merged pull requests:**
+- Test HTOA peak value against 0 \(integer comparison\) [\#224](https://github.com/whipper-team/whipper/pull/224) ([JoeLametta](https://github.com/JoeLametta))
- Fix appearance of template description text. [\#223](https://github.com/whipper-team/whipper/pull/223) ([calumchisholm](https://github.com/calumchisholm))
- Run whipper without installation [\#222](https://github.com/whipper-team/whipper/pull/222) ([vmx](https://github.com/vmx))
- Remove doc/release [\#218](https://github.com/whipper-team/whipper/pull/218) ([MerlijnWajer](https://github.com/MerlijnWajer))
@@ -166,21 +210,29 @@
## [v0.5.0](https://github.com/whipper-team/whipper/tree/v0.5.0) (2017-04-24)
[Full Changelog](https://github.com/whipper-team/whipper/compare/v0.4.2...v0.5.0)
-**Implemented enhancements:**
-
-- overly verbose warning logging [\#131](https://github.com/whipper-team/whipper/issues/131)
-- Check that whipper deals properly with CD pre-emphasis [\#120](https://github.com/whipper-team/whipper/issues/120)
-- Remove gstreamer dependency [\#29](https://github.com/whipper-team/whipper/issues/29)
-
**Fixed bugs:**
- Final track rip failure due to file size mismatch [\#146](https://github.com/whipper-team/whipper/issues/146)
- Fails to rip if MB Release doesn't have a release date/year [\#133](https://github.com/whipper-team/whipper/issues/133)
+- overly verbose warning logging [\#131](https://github.com/whipper-team/whipper/issues/131)
- fb271f08cdee877795091065c344dcc902d1dcbf breaks HEAD [\#129](https://github.com/whipper-team/whipper/issues/129)
- 'whipper drive list' returns a suggestion to run 'rip offset find' [\#112](https://github.com/whipper-team/whipper/issues/112)
- EmptyError\('not a single buffer gotten',\) [\#101](https://github.com/whipper-team/whipper/issues/101)
- Julie Roberts bug [\#74](https://github.com/whipper-team/whipper/issues/74)
+**Closed issues:**
+
+- `whipper find offset` still requiring gst [\#141](https://github.com/whipper-team/whipper/issues/141)
+- Burn FLACs 1:1 CD ? [\#125](https://github.com/whipper-team/whipper/issues/125)
+- whipper offset find -o OFFSET not working [\#123](https://github.com/whipper-team/whipper/issues/123)
+- Check that whipper deals properly with CD pre-emphasis [\#120](https://github.com/whipper-team/whipper/issues/120)
+- FreeDB metadata not honored [\#119](https://github.com/whipper-team/whipper/issues/119)
+- Difficulty getting flac encoding working. [\#118](https://github.com/whipper-team/whipper/issues/118)
+- enabling external loggers triggers python errors [\#111](https://github.com/whipper-team/whipper/issues/111)
+- additional tag creation [\#108](https://github.com/whipper-team/whipper/issues/108)
+- False positive on HTOA [\#82](https://github.com/whipper-team/whipper/issues/82)
+- Remove gstreamer dependency [\#29](https://github.com/whipper-team/whipper/issues/29)
+
**Merged pull requests:**
- Remove notes related to GStreamer flacparse [\#140](https://github.com/whipper-team/whipper/pull/140) ([Freso](https://github.com/Freso))
@@ -195,13 +247,14 @@
## [v0.4.2](https://github.com/whipper-team/whipper/tree/v0.4.2) (2017-01-08)
[Full Changelog](https://github.com/whipper-team/whipper/compare/v0.4.1...v0.4.2)
-**Implemented enhancements:**
-
-- Whipper attempts to rip with no CD inserted [\#81](https://github.com/whipper-team/whipper/issues/81)
-
**Fixed bugs:**
- 0.4.1 Release created but version number in code not bumped [\#105](https://github.com/whipper-team/whipper/issues/105)
+- Whipper attempts to rip with no CD inserted [\#81](https://github.com/whipper-team/whipper/issues/81)
+
+**Closed issues:**
+
+- Make a 0.4.1 release [\#104](https://github.com/whipper-team/whipper/issues/104)
**Merged pull requests:**
@@ -211,10 +264,10 @@
## [v0.4.1](https://github.com/whipper-team/whipper/tree/v0.4.1) (2017-01-06)
[Full Changelog](https://github.com/whipper-team/whipper/compare/v0.4.0...v0.4.1)
-**Implemented enhancements:**
+**Closed issues:**
+- Please don't stop - despite the recent events \(ANSWERED\) [\#76](https://github.com/whipper-team/whipper/issues/76)
- Migrate away from the "rip" command [\#21](https://github.com/whipper-team/whipper/issues/21)
-- Fixed README broken links and added a better changelog [\#90](https://github.com/whipper-team/whipper/pull/90) ([JoeLametta](https://github.com/JoeLametta))
**Merged pull requests:**
@@ -226,6 +279,7 @@
- cdrdao no-disc ejection & --eject [\#93](https://github.com/whipper-team/whipper/pull/93) ([RecursiveForest](https://github.com/RecursiveForest))
- argparse & logging [\#92](https://github.com/whipper-team/whipper/pull/92) ([RecursiveForest](https://github.com/RecursiveForest))
- Update README.md [\#91](https://github.com/whipper-team/whipper/pull/91) ([pieqq](https://github.com/pieqq))
+- Fixed README broken links and added a better changelog [\#90](https://github.com/whipper-team/whipper/pull/90) ([JoeLametta](https://github.com/JoeLametta))
- soxi: remove self.\_path unused variable, mark dep as 'soxi' [\#89](https://github.com/whipper-team/whipper/pull/89) ([RecursiveForest](https://github.com/RecursiveForest))
- Fix spelling mistake in README.md [\#86](https://github.com/whipper-team/whipper/pull/86) ([takeshibaconsuzuki](https://github.com/takeshibaconsuzuki))
- Error reporting enhancements \(conditional-raise-instead-of-assert version\) [\#80](https://github.com/whipper-team/whipper/pull/80) ([chrysn](https://github.com/chrysn))
@@ -240,6 +294,11 @@
- wrong status code when giving up [\#57](https://github.com/whipper-team/whipper/issues/57)
- CD-TEXT issue [\#49](https://github.com/whipper-team/whipper/issues/49)
+**Closed issues:**
+
+- ImportError: No module named log [\#64](https://github.com/whipper-team/whipper/issues/64)
+- whatlogger no longer recognized [\#56](https://github.com/whipper-team/whipper/issues/56)
+
**Merged pull requests:**
- Invoke whipper by its name + Readme rewrite [\#70](https://github.com/whipper-team/whipper/pull/70) ([JoeLametta](https://github.com/JoeLametta))
@@ -261,6 +320,11 @@
- UnicodeEncodeError [\#43](https://github.com/whipper-team/whipper/issues/43)
- Use a single standard for config/cache/state files [\#24](https://github.com/whipper-team/whipper/issues/24)
+**Closed issues:**
+
+- offset find fails [\#46](https://github.com/whipper-team/whipper/issues/46)
+- Error launching rip cd rip command [\#41](https://github.com/whipper-team/whipper/issues/41)
+
**Merged pull requests:**
- Sox [\#48](https://github.com/whipper-team/whipper/pull/48) ([RecursiveForest](https://github.com/RecursiveForest))
@@ -269,14 +333,6 @@
## [v0.2.4](https://github.com/whipper-team/whipper/tree/v0.2.4) (2016-10-09)
[Full Changelog](https://github.com/whipper-team/whipper/compare/v0.2.3...v0.2.4)
-**Implemented enhancements:**
-
-- Don't allow ripping without an explicit offset, and make pycdio a required dependency [\#23](https://github.com/whipper-team/whipper/issues/23)
-- Minimal makedepends for building [\#17](https://github.com/whipper-team/whipper/issues/17)
-- Delete stale branches [\#7](https://github.com/whipper-team/whipper/issues/7)
-- get rid of the gstreamer-0.10 dependency [\#2](https://github.com/whipper-team/whipper/issues/2)
-- Merge 'fork' into 'master' [\#1](https://github.com/whipper-team/whipper/issues/1)
-
**Fixed bugs:**
- whipper fails to build on bash-compgen [\#25](https://github.com/whipper-team/whipper/issues/25)
@@ -286,6 +342,18 @@
- rip offset find seems to fail [\#4](https://github.com/whipper-team/whipper/issues/4)
- rip cd info seems to fail [\#3](https://github.com/whipper-team/whipper/issues/3)
+**Closed issues:**
+
+- Error selecting Drive for ripping [\#34](https://github.com/whipper-team/whipper/issues/34)
+- Offset not saved: could not get device info \(requires pycdio\) [\#33](https://github.com/whipper-team/whipper/issues/33)
+- On Arch Linux, CDDB does not know how to install morituri. [\#28](https://github.com/whipper-team/whipper/issues/28)
+- Error reading TOC [\#26](https://github.com/whipper-team/whipper/issues/26)
+- Don't allow ripping without an explicit offset, and make pycdio a required dependency [\#23](https://github.com/whipper-team/whipper/issues/23)
+- Minimal makedepends for building [\#17](https://github.com/whipper-team/whipper/issues/17)
+- Delete stale branches [\#7](https://github.com/whipper-team/whipper/issues/7)
+- get rid of the gstreamer-0.10 dependency [\#2](https://github.com/whipper-team/whipper/issues/2)
+- Merge 'fork' into 'master' [\#1](https://github.com/whipper-team/whipper/issues/1)
+
**Merged pull requests:**
- Issue24 [\#42](https://github.com/whipper-team/whipper/pull/42) ([JoeLametta](https://github.com/JoeLametta))
diff --git a/COVERAGE b/COVERAGE
index 9bc7dcb..c51cd12 100644
--- a/COVERAGE
+++ b/COVERAGE
@@ -1,4 +1,4 @@
-Coverage.py 4.5.1 text report against whipper v0.7.2
+Coverage.py 4.5.2 text report against whipper v0.7.3
$ coverage run --branch --omit='whipper/test/*' --source=whipper -m unittest discover
$ coverage report -m
@@ -8,42 +8,42 @@ Name Stmts Miss Branch BrPart Cover Missing
whipper/__init__.py 10 2 4 2 71% 9, 11, 8->9, 10->11
whipper/__main__.py 7 7 2 0 0% 4-14
whipper/command/__init__.py 0 0 0 0 100%
-whipper/command/accurip.py 44 44 18 0 0% 21-96
+whipper/command/accurip.py 43 43 18 0 0% 21-92
whipper/command/basecommand.py 69 53 30 0 16% 56-114, 121-130, 133, 136, 139, 142-145
-whipper/command/cd.py 219 181 56 0 14% 71-79, 84-184, 187, 199, 222-276, 283-308, 311-488
-whipper/command/drive.py 62 62 12 0 0% 21-122
+whipper/command/cd.py 224 186 58 0 13% 71-79, 84-193, 196, 208, 231-284, 291-318, 321-491
+whipper/command/drive.py 57 57 10 0 0% 21-107
whipper/command/image.py 38 38 6 0 0% 21-76
-whipper/command/main.py 65 65 20 0 0% 4-109
+whipper/command/main.py 68 68 22 0 0% 4-115
whipper/command/mblookup.py 28 28 8 0 0% 1-41
-whipper/command/offset.py 111 111 32 0 0% 21-227
+whipper/command/offset.py 110 110 32 0 0% 21-221
whipper/common/__init__.py 0 0 0 0 100%
-whipper/common/accurip.py 133 5 54 5 95% 123, 134, 143-145, 116->123, 127->134, 160->163, 252->258, 261->267
-whipper/common/cache.py 105 50 34 6 44% 66-90, 96, 99, 107-112, 115-116, 132, 144-149, 172-179, 203-208, 213-230, 95->96, 98->99, 131->132, 142->153, 143->144, 171->172
+whipper/common/accurip.py 133 5 54 5 95% 121, 130, 139-141, 116->121, 125->130, 155->158, 246->252, 255->261
+whipper/common/cache.py 105 50 34 6 44% 66-90, 96, 99, 107-112, 115-116, 132, 144-148, 171-178, 202-207, 212-228, 95->96, 98->99, 131->132, 142->152, 143->144, 170->171
whipper/common/checksum.py 26 14 2 0 43% 41-42, 45-46, 49-64
-whipper/common/common.py 142 22 32 6 83% 50-51, 118-119, 142-143, 161-168, 180, 274-280, 316-320, 117->118, 130->133, 179->180, 189->196, 270->274, 314->322
-whipper/common/config.py 92 8 18 4 89% 105-106, 124-125, 131, 142, 144, 146, 130->131, 141->142, 143->144, 145->146
+whipper/common/common.py 150 28 38 6 78% 51-52, 119-120, 143-144, 162-169, 181, 275-280, 287-292, 329-333, 118->119, 131->134, 180->181, 190->197, 271->275, 327->335
+whipper/common/config.py 92 8 18 4 89% 105-106, 124-125, 131, 141, 143, 145, 130->131, 140->141, 142->143, 144->145
whipper/common/directory.py 21 8 10 2 55% 29, 39, 44-51, 28->29, 38->39
whipper/common/drive.py 31 20 6 0 35% 35-40, 44-50, 54-60, 64-71
whipper/common/encode.py 44 23 2 0 46% 37-38, 41-42, 45-46, 53-56, 59-60, 63-64, 76-77, 80-81, 84-91
-whipper/common/mbngs.py 159 53 58 7 66% 38-39, 45, 90-96, 158-159, 164-165, 209, 212, 215, 238-241, 250, 270-324, 157->158, 163->164, 208->209, 211->212, 214->215, 237->238, 247->250
+whipper/common/mbngs.py 159 53 58 7 66% 38-39, 45, 90-96, 157-158, 163-164, 208, 211, 214, 237-239, 248, 268-322, 156->157, 162->163, 207->208, 210->211, 213->214, 236->237, 245->248
whipper/common/path.py 24 0 8 3 91% 42->45, 52->57, 62->67
-whipper/common/program.py 344 263 108 5 20% 89-91, 97-105, 113-145, 154-159, 162, 166-170, 215, 226-227, 229-233, 249-264, 272-398, 409-459, 467-475, 478-494, 505-545, 557-574, 577-595, 598-608, 611-619, 81->84, 212->215, 225->226, 228->229, 235->239
+whipper/common/program.py 337 259 110 5 20% 85-87, 93-100, 109-141, 150-155, 158, 162-166, 211, 222-223, 225-229, 245-260, 268-380, 391-442, 450-458, 461-476, 487-527, 539-556, 559-577, 580-590, 593-601, 77->80, 208->211, 221->222, 224->225, 231->235
whipper/common/renamer.py 102 2 16 1 97% 135, 158, 60->68
-whipper/common/task.py 77 19 14 2 75% 47-52, 87-88, 91-94, 102, 116-117, 124, 130, 136, 142, 148, 85->87, 99->102
+whipper/common/task.py 77 19 14 2 75% 47-52, 86-87, 90-93, 101, 114-115, 122, 128, 134, 140, 146, 84->86, 98->101
whipper/extern/__init__.py 0 0 0 0 100%
whipper/extern/asyncsub.py 130 71 66 12 40% 15-17, 32, 37-38, 47-84, 89-102, 115, 122, 134, 145, 151, 156-160, 164-176, 14->15, 35->37, 45->47, 110->113, 114->115, 121->122, 133->134, 139->141, 141->152, 144->145, 148->151, 163->164
whipper/extern/freedb.py 104 83 38 1 17% 49, 57-58, 61, 64, 84-162, 170-222, 56->57
whipper/extern/task/__init__.py 0 0 0 0 100%
-whipper/extern/task/task.py 276 117 54 11 53% 26-27, 56, 60, 86, 152-154, 172-174, 182-198, 216-219, 239-240, 281-282, 285-291, 306-307, 315-317, 326-333, 339-355, 359, 362, 369-386, 397-398, 401-404, 408, 411, 426, 429-431, 447, 459, 504-509, 518-523, 534-542, 545-553, 556-557, 565, 570-572, 55->56, 59->60, 68->70, 151->152, 165->exit, 215->216, 229->231, 234->exit, 494->496, 531->534, 569->570
+whipper/extern/task/task.py 277 116 54 11 54% 57, 61, 81, 87, 153-155, 174-176, 184-200, 218-221, 241-242, 283-284, 287-293, 308-309, 317-319, 328-335, 341-357, 361, 364, 371-388, 399-400, 403-406, 410, 413, 428, 431-433, 449, 461, 506-511, 520-525, 536-544, 547-555, 558-559, 567, 572-574, 56->57, 60->61, 69->71, 152->153, 166->exit, 217->218, 231->233, 236->exit, 496->498, 533->536, 571->572
whipper/image/__init__.py 0 0 0 0 100%
whipper/image/cue.py 91 9 20 3 89% 99, 116-117, 132-134, 159, 187, 205, 98->99, 115->116, 131->132
-whipper/image/image.py 117 94 18 0 17% 49-57, 65-67, 74-107, 121-153, 156-172, 183-214
-whipper/image/table.py 398 22 114 16 93% 237, 346-347, 499, 578, 664-665, 685-686, 695-698, 702-703, 750, 796-797, 799-800, 844-845, 850-852, 180->183, 498->499, 532->536, 555->558, 577->578, 585->592, 684->685, 693->699, 694->695, 723->728, 728->722, 749->750, 795->796, 798->799, 843->844, 849->850
-whipper/image/toc.py 203 15 60 10 90% 134, 262-263, 279-282, 340-342, 364-366, 386, 410, 130->134, 213->221, 261->262, 278->279, 288->293, 324->331, 339->340, 363->364, 373->377, 405->410
+whipper/image/image.py 117 94 18 0 17% 49-57, 65-67, 74-107, 121-153, 156-172, 183-213
+whipper/image/table.py 398 22 114 16 93% 237, 346-347, 499, 578, 663-664, 684-685, 694-697, 701-702, 747, 793-794, 796-797, 841-842, 847-849, 180->183, 498->499, 532->536, 555->558, 577->578, 585->592, 683->684, 692->698, 693->694, 722->726, 726->721, 746->747, 792->793, 795->796, 840->841, 846->847
+whipper/image/toc.py 203 16 60 10 90% 134, 261-262, 278-281, 339-341, 363-365, 385, 409, 439, 130->134, 212->220, 260->261, 277->278, 287->292, 323->330, 338->339, 362->363, 372->376, 404->409
whipper/program/__init__.py 0 0 0 0 100%
-whipper/program/arc.py 38 15 12 4 58% 26-28, 32, 37-43, 52-58, 22->26, 31->32, 36->37, 45->52
-whipper/program/cdparanoia.py 315 185 86 3 39% 48-50, 59-60, 124-126, 163-166, 199-200, 241-255, 258-310, 313-351, 354-358, 361-397, 452-504, 509-554, 587-590, 593, 600, 606, 611-616, 123->124, 599->600, 603->606
-whipper/program/cdrdao.py 51 29 10 2 39% 25-47, 54-60, 70-72, 76-78, 86, 93, 69->70, 75->76
+whipper/program/arc.py 38 15 12 4 58% 26-28, 32, 37-41, 48-54, 22->26, 31->32, 36->37, 43->48
+whipper/program/cdparanoia.py 315 185 86 3 39% 48-50, 59-60, 124-126, 163-166, 199-200, 242-256, 259-310, 313-351, 354-358, 361-397, 452-504, 509-554, 587-590, 593, 600, 606, 611-616, 123->124, 599->600, 603->606
+whipper/program/cdrdao.py 59 36 14 2 34% 26-56, 63-69, 79-81, 85-87, 95, 102, 78->79, 84->85
whipper/program/flac.py 9 5 0 0 44% 12-19
whipper/program/sox.py 17 4 4 2 71% 18-19, 23-24, 17->18, 22->23
whipper/program/soxi.py 28 2 2 1 90% 36, 49, 48->49
@@ -52,4 +52,4 @@ whipper/result/__init__.py 0 0 0 0 100%
whipper/result/logger.py 148 148 48 0 0% 1-242
whipper/result/result.py 56 13 6 0 69% 112-116, 134, 144-145, 154-161
-----------------------------------------------------------------------------
-TOTAL 3950 1900 1090 108 49%
+TOTAL 3961 1910 1104 108 49%
diff --git a/Dockerfile b/Dockerfile
index 0d79d0d..ae6509b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -9,7 +9,7 @@ RUN apt-get update \
&& pip install pycdio==2.0.0
# libcdio-paranoia / libcdio-utils are wrongfully packaged in Debian, thus built manually
-# see https://github.com/JoeLametta/whipper/pull/237#issuecomment-367985625
+# see https://github.com/whipper-team/whipper/pull/237#issuecomment-367985625
RUN curl -o - 'https://ftp.gnu.org/gnu/libcdio/libcdio-2.0.0.tar.gz' | tar zxf - \
&& cd libcdio-2.0.0 \
&& autoreconf -fi \
diff --git a/README.md b/README.md
index e7f2ff3..ec68d98 100644
--- a/README.md
+++ b/README.md
@@ -117,7 +117,8 @@ If you are building from a source tarball or checkout, you can choose to use whi
Whipper relies on the following packages in order to run correctly and provide all the supported features:
- [cd-paranoia](https://www.gnu.org/software/libcdio/), for the actual ripping
- - To avoid bugs it's advised to use `cd-paranoia` **10.2+0.94+2-2**
+ - To avoid bugs it's advised to use `cd-paranoia` version **10.2+0.94+2-2**
+ - The package named `libcdio-utils`, available on Debian and Ubuntu, is affected by a bug: it doesn't include the `cd-paranoia` binary (needed by whipper). For more details see: [#888053 (Debian)](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=888053), [#1750264 (Ubuntu)](https://bugs.launchpad.net/ubuntu/+source/libcdio/+bug/1750264).
- [cdrdao](http://cdrdao.sourceforge.net/), for session, TOC, pre-gap, and ISRC extraction
- [GObject Introspection](https://wiki.gnome.org/Projects/GObjectIntrospection), to provide GLib-2.0 methods used by `task.py`
- [PyGObject](https://pypi.org/project/PyGObject/), required by `task.py`
@@ -126,7 +127,7 @@ Whipper relies on the following packages in order to run correctly and provide a
- [python-setuptools](https://pypi.python.org/pypi/setuptools), for installation, plugins support
- [python-requests](https://pypi.python.org/pypi/requests), for retrieving AccurateRip database entries
- [pycdio](https://pypi.python.org/pypi/pycdio/), for drive identification (required for drive offset and caching behavior to be stored in the configuration file).
- - To avoid bugs it's advised to use `pycdio` **0.20** or **0.21** with `libcdio` ≥ **0.90** ≤ **0.94**. If using `libcdio` **0.83**, which is _too old_ to satisfy all the requirements of whipper, just stick to `pycdio` **0.17**. Altough it needs additional testing, `libcdio` **2.0.0** seems to work fine if used with `pycdio` **2.0.0**. All other combinations aren't guaranteed to work.
+ - To avoid bugs it's advised to use `pycdio` **0.20** or **0.21** with `libcdio` ≥ **0.90** ≤ **0.94* or `pycdio` **2.0.0** with `libcdio` **2.0.0**. All other combinations won't probably work.
- [libsndfile](http://www.mega-nerd.com/libsndfile/), for reading wav files
- [flac](https://xiph.org/flac/), for reading flac files
- [sox](http://sox.sourceforge.net/), for track peak detection
@@ -250,7 +251,7 @@ read_offset = 6 ; drive read offset in positive/negative frames (no leading +)
unknown = True
output_directory = ~/My Music
track_template = new/%%A/%%y - %%d/%%t - %%n ; note: the format char '%' must be represented '%%'
-disc_template = %(track_template)s
+disc_template = new/%%A/%%y - %%d/%%A - %%d
# ...
```
@@ -265,14 +266,31 @@ python2 -m whipper -h
## Logger plugins
-Whipper supports using external logger plugins to write rip `.log` files.
+Whipper allows using external logger plugins to customize the template of `.log` files.
-List available plugins with `whipper cd rip -h`. Specify a logger to rip with by passing `-L loggername`:
+The available plugins can be listed with `whipper cd rip -h`. Specify a logger to rip with by passing `-L loggername`:
```bash
-whipper cd rip -L what
+whipper cd rip -L eac
```
+Whipper searches for logger plugins in the following paths:
+
+- `$XDG_DATA_HOME/whipper/plugins`
+- Paths returned by the following Python instruction:
+
+ `[x + '/whipper/plugins' for x in site.getsitepackages()]`
+
+- If whipper is run in a `virtualenv`, it will use these alternative instructions (from `distutils.sysconfig`):
+ - `get_python_lib(plat_specific=False, standard_lib=False, prefix='/usr/local') + '/whipper/plugins'`
+ - `get_python_lib(plat_specific=False, standard_lib=False) + '/whipper/plugins'`
+
+On a default Debian/Ubuntu installation, the following paths are searched by whipper:
+
+- `$HOME/.local/share/whipper/plugins`
+- `/usr/local/lib/python2.7/dist-packages/whipper/plugins`
+- `/usr/lib/python2.7/dist-packages/whipper/plugins`
+
### Official logger plugins
I suggest using whipper's default logger unless you've got particular requirements.
diff --git a/TODO b/TODO
index 9814e7d..3e2b835 100644
--- a/TODO
+++ b/TODO
@@ -1,6 +1,6 @@
TODO:
-Please see https://github.com/JoeLametta/whipper/milestones for further
+Please see https://github.com/whipper-team/whipper/milestones for further
TODO items; this file exists only to have contents individually removed
eventually, not to be continually updated.
diff --git a/setup.py b/setup.py
index 3ea6a12..8d891d3 100644
--- a/setup.py
+++ b/setup.py
@@ -5,9 +5,9 @@ setup(
name="whipper",
version=whipper_version,
description="a secure cd ripper preferring accuracy over speed",
- author=['Thomas Vander Stichele', 'Joe Lametta', 'Samantha Baldwin'],
- maintainer=['Joe Lametta', 'Samantha Baldwin'],
- url='https://github.com/JoeLametta/whipper',
+ author=['Thomas Vander Stichele', 'The Whipper Team'],
+ maintainer=['The Whipper Team'],
+ url='https://github.com/whipper-team/whipper',
license='GPL3',
packages=find_packages(),
entry_points={
diff --git a/whipper/__init__.py b/whipper/__init__.py
index d795e3f..e06df53 100644
--- a/whipper/__init__.py
+++ b/whipper/__init__.py
@@ -2,9 +2,9 @@ import logging
import os
import sys
-__version__ = '0.7.2'
+__version__ = '0.7.3'
-level = logging.WARNING
+level = logging.INFO
if 'WHIPPER_DEBUG' in os.environ:
level = os.environ['WHIPPER_DEBUG'].upper()
if 'WHIPPER_LOGFILE' in os.environ:
diff --git a/whipper/command/accurip.py b/whipper/command/accurip.py
index 1859fdb..e12f6c8 100644
--- a/whipper/command/accurip.py
+++ b/whipper/command/accurip.py
@@ -18,8 +18,6 @@
# You should have received a copy of the GNU General Public License
# along with whipper. If not, see .
-import sys
-
from whipper.command.basecommand import BaseCommand
from whipper.common.accurip import get_db_entry, ACCURATERIP_URL
@@ -42,18 +40,16 @@ retrieves and display accuraterip data from the given URL
count = responses[0].num_tracks
- sys.stdout.write("Found %d responses for %d tracks\n\n" % (
- len(responses), count))
+ logger.info("found %d responses for %d tracks", len(responses), count)
for (i, r) in enumerate(responses):
if r.num_tracks != count:
- sys.stdout.write(
- "Warning: response %d has %d tracks instead of %d\n" % (
- i, r.num_tracks, count))
+ logger.warning("response %d has %d tracks instead of %d",
+ i, r.num_tracks, count)
# checksum and confidence by track
for track in range(count):
- sys.stdout.write("Track %d:\n" % (track + 1))
+ print("Track %d:" % (track + 1))
checksums = {}
for (i, r) in enumerate(responses):
@@ -82,9 +78,9 @@ retrieves and display accuraterip data from the given URL
sortedChecksums.reverse()
for highest, checksum in sortedChecksums:
- sys.stdout.write(" %d result(s) for checksum %s: %s\n" % (
- len(checksums[checksum]), checksum,
- str(checksums[checksum])))
+ print(" %d result(s) for checksum %s: %s" % (
+ len(checksums[checksum]),
+ checksum, checksums[checksum]))
class AccuRip(BaseCommand):
diff --git a/whipper/command/cd.py b/whipper/command/cd.py
index 502709f..e47b80e 100644
--- a/whipper/command/cd.py
+++ b/whipper/command/cd.py
@@ -22,12 +22,12 @@ import argparse
import cdio
import os
import glob
-import sys
import logging
from whipper.command.basecommand import BaseCommand
from whipper.common import (
accurip, config, drive, program, task
)
+from whipper.common.common import validate_template
from whipper.program import cdrdao, cdparanoia, utils
from whipper.result import result
@@ -83,29 +83,28 @@ class _CD(BaseCommand):
def do(self):
self.config = config.Config()
self.program = program.Program(self.config,
- record=self.options.record,
- stdout=sys.stdout)
+ record=self.options.record)
self.runner = task.SyncRunner()
# if the device is mounted (data session), unmount it
self.device = self.options.device
- sys.stdout.write('Checking device %s\n' % self.device)
+ logger.info('checking device %s', self.device)
utils.load_device(self.device)
utils.unmount_device(self.device)
# first, read the normal TOC, which is fast
- print("Reading TOC...")
+ logger.info("reading TOC...")
self.ittoc = self.program.getFastToc(self.runner, self.device)
# already show us some info based on this
self.program.getRipResult(self.ittoc.getCDDBDiscId())
- sys.stdout.write("CDDB disc id: %s\n" % self.ittoc.getCDDBDiscId())
+ print("CDDB disc id: %s" % self.ittoc.getCDDBDiscId())
self.mbdiscid = self.ittoc.getMusicBrainzDiscId()
- sys.stdout.write("MusicBrainz disc id %s\n" % self.mbdiscid)
+ print("MusicBrainz disc id %s" % self.mbdiscid)
- sys.stdout.write("MusicBrainz lookup URL %s\n" %
- self.ittoc.getMusicBrainzSubmitURL())
+ print("MusicBrainz lookup URL %s" %
+ self.ittoc.getMusicBrainzSubmitURL())
self.program.metadata = (
self.program.getMusicBrainz(self.ittoc, self.mbdiscid,
@@ -119,12 +118,12 @@ class _CD(BaseCommand):
cddbid = self.ittoc.getCDDBValues()
cddbmd = self.program.getCDDB(cddbid)
if cddbmd:
- sys.stdout.write('FreeDB identifies disc as %s\n' % cddbmd)
+ logger.info('FreeDB identifies disc as %s', cddbmd)
# also used by rip cd info
if not getattr(self.options, 'unknown', False):
logger.critical("unable to retrieve disc metadata, "
- "--unknown not passed")
+ "--unknown argument not passed")
return -1
self.program.result.isCdr = cdrdao.DetectCdr(self.device)
@@ -134,11 +133,21 @@ class _CD(BaseCommand):
"--cdr not passed")
return -1
+ # Change working directory before cdrdao's task
+ if self.options.working_directory is not None:
+ os.chdir(os.path.expanduser(self.options.working_directory))
+ out_bpath = self.options.output_directory.decode('utf-8')
+ # Needed to preserve cdrdao's tocfile
+ out_fpath = self.program.getPath(out_bpath,
+ self.options.disc_template,
+ self.mbdiscid,
+ self.program.metadata)
# now, read the complete index table, which is slower
self.itable = self.program.getTable(self.runner,
self.ittoc.getCDDBDiscId(),
self.ittoc.getMusicBrainzDiscId(),
- self.device, self.options.offset)
+ self.device, self.options.offset,
+ out_fpath)
assert self.itable.getCDDBDiscId() == self.ittoc.getCDDBDiscId(), \
"full table's id %s differs from toc id %s" % (
@@ -167,7 +176,7 @@ class _CD(BaseCommand):
self.program.result.cdparanoiaDefeatsCache = \
self.config.getDefeatsCache(*info)
except KeyError as e:
- logger.debug('Got key error: %r' % (e, ))
+ logger.debug('got key error: %r', (e, ))
self.program.result.artist = self.program.metadata \
and self.program.metadata.artist \
or 'Unknown Artist'
@@ -225,8 +234,7 @@ Log files will log the path to tracks relative to this directory.
if info:
try:
default_offset = config.Config().getReadOffset(*info)
- sys.stdout.write("Using configured read offset %d\n" %
- default_offset)
+ logger.info("using configured read offset %d", default_offset)
except KeyError:
pass
@@ -235,8 +243,8 @@ Log files will log the path to tracks relative to this directory.
self.parser.add_argument('-L', '--logger',
action="store", dest="logger",
default='whipper',
- help="logger to use (choose from '"
- "', '".join(loggers) + "')")
+ help=("logger to use (choose from: '%s" %
+ "', '".join(loggers) + "')"))
# FIXME: get from config
self.parser.add_argument('-o', '--offset',
action="store", dest="offset",
@@ -285,7 +293,9 @@ Log files will log the path to tracks relative to this directory.
self.options.track_template = self.options.track_template.decode(
'utf-8')
+ validate_template(self.options.track_template, 'track')
self.options.disc_template = self.options.disc_template.decode('utf-8')
+ validate_template(self.options.disc_template, 'disc')
if self.options.offset is None:
raise ValueError("Drive offset is unconfigured.\n"
@@ -324,26 +334,24 @@ Log files will log the path to tracks relative to this directory.
if logs:
msg = ("output directory %s is a finished rip" %
dirname.encode('utf-8'))
- logger.critical(msg)
+ logger.debug(msg)
raise RuntimeError(msg)
- else:
- sys.stdout.write("output directory %s already exists\n" %
- dirname.encode('utf-8'))
else:
- print("creating output directory %s" % dirname.encode('utf-8'))
+ logger.info("creating output directory %s",
+ dirname.encode('utf-8'))
os.makedirs(dirname)
# FIXME: turn this into a method
def _ripIfNotRipped(number):
- logger.debug('ripIfNotRipped for track %d' % number)
+ logger.debug('ripIfNotRipped for track %d', number)
# we can have a previous result
trackResult = self.program.result.getTrackResult(number)
if not trackResult:
trackResult = result.TrackResult()
self.program.result.tracks.append(trackResult)
else:
- logger.debug('ripIfNotRipped have trackresult, path %r' %
+ logger.debug('ripIfNotRipped have trackresult, path %r',
trackResult.filename)
path = self.program.getPath(self.program.outdir,
@@ -351,7 +359,7 @@ Log files will log the path to tracks relative to this directory.
self.mbdiscid,
self.program.metadata,
track_number=number) + '.flac'
- logger.debug('ripIfNotRipped: path %r' % path)
+ logger.debug('ripIfNotRipped: path %r', path)
trackResult.number = number
assert isinstance(path, unicode), "%r is not unicode" % path
@@ -368,18 +376,18 @@ Log files will log the path to tracks relative to this directory.
if path != trackResult.filename:
# the path is different (different name/template ?)
# but we can copy it
- logger.debug('previous result %r, expected %r' % (
- trackResult.filename, path))
+ logger.debug('previous result %r, expected %r',
+ trackResult.filename, path)
- sys.stdout.write('Verifying track %d of %d: %s\n' % (
- number, len(self.itable.tracks),
- os.path.basename(path).encode('utf-8')))
+ logger.info('verifying track %d of %d: %s',
+ number, len(self.itable.tracks),
+ os.path.basename(path).encode('utf-8'))
if not self.program.verifyTrack(self.runner, trackResult):
- sys.stdout.write('Verification failed, reripping...\n')
+ logger.warning('verification failed, reripping...')
os.unlink(path)
if not os.path.exists(path):
- logger.debug('path %r does not exist, ripping...' % path)
+ logger.debug('path %r does not exist, ripping...', path)
tries = 0
# we reset durations for test and copy here
trackResult.testduration = 0.0
@@ -389,9 +397,9 @@ Log files will log the path to tracks relative to this directory.
tries += 1
if tries > 1:
extra = " (try %d)" % tries
- sys.stdout.write('Ripping track %d of %d%s: %s\n' % (
- number, len(self.itable.tracks), extra,
- os.path.basename(path).encode('utf-8')))
+ logger.info('ripping track %d of %d%s: %s',
+ number, len(self.itable.tracks), extra,
+ os.path.basename(path).encode('utf-8'))
try:
logger.debug('ripIfNotRipped: track %d, try %d',
number, tries)
@@ -399,7 +407,7 @@ Log files will log the path to tracks relative to this directory.
offset=int(self.options.offset),
device=self.device,
taglist=self.program.getTagList(
- number),
+ number, self.mbdiscid),
overread=self.options.overread,
what='track %d of %d%s' % (
number,
@@ -407,43 +415,37 @@ Log files will log the path to tracks relative to this directory.
extra))
break
except Exception as e:
- logger.debug('Got exception %r on try %d',
- e, tries)
+ logger.debug('got exception %r on try %d', e, tries)
if tries == MAX_TRIES:
- logger.critical('Giving up on track %d after %d times' % (
- number, tries))
+ logger.critical('giving up on track %d after %d times',
+ number, tries)
raise RuntimeError(
"track can't be ripped. "
"Rip attempts number is equal to 'MAX_TRIES'")
if trackResult.testcrc == trackResult.copycrc:
- sys.stdout.write('CRCs match for track %d\n' % number)
+ logger.info('CRCs match for track %d', number)
else:
raise RuntimeError(
- "CRCs did not match for track %d\n" % number
+ "CRCs did not match for track %d" % number
)
- sys.stdout.write(
- 'Peak level: {}\n'.format(trackResult.peak))
-
- sys.stdout.write(
- 'Rip quality: {:.2%}\n'.format(trackResult.quality))
+ print('Peak level: %.6f' % (trackResult.peak / 32768.0))
+ print('Rip quality: {:.2%}'.format(trackResult.quality))
# overlay this rip onto the Table
if number == 0:
# HTOA goes on index 0 of track 1
# ignore silence in PREGAP
if trackResult.peak == SILENT:
- logger.debug(
- 'HTOA peak %r is equal to the SILENT '
- 'threshold, disregarding', trackResult.peak)
+ logger.debug('HTOA peak %r is equal to the SILENT '
+ 'threshold, disregarding', trackResult.peak)
self.itable.setFile(1, 0, None,
self.ittoc.getTrackStart(1), number)
- logger.debug('Unlinking %r', trackResult.filename)
+ logger.debug('unlinking %r', trackResult.filename)
os.unlink(trackResult.filename)
trackResult.filename = None
- sys.stdout.write(
- 'HTOA discarded, contains digital silence\n')
+ logger.info('HTOA discarded, contains digital silence')
else:
self.itable.setFile(1, 0, trackResult.filename,
self.ittoc.getTrackStart(1), number)
@@ -457,14 +459,15 @@ Log files will log the path to tracks relative to this directory.
htoa = self.program.getHTOA()
if htoa:
start, stop = htoa
- print('found Hidden Track One Audio from frame %d to %d' % (
- start, stop))
+ logger.info('found Hidden Track One Audio from frame %d to %d',
+ start, stop)
_ripIfNotRipped(0)
for i, track in enumerate(self.itable.tracks):
# FIXME: rip data tracks differently
if not track.audio:
- print('skipping data track %d, not implemented' % (i + 1))
+ logger.warning('skipping data track %d, not implemented',
+ i + 1)
# FIXME: make it work for now
track.indexes[1].relative = 0
continue
@@ -479,7 +482,7 @@ Log files will log the path to tracks relative to this directory.
try:
self.program.verifyImage(self.runner, self.ittoc)
except accurip.EntryNotFound:
- print('AccurateRip entry not found')
+ logger.warning('AccurateRip entry not found')
accurip.print_report(self.program.result)
diff --git a/whipper/command/drive.py b/whipper/command/drive.py
index 0046034..0860cec 100644
--- a/whipper/command/drive.py
+++ b/whipper/command/drive.py
@@ -18,8 +18,6 @@
# You should have received a copy of the GNU General Public License
# along with whipper. If not, see .
-import sys
-
from whipper.command.basecommand import BaseCommand
from whipper.common import config, drive
from whipper.extern.task import task
@@ -40,24 +38,21 @@ class Analyze(BaseCommand):
runner.run(t)
if t.defeatsCache is None:
- sys.stdout.write(
- 'Cannot analyze the drive. Is there a CD in it?\n')
+ logger.critical('cannot analyze the drive: is there a CD in it?')
return
if not t.defeatsCache:
- sys.stdout.write(
- 'cdparanoia cannot defeat the audio cache on this drive.\n')
+ logger.info('cdparanoia cannot defeat the audio cache '
+ 'on this drive')
else:
- sys.stdout.write(
- 'cdparanoia can defeat the audio cache on this drive.\n')
+ logger.info('cdparanoia can defeat the audio cache on this drive')
info = drive.getDeviceInfo(self.options.device)
if not info:
- sys.stdout.write('Drive caching behaviour not saved:'
- 'could not get device info (requires pycdio).\n')
+ logger.error('drive caching behaviour not saved: '
+ 'could not get device info')
return
- sys.stdout.write(
- 'Adding drive cache behaviour to configuration file.\n')
+ logger.info('adding drive cache behaviour to configuration file')
config.Config().setDefeatsCache(
info[0], info[1], info[2], t.defeatsCache)
@@ -72,48 +67,38 @@ class List(BaseCommand):
self.config = config.Config()
if not paths:
- sys.stdout.write('No drives found.\n')
- sys.stdout.write('Create /dev/cdrom if you have a CD drive, \n')
- sys.stdout.write('or install pycdio for better detection.\n')
-
+ logger.critical('no drives found. Create /dev/cdrom '
+ 'if you have a CD drive, or install '
+ 'pycdio for better detection')
return
try:
import cdio as _ # noqa: F401 (TODO: fix it in a separate PR?)
except ImportError:
- sys.stdout.write(
- 'Install pycdio for vendor/model/release detection.\n')
+ logger.error('install pycdio for vendor/model/release detection')
return
for path in paths:
vendor, model, release = drive.getDeviceInfo(path)
- sys.stdout.write(
- "drive: %s, vendor: %s, model: %s, release: %s\n" % (
- path, vendor, model, release))
+ print("drive: %s, vendor: %s, model: %s, release: %s" % (
+ path, vendor, model, release))
try:
offset = self.config.getReadOffset(
vendor, model, release)
- sys.stdout.write(
- " Configured read offset: %d\n" % offset)
+ print(" Configured read offset: %d" % offset)
except KeyError:
# Note spaces at the beginning for pretty terminal output
- sys.stdout.write(" "
- "No read offset found. "
- "Run 'whipper offset find'\n")
+ logger.warning("no read offset found. "
+ "Run 'whipper offset find'")
try:
defeats = self.config.getDefeatsCache(
vendor, model, release)
- sys.stdout.write(
- " Can defeat audio cache: %s\n" % defeats)
+ print(" Can defeat audio cache: %s" % defeats)
except KeyError:
- sys.stdout.write(
- " Unknown whether audio cache can be defeated. "
- "Run 'whipper drive analyze'\n")
-
- if not paths:
- sys.stdout.write('No drives found.\n')
+ logger.warning("unknown whether audio cache can be "
+ "defeated. Run 'whipper drive analyze'")
class Drive(BaseCommand):
diff --git a/whipper/command/main.py b/whipper/command/main.py
index 50b3f60..adaec7b 100644
--- a/whipper/command/main.py
+++ b/whipper/command/main.py
@@ -5,9 +5,9 @@ import os
import sys
import pkg_resources
import musicbrainzngs
-
+import site
import whipper
-
+from distutils.sysconfig import get_python_lib
from whipper.command import cd, offset, drive, image, accurip, mblookup
from whipper.command.basecommand import BaseCommand
from whipper.common import common, directory, config
@@ -19,23 +19,30 @@ logger = logging.getLogger(__name__)
def main():
- try:
- server = config.Config().get_musicbrainz_server()
- except KeyError as e:
- sys.stderr.write('whipper: %s\n' % str(e))
- sys.exit()
-
+ server = config.Config().get_musicbrainz_server()
musicbrainzngs.set_hostname(server)
+
+ # Find whipper's plugins paths (local paths have higher priority)
+ plugins_p = [directory.data_path('plugins')] # local path (in $HOME)
+ if hasattr(sys, 'real_prefix'): # no getsitepackages() in virtualenv
+ plugins_p.append(
+ get_python_lib(plat_specific=False, standard_lib=False,
+ prefix='/usr/local') + '/whipper/plugins')
+ plugins_p.append(get_python_lib(plat_specific=False,
+ standard_lib=False) + '/whipper/plugins')
+ else:
+ plugins_p += [x + '/whipper/plugins' for x in site.getsitepackages()]
+
# register plugins with pkg_resources
distributions, _ = pkg_resources.working_set.find_plugins(
- pkg_resources.Environment([directory.data_path('plugins')])
+ pkg_resources.Environment(plugins_p)
)
list(map(pkg_resources.working_set.add, distributions))
try:
cmd = Whipper(sys.argv[1:], os.path.basename(sys.argv[0]), None)
ret = cmd.do()
except SystemError as e:
- sys.stderr.write('whipper: error: %s\n' % e)
+ logger.critical("SystemError: %s", e)
if (isinstance(e, common.EjectError) and
cmd.options.eject in ('failure', 'always')):
eject_device(e.device)
@@ -51,18 +58,17 @@ def main():
if isinstance(e.exception, ImportError):
raise ImportError(e.exception)
elif isinstance(e.exception, common.MissingDependencyException):
- sys.stderr.write('whipper: error: missing dependency "%s"\n' %
- e.exception.dependency)
+ logger.critical('missing dependency "%s"', e.exception.dependency)
return 255
if isinstance(e.exception, common.EmptyError):
- logger.debug("EmptyError: %r", str(e.exception))
- sys.stderr.write('whipper: error: Could not create encoded file.\n') # noqa: E501
+ logger.debug("EmptyError: %s", e.exception)
+ logger.critical('could not create encoded file')
return 255
# in python3 we can instead do `raise e.exception` as that would show
# the exception's original context
- sys.stderr.write(e.exceptionMessage)
+ logger.critical(e.exceptionMessage)
return 255
return ret if ret else 0
diff --git a/whipper/command/offset.py b/whipper/command/offset.py
index d7f7e24..9fe620c 100644
--- a/whipper/command/offset.py
+++ b/whipper/command/offset.py
@@ -20,7 +20,6 @@
import argparse
import os
-import sys
import tempfile
import logging
from whipper.command.basecommand import BaseCommand
@@ -71,7 +70,7 @@ CD in the AccurateRip database."""
else:
self._offsets.append(int(b))
- logger.debug('Trying with offsets %r', self._offsets)
+ logger.debug('trying with offsets %r', self._offsets)
def do(self):
runner = ctask.SyncRunner()
@@ -79,7 +78,7 @@ CD in the AccurateRip database."""
device = self.options.device
# if necessary, load and unmount
- sys.stdout.write('Checking device %s\n' % device)
+ logger.info('checking device %s', device)
utils.load_device(device)
utils.unmount_device(device)
@@ -93,10 +92,12 @@ CD in the AccurateRip database."""
try:
responses = accurip.get_db_entry(table.accuraterip_path())
except accurip.EntryNotFound:
- print('Accuraterip entry not found')
+ logger.warning("AccurateRip entry not found: drive offset "
+ "can't be determined, try again with another disc")
+ return
if responses:
- logger.debug('%d AccurateRip responses found.' % len(responses))
+ logger.debug('%d AccurateRip responses found.', len(responses))
if responses[0].cddbDiscId != table.getCDDBDiscId():
logger.warning("AccurateRip response discid different: %s",
responses[0].cddbDiscId)
@@ -114,36 +115,32 @@ CD in the AccurateRip database."""
return None, None
for offset in self._offsets:
- sys.stdout.write('Trying read offset %d ...\n' % offset)
+ logger.info('trying read offset %d...', offset)
try:
archecksums = self._arcs(runner, table, 1, offset)
except task.TaskException as e:
# let MissingDependency fall through
- if isinstance(e.exception,
- common.MissingDependencyException):
+ if isinstance(e.exception, common.MissingDependencyException):
raise e
if isinstance(e.exception, cdparanoia.FileSizeError):
- sys.stdout.write(
- 'WARNING: cannot rip with offset %d...\n' % offset)
+ logger.warning('cannot rip with offset %d...', offset)
continue
- logger.warning("Unknown task exception for offset %d: %r" % (
- offset, e))
- sys.stdout.write(
- 'WARNING: cannot rip with offset %d...\n' % offset)
+ logger.warning("unknown task exception for offset %d: %s",
+ offset, e)
+ logger.warning('cannot rip with offset %d...', offset)
continue
- logger.debug('AR checksums calculated: %s %s' % archecksums)
+ logger.debug('AR checksums calculated: %s %s', archecksums)
c, i = match(archecksums, 1, responses)
if c:
count = 1
- logger.debug('MATCHED against response %d' % i)
- sys.stdout.write(
- 'Offset of device is likely %d, confirming ...\n' %
- offset)
+ logger.debug('matched against response %d', i)
+ logger.info('offset of device is likely %d, confirming...',
+ offset)
# now try and rip all other tracks as well, except for the
# last one (to avoid readers that can't do overread
@@ -152,31 +149,30 @@ CD in the AccurateRip database."""
archecksums = self._arcs(runner, table, track, offset)
except task.TaskException as e:
if isinstance(e.exception, cdparanoia.FileSizeError):
- sys.stdout.write(
- 'WARNING: cannot rip with offset %d...\n' %
- offset)
+ logger.warning('cannot rip with offset %d...',
+ offset)
continue
c, i = match(archecksums, track, responses)
if c:
- logger.debug('MATCHED track %d against response %d' % (
- track, i))
+ logger.debug('matched track %d against response %d',
+ track, i)
count += 1
if count == len(table.tracks) - 1:
self._foundOffset(device, offset)
return 0
else:
- sys.stdout.write(
- 'Only %d of %d tracks matched, continuing ...\n' % (
- count, len(table.tracks)))
+ logger.warning('only %d of %d tracks matched, '
+ 'continuing...', count,
+ len(table.tracks))
- sys.stdout.write('No matching offset found.\n')
- sys.stdout.write('Consider trying again with a different disc.\n')
+ logger.error('no matching offset found. '
+ 'Consider trying again with a different disc')
def _arcs(self, runner, table, track, offset):
# rips the track with the given offset, return the arcs checksums
- logger.debug('Ripping track %r with offset %d ...', track, offset)
+ logger.debug('ripping track %r with offset %d...', track, offset)
fd, path = tempfile.mkstemp(
suffix=u'.track%02d.offset%d.whipper.wav' % (
@@ -203,17 +199,15 @@ CD in the AccurateRip database."""
return ("%08x" % v1, "%08x" % v2)
def _foundOffset(self, device, offset):
- sys.stdout.write('\nRead offset of device is: %d.\n' %
- offset)
+ print('\nRead offset of device is: %d.' % offset)
info = drive.getDeviceInfo(device)
if not info:
- sys.stdout.write(
- 'Offset not saved: could not get '
- 'device info (requires pycdio).\n')
+ logger.error('offset not saved: '
+ 'could not get device info (requires pycdio)')
return
- sys.stdout.write('Adding read offset to configuration file.\n')
+ logger.info('adding read offset to configuration file')
config.Config().setReadOffset(info[0], info[1], info[2],
offset)
diff --git a/whipper/common/accurip.py b/whipper/common/accurip.py
index 3b64c85..9085bd5 100644
--- a/whipper/common/accurip.py
+++ b/whipper/common/accurip.py
@@ -107,17 +107,15 @@ def calculate_checksums(track_paths):
track_count = len(track_paths)
v1_checksums = []
v2_checksums = []
- logger.debug('checksumming %d tracks' % track_count)
+ logger.debug('checksumming %d tracks', track_count)
# This is done sequentially because it is very fast.
for i, path in enumerate(track_paths):
v1_sum = accuraterip_checksum(
path, i+1, track_count, wave=True, v2=False
)
if not v1_sum:
- logger.error(
- 'could not calculate AccurateRip v1 checksum for track %d %r' %
- (i+1, path)
- )
+ logger.error('could not calculate AccurateRip v1 checksum '
+ 'for track %d %r', i + 1, path)
v1_checksums.append(None)
else:
v1_checksums.append("%08x" % v1_sum)
@@ -125,10 +123,8 @@ def calculate_checksums(track_paths):
path, i+1, track_count, wave=True, v2=True
)
if not v2_sum:
- logger.error(
- 'could not calculate AccurateRip v2 checksum for track %d %r' %
- (i+1, path)
- )
+ logger.error('could not calculate AccurateRip v2 checksum '
+ 'for track %d %r', i + 1, path)
v2_checksums.append(None)
else:
v2_checksums.append("%08x" % v2_sum)
@@ -141,12 +137,11 @@ def _download_entry(path):
try:
resp = requests.get(url)
except requests.exceptions.ConnectionError as e:
- logger.error('error retrieving AccurateRip entry: %r' % e)
+ logger.error('error retrieving AccurateRip entry: %r', e)
return None
if not resp.ok:
- logger.error('error retrieving AccurateRip entry: %s %s %r' % (
- resp.status_code, resp.reason, resp
- ))
+ logger.error('error retrieving AccurateRip entry: %s %s %r',
+ resp.status_code, resp.reason, resp)
return None
return resp.content
@@ -158,7 +153,7 @@ def _save_entry(raw_entry, path):
makedirs(dirname(path))
except OSError as e:
if e.errno != EEXIST:
- logger.error('could not save entry to %s: %r' % (path, str(e)))
+ logger.error('could not save entry to %s: %s', path, e)
return
open(path, 'wb').write(raw_entry)
@@ -211,10 +206,9 @@ def _match_responses(tracks, responses):
track.AR[v]['DBConfidence'] = r.confidences[i]
logger.debug(
'track %d matched response %s in AccurateRip'
- ' database: %s crc %s confidence %s' %
- (i, r.cddbDiscId, v, track.AR[v]['DBCRC'],
- track.AR[v]['DBConfidence'])
- )
+ ' database: %s crc %s confidence %s',
+ i, r.cddbDiscId, v, track.AR[v]['DBCRC'],
+ track.AR[v]['DBConfidence'])
return any((
all([t.AR['v1']['DBCRC'] for t in tracks]),
all([t.AR['v2']['DBCRC'] for t in tracks])
@@ -240,7 +234,7 @@ def verify_result(result, responses, checksums):
def print_report(result):
"""
- Print AccurateRip verification results to stdout.
+ Print AccurateRip verification results.
"""
for i, track in enumerate(result.tracks):
status = 'rip NOT accurate'
@@ -268,9 +262,7 @@ def print_report(result):
print('track 0: unknown (not tracked)')
continue
if not (track.AR['v1']['CRC'] or track.AR['v2']['CRC']):
- logger.error(
- 'no track AR CRC on non-HTOA track %d' % track.number
- )
+ logger.error('no track AR CRC on non-HTOA track %d', track.number)
print('track %2d: unknown (error)' % track.number)
else:
print('track %2d: %-16s %-23s v1 [%s], v2 [%s], DB [%s]' % (
diff --git a/whipper/common/cache.py b/whipper/common/cache.py
index d57cb04..5305ed3 100644
--- a/whipper/common/cache.py
+++ b/whipper/common/cache.py
@@ -87,7 +87,7 @@ class Persister:
handle.close()
# do an atomic move
shutil.move(path, self._path)
- logger.debug('saved persisted object to %r' % self._path)
+ logger.debug('saved persisted object to %r', self._path)
def _unpickle(self, default=None):
self.object = default
@@ -103,7 +103,7 @@ class Persister:
try:
self.object = pickle.load(handle)
- logger.debug('loaded persisted object from %r' % self._path)
+ logger.debug('loaded persisted object from %r', self._path)
except Exception as e:
# TODO: restrict kind of caught exceptions?
# can fail for various reasons; in that case, pretend we didn't
@@ -143,9 +143,8 @@ class PersistedCache:
if hasattr(persister.object, 'instanceVersion'):
o = persister.object
if o.instanceVersion < o.__class__.classVersion:
- logger.debug(
- 'key %r persisted object version %d is outdated',
- key, o.instanceVersion)
+ logger.debug('key %r persisted object version %d '
+ 'is outdated', key, o.instanceVersion)
persister.object = None
# FIXME: don't delete old objects atm
# persister.delete()
@@ -216,12 +215,11 @@ class TableCache:
ptable = self._pcache.get(cddbdiscid)
if ptable.object:
if ptable.object.getMusicBrainzDiscId() != mbdiscid:
- logger.debug('cached table is for different mb id %r' % (
- ptable.object.getMusicBrainzDiscId()))
+ logger.debug('cached table is for different mb id %r',
+ ptable.object.getMusicBrainzDiscId())
ptable.object = None
else:
- logger.debug('no valid cached table found for %r' %
- cddbdiscid)
+ logger.debug('no valid cached table found for %r', cddbdiscid)
if not ptable.object:
# get an empty persistable from the writable location
diff --git a/whipper/common/common.py b/whipper/common/common.py
index 9bd20de..1b46d4e 100644
--- a/whipper/common/common.py
+++ b/whipper/common/common.py
@@ -22,6 +22,7 @@
import os
import os.path
import math
+import re
import subprocess
import unicodedata
@@ -262,8 +263,8 @@ def getRelativePath(targetPath, collectionPath):
Used to determine the path to use in .cue/.m3u files
"""
- logger.debug('getRelativePath: target %r, collection %r' % (
- targetPath, collectionPath))
+ logger.debug('getRelativePath: target %r, collection %r',
+ targetPath, collectionPath)
targetDir = os.path.dirname(targetPath)
collectionDir = os.path.dirname(collectionPath)
@@ -274,12 +275,24 @@ def getRelativePath(targetPath, collectionPath):
rel = os.path.relpath(
targetDir + os.path.sep,
collectionDir + os.path.sep)
- logger.debug(
- 'getRelativePath: target and collection in different dir, %r' % rel
- )
+ logger.debug('getRelativePath: target and collection '
+ 'in different dir, %r', rel)
return os.path.join(rel, os.path.basename(targetPath))
+def validate_template(template, kind):
+ """
+ Raise exception if disc/track template includes invalid variables
+ """
+ if kind == 'disc':
+ matches = re.findall(r'%[^A,R,S,X,d,r,x,y]', template)
+ elif kind == 'track':
+ matches = re.findall(r'%[^A,R,S,X,a,d,n,r,s,t,x,y]', template)
+ if '%' in template and matches:
+ raise ValueError(kind + ' template string contains invalid '
+ 'variable(s): {}'.format(', '.join(matches)))
+
+
class VersionGetter(object):
"""
I get the version of a program by looking for it in command output
diff --git a/whipper/common/config.py b/whipper/common/config.py
index 2b7f7cb..8d10935 100644
--- a/whipper/common/config.py
+++ b/whipper/common/config.py
@@ -47,8 +47,8 @@ class Config:
with codecs.open(self._path, 'r', encoding='utf-8') as f:
self._parser.readfp(f)
- logger.info('Loaded %d sections from config file' %
- len(self._parser.sections()))
+ logger.debug('loaded %d sections from config file',
+ len(self._parser.sections()))
def write(self):
fd, path = tempfile.mkstemp(suffix=u'.whipperrc')
@@ -130,14 +130,13 @@ class Config:
if not name.startswith('drive:'):
continue
- logger.debug('Looking at section %r' % name)
+ logger.debug('looking at section %r', name)
conf = {}
for key in ['vendor', 'model', 'release']:
locals()[key] = locals()[key].strip()
conf[key] = self._parser.get(name, key)
- logger.debug("%s: '%s' versus '%s'" % (
- key, locals()[key], conf[key]
- ))
+ logger.debug("%s: '%s' versus '%s'",
+ key, locals()[key], conf[key])
if vendor.strip() != conf['vendor']:
continue
if model.strip() != conf['model']:
diff --git a/whipper/common/drive.py b/whipper/common/drive.py
index ef5281c..37950fd 100644
--- a/whipper/common/drive.py
+++ b/whipper/common/drive.py
@@ -36,7 +36,7 @@ def getAllDevicePaths():
# see https://savannah.gnu.org/bugs/index.php?38477
return [str(dev) for dev in _getAllDevicePathsPyCdio()]
except ImportError:
- logger.info('Cannot import pycdio')
+ logger.info('cannot import pycdio')
return _getAllDevicePathsStatic()
diff --git a/whipper/common/mbngs.py b/whipper/common/mbngs.py
index d0d18e0..aaab4c1 100644
--- a/whipper/common/mbngs.py
+++ b/whipper/common/mbngs.py
@@ -93,7 +93,7 @@ def _record(record, which, name, what):
handle = open(filename, 'w')
handle.write(json.dumps(what))
handle.close()
- logger.info('Wrote %s %s to %s', which, name, filename)
+ logger.info('wrote %s %s to %s', which, name, filename)
# credit is of the form [dict, str, dict, ... ]
# e.g. [
@@ -152,10 +152,9 @@ def _getMetadata(releaseShort, release, discid, country=None):
@rtype: L{DiscMetadata} or None
"""
- logger.debug('getMetadata for release id %r',
- release['id'])
+ logger.debug('getMetadata for release id %r', release['id'])
if not release['id']:
- logger.warning('No id for release %r', release)
+ logger.warning('no id for release %r', release)
return None
assert release['id'], 'Release does not have an id'
@@ -183,7 +182,7 @@ def _getMetadata(releaseShort, release, discid, country=None):
discMD.artist = albumArtistName
discMD.sortName = discCredit.getSortName()
if 'date' not in release:
- logger.warning("Release with ID '%s' (%s - %s) does not have a date",
+ logger.warning("release with ID '%s' (%s - %s) does not have a date",
release['id'], discMD.artist, release['title'])
else:
discMD.release = release['date']
@@ -235,9 +234,8 @@ def _getMetadata(releaseShort, release, discid, country=None):
# FIXME: unit of duration ?
track.duration = int(t['recording'].get('length', 0))
if not track.duration:
- logger.warning(
- 'track %r (%r) does not have duration' %
- (track.title, track.mbid))
+ logger.warning('track %r (%r) does not have duration',
+ track.title, track.mbid)
tainted = True
else:
duration += track.duration
@@ -271,7 +269,7 @@ def musicbrainz(discid, country=None, record=False):
import musicbrainzngs
musicbrainzngs.set_useragent("whipper", whipper.__version__,
- "https://github.com/JoeLametta/whipper")
+ "https://github.com/whipper-team/whipper")
ret = []
try:
@@ -297,8 +295,8 @@ def musicbrainz(discid, country=None, record=False):
import json
for release in result['disc']['release-list']:
formatted = json.dumps(release, sort_keys=False, indent=4)
- logger.debug('result %s: artist %r, title %r' % (
- formatted, release['artist-credit-phrase'], release['title']))
+ logger.debug('result %s: artist %r, title %r', formatted,
+ release['artist-credit-phrase'], release['title'])
# to get titles of recordings, we need to query the release with
# artist-credits
@@ -309,7 +307,7 @@ def musicbrainz(discid, country=None, record=False):
_record(record, 'release', release['id'], res)
releaseDetail = res['release']
formatted = json.dumps(releaseDetail, sort_keys=False, indent=4)
- logger.debug('release %s' % formatted)
+ logger.debug('release %s', formatted)
md = _getMetadata(release, releaseDetail, discid, country)
if md:
diff --git a/whipper/common/program.py b/whipper/common/program.py
index 78790b4..08d892a 100644
--- a/whipper/common/program.py
+++ b/whipper/common/program.py
@@ -25,7 +25,6 @@ Common functionality and class for all programs using whipper.
import musicbrainzngs
import re
import os
-import sys
import time
from whipper.common import accurip, cache, checksum, common, mbngs, path
@@ -59,15 +58,12 @@ class Program:
outdir = None
result = None
- _stdout = None
-
- def __init__(self, config, record=False, stdout=sys.stdout):
+ def __init__(self, config, record=False):
"""
@param record: whether to record results of API calls for playback.
"""
self._record = record
self._cache = cache.ResultCache()
- self._stdout = stdout
self._config = config
d = {}
@@ -87,7 +83,7 @@ class Program:
def setWorkingDirectory(self, workingDirectory):
if workingDirectory:
- logger.info('Changing to working directory %s' % workingDirectory)
+ logger.info('changing to working directory %s', workingDirectory)
os.chdir(workingDirectory)
def getFastToc(self, runner, device):
@@ -97,14 +93,14 @@ class Program:
from pkg_resources import parse_version as V
version = cdrdao.getCDRDAOVersion()
if V(version) < V('1.2.3rc2'):
- sys.stdout.write('Warning: cdrdao older than 1.2.3 has a '
- 'pre-gap length bug.\n'
- 'See http://sourceforge.net/tracker/?func=detail&aid=604751&group_id=2171&atid=102171\n') # noqa: E501
+ logger.warning('cdrdao older than 1.2.3 has a pre-gap length bug.'
+ ' See http://sourceforge.net/tracker/?func=detail&aid=604751&group_id=2171&atid=102171') # noqa: E501
toc = cdrdao.ReadTOCTask(device).table
assert toc.hasTOC()
return toc
- def getTable(self, runner, cddbdiscid, mbdiscid, device, offset):
+ def getTable(self, runner, cddbdiscid, mbdiscid, device, offset,
+ out_path):
"""
Retrieve the Table either from the cache or the drive.
@@ -123,24 +119,24 @@ class Program:
itable = tdict[offset]
if not itable:
- logger.debug('getTable: cddbdiscid %s, mbdiscid %s not '
- 'in cache for offset %s, reading table' % (
- cddbdiscid, mbdiscid, offset))
- t = cdrdao.ReadTableTask(device)
+ logger.debug('getTable: cddbdiscid %s, mbdiscid %s not in cache '
+ 'for offset %s, reading table', cddbdiscid, mbdiscid,
+ offset)
+ t = cdrdao.ReadTableTask(device, out_path)
itable = t.table
tdict[offset] = itable
ptable.persist(tdict)
- logger.debug('getTable: read table %r' % itable)
+ logger.debug('getTable: read table %r', itable)
else:
logger.debug('getTable: cddbdiscid %s, mbdiscid %s in cache '
- 'for offset %s' % (cddbdiscid, mbdiscid, offset))
- logger.debug('getTable: loaded table %r' % itable)
+ 'for offset %s', cddbdiscid, mbdiscid, offset)
+ logger.debug('getTable: loaded table %r', itable)
assert itable.hasTOC()
self.result.table = itable
- logger.debug('getTable: returning table with mb id %s' %
+ logger.debug('getTable: returning table with mb id %s',
itable.getMusicBrainzDiscId())
return itable
@@ -252,12 +248,12 @@ class Program:
return [item['DTITLE'] for item in md if 'DTITLE' in item] or None
except ValueError as e:
- self._stdout.write("WARNING: CDDB protocol error: %s\n" % e)
+ logger.warning("CDDB protocol error: %s", e)
except IOError as e:
# FIXME: for some reason errno is a str ?
if e.errno == 'socket error':
- self._stdout.write("WARNING: CDDB network error: %r\n" % (e, ))
+ logger.warning("CDDB network error: %r", (e, ))
else:
raise
@@ -269,7 +265,7 @@ class Program:
@type ittoc: L{whipper.image.table.Table}
"""
# look up disc on MusicBrainz
- self._stdout.write('Disc duration: %s, %d audio tracks\n' % (
+ print('Disc duration: %s, %d audio tracks' % (
common.formatTime(ittoc.duration() / 1000.0),
ittoc.getAudioTracks()))
logger.debug('MusicBrainz submit url: %r',
@@ -286,41 +282,37 @@ class Program:
record=self._record)
break
except mbngs.NotFoundException as e:
- logger.warning("release not found: %r" % (e, ))
+ logger.warning("release not found: %r", (e, ))
break
except musicbrainzngs.NetworkError as e:
- logger.warning("network error: %r" % (e, ))
+ logger.warning("network error: %r", (e, ))
break
except mbngs.MusicBrainzException as e:
- logger.warning("musicbrainz exception: %r" % (e, ))
+ logger.warning("musicbrainz exception: %r", (e, ))
time.sleep(5)
continue
if not metadatas:
- self._stdout.write('Continuing without metadata\n')
+ logger.warning('continuing without metadata')
if metadatas:
deltas = {}
- self._stdout.write('\nMatching releases:\n')
+ print('\nMatching releases:')
for metadata in metadatas:
- self._stdout.write('\n')
- self._stdout.write('Artist : %s\n' %
- metadata.artist.encode('utf-8'))
- self._stdout.write('Title : %s\n' %
- metadata.title.encode('utf-8'))
- self._stdout.write('Duration: %s\n' %
- common.formatTime(metadata.duration /
- 1000.0))
- self._stdout.write('URL : %s\n' % metadata.url)
- self._stdout.write('Release : %s\n' % metadata.mbid)
- self._stdout.write('Type : %s\n' % metadata.releaseType)
+ print('\nArtist : %s' % metadata.artist.encode('utf-8'))
+ print('Title : %s' % metadata.title.encode('utf-8'))
+ print('Duration: %s' % common.formatTime(
+ metadata.duration / 1000.0))
+ print('URL : %s' % metadata.url)
+ print('Release : %s' % metadata.mbid)
+ print('Type : %s' % metadata.releaseType)
if metadata.barcode:
- self._stdout.write("Barcode : %s\n" % metadata.barcode)
+ print("Barcode : %s" % metadata.barcode)
if metadata.catalogNumber:
- self._stdout.write("Cat no : %s\n" %
- metadata.catalogNumber)
+ print("Cat no : %s" %
+ metadata.catalogNumber.encode('utf-8'))
delta = abs(metadata.duration - ittoc.duration())
if delta not in deltas:
@@ -343,20 +335,15 @@ class Program:
if release:
metadatas = [m for m in metadatas if m.url.endswith(release)]
- logger.debug('Asked for release %r, only kept %r',
- release, metadatas)
+ logger.debug('asked for release %r, only kept %r', release,
+ metadatas)
if len(metadatas) == 1:
- self._stdout.write('\n')
- self._stdout.write('Picked requested release id %s\n' %
- release)
- self._stdout.write('Artist : %s\n' %
- metadatas[0].artist.encode('utf-8'))
- self._stdout.write('Title : %s\n' %
- metadatas[0].title.encode('utf-8'))
+ logger.info('picked requested release id %s', release)
+ print('Artist: %s' % metadatas[0].artist.encode('utf-8'))
+ print('Title : %s' % metadatas[0].title.encode('utf-8'))
elif not metadatas:
- self._stdout.write(
- "Requested release id '%s', "
- "but none of the found releases match\n" % release)
+ logger.warning("requested release id '%s', but none of "
+ "the found releases match", release)
return
else:
if lowest:
@@ -368,36 +355,31 @@ class Program:
releaseTitle = metadatas[0].releaseTitle
for i, metadata in enumerate(metadatas):
if not artist == metadata.artist:
- logger.warning("artist 0: %r and artist %d: %r "
- "are not the same" % (
- artist, i, metadata.artist))
+ logger.warning("artist 0: %r and artist %d: %r are "
+ "not the same", artist, i,
+ metadata.artist)
if not releaseTitle == metadata.releaseTitle:
- logger.warning("title 0: %r and title %d: %r "
- "are not the same" % (
- releaseTitle, i,
- metadata.releaseTitle))
+ logger.warning("title 0: %r and title %d: %r are "
+ "not the same", releaseTitle, i,
+ metadata.releaseTitle)
if (not release and len(list(deltas)) > 1):
- self._stdout.write('\n')
- self._stdout.write('Picked closest match in duration.\n')
- self._stdout.write('Others may be wrong in MusicBrainz, '
- 'please correct.\n')
- self._stdout.write('Artist : %s\n' %
- artist.encode('utf-8'))
- self._stdout.write('Title : %s\n' %
- metadatas[0].title.encode('utf-8'))
+ logger.warning('picked closest match in duration. '
+ 'Others may be wrong in MusicBrainz, '
+ 'please correct')
+ print('Artist : %s' % artist.encode('utf-8'))
+ print('Title : %s' % metadatas[0].title.encode('utf-8'))
# Select one of the returned releases. We just pick the first one.
ret = metadatas[0]
else:
- self._stdout.write(
- 'Submit this disc to MusicBrainz at the above URL.\n')
+ print('Submit this disc to MusicBrainz at the above URL.')
ret = None
- self._stdout.write('\n')
+ print('')
return ret
- def getTagList(self, number):
+ def getTagList(self, number, mbdiscid):
"""
Based on the metadata, get a dict of tags for the given track.
@@ -417,7 +399,6 @@ class Program:
disc = self.metadata.title
mbidAlbum = self.metadata.mbid
mbidTrackAlbum = self.metadata.mbidArtist
- mbDiscId = self.metadata.discid
if number > 0:
try:
@@ -427,7 +408,7 @@ class Program:
mbidTrack = track.mbid
mbidTrackArtist = track.mbidArtist
except IndexError as e:
- print('ERROR: no track %d found, %r' % (number, e))
+ logger.error('no track %d found, %r', number, e)
raise
else:
# htoa defaults to disc's artist
@@ -435,6 +416,9 @@ class Program:
tags = {}
+ if number > 0:
+ tags['MUSICBRAINZ_DISCID'] = mbdiscid
+
if self.metadata and not self.metadata.various:
tags['ALBUMARTIST'] = albumArtist
tags['ARTIST'] = trackArtist
@@ -452,7 +436,6 @@ class Program:
tags['MUSICBRAINZ_ARTISTID'] = mbidTrackArtist
tags['MUSICBRAINZ_ALBUMID'] = mbidAlbum
tags['MUSICBRAINZ_ALBUMARTISTID'] = mbidTrackAlbum
- tags['MUSICBRAINZ_DISCID'] = mbDiscId
# TODO/FIXME: ISRC tag
@@ -482,15 +465,14 @@ class Program:
runner.run(t)
except task.TaskException as e:
if isinstance(e.exception, common.MissingFrames):
- logger.warning('missing frames for %r' % trackResult.filename)
+ logger.warning('missing frames for %r', trackResult.filename)
return False
else:
raise
ret = trackResult.testcrc == t.checksum
- logger.debug('verifyTrack: track result crc %r, '
- 'file crc %r, result %r',
- trackResult.testcrc, t.checksum, ret)
+ logger.debug('verifyTrack: track result crc %r, file crc %r, '
+ 'result %r', trackResult.testcrc, t.checksum, ret)
return ret
def ripTrack(self, runner, trackResult, offset, device, taglist,
@@ -526,10 +508,10 @@ class Program:
runner.run(t)
logger.debug('ripped track')
- logger.debug('test speed %.3f/%.3f seconds' % (
- t.testspeed, t.testduration))
- logger.debug('copy speed %.3f/%.3f seconds' % (
- t.copyspeed, t.copyduration))
+ logger.debug('test speed %.3f/%.3f seconds',
+ t.testspeed, t.testduration)
+ logger.debug('copy speed %.3f/%.3f seconds',
+ t.copyspeed, t.copyduration)
trackResult.testcrc = t.testchecksum
trackResult.copycrc = t.copychecksum
trackResult.peak = t.peak
@@ -542,7 +524,7 @@ class Program:
if trackResult.filename != t.path:
trackResult.filename = t.path
- logger.info('Filename changed to %r', trackResult.filename)
+ logger.info('filename changed to %r', trackResult.filename)
def verifyImage(self, runner, table):
"""
@@ -563,7 +545,7 @@ class Program:
return False
responses = accurip.get_db_entry(table.accuraterip_path())
- logger.info('%d AccurateRip response(s) found' % len(responses))
+ logger.info('%d AccurateRip response(s) found', len(responses))
checksums = accurip.calculate_checksums([
os.path.join(os.path.dirname(self.cuePath), t.indexes[1].path)
diff --git a/whipper/common/task.py b/whipper/common/task.py
index f9c39cc..5e61933 100644
--- a/whipper/common/task.py
+++ b/whipper/common/task.py
@@ -51,8 +51,7 @@ class PopenTask(task.Task):
raise
- logger.debug('Started %r with pid %d', self.command,
- self._popen.pid)
+ logger.debug('started %r with pid %d', self.command, self._popen.pid)
self.schedule(1.0, self._read, runner)
@@ -89,7 +88,7 @@ class PopenTask(task.Task):
self._done()
except Exception as e:
- logger.debug('exception during _read(): %r', str(e))
+ logger.debug('exception during _read(): %s', e)
self.setException(e)
self.stop()
@@ -97,10 +96,9 @@ class PopenTask(task.Task):
assert self._popen.returncode is not None, "No returncode"
if self._popen.returncode >= 0:
- logger.debug('Return code was %d', self._popen.returncode)
+ logger.debug('return code was %d', self._popen.returncode)
else:
- logger.debug('Terminated with signal %d',
- -self._popen.returncode)
+ logger.debug('terminated with signal %d', -self._popen.returncode)
self.setProgress(1.0)
@@ -113,7 +111,7 @@ class PopenTask(task.Task):
return
def abort(self):
- logger.debug('Aborting, sending SIGTERM to %d', self._popen.pid)
+ logger.debug('aborting, sending SIGTERM to %d', self._popen.pid)
os.kill(self._popen.pid, signal.SIGTERM)
# self.stop()
diff --git a/whipper/extern/task/task.py b/whipper/extern/task/task.py
index 3c2b4a6..250ccd6 100644
--- a/whipper/extern/task/task.py
+++ b/whipper/extern/task/task.py
@@ -18,6 +18,7 @@
# You should have received a copy of the GNU General Public License
# along with whipper. If not, see .
+from __future__ import print_function
import logging
import sys
@@ -166,7 +167,8 @@ class Task(LogStub):
value >= 1.0 or value == 0.0):
self.progress = value
self._notifyListeners('progressed', value)
- self.log('notifying progress: %r on %r', value, self.description)
+ self.debug('notifying progress: %r on %r',
+ value, self.description)
def setDescription(self, description):
if description != self.description:
@@ -366,23 +368,23 @@ class BaseMultiTask(Task, ITaskListener):
Subclasses should chain up to me at the end of their implementation.
They should fall through to chaining up if there is an exception.
"""
- self.log('BaseMultiTask.stopped: task %r (%d of %d)',
- task, self.tasks.index(task) + 1, len(self.tasks))
+ self.debug('BaseMultiTask.stopped: task %r (%d of %d)',
+ task, self.tasks.index(task) + 1, len(self.tasks))
if task.exception:
- self.log('BaseMultiTask.stopped: exception %r',
- task.exceptionMessage)
+ self.warning('BaseMultiTask.stopped: exception %r',
+ task.exceptionMessage)
self.exception = task.exception
self.exceptionMessage = task.exceptionMessage
self.stop()
return
if self._task == len(self.tasks):
- self.log('BaseMultiTask.stopped: all tasks done')
+ self.debug('BaseMultiTask.stopped: all tasks done')
self.stop()
return
# pick another
- self.log('BaseMultiTask.stopped: pick next task')
+ self.debug('BaseMultiTask.stopped: pick next task')
self.schedule(0, self.next)
@@ -511,8 +513,8 @@ class SyncRunner(TaskRunner, ITaskListener):
def schedule(self, task, delta, callable, *args, **kwargs):
def c():
try:
- self.log('schedule: calling %r(*args=%r, **kwargs=%r)',
- callable, args, kwargs)
+ self.debug('schedule: calling %r(*args=%r, **kwargs=%r)',
+ callable, args, kwargs)
callable(*args, **kwargs)
return False
except Exception as e:
@@ -521,8 +523,8 @@ class SyncRunner(TaskRunner, ITaskListener):
task.setException(e)
self.stopped(task)
raise
- self.log('schedule: scheduling %r(*args=%r, **kwargs=%r)',
- callable, args, kwargs)
+ self.debug('schedule: scheduling %r(*args=%r, **kwargs=%r)',
+ callable, args, kwargs)
gobject.timeout_add(int(delta * 1000L), c)
@@ -539,15 +541,15 @@ class SyncRunner(TaskRunner, ITaskListener):
self._task.description, 100.0))
else:
# clear with whitespace
- sys.stdout.write("%s\r" % (' ' * self._longest, ))
+ print(("%s\r" % (' ' * self._longest, )), end='')
def _output(self, what, newline=False, ret=True):
- sys.stdout.write(what)
- sys.stdout.write(' ' * (self._longest - len(what)))
+ print(what, end='')
+ print((' ' * (self._longest - len(what))), end='')
if ret:
- sys.stdout.write('\r')
+ print('\r', end='')
if newline:
- sys.stdout.write('\n')
+ print('')
sys.stdout.flush()
if len(what) > self._longest:
self._longest = len(what)
diff --git a/whipper/image/cue.py b/whipper/image/cue.py
index 2edee6f..4088c14 100644
--- a/whipper/image/cue.py
+++ b/whipper/image/cue.py
@@ -85,7 +85,7 @@ class CueFile(object):
currentTrack = None
counter = 0
- logger.info('Parsing .cue file %r', self._path)
+ logger.info('parsing .cue file %r', self._path)
handle = codecs.open(self._path, 'r', 'utf-8')
for number, line in enumerate(handle.readlines()):
diff --git a/whipper/image/image.py b/whipper/image/image.py
index be71127..93120cc 100644
--- a/whipper/image/image.py
+++ b/whipper/image/image.py
@@ -135,7 +135,7 @@ class ImageVerifyTask(task.MultiSeparateTask):
self.addTask(taskk)
self._tasks.append((0, track, taskk))
except (KeyError, IndexError):
- logger.debug('no htoa track')
+ logger.debug('no HTOA track')
for trackIndex, track in enumerate(cue.table.tracks):
logger.debug('verifying track %d', trackIndex + 1)
@@ -155,8 +155,8 @@ class ImageVerifyTask(task.MultiSeparateTask):
def stop(self):
for trackIndex, track, taskk in self._tasks:
if taskk.exception:
- logger.debug('subtask %r had exception %r, shutting down' % (
- taskk, taskk.exception))
+ logger.debug('subtask %r had exception %r, shutting down',
+ taskk, taskk.exception)
self.setException(taskk.exception)
break
@@ -195,17 +195,16 @@ class ImageEncodeTask(task.MultiSeparateTask):
root, ext = os.path.splitext(os.path.basename(path))
outpath = os.path.join(outdir, root + '.' + 'flac')
logger.debug('schedule encode to %r', outpath)
- taskk = encode.FlacEncodeTask(path,
- os.path.join(outdir,
- root + '.' + 'flac'))
+ taskk = encode.FlacEncodeTask(
+ path, os.path.join(outdir, root + '.' + 'flac'))
self.addTask(taskk)
try:
htoa = cue.table.tracks[0].indexes[0]
- logger.debug('encoding htoa track')
+ logger.debug('encoding HTOA track')
add(htoa)
except (KeyError, IndexError):
- logger.debug('no htoa track')
+ logger.debug('no HTOA track')
pass
for trackIndex, track in enumerate(cue.table.tracks):
diff --git a/whipper/image/table.py b/whipper/image/table.py
index 387a5e1..1d03f96 100644
--- a/whipper/image/table.py
+++ b/whipper/image/table.py
@@ -333,8 +333,8 @@ class Table(object):
@returns: the 28-character base64-encoded disc ID
"""
if self.mbdiscid:
- logger.debug('getMusicBrainzDiscId: returning cached %r'
- % self.mbdiscid)
+ logger.debug('getMusicBrainzDiscId: returning cached %r',
+ self.mbdiscid)
return self.mbdiscid
values = self._getMusicBrainzValues()
@@ -381,7 +381,7 @@ class Table(object):
assert len(result) == 28, \
"Result should be 28 characters, not %d" % len(result)
- logger.debug('getMusicBrainzDiscId: returning %r' % result)
+ logger.debug('getMusicBrainzDiscId: returning %r', result)
self.mbdiscid = result
return result
@@ -489,7 +489,7 @@ class Table(object):
targetPath = common.getRelativePath(path, cuePath)
line = 'FILE "%s" WAVE' % targetPath
lines.append(line)
- logger.debug('writeFile: %r' % line)
+ logger.debug('writeFile: %r', line)
# header
main = ['PERFORMER', 'TITLE']
@@ -530,11 +530,11 @@ class Table(object):
counter = index.counter
if index.path:
- logger.debug('counter %d, writeFile' % counter)
+ logger.debug('counter %d, writeFile', counter)
writeFile(index.path)
for i, track in enumerate(self.tracks):
- logger.debug('track i %r, track %r' % (i, track))
+ logger.debug('track i %r, track %r', i, track)
# FIXME: skip data tracks for now
if not track.audio:
continue
@@ -545,7 +545,7 @@ class Table(object):
for number in indexes:
index = track.indexes[number]
- logger.debug('index %r, %r' % (number, index))
+ logger.debug('index %r, %r', number, index)
# any time the source counter changes to a higher value,
# write a FILE statement
@@ -553,9 +553,9 @@ class Table(object):
# at counter 0 here
if index.counter > counter:
if index.path:
- logger.debug('counter %d, writeFile' % counter)
+ logger.debug('counter %d, writeFile', counter)
writeFile(index.path)
- logger.debug('setting counter to index.counter %r' %
+ logger.debug('setting counter to index.counter %r',
index.counter)
counter = index.counter
@@ -564,7 +564,7 @@ class Table(object):
wroteTrack = True
line = " TRACK %02d %s" % (i + 1, 'AUDIO')
lines.append(line)
- logger.debug('%r' % line)
+ logger.debug('%r', line)
for key in CDTEXT_FIELDS:
if key in track.cdtext:
@@ -620,7 +620,7 @@ class Table(object):
while True:
track = self.tracks[t - 1]
index = track.getIndex(i)
- logger.debug('Clearing path on track %d, index %d', t, i)
+ logger.debug('clearing path on track %d, index %d', t, i)
index.path = None
index.relative = None
try:
@@ -639,9 +639,8 @@ class Table(object):
@type track: C{int}
@type index: C{int}
"""
- logger.debug('setFile: track %d, index %d, path %r, '
- 'length %r, counter %r', track, index, path, length,
- counter)
+ logger.debug('setFile: track %d, index %d, path %r, length %r, '
+ 'counter %r', track, index, path, length, counter)
t = self.tracks[track - 1]
i = t.indexes[index]
@@ -654,9 +653,9 @@ class Table(object):
i.path = path
i.relative = i.absolute - start
i.counter = counter
- logger.debug('Setting path %r, relative %r on '
- 'track %d, index %d, counter %r',
- path, i.relative, track, index, counter)
+ logger.debug('setting path %r, relative %r on track %d, '
+ 'index %d, counter %r', path, i.relative, track,
+ index, counter)
try:
track, index = self.getNextTrackIndex(track, index)
t = self.tracks[track - 1]
@@ -682,13 +681,13 @@ class Table(object):
assert track.number == t
assert index.number == i
if index.counter is None:
- logger.debug('Track %d, index %d has no counter', t, i)
+ logger.debug('track %d, index %d has no counter', t, i)
break
if index.counter != counter:
- logger.debug(
- 'Track %d, index %d has a different counter', t, i)
+ logger.debug('track %d, index %d has a different counter',
+ t, i)
break
- logger.debug('Setting absolute offset %d on track %d, index %d',
+ logger.debug('setting absolute offset %d on track %d, index %d',
index.relative, t, i)
if index.absolute is not None:
if index.absolute != index.relative:
@@ -722,18 +721,16 @@ class Table(object):
for i in list(t.indexes.values()):
if i.absolute is not None:
i.absolute += self.leadout + gap
- logger.debug('Fixing track %02d, index %02d, '
- 'absolute %d' % (
- t.number, i.number, i.absolute))
+ logger.debug('fixing track %02d, index %02d, absolute %d',
+ t.number, i.number, i.absolute)
if i.counter is not None:
i.counter += sourceCounter
- logger.debug('Fixing track %02d, index %02d, '
- 'counter %d' % (
- t.number, i.number, i.counter))
+ logger.debug('fixing track %02d, index %02d, counter %d',
+ t.number, i.number, i.counter)
self.tracks.append(t)
self.leadout += other.leadout + gap # FIXME
- logger.debug('Fixing leadout, now %d', self.leadout)
+ logger.debug('fixing leadout, now %d', self.leadout)
def _getSessionGap(self, session):
# From cdrecord multi-session info:
@@ -841,13 +838,13 @@ class Table(object):
Check if this table can be used to generate a .cue file
"""
if not self.hasTOC():
- logger.debug('No TOC, cannot cue')
+ logger.debug('no TOC, cannot cue')
return False
for t in self.tracks:
for i in list(t.indexes.values()):
if i.relative is None:
- logger.debug('Track %02d, Index %02d does not '
+ logger.debug('track %02d, Index %02d does not '
'have relative', t.number, i.number)
return False
diff --git a/whipper/image/toc.py b/whipper/image/toc.py
index f327b5c..be5e521 100644
--- a/whipper/image/toc.py
+++ b/whipper/image/toc.py
@@ -109,8 +109,8 @@ class Sources:
@type counter: int
@param offset: the absolute disc offset where this source starts
"""
- logger.debug('Appending source, counter %d, abs offset %d, '
- 'source %r' % (counter, offset, source))
+ logger.debug('appending source, counter %d, abs offset %d, '
+ 'source %r', counter, offset, source)
self._sources.append((counter, offset, source))
def get(self, offset):
@@ -152,8 +152,8 @@ class TocFile(object):
absolute = absoluteOffset + trackOffset
# this may be in a new source, so calculate relative
c, o, s = self._sources.get(absolute)
- logger.debug('at abs offset %d, we are in source %r' % (
- absolute, s))
+ logger.debug('at abs offset %d, we are in source %r',
+ absolute, s)
counterStart = self._sources.getCounterStart(c)
relative = absolute - counterStart
@@ -161,10 +161,9 @@ class TocFile(object):
absolute=absolute,
relative=relative,
counter=c)
- logger.debug(
- '[track %02d index %02d] trackOffset %r, added %r',
- currentTrack.number, i, trackOffset,
- currentTrack.getIndex(i))
+ logger.debug('[track %02d index %02d] trackOffset %r, added %r',
+ currentTrack.number, i, trackOffset,
+ currentTrack.getIndex(i))
def parse(self):
currentFile = None
@@ -209,11 +208,11 @@ class TocFile(object):
# is a limitation of our parser approach
if state == 'HEADER':
self.table.cdtext[key] = value
- logger.debug('Found disc CD-Text %s: %r', key, value)
+ logger.debug('found disc CD-Text %s: %r', key, value)
elif state == 'TRACK':
if key != 'ISRC' or not currentTrack \
or currentTrack.isrc is not None:
- logger.debug('Found track CD-Text %s: %r',
+ logger.debug('found track CD-Text %s: %r',
key, value)
currentTrack.cdtext[key] = value
@@ -221,7 +220,7 @@ class TocFile(object):
m = _CATALOG_RE.search(line)
if m:
self.table.catalog = m.group('catalog')
- logger.debug("Found catalog number %s", self.table.catalog)
+ logger.debug("found catalog number %s", self.table.catalog)
# look for TRACK lines
m = _TRACK_RE.search(line)
@@ -260,23 +259,23 @@ class TocFile(object):
m = _PRE_EMPHASIS_RE.search(line)
if m:
currentTrack.pre_emphasis = True
- logger.debug('Track has PRE_EMPHASIS')
+ logger.debug('track has PRE_EMPHASIS')
# look for ISRC lines
m = _ISRC_RE.search(line)
if m:
isrc = m.group('isrc')
currentTrack.isrc = isrc
- logger.debug('Found ISRC code %s', isrc)
+ logger.debug('found ISRC code %s', isrc)
# look for SILENCE lines
m = _SILENCE_RE.search(line)
if m:
length = m.group('length')
- logger.debug('SILENCE of %r', length)
+ logger.debug('silence of %r', length)
self._sources.append(counter, absoluteOffset, None)
if currentFile is not None:
- logger.debug('SILENCE after FILE, increasing counter')
+ logger.debug('silence after file, increasing counter')
counter += 1
relativeOffset = 0
currentFile = None
@@ -286,7 +285,7 @@ class TocFile(object):
m = _ZERO_RE.search(line)
if m:
if currentFile is not None:
- logger.debug('ZERO after FILE, increasing counter')
+ logger.debug('zero after file, increasing counter')
counter += 1
relativeOffset = 0
currentFile = None
@@ -299,13 +298,13 @@ class TocFile(object):
filePath = m.group('name')
start = m.group('start')
length = m.group('length')
- logger.debug('FILE %s, start %r, length %r',
+ logger.debug('file %s, start %r, length %r',
filePath, common.msfToFrames(start),
common.msfToFrames(length))
if not currentFile or filePath != currentFile.path:
counter += 1
relativeOffset = 0
- logger.debug('track %d, switched to new FILE, '
+ logger.debug('track %d, switched to new file, '
'increased counter to %d',
trackNumber, counter)
currentFile = File(filePath, common.msfToFrames(start),
@@ -319,12 +318,12 @@ class TocFile(object):
if m:
filePath = m.group('name')
length = m.group('length')
- logger.debug('FILE %s, length %r',
+ logger.debug('file %s, length %r',
filePath, common.msfToFrames(length))
if not currentFile or filePath != currentFile.path:
counter += 1
relativeOffset = 0
- logger.debug('track %d, switched to new FILE, '
+ logger.debug('track %d, switched to new file, '
'increased counter to %d',
trackNumber, counter)
# FIXME: assume that a MODE2_FORM_MIX track always starts at 0
@@ -343,8 +342,8 @@ class TocFile(object):
length = common.msfToFrames(m.group('length'))
c, o, s = self._sources.get(absoluteOffset)
- logger.debug('at abs offset %d, we are in source %r' % (
- absoluteOffset, s))
+ logger.debug('at abs offset %d, we are in source %r',
+ absoluteOffset, s)
counterStart = self._sources.getCounterStart(c)
relativeOffset = absoluteOffset - counterStart
diff --git a/whipper/program/arc.py b/whipper/program/arc.py
index b5f41ad..e112d31 100644
--- a/whipper/program/arc.py
+++ b/whipper/program/arc.py
@@ -36,17 +36,13 @@ def accuraterip_checksum(f, track_number, total_tracks, wave=False, v2=False):
if not wave:
flac.wait()
if flac.returncode != 0:
- logger.warning(
- 'ARC calculation failed: flac return code is non zero: %r' %
- flac.returncode
- )
+ logger.warning('ARC calculation failed: flac '
+ 'return code is non zero: %r', flac.returncode)
return None
if arc.returncode != 0:
- logger.warning(
- 'ARC calculation failed: arc return code is non zero: %r' %
- arc.returncode
- )
+ logger.warning('ARC calculation failed: '
+ 'arc return code is non zero: %r', arc.returncode)
return None
try:
diff --git a/whipper/program/cdparanoia.py b/whipper/program/cdparanoia.py
index 07f2e69..74871a4 100644
--- a/whipper/program/cdparanoia.py
+++ b/whipper/program/cdparanoia.py
@@ -121,8 +121,8 @@ class ProgressParser:
def _parse_read(self, wordOffset):
if wordOffset % common.WORDS_PER_FRAME != 0:
- logger.debug('THOMAS: not a multiple of %d: %d' % (
- common.WORDS_PER_FRAME, wordOffset))
+ logger.debug('THOMAS: not a multiple of %d: %d',
+ common.WORDS_PER_FRAME, wordOffset)
return
frameOffset = wordOffset / common.WORDS_PER_FRAME
@@ -190,18 +190,19 @@ class ProgressParser:
"""
frames = self.stop - self.start + 1 # + 1 since stop is inclusive
reads = self.reads
- logger.debug('getTrackQuality: frames %d, reads %d' % (frames, reads))
+ logger.debug('getTrackQuality: frames %d, reads %d', frames, reads)
- # don't go over a 100%; we know cdparanoia reads each frame at least
- # twice
try:
+ # don't go over a 100%
+ # we know that cdparanoia reads each frame at least twice
return min(frames * 2.0 / reads, 1.0)
except ZeroDivisionError:
- return 0
-
+ raise RuntimeError("cdparanoia couldn't read any frames "
+ "for the current track")
# FIXME: handle errors
+
class ReadTrackTask(task.Task):
"""
I am a task that reads a track using cdparanoia.
@@ -271,12 +272,11 @@ class ReadTrackTask(task.Task):
stopTrack = i + 1
stopOffset = self._stop - self._table.getTrackStart(i + 1)
- logger.debug('Ripping from %d to %d (inclusive)',
- self._start, self._stop)
- logger.debug('Starting at track %d, offset %d',
- startTrack, startOffset)
- logger.debug('Stopping at track %d, offset %d',
- stopTrack, stopOffset)
+ logger.debug('ripping from %d to %d (inclusive)', self._start,
+ self._stop)
+ logger.debug('starting at track %d, offset %d', startTrack,
+ startOffset)
+ logger.debug('stopping at track %d, offset %d', stopTrack, stopOffset)
bufsize = 1024
if self._overread:
@@ -291,7 +291,7 @@ class ReadTrackTask(task.Task):
startTrack, common.framesToHMSF(startOffset),
stopTrack, common.framesToHMSF(stopOffset)),
self.path])
- logger.debug('Running %s' % (" ".join(argv), ))
+ logger.debug('running %s', (" ".join(argv), ))
try:
self._popen = asyncsub.Popen(argv,
bufsize=bufsize,
@@ -371,7 +371,7 @@ class ReadTrackTask(task.Task):
logger.warning('file size %d did not match expected size %d',
size, expected)
if (size - expected) % common.BYTES_PER_FRAME == 0:
- logger.warning('%d frames difference' % (
+ logger.warning('%d frames difference', (
(size - expected) / common.BYTES_PER_FRAME))
else:
logger.warning('non-integral amount of frames difference')
@@ -451,7 +451,7 @@ class ReadVerifyTrackTask(task.MultiSeparateTask):
"""
task.MultiSeparateTask.__init__(self)
- logger.debug('Creating read and verify task on %r', path)
+ logger.debug('creating read and verify task on %r', path)
if taglist:
logger.debug('read and verify with taglist %r', taglist)
@@ -520,12 +520,12 @@ class ReadVerifyTrackTask(task.MultiSeparateTask):
self.testchecksum = c1 = self.tasks[1].checksum
self.copychecksum = c2 = self.tasks[3].checksum
if c1 == c2:
- logger.info('Checksums match, %08x' % c1)
+ logger.info('checksums match, %08x', c1)
self.checksum = self.testchecksum
else:
# FIXME: detect this before encoding
- logger.info('Checksums do not match, %08x %08x' % (
- c1, c2))
+ logger.info('checksums do not match, %08x %08x',
+ c1, c2)
self.exception = ChecksumException(
'read and verify failed: test checksum')
@@ -538,11 +538,11 @@ class ReadVerifyTrackTask(task.MultiSeparateTask):
if not self.exception:
try:
- logger.debug('Moving to final path %r', self.path)
+ logger.debug('moving to final path %r', self.path)
os.rename(self._tmppath, self.path)
except Exception as e:
- logger.debug('Exception while moving to final '
- 'path %r: %r', self.path, str(e))
+ logger.debug('exception while moving to final '
+ 'path %r: %s', self.path, e)
self.exception = e
else:
os.unlink(self._tmppath)
diff --git a/whipper/program/cdrdao.py b/whipper/program/cdrdao.py
index e0da15c..148c9aa 100644
--- a/whipper/program/cdrdao.py
+++ b/whipper/program/cdrdao.py
@@ -1,9 +1,10 @@
import os
import re
+import shutil
import tempfile
from subprocess import Popen, PIPE
-from whipper.common.common import EjectError
+from whipper.common.common import EjectError, truncate_filename
from whipper.image.toc import TocFile
import logging
@@ -12,7 +13,7 @@ logger = logging.getLogger(__name__)
CDRDAO = 'cdrdao'
-def read_toc(device, fast_toc=False):
+def read_toc(device, fast_toc=False, toc_path=None):
"""
Return cdrdao-generated table of contents for 'device'.
"""
@@ -43,6 +44,14 @@ def read_toc(device, fast_toc=False):
toc = TocFile(tocfile)
toc.parse()
+ if toc_path is not None:
+ t_comp = os.path.abspath(toc_path).split(os.sep)
+ t_dirn = os.sep.join(t_comp[:-1])
+ # If the output path doesn't exist, make it recursively
+ if not os.path.isdir(t_dirn):
+ os.makedirs(t_dirn)
+ t_dst = truncate_filename(os.path.join(t_dirn, t_comp[-1] + '.toc'))
+ shutil.copy(tocfile, os.path.join(t_dirn, t_dst))
os.unlink(tocfile)
return toc
@@ -68,7 +77,7 @@ def version():
out, err = cdrdao.communicate()
if cdrdao.returncode != 1:
logger.warning("cdrdao version detection failed: "
- "return code is " + str(cdrdao.returncode))
+ "return code is %s", cdrdao.returncode)
return None
m = re.compile(r'^Cdrdao version (?P.*) - \(C\)').search(
err.decode('utf-8'))
@@ -86,11 +95,11 @@ def ReadTOCTask(device):
return read_toc(device, fast_toc=True)
-def ReadTableTask(device):
+def ReadTableTask(device, toc_path=None):
"""
stopgap morituri-insanity compatibility layer
"""
- return read_toc(device)
+ return read_toc(device, toc_path=toc_path)
def getCDRDAOVersion():
diff --git a/whipper/program/sox.py b/whipper/program/sox.py
index 1d40875..1ec54d3 100644
--- a/whipper/program/sox.py
+++ b/whipper/program/sox.py
@@ -20,7 +20,7 @@ def peak_level(track_path):
sox = Popen([SOX, track_path, "-n", "stats", "-b", "16"], stderr=PIPE)
out, err = sox.communicate()
if sox.returncode:
- logger.warning("SoX peak detection failed: " + str(sox.returncode))
+ logger.warning("SoX peak detection failed: %s", sox.returncode)
return None
# relevant captured lines looks like this:
# Min level -26215
diff --git a/whipper/result/logger.py b/whipper/result/logger.py
index 9950363..eda4af0 100644
--- a/whipper/result/logger.py
+++ b/whipper/result/logger.py
@@ -89,7 +89,7 @@ class WhipperLogger(result.Logger):
htoastart = htoa.absolute
htoaend = table.getTrackEnd(0)
htoalength = table.tracks[0].getIndex(1).absolute - htoastart
- lines.append(" 00:")
+ lines.append(" 0:")
lines.append(" Start: %s" % common.framesToMSF(htoastart))
lines.append(" Length: %s" % common.framesToMSF(htoalength))
lines.append(" Start sector: %d" % htoastart)
@@ -103,7 +103,7 @@ class WhipperLogger(result.Logger):
start = t.getIndex(1).absolute
length = table.getTrackLength(t.number)
end = table.getTrackEnd(t.number)
- lines.append(" %02d:" % t.number)
+ lines.append(" %d:" % t.number)
lines.append(" Start: %s" % common.framesToMSF(start))
lines.append(" Length: %s" % common.framesToMSF(length))
lines.append(" Start sector: %d" % start)
@@ -166,7 +166,7 @@ class WhipperLogger(result.Logger):
lines = []
# Track number
- lines.append(" %02d:" % trackResult.number)
+ lines.append(" %d:" % trackResult.number)
# Filename (including path) of ripped track
lines.append(" Filename: %s" % trackResult.filename)
diff --git a/whipper/test/dBAR-002-0000f21c-00027ef8-05021002.bin b/whipper/test/dBAR-002-0000f21c-00027ef8-05021002.bin
index 6ff761c..8c804e6 100644
Binary files a/whipper/test/dBAR-002-0000f21c-00027ef8-05021002.bin and b/whipper/test/dBAR-002-0000f21c-00027ef8-05021002.bin differ
diff --git a/whipper/test/test_common_accurip.py b/whipper/test/test_common_accurip.py
index b28193c..ec6fe8a 100644
--- a/whipper/test/test_common_accurip.py
+++ b/whipper/test/test_common_accurip.py
@@ -78,8 +78,8 @@ class TestAccurateRipResponse(TestCase):
self.assertEqual(responses[1].discId1, '0000f21c')
self.assertEqual(responses[1].discId2, '00027ef8')
self.assertEqual(responses[1].cddbDiscId, '05021002')
- self.assertEqual(responses[1].confidences[0], 4)
- self.assertEqual(responses[1].confidences[1], 4)
+ self.assertEqual(responses[1].confidences[0], 5)
+ self.assertEqual(responses[1].confidences[1], 5)
self.assertEqual(responses[1].checksums[0], 'dc77f9ab')
self.assertEqual(responses[1].checksums[1], 'dd97d2c3')
@@ -203,7 +203,7 @@ class TestVerifyResult(TestCase):
'v2': {
'CRC': 'dc77f9ab',
'DBCRC': 'dc77f9ab',
- 'DBConfidence': 4,
+ 'DBConfidence': 5,
},
'DBMaxConfidence': 12,
'DBMaxConfidenceCRC': '284fc705',
@@ -217,7 +217,7 @@ class TestVerifyResult(TestCase):
'v2': {
'CRC': 'dd97d2c3',
'DBCRC': 'dd97d2c3',
- 'DBConfidence': 4,
+ 'DBConfidence': 5,
},
'DBMaxConfidence': 20,
'DBMaxConfidenceCRC': '9cc1f32e',
diff --git a/whipper/test/test_common_mbngs.py b/whipper/test/test_common_mbngs.py
index 8ca401c..231fba7 100644
--- a/whipper/test/test_common_mbngs.py
+++ b/whipper/test/test_common_mbngs.py
@@ -117,7 +117,7 @@ class MetadataTestCase(unittest.TestCase):
check the received metadata for artists tagged with [unknown]
and artists tagged with an alias in MusicBrainz
- see https://github.com/JoeLametta/whipper/issues/155
+ see https://github.com/whipper-team/whipper/issues/155
"""
filename = 'whipper.release.38b05c7d-65fe-4dc0-9c10-33a391b86703.json'
path = os.path.join(os.path.dirname(__file__), filename)