From a011ca8d732b3ca4291ae2000f7a394a044cda30 Mon Sep 17 00:00:00 2001 From: JoeLametta Date: Fri, 2 Nov 2018 08:00:00 +0000 Subject: [PATCH 01/23] Update 'JoeLametta' to 'whipper-team' everywhere in repo --- Dockerfile | 2 +- TODO | 2 +- setup.py | 6 +++--- whipper/common/mbngs.py | 2 +- whipper/test/test_common_mbngs.py | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) 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/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/common/mbngs.py b/whipper/common/mbngs.py index d0d18e0..c45e638 100644 --- a/whipper/common/mbngs.py +++ b/whipper/common/mbngs.py @@ -271,7 +271,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: 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) From 69bac864abee684b6885cd201f1279d1121bf2ec Mon Sep 17 00:00:00 2001 From: JoeLametta Date: Fri, 2 Nov 2018 08:00:00 +0000 Subject: [PATCH 02/23] Raise exception if template has invalid variables If the template string contains variables which aren't valid for whipper, raise ValueError exception listing all the included unrecognized variables. I've also corrected the example template configuration lines in the README. Fixes #279. --- README.md | 2 +- whipper/common/program.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e7f2ff3..1c62f0e 100644 --- a/README.md +++ b/README.md @@ -250,7 +250,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 # ... ``` diff --git a/whipper/common/program.py b/whipper/common/program.py index 78790b4..8401916 100644 --- a/whipper/common/program.py +++ b/whipper/common/program.py @@ -196,6 +196,10 @@ class Program: """ assert isinstance(outdir, unicode), "%r is not unicode" % outdir assert isinstance(template, unicode), "%r is not unicode" % template + matches = re.findall(r"%[^A,S,d,y,r,R,x,X]", template) + if '%' in template and matches: + raise ValueError('disc template string contains invalid ' + 'variable(s): {}.'.format(', '.join(matches))) v = {} v['A'] = 'Unknown Artist' v['d'] = mbdiscid # fallback for title From 59d5fdb84a7351deed09cc94ac76f4f17006877e Mon Sep 17 00:00:00 2001 From: JoeLametta Date: Fri, 2 Nov 2018 12:05:00 +0000 Subject: [PATCH 03/23] Fix UnicodeEncodeError with non ASCII MusicBrainz's catalog numbers Fixes #215. --- whipper/common/program.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/whipper/common/program.py b/whipper/common/program.py index 8401916..56b3d39 100644 --- a/whipper/common/program.py +++ b/whipper/common/program.py @@ -324,7 +324,7 @@ class Program: self._stdout.write("Barcode : %s\n" % metadata.barcode) if metadata.catalogNumber: self._stdout.write("Cat no : %s\n" % - metadata.catalogNumber) + metadata.catalogNumber.encode('utf-8')) delta = abs(metadata.duration - ittoc.duration()) if delta not in deltas: From f0fcae872d98ac48e02c92861f39aa9b861b18af Mon Sep 17 00:00:00 2001 From: JoeLametta Date: Fri, 2 Nov 2018 13:00:00 +0000 Subject: [PATCH 04/23] Write musicbrainz_discid tag when disc is unknown Fixes #280. --- whipper/command/cd.py | 2 +- whipper/common/program.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/whipper/command/cd.py b/whipper/command/cd.py index 502709f..aa7d11b 100644 --- a/whipper/command/cd.py +++ b/whipper/command/cd.py @@ -399,7 +399,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, diff --git a/whipper/common/program.py b/whipper/common/program.py index 8401916..5f8b400 100644 --- a/whipper/common/program.py +++ b/whipper/common/program.py @@ -401,7 +401,7 @@ class Program: self._stdout.write('\n') return ret - def getTagList(self, number): + def getTagList(self, number, mbdiscid): """ Based on the metadata, get a dict of tags for the given track. @@ -421,7 +421,6 @@ class Program: disc = self.metadata.title mbidAlbum = self.metadata.mbid mbidTrackAlbum = self.metadata.mbidArtist - mbDiscId = self.metadata.discid if number > 0: try: @@ -439,6 +438,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 @@ -456,7 +458,6 @@ class Program: tags['MUSICBRAINZ_ARTISTID'] = mbidTrackArtist tags['MUSICBRAINZ_ALBUMID'] = mbidAlbum tags['MUSICBRAINZ_ALBUMARTISTID'] = mbidTrackAlbum - tags['MUSICBRAINZ_DISCID'] = mbDiscId # TODO/FIXME: ISRC tag From 4f5b684ec712bb63260a931ce1f95dfe3bc12dcf Mon Sep 17 00:00:00 2001 From: JoeLametta Date: Sun, 4 Nov 2018 08:00:00 +0000 Subject: [PATCH 05/23] Fix template validation error Commit 9c72ebccd31f32c78bdc4741971233f8792db7b7 introduced a bug: all template strings are validated against the disc template RegEx (which, for example, is wrong if we're testing the track template). --- whipper/command/cd.py | 3 +++ whipper/common/common.py | 14 ++++++++++++++ whipper/common/program.py | 4 ---- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/whipper/command/cd.py b/whipper/command/cd.py index aa7d11b..1ba0658 100644 --- a/whipper/command/cd.py +++ b/whipper/command/cd.py @@ -28,6 +28,7 @@ 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 @@ -285,7 +286,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" diff --git a/whipper/common/common.py b/whipper/common/common.py index 9bd20de..39c0913 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 @@ -280,6 +281,19 @@ def getRelativePath(targetPath, collectionPath): 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/program.py b/whipper/common/program.py index 5f8b400..7469b09 100644 --- a/whipper/common/program.py +++ b/whipper/common/program.py @@ -196,10 +196,6 @@ class Program: """ assert isinstance(outdir, unicode), "%r is not unicode" % outdir assert isinstance(template, unicode), "%r is not unicode" % template - matches = re.findall(r"%[^A,S,d,y,r,R,x,X]", template) - if '%' in template and matches: - raise ValueError('disc template string contains invalid ' - 'variable(s): {}.'.format(', '.join(matches))) v = {} v['A'] = 'Unknown Artist' v['d'] = mbdiscid # fallback for title From d7fd3fb5075c1de820988a59749ffb722d726bd7 Mon Sep 17 00:00:00 2001 From: JoeLametta Date: Fri, 9 Nov 2018 08:00:00 +0000 Subject: [PATCH 06/23] Convert print statement to logger warning Clarify the description of the error. --- whipper/command/offset.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/whipper/command/offset.py b/whipper/command/offset.py index d7f7e24..d347be7 100644 --- a/whipper/command/offset.py +++ b/whipper/command/offset.py @@ -93,7 +93,8 @@ 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") if responses: logger.debug('%d AccurateRip responses found.' % len(responses)) From 09c3e4771d955473e4adf8c7ab3b5ae415acc185 Mon Sep 17 00:00:00 2001 From: JoeLametta Date: Fri, 9 Nov 2018 08:00:00 +0000 Subject: [PATCH 07/23] Prevent exception in offset find Whipper offset find needs a (remote) matching AccurateRip entry to perform its task and confirm the drive offset: if none is found, return early to prevent exception. Fixes #208. --- whipper/command/offset.py | 1 + 1 file changed, 1 insertion(+) diff --git a/whipper/command/offset.py b/whipper/command/offset.py index d347be7..201cd1a 100644 --- a/whipper/command/offset.py +++ b/whipper/command/offset.py @@ -95,6 +95,7 @@ CD in the AccurateRip database.""" except accurip.EntryNotFound: 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)) From c0637b1b921ee1598bfb4cd41b18eabdc3f7e513 Mon Sep 17 00:00:00 2001 From: JoeLametta Date: Fri, 9 Nov 2018 08:00:00 +0000 Subject: [PATCH 08/23] Raise exception when cdparanoia can't read any frames This commit effectively reverts #159. Whipper now raises an exception again but with a clearer textual description. Closes #202. --- whipper/program/cdparanoia.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/whipper/program/cdparanoia.py b/whipper/program/cdparanoia.py index 07f2e69..936c3e5 100644 --- a/whipper/program/cdparanoia.py +++ b/whipper/program/cdparanoia.py @@ -192,16 +192,17 @@ class ProgressParser: reads = self.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. From 688574a5d8f97419641f743be1ad9b32cfbd6349 Mon Sep 17 00:00:00 2001 From: JoeLametta Date: Tue, 13 Nov 2018 15:42:28 +0000 Subject: [PATCH 09/23] Add Probot apps to improve workflow (#329) * Add Probot Stale app * Add Probot Welcome app --- .github/config.yml | 19 +++++++++++++++++++ .github/stale.yml | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 .github/config.yml create mode 100644 .github/stale.yml 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 From 2b286be91cd3c697b75fd7f93733efbfea47ddd2 Mon Sep 17 00:00:00 2001 From: JoeLametta Date: Sat, 17 Nov 2018 09:05:34 +0000 Subject: [PATCH 10/23] Update failing AccurateRipResponse tests (#334) * Update failing AccurateRipResponse tests [1/2] The failures seem to be caused by a remote metadata change (metadata from AccurateRip about a release ID changed) which means some of the asserts related to a specific release ID are failing. I've fixed this issue with two commits. This is part 1: here I've updated the confidence values of two tracks making it agree with AccurateRip's remote. * Update failing AccurateRipResponse tests [2/2] This is part 2: here I've replaced the outdated AccurateRip bin file with a freshly downloaded one. --- .../test/dBAR-002-0000f21c-00027ef8-05021002.bin | Bin 62 -> 62 bytes whipper/test/test_common_accurip.py | 8 ++++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/whipper/test/dBAR-002-0000f21c-00027ef8-05021002.bin b/whipper/test/dBAR-002-0000f21c-00027ef8-05021002.bin index 6ff761c6e7533df870442a1bcb2dbf9a3a7f16f3..8c804e6e6ebf526ff6a58e6eceacb855a3d2ece6 100644 GIT binary patch delta 23 bcmcDso1i1ay837N9R?s^J$z~UT@V8RUAG5S delta 23 bcmcDso1i1avifKF9R?s^IecmQT@V8RU7QC~ 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', From c2af4459ef2b67ed2b5bc35c4fc50713eece7378 Mon Sep 17 00:00:00 2001 From: JoeLametta Date: Fri, 26 Oct 2018 08:00:00 +0000 Subject: [PATCH 11/23] Preserve ToC file generated by cdrdao Whipper uses cdrdao during its ripping process. With this commit it will now store cdrdao's generated tocfile in the ripping path. Preserving the tocfile allows users to easily burn ripped discs having a non-compliant cue sheet. Fixes #214. --- whipper/command/cd.py | 15 +++++++++++---- whipper/common/program.py | 5 +++-- whipper/program/cdrdao.py | 17 +++++++++++++---- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/whipper/command/cd.py b/whipper/command/cd.py index 1ba0658..8fa6e63 100644 --- a/whipper/command/cd.py +++ b/whipper/command/cd.py @@ -135,11 +135,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" % ( @@ -329,9 +339,6 @@ Log files will log the path to tracks relative to this directory. dirname.encode('utf-8')) logger.critical(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')) os.makedirs(dirname) diff --git a/whipper/common/program.py b/whipper/common/program.py index 7469b09..ee93009 100644 --- a/whipper/common/program.py +++ b/whipper/common/program.py @@ -104,7 +104,8 @@ class Program: 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. @@ -126,7 +127,7 @@ class Program: logger.debug('getTable: cddbdiscid %s, mbdiscid %s not ' 'in cache for offset %s, reading table' % ( cddbdiscid, mbdiscid, offset)) - t = cdrdao.ReadTableTask(device) + t = cdrdao.ReadTableTask(device, out_path) itable = t.table tdict[offset] = itable ptable.persist(tdict) diff --git a/whipper/program/cdrdao.py b/whipper/program/cdrdao.py index e0da15c..01f5a20 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 @@ -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(): From 4a53ecba2c25dc580df7b05d9ec0dc55c7b4db15 Mon Sep 17 00:00:00 2001 From: JoeLametta Date: Thu, 29 Nov 2018 08:00:00 +0000 Subject: [PATCH 12/23] Avoid zero padding in logger track numbers Fixes #340. --- whipper/result/logger.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/whipper/result/logger.py b/whipper/result/logger.py index 9950363..066d311 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) From 25800e0d3f9a6a8ed4acfe74434b2ceb7bed467b Mon Sep 17 00:00:00 2001 From: it is madness Date: Thu, 29 Nov 2018 09:41:58 -0500 Subject: [PATCH 13/23] Avoid zero padding in logger track numbers Fixes #340 --- whipper/result/logger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/whipper/result/logger.py b/whipper/result/logger.py index 066d311..eda4af0 100644 --- a/whipper/result/logger.py +++ b/whipper/result/logger.py @@ -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) From c377417108c91abac0aaafaf62bd345162dc3db7 Mon Sep 17 00:00:00 2001 From: JoeLametta Date: Thu, 13 Dec 2018 20:21:26 +0000 Subject: [PATCH 14/23] Replace sys.std{out,err} statements with logger/print calls (#331) * Change global default log level to 'INFO' We're going to increase our usage of logger statements instead of print instructions (where deemed opportune). * Replace sys.std{out,err} statements with logger/print calls Fixes #303. --- whipper/__init__.py | 2 +- whipper/command/accurip.py | 19 +++--- whipper/command/cd.py | 63 +++++++++----------- whipper/command/drive.py | 53 ++++++----------- whipper/command/main.py | 16 ++--- whipper/command/offset.py | 50 +++++++--------- whipper/common/accurip.py | 2 +- whipper/common/program.py | 115 +++++++++++++++--------------------- whipper/extern/task/task.py | 12 ++-- 9 files changed, 139 insertions(+), 193 deletions(-) diff --git a/whipper/__init__.py b/whipper/__init__.py index d795e3f..550c41f 100644 --- a/whipper/__init__.py +++ b/whipper/__init__.py @@ -4,7 +4,7 @@ import sys __version__ = '0.7.2' -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..d1f551b 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,17 @@ 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 +79,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, str(checksums[checksum]))) class AccuRip(BaseCommand): diff --git a/whipper/command/cd.py b/whipper/command/cd.py index 8fa6e63..2f284b5 100644 --- a/whipper/command/cd.py +++ b/whipper/command/cd.py @@ -22,7 +22,6 @@ import argparse import cdio import os import glob -import sys import logging from whipper.command.basecommand import BaseCommand from whipper.common import ( @@ -84,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, @@ -120,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) @@ -236,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 @@ -340,7 +337,8 @@ Log files will log the path to tracks relative to this directory. logger.critical(msg) raise RuntimeError(msg) 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 @@ -381,11 +379,11 @@ Log files will log the path to tracks relative to this directory. 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): @@ -399,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) @@ -427,17 +425,14 @@ Log files will log the path to tracks relative to this directory. "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: @@ -452,8 +447,7 @@ Log files will log the path to tracks relative to this directory. 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) @@ -467,14 +461,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 @@ -489,7 +484,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..0932375 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..e9c41d4 100644 --- a/whipper/command/main.py +++ b/whipper/command/main.py @@ -19,12 +19,7 @@ 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) # register plugins with pkg_resources distributions, _ = pkg_resources.working_set.find_plugins( @@ -35,7 +30,7 @@ def main(): 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 +46,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.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 201cd1a..e0b94a8 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 @@ -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) @@ -116,25 +115,22 @@ 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" % ( + logger.warning("unknown task exception for offset %d: %r" % ( offset, e)) - sys.stdout.write( - 'WARNING: cannot rip with offset %d...\n' % offset) + logger.warning('cannot rip with offset %d...' % offset) continue logger.debug('AR checksums calculated: %s %s' % archecksums) @@ -142,10 +138,9 @@ CD in the AccurateRip database.""" 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 @@ -154,9 +149,8 @@ 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) @@ -169,16 +163,16 @@ CD in the AccurateRip database.""" 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' % ( @@ -205,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..1fc6d35 100644 --- a/whipper/common/accurip.py +++ b/whipper/common/accurip.py @@ -240,7 +240,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' diff --git a/whipper/common/program.py b/whipper/common/program.py index 477b253..9287f93 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,9 +93,9 @@ 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 @@ -125,23 +121,23 @@ class Program: if not itable: logger.debug('getTable: cddbdiscid %s, mbdiscid %s not ' - 'in cache for offset %s, reading table' % ( + '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 @@ -253,12 +249,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 @@ -270,7 +266,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', @@ -287,41 +283,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.encode('utf-8')) + print("Cat no : %s" % + metadata.catalogNumber.encode('utf-8')) delta = abs(metadata.duration - ittoc.duration()) if delta not in deltas: @@ -344,20 +336,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', + 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: @@ -370,32 +357,28 @@ class Program: for i, metadata in enumerate(metadatas): if not artist == metadata.artist: logger.warning("artist 0: %r and artist %d: %r " - "are not the same" % ( + "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" % ( + "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, mbdiscid): @@ -427,7 +410,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 @@ -484,7 +467,7 @@ 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 @@ -528,9 +511,9 @@ class Program: runner.run(t) logger.debug('ripped track') - logger.debug('test speed %.3f/%.3f seconds' % ( + logger.debug('test speed %.3f/%.3f seconds', ( t.testspeed, t.testduration)) - logger.debug('copy speed %.3f/%.3f seconds' % ( + logger.debug('copy speed %.3f/%.3f seconds', ( t.copyspeed, t.copyduration)) trackResult.testcrc = t.testchecksum trackResult.copycrc = t.copychecksum @@ -544,7 +527,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): """ @@ -565,7 +548,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/extern/task/task.py b/whipper/extern/task/task.py index 3c2b4a6..6c396af 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 @@ -539,16 +540,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') - sys.stdout.flush() + print() if len(what) > self._longest: self._longest = len(what) From e3bb215d4fd96def19729c1651575571e814a3d3 Mon Sep 17 00:00:00 2001 From: JoeLametta Date: Fri, 14 Dec 2018 09:52:24 +0000 Subject: [PATCH 15/23] Convert noisy logger statements to debug level --- whipper/common/config.py | 4 ++-- whipper/extern/task/task.py | 23 ++++++++++++----------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/whipper/common/config.py b/whipper/common/config.py index 2b7f7cb..0762c7c 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') diff --git a/whipper/extern/task/task.py b/whipper/extern/task/task.py index 6c396af..dc23328 100644 --- a/whipper/extern/task/task.py +++ b/whipper/extern/task/task.py @@ -167,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: @@ -367,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) @@ -512,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: @@ -522,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) From a23b2148149898c15eabbd6cc961408bd3426c50 Mon Sep 17 00:00:00 2001 From: JoeLametta Date: Fri, 14 Dec 2018 10:44:53 +0000 Subject: [PATCH 16/23] Print empty lines, not parentheses --- whipper/common/program.py | 2 +- whipper/extern/task/task.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/whipper/common/program.py b/whipper/common/program.py index 9287f93..7c0d5c7 100644 --- a/whipper/common/program.py +++ b/whipper/common/program.py @@ -378,7 +378,7 @@ class Program: print('Submit this disc to MusicBrainz at the above URL.') ret = None - print() + print('') return ret def getTagList(self, number, mbdiscid): diff --git a/whipper/extern/task/task.py b/whipper/extern/task/task.py index dc23328..86c00a8 100644 --- a/whipper/extern/task/task.py +++ b/whipper/extern/task/task.py @@ -549,7 +549,7 @@ class SyncRunner(TaskRunner, ITaskListener): if ret: print('\r', end='') if newline: - print() + print('') if len(what) > self._longest: self._longest = len(what) From a4f654a3f3eece9e7139ebd74ef64c1d604260b2 Mon Sep 17 00:00:00 2001 From: JoeLametta Date: Fri, 14 Dec 2018 11:02:20 +0000 Subject: [PATCH 17/23] Fix logger statements having multiple arguments Some of the instructions have been rendered invalid during the conversion to logger statements... Also performed various stylistic fixes --- whipper/command/accurip.py | 9 +++--- whipper/command/cd.py | 42 ++++++++++++-------------- whipper/command/drive.py | 4 +-- whipper/command/main.py | 2 +- whipper/command/offset.py | 26 ++++++++-------- whipper/common/accurip.py | 34 ++++++++------------- whipper/common/cache.py | 16 +++++----- whipper/common/common.py | 9 +++--- whipper/common/config.py | 7 ++--- whipper/common/drive.py | 2 +- whipper/common/mbngs.py | 20 ++++++------ whipper/common/program.py | 45 +++++++++++++-------------- whipper/common/task.py | 12 +++----- whipper/image/cue.py | 2 +- whipper/image/image.py | 15 +++++---- whipper/image/table.py | 57 +++++++++++++++++------------------ whipper/image/toc.py | 43 +++++++++++++------------- whipper/program/arc.py | 12 +++----- whipper/program/cdparanoia.py | 35 +++++++++++---------- whipper/program/cdrdao.py | 2 +- whipper/program/sox.py | 2 +- 21 files changed, 182 insertions(+), 214 deletions(-) diff --git a/whipper/command/accurip.py b/whipper/command/accurip.py index d1f551b..e12f6c8 100644 --- a/whipper/command/accurip.py +++ b/whipper/command/accurip.py @@ -40,13 +40,12 @@ retrieves and display accuraterip data from the given URL count = responses[0].num_tracks - logger.info("Found %d responses for %d tracks", - (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: - logger.warning("response %d has %d tracks instead of %d", ( - 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): @@ -81,7 +80,7 @@ retrieves and display accuraterip data from the given URL for highest, checksum in sortedChecksums: print(" %d result(s) for checksum %s: %s" % ( len(checksums[checksum]), - checksum, str(checksums[checksum]))) + checksum, checksums[checksum])) class AccuRip(BaseCommand): diff --git a/whipper/command/cd.py b/whipper/command/cd.py index 2f284b5..3419d73 100644 --- a/whipper/command/cd.py +++ b/whipper/command/cd.py @@ -176,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' @@ -344,14 +344,14 @@ Log files will log the path to tracks relative to this directory. # 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, @@ -359,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 @@ -376,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) - logger.info('verifying track %d of %d: %s', ( + logger.info('verifying track %d of %d: %s', number, len(self.itable.tracks), - os.path.basename(path).encode('utf-8'))) + os.path.basename(path).encode('utf-8')) if not self.program.verifyTrack(self.runner, trackResult): 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 @@ -397,9 +397,9 @@ Log files will log the path to tracks relative to this directory. tries += 1 if tries > 1: extra = " (try %d)" % tries - logger.info('ripping track %d of %d%s: %s', ( + logger.info('ripping track %d of %d%s: %s', number, len(self.itable.tracks), extra, - os.path.basename(path).encode('utf-8'))) + os.path.basename(path).encode('utf-8')) try: logger.debug('ripIfNotRipped: track %d, try %d', number, tries) @@ -415,12 +415,11 @@ 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'") @@ -439,12 +438,11 @@ Log files will log the path to tracks relative to this directory. # 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 logger.info('HTOA discarded, contains digital silence') @@ -461,15 +459,15 @@ Log files will log the path to tracks relative to this directory. htoa = self.program.getHTOA() if htoa: start, stop = htoa - logger.info('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: logger.warning('skipping data track %d, not implemented', - (i + 1)) + i + 1) # FIXME: make it work for now track.indexes[1].relative = 0 continue diff --git a/whipper/command/drive.py b/whipper/command/drive.py index 0932375..0860cec 100644 --- a/whipper/command/drive.py +++ b/whipper/command/drive.py @@ -48,7 +48,7 @@ class Analyze(BaseCommand): info = drive.getDeviceInfo(self.options.device) if not info: - logger.error('Drive caching behaviour not saved: ' + logger.error('drive caching behaviour not saved: ' 'could not get device info') return @@ -67,7 +67,7 @@ class List(BaseCommand): self.config = config.Config() if not paths: - logger.critical('No drives found. Create /dev/cdrom ' + logger.critical('no drives found. Create /dev/cdrom ' 'if you have a CD drive, or install ' 'pycdio for better detection') return diff --git a/whipper/command/main.py b/whipper/command/main.py index e9c41d4..3e28fe8 100644 --- a/whipper/command/main.py +++ b/whipper/command/main.py @@ -50,7 +50,7 @@ def main(): return 255 if isinstance(e.exception, common.EmptyError): - logger.debug("EmptyError: %r", str(e.exception)) + logger.debug("EmptyError: %s", e.exception) logger.critical('could not create encoded file') return 255 diff --git a/whipper/command/offset.py b/whipper/command/offset.py index e0b94a8..9fe620c 100644 --- a/whipper/command/offset.py +++ b/whipper/command/offset.py @@ -70,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() @@ -97,7 +97,7 @@ CD in the AccurateRip database.""" 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) @@ -125,20 +125,20 @@ CD in the AccurateRip database.""" raise e if isinstance(e.exception, cdparanoia.FileSizeError): - logger.warning('cannot rip with offset %d...' % offset) + logger.warning('cannot rip with offset %d...', offset) continue - logger.warning("unknown task exception for offset %d: %r" % ( - offset, e)) - logger.warning('cannot rip with offset %d...' % 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) + logger.debug('matched against response %d', i) logger.info('offset of device is likely %d, confirming...', offset) @@ -149,14 +149,14 @@ CD in the AccurateRip database.""" archecksums = self._arcs(runner, table, track, offset) except task.TaskException as e: if isinstance(e.exception, cdparanoia.FileSizeError): - logger.warning('cannot rip with offset %d...' % + 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: @@ -164,8 +164,8 @@ CD in the AccurateRip database.""" return 0 else: logger.warning('only %d of %d tracks matched, ' - 'continuing...', - (count, len(table.tracks))) + 'continuing...', count, + len(table.tracks)) logger.error('no matching offset found. ' 'Consider trying again with a different disc') diff --git a/whipper/common/accurip.py b/whipper/common/accurip.py index 1fc6d35..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]) @@ -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 39c0913..1b46d4e 100644 --- a/whipper/common/common.py +++ b/whipper/common/common.py @@ -263,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) @@ -275,9 +275,8 @@ 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)) diff --git a/whipper/common/config.py b/whipper/common/config.py index 0762c7c..8d10935 100644 --- a/whipper/common/config.py +++ b/whipper/common/config.py @@ -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 c45e638..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 @@ -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 7c0d5c7..08d892a 100644 --- a/whipper/common/program.py +++ b/whipper/common/program.py @@ -93,9 +93,8 @@ class Program: from pkg_resources import parse_version as V version = cdrdao.getCDRDAOVersion() if V(version) < V('1.2.3rc2'): - 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 + 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 @@ -120,9 +119,9 @@ 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)) + 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 @@ -130,7 +129,7 @@ class Program: logger.debug('getTable: read table %r', itable) else: logger.debug('getTable: cddbdiscid %s, mbdiscid %s in cache ' - 'for offset %s', (cddbdiscid, mbdiscid, offset)) + 'for offset %s', cddbdiscid, mbdiscid, offset) logger.debug('getTable: loaded table %r', itable) assert itable.hasTOC() @@ -336,8 +335,8 @@ 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: logger.info('picked requested release id %s', release) print('Artist: %s' % metadatas[0].artist.encode('utf-8')) @@ -356,14 +355,13 @@ 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): logger.warning('picked closest match in duration. ' @@ -410,7 +408,7 @@ class Program: mbidTrack = track.mbid mbidTrackArtist = track.mbidArtist except IndexError as e: - logger.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 @@ -473,9 +471,8 @@ class Program: 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, @@ -511,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 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/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 936c3e5..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,7 +190,7 @@ 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) try: # don't go over a 100% @@ -272,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: @@ -292,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, @@ -372,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') @@ -452,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) @@ -521,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') @@ -539,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 01f5a20..148c9aa 100644 --- a/whipper/program/cdrdao.py +++ b/whipper/program/cdrdao.py @@ -77,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')) 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 From 92cbb88f1f69ae06b972b3931c223a2eb393612d Mon Sep 17 00:00:00 2001 From: JoeLametta Date: Fri, 14 Dec 2018 14:08:55 +0000 Subject: [PATCH 18/23] Avoid double showing the same error message --- whipper/command/cd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/whipper/command/cd.py b/whipper/command/cd.py index 3419d73..2b60be9 100644 --- a/whipper/command/cd.py +++ b/whipper/command/cd.py @@ -334,7 +334,7 @@ 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: logger.info("creating output directory %s", From 134996824547842de028dce36c4cffbc5744ad30 Mon Sep 17 00:00:00 2001 From: JoeLametta Date: Fri, 14 Dec 2018 14:10:27 +0000 Subject: [PATCH 19/23] Restore stdout flushing Otherwise it seems that the output of some lines may appear garbled --- whipper/extern/task/task.py | 1 + 1 file changed, 1 insertion(+) diff --git a/whipper/extern/task/task.py b/whipper/extern/task/task.py index 86c00a8..250ccd6 100644 --- a/whipper/extern/task/task.py +++ b/whipper/extern/task/task.py @@ -550,6 +550,7 @@ class SyncRunner(TaskRunner, ITaskListener): print('\r', end='') if newline: print('') + sys.stdout.flush() if len(what) > self._longest: self._longest = len(what) From c803a59d87fbddb1de71c1042a4c4c51d167bdd0 Mon Sep 17 00:00:00 2001 From: JoeLametta Date: Fri, 14 Dec 2018 21:52:12 +0100 Subject: [PATCH 20/23] Fix whipper cd rip menu bug (loggers list) --- whipper/command/cd.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/whipper/command/cd.py b/whipper/command/cd.py index 2b60be9..e47b80e 100644 --- a/whipper/command/cd.py +++ b/whipper/command/cd.py @@ -243,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", From 3a569484e32b9097735776081281706c6d5f59f4 Mon Sep 17 00:00:00 2001 From: JoeLametta Date: Wed, 12 Dec 2018 08:00:00 +0000 Subject: [PATCH 21/23] Discover plugins in system directories too Fixes #135. --- README.md | 23 ++++++++++++++++++++--- whipper/command/main.py | 18 +++++++++++++++--- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1c62f0e..810993c 100644 --- a/README.md +++ b/README.md @@ -265,14 +265,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/whipper/command/main.py b/whipper/command/main.py index 3e28fe8..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 @@ -21,9 +21,21 @@ logger = logging.getLogger(__name__) def main(): 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: From 7c83670efab2110215f2398be6f75c39fd3ab408 Mon Sep 17 00:00:00 2001 From: JoeLametta Date: Fri, 14 Dec 2018 21:52:29 +0000 Subject: [PATCH 22/23] Clarify cd-paranoia's package issue Also updated pycdio/libcdio section Fixes #347. --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 810993c..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 From 3f0ccba727795054fa8ca99b452e53cacbe4bf4f Mon Sep 17 00:00:00 2001 From: JoeLametta Date: Fri, 14 Dec 2018 22:14:10 +0000 Subject: [PATCH 23/23] Push whipper v0.7.3 release --- CHANGELOG.md | 196 +++++++++++++++++++++++++++++--------------- COVERAGE | 42 +++++----- whipper/__init__.py | 2 +- 3 files changed, 154 insertions(+), 86 deletions(-) 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/whipper/__init__.py b/whipper/__init__.py index 550c41f..e06df53 100644 --- a/whipper/__init__.py +++ b/whipper/__init__.py @@ -2,7 +2,7 @@ import logging import os import sys -__version__ = '0.7.2' +__version__ = '0.7.3' level = logging.INFO if 'WHIPPER_DEBUG' in os.environ: