Exemple #1
0
    def add_arguments(self):
        loggers = result.getLoggers().keys()
        default_offset = None
        info = drive.getDeviceInfo(self.opts.device)
        if info:
            try:
                default_offset = config.Config().getReadOffset(*info)
                sys.stdout.write("Using configured read offset %d\n" %
                                 default_offset)
            except KeyError:
                pass

        _CD.add_arguments(self.parser)

        self.parser.add_argument('-L', '--logger',
                                 action="store", dest="logger",
                                 default='whipper',
                                 help="logger to use (choose from '"
                                 "', '".join(loggers) + "')")
        # FIXME: get from config
        self.parser.add_argument('-o', '--offset',
                                 action="store", dest="offset",
                                 default=default_offset,
                                 help="sample read offset")
        self.parser.add_argument('-x', '--force-overread',
                                 action="store_true", dest="overread",
                                 default=False,
                                 help="Force overreading into the "
                                 "lead-out portion of the disc. Works only "
                                 "if the patched cdparanoia package is "
                                 "installed and the drive "
                                 "supports this feature. ")
        self.parser.add_argument('-O', '--output-directory',
                                 action="store", dest="output_directory",
                                 default=os.path.relpath(os.getcwd()),
                                 help="output directory; will be included "
                                 "in file paths in log")
        self.parser.add_argument('-W', '--working-directory',
                                 action="store", dest="working_directory",
                                 help="working directory; whipper will "
                                 "change to this directory "
                                 "and files will be created relative to "
                                 "it when not absolute")
        self.parser.add_argument('--track-template',
                                 action="store", dest="track_template",
                                 default=DEFAULT_TRACK_TEMPLATE,
                                 help="template for track file naming")
        self.parser.add_argument('--disc-template',
                                 action="store", dest="disc_template",
                                 default=DEFAULT_DISC_TEMPLATE,
                                 help="template for disc file naming")
        self.parser.add_argument('-U', '--unknown',
                                 action="store_true", dest="unknown",
                                 help="whether to continue ripping if "
                                 "the CD is unknown", default=False)
        self.parser.add_argument('--cdr',
                                 action="store_true", dest="cdr",
                                 help="whether to continue ripping if "
                                 "the disc is a CD-R",
                                 default=False)
Exemple #2
0
    def testDisambiguateOnRelease(self):
        """Test that disambiguation gets placed in the same part of the path
        as the release name.

        See https://github.com/JoeLametta/whipper/issues/127"""
        prog = program.Program(config.Config())
        md = mbngs.DiscMetadata()
        md.artist = 'Guy Davis'
        md.sortName = 'Davis, Guy'
        md.title = 'Call Down the Thunder'
        md.release = '1996'
        md.catalogNumber = 'RHR CD 89'
        prog.metadata = md
        templates = {
            u'%A/%d - %y':
            u'Guy Davis/Call Down the Thunder - 1996 (RHR CD 89)',  # noqa: E501
            u'%A - %d - %y':
            u'Guy Davis - Call Down the Thunder - 1996 (RHR CD 89)',  # noqa: E501
            u'%A/%y/%d': u'Guy Davis/1996/Call Down the Thunder (RHR CD 89)',
            u'%y/%d/%A': u'1996/Call Down the Thunder (RHR CD 89)/Guy Davis',
            u'%d/%A/%y': u'Call Down the Thunder (RHR CD 89)/Guy Davis/1996',
        }

        for template, expected_path in templates.iteritems():
            path = prog.getPath(u'/tmp',
                                template,
                                'mbdiscid',
                                0,
                                disambiguate=True)  # noqa: E501
            self.assertEquals(path, u'/tmp/' + expected_path)
Exemple #3
0
    def do(self):
        runner = task.SyncRunner()
        t = cdparanoia.AnalyzeTask(self.options.device)
        runner.run(t)

        if t.defeatsCache is None:
            sys.stdout.write(
                'Cannot analyze the drive.  Is there a CD in it?\n')
            return
        if not t.defeatsCache:
            sys.stdout.write(
                'cdparanoia cannot defeat the audio cache on this drive.\n')
        else:
            sys.stdout.write(
                'cdparanoia can defeat the audio cache on this drive.\n')

        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')
            return

        sys.stdout.write(
            'Adding drive cache behaviour to configuration file.\n')

        config.Config().setDefeatsCache(info[0], info[1], info[2],
                                        t.defeatsCache)
Exemple #4
0
    def testStandardTemplateEmpty(self):
        prog = program.Program(config.Config())

        path = prog.getPath('/tmp', DEFAULT_DISC_TEMPLATE,
                            'mbdiscid', None)
        self.assertEqual(path, ('/tmp/unknown/Unknown Artist - mbdiscid/'
                                'Unknown Artist - mbdiscid'))
Exemple #5
0
    def testMusicBrainz(self):
        # output from mb-submit-disc:
        # https://musicbrainz.org/cdtoc/attach?toc=1+12+195856+150+
        # 15687+31841+51016+66616+81352+99559+116070+133243+149997+161710+
        # 177832&tracks=12&id=KnpGsLhvH.lPrNc1PBL21lb9Bg4-
        # however, not (yet) in MusicBrainz database

        # setup to test if MusicBrainz submit URL is hardcoded to use https
        env_original = environ.get('XDG_CONFIG_HOME')
        tmp_conf = mkdtemp(suffix='.config')
        # HACK: hijack env var to avoid overwriting user's whipper config file
        # This works because directory.config_path() builds the location where
        # whipper's conf will reside based on the value of env XDG_CONFIG_HOME
        environ['XDG_CONFIG_HOME'] = tmp_conf
        self.config = config.Config()
        self.config._parser.add_section('musicbrainz')
        self.config._parser.set('musicbrainz', 'server',
                                'http://musicbrainz.org')
        self.config.write()
        self.assertEqual(self.table.getMusicBrainzSubmitURL(),
                         "http://musicbrainz.org/cdtoc/attach?toc=1+12+1958"
                         "56+150+15687+31841+51016+66616+81352+99559+116070+13"
                         "3243+149997+161710+177832&tracks=12&id=KnpGsLhvH.lPr"
                         "Nc1PBL21lb9Bg4-")
        # HACK: continuation - restore original env value (if defined)
        if env_original is not None:
            environ['XDG_CONFIG_HOME'] = env_original
        else:
            environ.pop('XDG_CONFIG_HOME', None)
        self.assertEqual(self.table.getMusicBrainzDiscId(),
                         "KnpGsLhvH.lPrNc1PBL21lb9Bg4-")
        rmtree(tmp_conf)
Exemple #6
0
    def do(self):
        paths = drive.getAllDevicePaths()
        self.config = config.Config()

        if not paths:
            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:
            logger.error('install pycdio for vendor/model/release detection')
            return

        for path in paths:
            vendor, model, release = drive.getDeviceInfo(path)
            print("drive: %s, vendor: %s, model: %s, release: %s" %
                  (path, vendor, model, release))

            try:
                offset = self.config.getReadOffset(vendor, model, release)
                print("       Configured read offset: %d" % offset)
            except KeyError:
                # Note spaces at the beginning for pretty terminal output
                logger.warning("no read offset found. "
                               "Run 'whipper offset find'")

            try:
                defeats = self.config.getDefeatsCache(vendor, model, release)
                print("       Can defeat audio cache: %s" % defeats)
            except KeyError:
                logger.warning("unknown whether audio cache can be "
                               "defeated. Run 'whipper drive analyze'")
Exemple #7
0
    def do(self):
        prog = program.Program(config.Config())
        runner = task.SyncRunner()

        for arg in self.options.cuefile:
            arg = arg.decode('utf-8')
            cueImage = image.Image(arg)
            cueImage.setup(runner)

            # FIXME: this feels like we're poking at internals.
            prog.cuePath = arg
            prog.result = result.RipResult()
            for track in cueImage.table.tracks:
                tr = result.TrackResult()
                tr.number = track.number
                prog.result.tracks.append(tr)

            verified = False
            try:
                verified = prog.verifyImage(runner, cueImage.table)
            except accurip.EntryNotFound:
                print('AccurateRip entry not found')
            accurip.print_report(prog.result)
            if not verified:
                sys.exit(1)
    def testIssue66TemplateFilled(self):
        prog = program.Program(config.Config())
        md = mbngs.DiscMetadata()
        md.artist = md.sortName = 'Jeff Buckley'
        md.title = 'Grace'

        path = prog.getPath(u'/tmp', u'%A/%d', 'mbdiscid', md, 0)
        self.assertEquals(path, u'/tmp/Jeff Buckley/Grace')
    def testStandardTemplateFilled(self):
        prog = program.Program(config.Config())
        md = mbngs.DiscMetadata()
        md.artist = md.sortName = 'Jeff Buckley'
        md.releaseTitle = 'Grace'

        path = prog.getPath('/tmp', DEFAULT_DISC_TEMPLATE, 'mbdiscid', md, 0)
        self.assertEqual(path, ('/tmp/unknown/Jeff Buckley - Grace/'
                                'Jeff Buckley - Grace'))
Exemple #10
0
def main():
    # set user agent
    musicbrainzngs.set_useragent("whipper", whipper.__version__,
                                 "https://github.com/JoeLametta/whipper")

    try:
        server = config.Config().get_musicbrainz_server()
    except KeyError, e:
        sys.stderr.write('whipper: %s\n' % e.message)
        sys.exit()
Exemple #11
0
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(plugins_p))
    list(map(pkg_resources.working_set.add, distributions))
    try:
        cmd = Whipper(sys.argv[1:], os.path.basename(sys.argv[0]), None)
        ret = cmd.do()
    except SystemError as e:
        logger.critical("SystemError: %s", e)
        if (isinstance(e, common.EjectError)
                and cmd.options.eject in ('failure', 'always')):
            eject_device(e.device)
        return 255
    except RuntimeError as e:
        print(e)
        return 1
    except KeyboardInterrupt:
        return 2
    except ImportError as e:
        raise
    except task.TaskException as e:
        if isinstance(e.exception, ImportError):
            raise ImportError(e.exception)
        elif isinstance(e.exception, common.MissingDependencyException):
            logger.critical('missing dependency "%s"', e.exception.dependency)
            return 255

        if isinstance(e.exception, common.EmptyError):
            logger.debug("EmptyError: %s", e.exception)
            logger.critical('could not create encoded file')
            return 255

        # in python3 we can instead do `raise e.exception` as that would show
        # the exception's original context
        logger.critical(e.exceptionMessage)
        return 255
    return ret if ret else 0
Exemple #12
0
    def _foundOffset(self, device, offset):
        print('\nRead offset of device is: %d.' % offset)

        info = drive.getDeviceInfo(device)
        if not info:
            logger.error('offset not saved: '
                         'could not get device info (requires pycdio)')
            return

        logger.info('adding read offset to configuration file')

        config.Config().setReadOffset(info[0], info[1], info[2], offset)
Exemple #13
0
    def _foundOffset(self, device, offset):
        sys.stdout.write('\nRead offset of device is: %d.\n' % offset)

        info = drive.getDeviceInfo(device)
        if not info:
            sys.stdout.write('Offset not saved: could not get '
                             'device info (requires pycdio).\n')
            return

        sys.stdout.write('Adding read offset to configuration file.\n')

        config.Config().setReadOffset(info[0], info[1], info[2], offset)
Exemple #14
0
    def testVerify(self):
        path = os.path.join(os.path.dirname(__file__),
                            'dBAR-020-002e5023-029d8e49-040eaa14.bin')
        data = open(path, "rb").read()
        responses = accurip.getAccurateRipResponses(data)

        # these crc's were calculated from an actual rip
        checksums = [
            1644890007, 2945205445, 3983436658, 1528082495, 1203704270,
            1163423644, 3649097244, 100524219, 1583356174, 373652058,
            1842579359, 2850056507, 1329730252, 2526965856, 2525886806,
            209743350, 3184062337, 2099956663, 2943874164, 2321637196
        ]

        prog = program.Program(config.Config())
        prog.result = result.RipResult()
        # fill it with empty trackresults
        for i, c in enumerate(checksums):
            r = result.TrackResult()
            r.number = i + 1
            prog.result.tracks.append(r)

        prog._verifyImageWithChecksums(responses, checksums)

        # now check if the results were filled in properly
        tr = prog.result.getTrackResult(1)
        self.assertEquals(tr.accurip, False)
        self.assertEquals(tr.ARDBMaxConfidence, 0)
        self.assertEquals(tr.ARDBCRC, 0)
        self.assertEquals(tr.ARDBCRC, 0)

        tr = prog.result.getTrackResult(2)
        self.assertEquals(tr.accurip, True)
        self.assertEquals(tr.ARDBMaxConfidence, 2)
        self.assertEquals(tr.ARDBCRC, checksums[2 - 1])

        tr = prog.result.getTrackResult(10)
        self.assertEquals(tr.accurip, False)
        self.assertEquals(tr.ARDBMaxConfidence, 2)
        # we know track 10 was ripped wrong
        self.assertNotEquals(tr.ARDBCRC, checksums[10 - 1])

        res = prog.getAccurateRipResults()
        self.assertEquals(
            res[1 - 1], "Track  1: rip NOT accurate (not found)             "
            "[620b0797], DB [notfound]")
        self.assertEquals(
            res[2 - 1], "Track  2: rip accurate     (max confidence      2) "
            "[af8c44c5], DB [af8c44c5]")
        self.assertEquals(
            res[10 - 1], "Track 10: rip NOT accurate (max confidence      2) "
            "[16457a5a], DB [eb6e55b4]")
Exemple #15
0
    def getMusicBrainzSubmitURL(self):
        host = config.Config().get_musicbrainz_server()

        discid = self.getMusicBrainzDiscId()
        values = self._getMusicBrainzValues()

        query = urlencode([
            ('toc', ' '.join([str(v) for v in values])),
            ('tracks', self.getAudioTracks()),
            ('id', discid),
        ])

        return urlunparse(('https', host, '/cdtoc/attach', '', query, ''))
Exemple #16
0
def main():
    try:
        server = config.Config().get_musicbrainz_server()
    except KeyError as e:
        sys.stderr.write('whipper: %s\n' % str(e))
        sys.exit()

    musicbrainzngs.set_hostname(server)
    # register plugins with pkg_resources
    distributions, _ = pkg_resources.working_set.find_plugins(
        pkg_resources.Environment([directory.data_path('plugins')]))
    list(map(pkg_resources.working_set.add, distributions))
    try:
        cmd = Whipper(sys.argv[1:], os.path.basename(sys.argv[0]), None)
        ret = cmd.do()
    except SystemError as e:
        sys.stderr.write('whipper: error: %s\n' % e)
        if (isinstance(e, common.EjectError)
                and cmd.options.eject in ('failure', 'always')):
            eject_device(e.device)
        return 255
    except RuntimeError as e:
        print(e)
        return 1
    except KeyboardInterrupt:
        return 2
    except ImportError as e:
        raise
    except task.TaskException as e:
        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)
            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
            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)
        return 255
    return ret if ret else 0
Exemple #17
0
    def do(self):

        prog = program.Program(config.Config(), stdout=sys.stdout)
        runner = task.SyncRunner()

        for arg in self.options.cuefile:
            sys.stdout.write('Retagging image %r\n' % arg)
            arg = arg.decode('utf-8')
            cueImage = image.Image(arg)
            cueImage.setup(runner)

            mbdiscid = cueImage.table.getMusicBrainzDiscId()
            sys.stdout.write('MusicBrainz disc id is %s\n' % mbdiscid)

            sys.stdout.write("MusicBrainz lookup URL %s\n" %
                             cueImage.table.getMusicBrainzSubmitURL())
            prog.metadata = prog.getMusicBrainz(
                cueImage.table,
                mbdiscid,
                release=self.options.release_id,  # noqa: E501
                country=self.options.country,
                prompt=self.options.prompt)

            if not prog.metadata:
                print 'Not in MusicBrainz database, skipping'
                continue

            prog.metadata.discid = mbdiscid

            # FIXME: this feels like we're poking at internals.
            prog.cuePath = arg
            prog.result = result.RipResult()
            for track in cueImage.table.tracks:
                path = cueImage.getRealPath(track.indexes[1].path)

                taglist = prog.getTagList(track.number)
                logger.debug(
                    'possibly retagging %r from cue path %r with taglist %r',
                    path, arg, taglist)
                t = encode.SafeRetagTask(path, taglist)
                runner.run(t)
                path = os.path.basename(path)
                if t.changed:
                    print 'Retagged %s' % path
                else:
                    print '%s already tagged correctly' % path
            print
Exemple #18
0
    def do(self):
        paths = drive.getAllDevicePaths()
        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')

            return

        try:
            import cdio as _
        except ImportError:
            sys.stdout.write(
                'Install pycdio for vendor/model/release detection.\n')
            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))

            try:
                offset = self.config.getReadOffset(
                    vendor, model, release)
                sys.stdout.write(
                    "       Configured read offset: %d\n" % offset)
            except KeyError:
                sys.stdout.write(
                    "       No read offset found.  Run 'whipper offset find'\n")

            try:
                defeats = self.config.getDefeatsCache(
                    vendor, model, release)
                sys.stdout.write(
                    "       Can defeat audio cache: %s\n" % 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')
Exemple #19
0
    def testAddDisambiguationUnitTest(self):
        """Unit test for Program.addDisambiguation()."""
        prog = program.Program(config.Config())
        md = mbngs.DiscMetadata()

        # No relevant disambiguation metadata
        self.assertEquals(prog.addDisambiguation(u'Test', md), u'Test')

        # Only barcode available
        md.barcode = '033651008927'
        self.assertEquals(prog.addDisambiguation(u'Test', md),
                          u'Test (033651008927)')

        # Both catalog number and barcode available
        md.catalogNumber = 'RHR CD 89'
        self.assertEquals(prog.addDisambiguation(u'Test', md),
                          u'Test (RHR CD 89)')
Exemple #20
0
    def testDisambiguateOnReleaseOnlyOnce(self):
        """Test that disambiguation gets added only once."""
        prog = program.Program(config.Config())
        md = mbngs.DiscMetadata()
        md.artist = 'Guy Davis'
        md.sortName = 'Davis, Guy'
        md.title = 'Call Down the Thunder'
        md.release = '1996'
        md.catalogNumber = 'RHR CD 89'
        prog.metadata = md
        template = u'%A/%d - %y/%d/%d'

        path = prog.getPath(u'/tmp',
                            template,
                            'mbdiscid',
                            0,
                            disambiguate=True)  # noqa: E501
        self.assertEquals(
            path,
            u'/tmp/Guy Davis/Call Down the Thunder - 1996 (RHR CD 89)/Call Down the Thunder/Call Down the Thunder'
        )  # noqa: E501
Exemple #21
0
    def do(self):
        prog = program.Program(config.Config())
        runner = task.SyncRunner()
        cache = accurip.AccuCache()

        for arg in self.options.cuefile:
            arg = arg.decode('utf-8')
            cueImage = image.Image(arg)
            cueImage.setup(runner)

            url = cueImage.table.getAccurateRipURL()
            responses = cache.retrieve(url)

            # FIXME: this feels like we're poking at internals.
            prog.cuePath = arg
            prog.result = result.RipResult()
            for track in cueImage.table.tracks:
                tr = result.TrackResult()
                tr.number = track.number
                prog.result.tracks.append(tr)

            prog.verifyImage(runner, responses)

            print "\n".join(prog.getAccurateRipResults()) + "\n"
Exemple #22
0
    def testDisambiguateOnNoReleaseTitle(self):
        """Test that disambiguation gets added even if there's no release
        title in the template."""
        prog = program.Program(config.Config())
        md = mbngs.DiscMetadata()
        md.artist = 'Guy Davis'
        md.sortName = 'Davis, Guy'
        md.title = 'Call Down the Thunder'
        md.release = '1996'
        md.catalogNumber = 'RHR CD 89'
        prog.metadata = md
        templates = {
            u'%A/%y': u'Guy Davis/1996 (RHR CD 89)',
            u'%A - %y': u'Guy Davis - 1996 (RHR CD 89)',
            u'%y/%A': u'1996/Guy Davis (RHR CD 89)',
        }

        for template, expected_path in templates.iteritems():
            path = prog.getPath(u'/tmp',
                                template,
                                'mbdiscid',
                                0,
                                disambiguate=True)  # noqa: E501
            self.assertEquals(path, u'/tmp/' + expected_path)
Exemple #23
0
    def do(self):
        runner = task.SyncRunner()
        t = cdparanoia.AnalyzeTask(self.options.device)
        runner.run(t)

        if t.defeatsCache is None:
            logger.critical('cannot analyze the drive: is there a CD in it?')
            return
        if not t.defeatsCache:
            logger.info('cdparanoia cannot defeat the audio cache '
                        'on this drive')
        else:
            logger.info('cdparanoia can defeat the audio cache on this drive')

        info = drive.getDeviceInfo(self.options.device)
        if not info:
            logger.error('drive caching behaviour not saved: '
                         'could not get device info')
            return

        logger.info('adding drive cache behaviour to configuration file')

        config.Config().setDefeatsCache(info[0], info[1], info[2],
                                        t.defeatsCache)
Exemple #24
0
    def do(self):
        self.config = config.Config()
        self.program = program.Program(self.config,
                                       record=self.options.record)
        self.runner = task.SyncRunner()

        # if the device is mounted (data session), unmount it
        self.device = self.options.device
        logger.info('checking device %s', self.device)

        if self.options.drive_auto_close is True:
            utils.load_device(self.device)
        utils.unmount_device(self.device)
        # Exit and inform the user if there's no CD in the disk drive
        if drive.get_cdrom_drive_status(self.device) == 1:  # rc 1 -> no disc
            raise OSError("no CD detected, please insert one and retry")

        # first, read the normal TOC, which is fast
        self.ittoc = self.program.getFastToc(self.runner, self.device)

        # already show us some info based on this
        self.program.getRipResult()
        print("CDDB disc id: %s" % self.ittoc.getCDDBDiscId())
        self.mbdiscid = self.ittoc.getMusicBrainzDiscId()
        print("MusicBrainz disc id %s" % self.mbdiscid)

        print("MusicBrainz lookup URL %s" %
              self.ittoc.getMusicBrainzSubmitURL())

        self.program.metadata = (
            self.program.getMusicBrainz(self.ittoc, self.mbdiscid,
                                        release=self.options.release_id,
                                        country=self.options.country,
                                        prompt=self.options.prompt)
        )

        if not self.program.metadata:
            # fall back to FreeDB for lookup
            cddbid = self.ittoc.getCDDBValues()
            cddbmd = self.program.getCDDB(cddbid)
            if 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 argument not passed")
                return -1

        self.program.result.isCdr = cdrdao.DetectCdr(self.device)
        if (self.program.result.isCdr and
                not getattr(self.options, 'cdr', False)):
            logger.critical("inserted disc seems to be a CD-R, "
                            "--cdr not passed")
            return -1

        # Change working directory before cdrdao's task
        if getattr(self.options, 'working_directory', False):
            os.chdir(os.path.expanduser(self.options.working_directory))
        if hasattr(self.options, 'output_directory'):
            out_bpath = self.options.output_directory
            # Needed to preserve cdrdao's tocfile
            out_fpath = self.program.getPath(out_bpath,
                                             self.options.disc_template,
                                             self.mbdiscid,
                                             self.program.metadata)
        else:
            out_fpath = None
        # now, read the complete index table, which is slower
        offset = getattr(self.options, 'offset', 0)
        self.itable = self.program.getTable(self.runner,
                                            self.ittoc.getCDDBDiscId(),
                                            self.ittoc.getMusicBrainzDiscId(),
                                            self.device, offset, out_fpath)

        assert self.itable.getCDDBDiscId() == self.ittoc.getCDDBDiscId(), \
            "full table's id %s differs from toc id %s" % (
                self.itable.getCDDBDiscId(), self.ittoc.getCDDBDiscId())
        assert self.itable.getMusicBrainzDiscId() == \
            self.ittoc.getMusicBrainzDiscId(), \
            "full table's mb id %s differs from toc id mb %s" % (
            self.itable.getMusicBrainzDiscId(),
            self.ittoc.getMusicBrainzDiscId())

        if self.program.metadata:
            self.program.metadata.discid = self.ittoc.getMusicBrainzDiscId()

        # result

        self.program.result.cdrdaoVersion = cdrdao.version()
        self.program.result.cdparanoiaVersion = \
            cdparanoia.getCdParanoiaVersion()
        info = drive.getDeviceInfo(self.device)
        if info:
            try:
                self.program.result.cdparanoiaDefeatsCache = \
                    self.config.getDefeatsCache(*info)
            except KeyError as e:
                logger.debug('got key error: %r', (e, ))
        self.program.result.artist = self.program.metadata \
            and self.program.metadata.artist \
            or 'Unknown Artist'
        self.program.result.title = self.program.metadata \
            and self.program.metadata.releaseTitle \
            or 'Unknown Title'
        _, self.program.result.vendor, self.program.result.model, \
            self.program.result.release = \
            cdio.Device(self.device).get_hwinfo()
        self.program.result.metadata = self.program.metadata

        ret = self.doCommand()

        if (self.options.eject == 'success' and self.eject or
                self.options.eject == 'always'):
            utils.eject_device(self.device)

        return ret
 def setUp(self):
     fd, self._path = tempfile.mkstemp(suffix='.whipper.test.config')
     os.close(fd)
     self._config = config.Config(self._path)
Exemple #26
0
    def add_arguments(self):
        loggers = list(result.getLoggers())
        default_offset = None
        info = drive.getDeviceInfo(self.opts.device)
        if info:
            try:
                default_offset = config.Config().getReadOffset(*info)
                logger.info("using configured read offset %d", default_offset)
            except KeyError:
                pass

        _CD.add_arguments(self.parser)

        self.parser.add_argument('-L', '--logger',
                                 action="store", dest="logger",
                                 default='whipper',
                                 help=("logger to use (choose from: '%s" %
                                       "', '".join(loggers) + "')"))
        self.parser.add_argument('-o', '--offset',
                                 action="store", dest="offset",
                                 default=default_offset,
                                 help="sample read offset")
        self.parser.add_argument('-x', '--force-overread',
                                 action="store_true", dest="overread",
                                 default=False,
                                 help="Force overreading into the "
                                 "lead-out portion of the disc. Works only "
                                 "if the patched cdparanoia package is "
                                 "installed and the drive "
                                 "supports this feature. ")
        self.parser.add_argument('-O', '--output-directory',
                                 action="store", dest="output_directory",
                                 default=os.curdir,
                                 help="output directory; will be included "
                                 "in file paths in log")
        self.parser.add_argument('-W', '--working-directory',
                                 action="store", dest="working_directory",
                                 help="working directory; whipper will "
                                 "change to this directory "
                                 "and files will be created relative to "
                                 "it when not absolute")
        self.parser.add_argument('--track-template',
                                 action="store", dest="track_template",
                                 default=DEFAULT_TRACK_TEMPLATE,
                                 help="template for track file naming")
        self.parser.add_argument('--disc-template',
                                 action="store", dest="disc_template",
                                 default=DEFAULT_DISC_TEMPLATE,
                                 help="template for disc file naming")
        self.parser.add_argument('-U', '--unknown',
                                 action="store_true", dest="unknown",
                                 help="whether to continue ripping if "
                                 "the CD is unknown", default=False)
        self.parser.add_argument('--cdr',
                                 action="store_true", dest="cdr",
                                 help="whether to continue ripping if "
                                 "the disc is a CD-R",
                                 default=False)
        self.parser.add_argument('-C', '--cover-art',
                                 action="store", dest="cover_art",
                                 help="fetch cover art and save it as "
                                 "standalone file, embed into FLAC files "
                                 "or perform both actions: file, embed, "
                                 "complete option values respectively",
                                 choices=['file', 'embed', 'complete'],
                                 default=None)
        self.parser.add_argument('-r', '--max-retries',
                                 action="store", dest="max_retries",
                                 help="number of rip attempts before giving "
                                 "up if can't rip a track. This defaults to "
                                 "{}; 0 means "
                                 "infinity.".format(DEFAULT_MAX_RETRIES),
                                 default=DEFAULT_MAX_RETRIES)
        self.parser.add_argument('-k', '--keep-going',
                                 action='store_true',
                                 help="continue ripping further tracks "
                                 "instead of giving up if a track "
                                 "can't be ripped")
Exemple #27
0
def main():
    server = config.Config().get_musicbrainz_server()
    https_enabled = server['scheme'] == 'https'
    try:
        musicbrainzngs.set_hostname(server['netloc'], https_enabled)
    # Parameter 'use_https' is missing in versions of musicbrainzngs < 0.7
    except TypeError:
        logger.warning("Parameter 'use_https' is missing in versions of "
                       "musicbrainzngs < 0.7. This means whipper will only "
                       "be able to communicate with the configured "
                       "MusicBrainz server ('%s') over plain HTTP. If a "
                       "custom server which speaks HTTPS only has been "
                       "declared, a suitable version of the "
                       "musicbrainzngs module will be needed "
                       "to make it work in whipper.", server['netloc'])
        musicbrainzngs.set_hostname(server['netloc'])

    # 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(plugins_p)
    )
    list(map(pkg_resources.working_set.add, distributions))
    try:
        cmd = Whipper(sys.argv[1:], os.path.basename(sys.argv[0]), None)
        ret = cmd.do()
    except SystemError as e:
        logger.critical("SystemError: %s", e)
        if (isinstance(e, common.EjectError) and
                cmd.options.eject in ('failure', 'always')):
            # XXX: Pylint, instance of 'SystemError' has no 'device' member
            eject_device(e.device)
        return 255
    except RuntimeError as e:
        print(e)
        return 1
    except KeyboardInterrupt:
        return 2
    except ImportError:
        raise
    except task.TaskException as e:
        if isinstance(e.exception, ImportError):
            raise ImportError(e.exception)
        elif isinstance(e.exception, common.MissingDependencyException):
            logger.critical('missing dependency "%s"', e.exception.dependency)
            return 255

        if isinstance(e.exception, common.EmptyError):
            logger.debug("EmptyError: %s", e.exception)
            logger.critical('could not create encoded file')
            return 255

        # in python3 we can instead do `raise e.exception` as that would show
        # the exception's original context
        logger.critical(e.exceptionMessage)
        return 255
    return ret if ret else 0
Exemple #28
0
    def __init__(self, argv, prog_name, opts):
        self.opts = opts  # for Rip.add_arguments()
        self.prog_name = prog_name

        self.init_parser()
        self.add_arguments()

        config_section = prog_name.replace(' ', '.')
        defaults = {}
        for action in self.parser._actions:
            val = None
            if isinstance(action, argparse._StoreAction):
                val = config.Config().get(config_section, action.dest)
            elif isinstance(
                    action,
                (argparse._StoreTrueAction, argparse._StoreFalseAction)):
                val = config.Config().getboolean(config_section, action.dest)
            if val is not None:
                defaults[action.dest] = val
        self.parser.set_defaults(**defaults)

        if hasattr(self, 'subcommands'):
            self.parser.add_argument('remainder',
                                     nargs=argparse.REMAINDER,
                                     help=argparse.SUPPRESS)

        if self.device_option:
            # pick the first drive as default
            drives = drive.getAllDevicePaths()
            if not drives:
                msg = 'No CD-DA drives found!'
                logger.critical(msg)
                # whipper exited with return code 3 here
                raise IOError(msg)
            self.parser.add_argument('-d',
                                     '--device',
                                     action="store",
                                     dest="device",
                                     default=drives[0],
                                     help="CD-DA device")

        self.options = self.parser.parse_args(argv, namespace=opts)

        if self.device_option:
            # this can be a symlink to another device
            self.options.device = os.path.realpath(self.options.device)
            if not os.path.exists(self.options.device):
                msg = 'CD-DA device %s not found!' % self.options.device
                logger.critical(msg)
                raise IOError(msg)

        self.handle_arguments()

        if hasattr(self, 'subcommands'):
            if not self.options.remainder:
                self.parser.print_help()
                raise SystemExit()
            if not self.options.remainder[0] in self.subcommands:
                logger.critical("incorrect subcommand: %s",
                                self.options.remainder[0])
                raise SystemExit(1)
            self.cmd = self.subcommands[self.options.remainder[0]](
                self.options.remainder[1:],
                prog_name + " " + self.options.remainder[0], self.options)
Exemple #29
0
    def do(self):
        self.config = config.Config()
        self.program = program.Program(self.config,
                                       record=self.options.record,
                                       stdout=sys.stdout)
        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)

        utils.load_device(self.device)
        utils.unmount_device(self.device)

        # first, read the normal TOC, which is fast
        self.ittoc = self.program.getFastToc(self.runner,
                                             self.options.toc_pickle,
                                             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())
        self.mbdiscid = self.ittoc.getMusicBrainzDiscId()
        sys.stdout.write("MusicBrainz disc id %s\n" % self.mbdiscid)

        sys.stdout.write("MusicBrainz lookup URL %s\n" %
                         self.ittoc.getMusicBrainzSubmitURL())

        self.program.metadata = (
            self.program.getMusicBrainz(self.ittoc, self.mbdiscid,
                                        release=self.options.release_id,
                                        country=self.options.country,
                                        prompt=self.options.prompt)
        )

        if not self.program.metadata:
            # fall back to FreeDB for lookup
            cddbid = self.ittoc.getCDDBValues()
            cddbmd = self.program.getCDDB(cddbid)
            if cddbmd:
                sys.stdout.write('FreeDB identifies disc as %s\n' % cddbmd)

            # also used by rip cd info
            if not getattr(self.options, 'unknown', False):
                logger.critical("unable to retrieve disc metadata, "
                                "--unknown not passed")
                return -1

        self.program.result.isCdr = cdrdao.DetectCdr(self.device)
        if (self.program.result.isCdr and
                not getattr(self.options, 'cdr', False)):
            logger.critical("inserted disc seems to be a CD-R, "
                            "--cdr not passed")
            return -1

        # 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)

        assert self.itable.getCDDBDiscId() == self.ittoc.getCDDBDiscId(), \
            "full table's id %s differs from toc id %s" % (
                self.itable.getCDDBDiscId(), self.ittoc.getCDDBDiscId())
        assert self.itable.getMusicBrainzDiscId() == \
            self.ittoc.getMusicBrainzDiscId(), \
            "full table's mb id %s differs from toc id mb %s" % (
            self.itable.getMusicBrainzDiscId(),
            self.ittoc.getMusicBrainzDiscId())
        assert self.itable.accuraterip_path() == \
            self.ittoc.accuraterip_path(), \
            "full table's AR URL %s differs from toc AR URL %s" % (
            self.itable.accuraterip_url(), self.ittoc.accuraterip_url())

        if self.program.metadata:
            self.program.metadata.discid = self.ittoc.getMusicBrainzDiscId()

        # result

        self.program.result.cdrdaoVersion = cdrdao.getCDRDAOVersion()
        self.program.result.cdparanoiaVersion = \
            cdparanoia.getCdParanoiaVersion()
        info = drive.getDeviceInfo(self.device)
        if info:
            try:
                self.program.result.cdparanoiaDefeatsCache = \
                    self.config.getDefeatsCache(*info)
            except KeyError as e:
                logger.debug('Got key error: %r' % (e, ))
        self.program.result.artist = self.program.metadata \
            and self.program.metadata.artist \
            or 'Unknown Artist'
        self.program.result.title = self.program.metadata \
            and self.program.metadata.title \
            or 'Unknown Title'
        _, self.program.result.vendor, self.program.result.model, \
            self.program.result.release = \
            cdio.Device(self.device).get_hwinfo()

        self.doCommand()

        if self.options.eject in ('success', 'always'):
            utils.eject_device(self.device)
Exemple #30
0
    def testGetAccurateRipResults(self):
        prog = program.Program(config.Config())
        prog.result = result.RipResult()
        prog.result.tracks = self._tracks

        prog.getAccurateRipResults()