Beispiel #1
0
    def test_delete_art(self):
        mediafile = self._mediafile_fixture('empty')
        mediafile.art = self.jpg_data
        mediafile.save()

        mediafile = MediaFile(mediafile.path)
        self.assertIsNotNone(mediafile.art)

        del mediafile.art
        mediafile.save()

        mediafile = MediaFile(mediafile.path)
        self.assertIsNone(mediafile.art)
Beispiel #2
0
    def test_delete_partial_date(self):
        mediafile = self._mediafile_fixture('empty')

        mediafile.date = datetime.date(2001, 12, 3)
        mediafile.save()
        mediafile = MediaFile(mediafile.path)
        self.assertIsNotNone(mediafile.date)
        self.assertIsNotNone(mediafile.year)
        self.assertIsNotNone(mediafile.month)
        self.assertIsNotNone(mediafile.day)

        delattr(mediafile, 'month')
        mediafile.save()
        mediafile = MediaFile(mediafile.path)
        self.assertIsNotNone(mediafile.date)
        self.assertIsNotNone(mediafile.year)
        self.assertIsNone(mediafile.month)
        self.assertIsNone(mediafile.day)
Beispiel #3
0
def op_reload_md5(mdb, args, mf, dry_run):
    path = mdb.abspath(mf.relative_path)
    if not os.path.isfile(path):
        logging.error("Cannot find file %s." % mf.relative_path)
        return False

    new_mf = MediaFile(mf)
    new_mf.load_file_info(path)

    if \
            new_mf.path == mf.path and \
            new_mf.file_size == mf.file_size and \
            new_mf.middle_md5 == mf.middle_md5:
                return False

    # update file info

    logging.info("Updating: %s" % mdb.abspath(mf.relative_path))
    logging.info("old: %s" % mf)
    logging.info("new: %s" % new_mf)

    if dry_run:
        return False

    try:
        mdb.update(
                mf.id,
                path=new_mf.path,
                file_size=new_mf.file_size,
                middle_md5=new_mf.middle_md5
                )
    except sqlite3.IntegrityError:
        conflict_mf = mdb.get(middle_md5=new_mf.middle_md5)
        logging.error('IntegrityError: middle_md5 conflict with: %s' % conflict_mf)
        return False

    return True
Beispiel #4
0
def transfer_tags(src: Pathish, dst: Pathish, remove_art: bool = False) -> None:
    src = Path(src).resolve()
    dst = Path(dst).resolve()
    f = MediaFile(src)
    g = MediaFile(dst)

    for field in f.fields():
        if remove_art and field in ("art", "images"):
            continue
        try:
            setattr(g, field, getattr(f, field))
        except:
            pass

    g.save()
Beispiel #5
0
    def test_set_image_structure(self):
        mediafile = self._mediafile_fixture('empty')
        image = Image(data=self.png_data,
                      desc=u'album cover',
                      type=ImageType.front)
        mediafile.images = [image]
        mediafile.save()

        mediafile = MediaFile(mediafile.path)
        self.assertEqual(len(mediafile.images), 1)

        image = mediafile.images[0]
        self.assertEqual(image.data, self.png_data)
        self.assertEqual(image.mime_type, 'image/png')
        self.assertExtendedImageAttributes(image,
                                           desc=u'album cover',
                                           type=ImageType.front)
Beispiel #6
0
    def test_delete_tag(self):
        mediafile = self._mediafile_fixture('full')

        keys = self.full_initial_tags.keys()
        for key in set(keys) - set(['art', 'month', 'day']):
            self.assertIsNotNone(getattr(mediafile, key))
        for key in keys:
            delattr(mediafile, key)
        mediafile.save()
        mediafile = MediaFile(mediafile.filename)

        for key in keys:
            value = getattr(mediafile, key)
            if isinstance(value, list):
                assert not value
            else:
                self.assertIsNone(value)
Beispiel #7
0
    def test_add_image_structure(self):
        mediafile = self._mediafile_fixture('image')
        self.assertEqual(len(mediafile.images), 2)

        image = Image(data=self.png_data, desc=u'the composer',
                      type=ImageType.composer)
        mediafile.images += [image]
        mediafile.save()

        mediafile = MediaFile(mediafile.filename)
        self.assertEqual(len(mediafile.images), 3)

        images = (i for i in mediafile.images if i.desc == u'the composer')
        image = next(images, None)
        self.assertExtendedImageAttributes(
            image, desc=u'the composer', type=ImageType.composer
        )
Beispiel #8
0
    def test_add_tiff_image(self):
        mediafile = self._mediafile_fixture('image')
        self.assertEqual(len(mediafile.images), 2)

        image = Image(data=self.tiff_data, desc=u'the composer',
                      type=ImageType.composer)
        mediafile.images += [image]
        mediafile.save()

        mediafile = MediaFile(mediafile.filename)
        self.assertEqual(len(mediafile.images), 3)

        # WMA does not preserve the order, so we have to work around this
        image = list(filter(lambda i: i.mime_type == 'image/tiff',
                     mediafile.images))[0]
        self.assertExtendedImageAttributes(
            image, desc=u'the composer', type=ImageType.composer)
Beispiel #9
0
    def test_whitelist_and_blacklist(self):
        item = self.add_item_fixture(year=2016)
        item.write()
        mf = MediaFile(syspath(item.path))
        self.assertEqual(mf.year, 2016)

        item_id = item.id
        self.config['zero']['fields'] = [u'year']
        self.config['zero']['keep_fields'] = [u'comments']

        self.load_plugins('zero')
        with control_stdin('y'):
            self.run_command('zero')

        item = self.lib.get_item(item_id)

        self.assertEqual(item['year'], 2016)
        self.assertEqual(mf.year, 2016)
Beispiel #10
0
    def test_write_counters_without_total(self):
        mediafile = self._mediafile_fixture('full')
        self.assertEqual(mediafile.track, 2)
        self.assertEqual(mediafile.tracktotal, 3)
        self.assertEqual(mediafile.disc, 4)
        self.assertEqual(mediafile.disctotal, 5)

        mediafile.track = 10
        delattr(mediafile, 'tracktotal')
        mediafile.disc = 10
        delattr(mediafile, 'disctotal')
        mediafile.save()

        mediafile = MediaFile(mediafile.path)
        self.assertEqual(mediafile.track, 10)
        self.assertEqual(mediafile.tracktotal, None)
        self.assertEqual(mediafile.disc, 10)
        self.assertEqual(mediafile.disctotal, None)
Beispiel #11
0
 def _set_pattern(self, field):
     """Populate `self.fields_to_progs` for a given field.
     Do some sanity checks then compile the regexes.
     """
     if field not in MediaFile.fields():
         self._log.error(u'invalid field: {0}', field)
     elif field in ('id', 'path', 'album_id'):
         self._log.warning(
             u'field \'{0}\' ignored, zeroing '
             u'it would be dangerous', field)
     else:
         try:
             for pattern in self.config[field].as_str_seq():
                 prog = re.compile(pattern, re.IGNORECASE)
                 self.fields_to_progs.setdefault(field, []).append(prog)
         except confuse.NotFoundError:
             # Matches everything
             self.fields_to_progs[field] = []
Beispiel #12
0
    def test_delete_art(self):
        mediafile = self._mediafile_fixture('empty')
        mediafile.art = self.jpg_data
        mediafile.save()

        mediafile = MediaFile(mediafile.path)
        self.assertIsNotNone(mediafile.art)

        del mediafile.art
        mediafile.save()

        mediafile = MediaFile(mediafile.path)
        self.assertIsNone(mediafile.art)
Beispiel #13
0
def get_cover(metaDict):

    cover = None
    cover = Image.open(script_path + '/images/default-cover-v6.jpg')
    covers = [
        'Cover.jpg', 'cover.jpg', 'Cover.jpeg', 'cover.jpeg', 'Cover.png',
        'cover.png', 'Cover.tif', 'cover.tif', 'Cover.tiff', 'cover.tiff',
        'Folder.jpg', 'folder.jpg', 'Folder.jpeg', 'folder.jpeg', 'Folder.png',
        'folder.png', 'Folder.tif', 'folder.tif', 'Folder.tiff', 'folder.tiff'
    ]
    if metaDict['source'] == 'radio':
        if 'coverurl' in metaDict:
            rc = '/var/local/www/' + metaDict['coverurl']
            if path.exists(rc):
                if rc != '/var/local/www/images/default-cover-v6.svg':
                    cover = Image.open(rc)

    elif metaDict['source'] == 'airplay':
        cover = ap_back
    elif metaDict['source'] == 'bluetooth':
        cover = bt_back
    elif metaDict['source'] == 'input':
        cover = jp_back
    elif metaDict['source'] == 'spotify':
        cover = sp_back
    elif metaDict['source'] == 'squeeze':
        cover = sq_back
    else:
        if 'file' in metaDict:
            if len(metaDict['file']) > 0:

                fp = '/var/lib/mpd/music/' + metaDict['file']
                mf = MediaFile(fp)
                if mf.art:
                    cover = Image.open(BytesIO(mf.art))
                    return cover
                else:
                    for it in covers:
                        cp = os.path.dirname(fp) + '/' + it

                        if path.exists(cp):
                            cover = Image.open(cp)
                            return cover
    return cover
Beispiel #14
0
    def create_importer(self, item_count=1, album_count=1):
        """Create files to import and return corresponding session.

        Copies the specified number of files to a subdirectory of
        `self.temp_dir` and creates a `ImportSessionFixture` for this path.
        """
        import_dir = os.path.join(self.temp_dir, b'import')
        if not os.path.isdir(import_dir):
            os.mkdir(import_dir)

        album_no = 0
        while album_count:
            album = util.bytestring_path(f'album {album_no}')
            album_dir = os.path.join(import_dir, album)
            if os.path.exists(album_dir):
                album_no += 1
                continue
            os.mkdir(album_dir)
            album_count -= 1

            track_no = 0
            album_item_count = item_count
            while album_item_count:
                title = f'track {track_no}'
                src = os.path.join(_common.RSRC, b'full.mp3')
                title_file = util.bytestring_path(f'{title}.mp3')
                dest = os.path.join(album_dir, title_file)
                if os.path.exists(dest):
                    track_no += 1
                    continue
                album_item_count -= 1
                shutil.copy(src, dest)
                mediafile = MediaFile(dest)
                mediafile.update({
                    'artist': 'artist',
                    'albumartist': 'album artist',
                    'title': title,
                    'album': album,
                    'mb_albumid': None,
                    'mb_trackid': None,
                })
                mediafile.save()

        config['import']['quiet'] = True
        config['import']['autotag'] = False
        config['import']['resume'] = False

        return ImportSessionFixture(self.lib,
                                    loghandler=None,
                                    query=None,
                                    paths=[import_dir])
Beispiel #15
0
    def test_cli_writes_only_r128_tags(self):
        if self.backend == "command":
            # opus not supported by command backend
            return

        album = self.add_album_fixture(2, ext="opus")
        for item in album.items():
            self._reset_replaygain(item)

        self.run_command(u'replaygain', u'-a')

        for item in album.items():
            mediafile = MediaFile(item.path)
            # does not write REPLAYGAIN_* tags
            self.assertIsNone(mediafile.rg_track_gain)
            self.assertIsNone(mediafile.rg_album_gain)
            # writes R128_* tags
            self.assertIsNotNone(mediafile.r128_track_gain)
            self.assertIsNotNone(mediafile.r128_album_gain)
Beispiel #16
0
    def test_subcommand_query_exclude(self):
        item = self.add_item_fixture(year=2016,
                                     day=13,
                                     month=3,
                                     comments=u'test comment')

        item.write()

        self.config['zero']['fields'] = ['comments']
        self.config['zero']['update_database'] = False
        self.config['zero']['auto'] = False

        self.load_plugins('zero')
        self.run_command('zero', 'year: 0000')

        mf = MediaFile(syspath(item.path))

        self.assertEqual(mf.year, 2016)
        self.assertEqual(mf.comments, u'test comment')
Beispiel #17
0
    def test_write_date_components(self):
        mediafile = self._mediafile_fixture('full')
        mediafile.year = 2001
        mediafile.month = 1
        mediafile.day = 2
        mediafile.original_year = 1999
        mediafile.original_month = 12
        mediafile.original_day = 30
        mediafile.save()

        mediafile = MediaFile(mediafile.path)
        self.assertEqual(mediafile.year, 2001)
        self.assertEqual(mediafile.month, 1)
        self.assertEqual(mediafile.day, 2)
        self.assertEqual(mediafile.date, datetime.date(2001, 1, 2))
        self.assertEqual(mediafile.original_year, 1999)
        self.assertEqual(mediafile.original_month, 12)
        self.assertEqual(mediafile.original_day, 30)
        self.assertEqual(mediafile.original_date, datetime.date(1999, 12, 30))
Beispiel #18
0
    def test_no_patterns(self):
        self.config['zero']['fields'] = ['comments', 'month']

        item = self.add_item_fixture(
            comments=u'test comment',
            title=u'Title',
            month=1,
            year=2000,
        )
        item.write()

        self.load_plugins('zero')
        item.write()

        mf = MediaFile(syspath(item.path))
        self.assertIsNone(mf.comments)
        self.assertIsNone(mf.month)
        self.assertEqual(mf.title, u'Title')
        self.assertEqual(mf.year, 2000)
Beispiel #19
0
    def test_empty_query_n_response_no_changes(self):
        item = self.add_item_fixture(year=2016,
                                     day=13,
                                     month=3,
                                     comments=u'test comment')
        item.write()
        item_id = item.id
        self.config['zero']['fields'] = ['comments']
        self.config['zero']['update_database'] = True
        self.config['zero']['auto'] = False

        self.load_plugins('zero')
        with control_stdin('n'):
            self.run_command('zero')

        mf = MediaFile(syspath(item.path))
        item = self.lib.get_item(item_id)

        self.assertEqual(item['year'], 2016)
        self.assertEqual(mf.year, 2016)
        self.assertEqual(mf.comments, u'test comment')
        self.assertEqual(item['comments'], u'test comment')
Beispiel #20
0
    def test_delete_partial_date(self):
        mediafile = self._mediafile_fixture('empty')

        mediafile.date = datetime.date(2001, 12, 3)
        mediafile.save()
        mediafile = MediaFile(mediafile.path)
        self.assertIsNotNone(mediafile.date)
        self.assertIsNotNone(mediafile.year)
        self.assertIsNotNone(mediafile.month)
        self.assertIsNotNone(mediafile.day)

        delattr(mediafile, 'month')
        mediafile.save()
        mediafile = MediaFile(mediafile.path)
        self.assertIsNotNone(mediafile.date)
        self.assertIsNotNone(mediafile.year)
        self.assertIsNone(mediafile.month)
        self.assertIsNone(mediafile.day)
Beispiel #21
0
    def __init__(self):
        super(ZeroPlugin, self).__init__()

        self.register_listener('write', self.write_event)
        self.register_listener('import_task_choice',
                               self.import_task_choice_event)

        self.config.add({
            'auto': True,
            'fields': [],
            'keep_fields': [],
            'update_database': False,
        })

        self.fields_to_progs = {}
        self.warned = False
        """Read the bulk of the config into `self.fields_to_progs`.
        After construction, `fields_to_progs` contains all the fields that
        should be zeroed as keys and maps each of those to a list of compiled
        regexes (progs) as values.
        A field is zeroed if its value matches one of the associated progs. If
        progs is empty, then the associated field is always zeroed.
        """
        if self.config['fields'] and self.config['keep_fields']:
            self._log.warning(
                u'cannot blacklist and whitelist at the same time')
        # Blacklist mode.
        elif self.config['fields']:
            for field in self.config['fields'].as_str_seq():
                self._set_pattern(field)
        # Whitelist mode.
        elif self.config['keep_fields']:
            for field in MediaFile.fields():
                if (field not in self.config['keep_fields'].as_str_seq() and
                        # These fields should always be preserved.
                        field not in ('id', 'path', 'album_id')):
                    self._set_pattern(field)
Beispiel #22
0
    def test_subcommand_update_database_true(self):
        item = self.add_item_fixture(
            year=2016,
            day=13,
            month=3,
            comments='test comment'
        )
        item.write()
        item_id = item.id
        self.config['zero']['fields'] = ['comments']
        self.config['zero']['update_database'] = True
        self.config['zero']['auto'] = False

        self.load_plugins('zero')
        with control_stdin('y'):
            self.run_command('zero')

        mf = MediaFile(syspath(item.path))
        item = self.lib.get_item(item_id)

        self.assertEqual(item['year'], 2016)
        self.assertEqual(mf.year, 2016)
        self.assertEqual(mf.comments, None)
        self.assertEqual(item['comments'], '')
Beispiel #23
0
def test_convert_an_audio_file(testfile_tree):
    """qop can copy a file"""
    root, src_dir, dst_dir = testfile_tree

    src = src_dir.joinpath("sine.flac")
    dst = dst_dir.joinpath("sine.mp3")  # expected destination name

    _utils_tests.make_dummy_flac(src)
    f = MediaFile(src)
    f.artist = "foobar"
    f.save()

    subprocess.run([
        "python3", QOP, "-v", "--log-file", "/dev/null", "convert", src,
        dst_dir
    ],
                   cwd=root)
    wait_for_queue()

    assert src.exists()
    assert dst.exists()
    assert MediaFile(dst).artist == f.artist
Beispiel #24
0
 def test_overwrite_property(self):
     with self.assertRaises(ValueError) as cm:
         MediaFile.add_field('artist', MediaField())
     self.assertIn(u'property "artist" already exists',
                   unicode(cm.exception))
Beispiel #25
0
 def test_properties_from_readable_fields(self):
     path = os.path.join(_common.RSRC, b'full.mp3')
     mediafile = MediaFile(path)
     for field in MediaFile.readable_fields():
         self.assertTrue(hasattr(mediafile, field))
Beispiel #26
0
 def test_known_fields(self):
     fields = list(ReadWriteTestBase.tag_fields)
     fields.extend(('encoder', 'images', 'genres', 'albumtype'))
     assertCountEqual(self, MediaFile.fields(), fields)
Beispiel #27
0
 def test_fields_in_readable_fields(self):
     readable = MediaFile.readable_fields()
     for field in MediaFile.fields():
         self.assertIn(field, readable)
Beispiel #28
0
def cover_art():
    @after_this_request
    def add_header(resp):
        if 'X-Sendfile' in resp.headers:
            app.logger.debug('Using X-Sendfile or X-Accel-Redirect')
            app.logger.debug(resp.headers['X-Sendfile'])
            resp.headers['X-Accel-Redirect'] = resp.headers['X-Sendfile']
        return resp

    # retrieve folder from database
    status, res = get_entity(request, Folder)

    if not status:
        return res

    # Check the folder id given for jpgs
    app.logger.debug('Cover Art Check: ' + res.path + '/*.jp*g')

    coverfile = os.listdir(res.path)
    coverfile = fnmatch.filter(coverfile, '*.jp*g')

    # when there is not a jpeg in the folder check files for embedded art
    if not coverfile:
        app.logger.debug('No Art Found in Folder, Checking Files!')

        for tr in res.tracks:
            app.logger.debug('Checking ' + tr.path + ' For Artwork')

            try:
                mf = MediaFile(tr.path)
                coverfile = getattr(mf, 'art')

                if coverfile is not None:
                    if type(coverfile) is list:
                        coverfile = coverfile[0]
                    coverfile = StringIO(coverfile)
                    app.logger.debug('Serving embedded cover art')
                    break

            except:
                app.logger.debug('Problem reading embedded art')
                return request.error_formatter(70, 'Cover art not found'), 404

            return request.error_formatter(70, 'Cover art not found'), 404
    else:
        app.logger.debug('Found Images: ' + str(coverfile))
        coverfile = coverfile[0]
        coverfile = os.path.join(res.path, coverfile)
        app.logger.debug('Serving cover art: ' + coverfile)

    size = request.args.get('size')
    if size:
        try:
            size = int(size)
        except:
            return request.error_formatter(0, 'Invalid size value'), 500
    else:
        size = 1000

    im = Image.open(coverfile)

    size_path = os.path.join(config.get('base', 'cache_dir'), str(size))
    path = os.path.join(size_path, str(res.id))

    if not os.path.exists(size_path):
        os.makedirs(size_path)

    if size > im.size[0] and size > im.size[1]:
        app.logger.debug('Not resizing Image, adding to cache')
        im.save(path, 'JPEG')
        return send_file(path)

    app.logger.debug('Saving resized image to: ' + path)

    if os.path.exists(path):
        app.logger.debug('Serving cover art: ' + path)
        return send_file(path)

    im.thumbnail([size, size], Image.ANTIALIAS)
    im.save(path, 'JPEG')

    app.logger.debug('Serving cover art: ' + path)
    return send_file(path)
Beispiel #29
0
 def analyse(target_level):
     self.config['replaygain']['targetlevel'] = target_level
     self._reset_replaygain(item)
     self.run_command(u'replaygain', '-f')
     mediafile = MediaFile(item.path)
     return mediafile.rg_track_gain
Beispiel #30
0
def write_tags(filename, tags):
    f1 = MediaFile(filename)
    writeTags = "y"
    showMoreOptions = "n"
    date = tags["release_date"].split("-")

    if not "-A" in OPTIONS:
        writeTags = input("\nWrite tags? (y/n): ")

    if writeTags == "y":
        print("\nWriting tags...")

        # Deletes current tags from the song
        f1.delete()

        date = tags["release_date"].split("-")
        f1.title = tags["title"]
        f1.album = tags["album_title"]
        # f1.artist = tags["artists"]
        f1.day = date[0]
        f1.month = date[1]
        f1.year = date[2]
        # f1.track = tags["track_number"]
        # f1.tracktotal = tags["total_tracks"]
        # f1.disc = f"{tags["disc_number"]}
        # f1.disctotal = tags["total_discs"]
        f1.genre = tags["genre"]
        f1.albumartist = tags["album_artist"]

        # Drops "Album Artist" Tag
        f1.__dict__["mgfile"].pop("album artist")

        file_dir = os.path.dirname(filename)

        if (file_dir != ""):
            os.chdir(file_dir)

        if not "-p" in OPTIONS:
            f1.art = get_artwork(filename,
                                 (tags["album_title"], tags["album_artist"]))

        f1.save()

        f2 = FLAC(filename)
        f2["Artist"] = tags["artists"]
        f2["tracknumber"] = [f"{tags['track_number']}"]
        f2["discnumber"] = [f"{tags['disc_number']}"]

        f2.save()

        print("Tags written successfully!")

        filename_ext = os.path.splitext(filename)[1]
        title = f1.title + " - " + f1.albumartist + filename_ext

        if not os.path.isfile(title):
            os.rename(filename, title)

        if os.path.isfile("./cover.jpg"):
            os.remove("./cover.jpg")

        if len(OUTPUT) != 0:
            dest = os.path.join(OUTPUT[0], os.path.basename(filename))

            if os.path.exists(dest):
                os.remove(dest)

            if os.path.exists(filename):
                shutil.move(filename, OUTPUT[0])
            elif os.path.exists(title):
                shutil.move(title, OUTPUT[0])

    if writeTags == "n" and not "-A" in OPTIONS:
        showMoreOptions = input("Show more options? (y/n): ")

    return showMoreOptions
Beispiel #31
0
 def test_fields_in_readable_fields(self):
     readable = MediaFile.readable_fields()
     for field in MediaFile.fields():
         self.assertIn(field, readable)
Beispiel #32
0
 def __init__(self, file):
     self.path = file
     self.item = MediaFile(self.path)
Beispiel #33
0
 def test_known_fields(self):
     fields = list(ReadWriteTestBase.tag_fields)
     fields.extend(('encoder', 'images', 'genres', 'albumtype'))
     assertCountEqual(self, MediaFile.fields(), fields)
Beispiel #34
0
 def test_properties_from_readable_fields(self):
     path = os.path.join(_common.RSRC, b'full.mp3')
     mediafile = MediaFile(path)
     for field in MediaFile.readable_fields():
         self.assertTrue(hasattr(mediafile, field))
Beispiel #35
0
def _open_file(file: io.BufferedReader) -> MediaFile:
    start_position = file.tell()
    in_file = MediaFile(file)
    file.seek(start_position)
    return in_file
Beispiel #36
0
def tagger(conf, destination, releaseid, source):

    _log = init_logging(conf)

    if not destination:
        destination = source

    cfg = TaggerConfig(source, destination, conf)

    if cfg.id_tag in cfg.release_tags:
        releaseid = cfg.release_tags[cfg.id_tag].strip()

    if releaseid:
        release_id = releaseid

    if not releaseid:
        click.echo('Please specify the discogs.com releaseid ("-r")')
        sys.exit(1)

    _log.info('Attempting to tag files from target destination={0}'.format(destination))

    discogs_release = DiscogsAlbum(DiscogsWrapper().discogs, release_id, cfg.split_artists, cfg.split_genres_and_styles)
    release = TaggerUtils(discogs_release, cfg)

    # ensure we were able to map the release appropriately.
    if not release.tag_map:
        _log.fatal("Unable to map available audio files to the number of tracks in the Discogs release '{0}'. Exiting".format(
                   release_id))
        sys.exit(1)

    artist = cfg.split_artists.join(release.album.artists)
    artist = release.album.clean_name(artist)

    _log.info("Tagging album '{0} - {1}'".format(artist, release.album.title))

    dest_dir_name = release.dest_dir_name

    if os.path.exists(dest_dir_name):
        _log.fatal('Destination directory already exists. directory={0}. Aborting operation'.format(dest_dir_name))
        sys.exit(1)
    else:
        _log.info("Creating destination directory '{0}'".format(dest_dir_name))
        mkdir_p(dest_dir_name)

    _log.info("Downloading and storing images")
    release.album.get_images(dest_dir_name, cfg.images_format, cfg.first_image_name)

    disc_names = dict()
    folder_names = dict()
    if release.album.disctotal > 1 and cfg.split_discs_folder:
        _log.debug("Creating disc structure")
        for i in range(1, release.album.disctotal + 1):
            folder_name = "%s%.d" % (release.album_folder_name, i)
            disc_dir_name = os.path.join(dest_dir_name, folder_name)
            mkdir_p(disc_dir_name)
    # This is duplicate, remove one of the following statements
            disc_names[i] = disc_dir_name
            folder_names[i] = folder_name
    else:
        folder_names[1] = ""

    for track in release.tag_map:
        # copy old file into new location
        if release.album.disctotal > 1 and cfg.split_discs_folder:
            target_folder = disc_names[int(track['discnumber'])]
        else:
            target_folder = dest_dir_name

        _log.debug("Source file {0}".format(os.path.join(source, track['orig_file'])))
        _log.info("Writing file {0}".format(os.path.join(target_folder, track['new_file'])))
        _log.debug("metadata -> {0:2d} {1} - {2}".format(track['tracknumber'], track['artist'], track['title']))
        _log.debug("----------> {0}".format(track['new_file']))

        shutil.copyfile(track['orig_file'], os.path.join(target_folder, track['new_file']))

        # load metadata information
        metadata = MediaFile(os.path.join(target_folder, track['new_file']))

        # read already existing (and still wanted) properties
        keep_tags = {}
        if cfg.keep_tags:
            for name in cfg.keep_tags.split(","):
                try:
                    getattr(metadata, name)
                except AttributeError:
                    _log.warn('Unable to keep_tag. tag={0}'.format(name))
                    continue
                keep_tags[name] = getattr(metadata, name)

        # remove current metadata
        metadata.delete()

        # set album metadata
        metadata.album = release.album.title

        if cfg.split_discs_folder and release.album.disctotal > 1:
            # the fileext should be stored on the album/track as well
            fileext = os.path.splitext(track['orig_file'])[1]
            disc_title_extension = release._value_from_tag_format(cfg.split_discs_extension,
                                                                  track['tracknumber'],
                                                                  track['position'] - 1, fileext)
            metadata.album = "{0}{1}".format(metadata.album, disc_title_extension)

        metadata.composer = artist
        metadata.albumartist = artist
        metadata.albumartist_sort = release.album.sort_artist
        metadata.label = release.album.label
        metadata.year = release.album.year
        metadata.country = release.album.country
        metadata.url = release.album.url
        # add styles to the grouping tag (right now, we can just use one)
        metadata.grouping = release.album.styles

        # adding two as there is no standard. discogstagger pre v1
        # used (TXXX desc="Catalog #")
        # mediafile uses TXXX desc="CATALOGNUMBER"
        metadata.catalognum = release.album.catno
        metadata.catalognumber = release.album.catno

        # use the correct genre field, on config use the first style
        genre = release.album.genres
        if cfg.use_style:
            genre = release.album.style

        metadata.genre = genre
        metadata.discogs_id = release_id

        if release.album.disctotal and release.album.disctotal > 1 and track['discnumber']:
            _log.debug("writing disctotal and discnumber")
            metadata.disc = track['discnumber']
            metadata.disctotal = release.album.disctotal

        if release.album.is_compilation:
            metadata.comp = True

        metadata.comments = release.album.note

        # encoder
        if cfg.encoder_tag is not None:
            metadata.encoder = cfg.encoder_tag

        #    if track.discsubtotal:
        #        metadata.discsubtotal = track.discsubtotal

        # set track metadata
        metadata.title = track['title']
        metadata.artist = track['artist']
        metadata.artist_sort = track['sortartist']
        metadata.track = track['tracknumber']

        # the following value will be wrong, if the disc has a name or is a multi
        # disc release --> fix it
        metadata.tracktotal = release.album.tracktotal_on_disc(track['discnumber'])

        # it does not make sense to store this in the "common" configuration, but only in the
        # id.txt. we use a special naming convention --> most probably we should reuse the
        # configuration parser for this one as well, no?
        for name, value in list(cfg.release_tags.items()):
            if name.startswith("tag:"):
                name = name.split(":")
                name = name[1]
                setattr(metadata, name, value)

        first_image_name = cfg.first_image_name
        # this should be done in a cleaner way to avoid multiple images in different
        # folders (use the dest_dir again....)
        if cfg.embed_coverart and os.path.exists(os.path.join(dest_dir_name,
                                                 first_image_name)):
            imgdata = open(os.path.join(dest_dir_name,
                           first_image_name), 'rb').read()
            imgtype = imghdr.what(None, imgdata)

            if imgtype in ("jpeg", "png"):
                _log.debug("Embedding album art.")
                metadata.art = imgdata

        if keep_tags is not None:
            for name in keep_tags:
                setattr(metadata, name, keep_tags[name])

        metadata.save()

    # start supplementary actions

    if cfg.write_nfo:
        _log.info("Generating .nfo file")
        release.create_nfo()

    # adopt for multi disc support
    if cfg.write_m3u:
        _log.info("Generating .m3u file")
        release.create_m3u(folder_names)

    # copy "other files" on request
    if cfg.copy_other_files and len(release.copy_files) > 0:
        _log.info("copying files from source directory")
        copy_files = release.copy_files
        dir_list = os.listdir(source)
        _log.debug("start_dir: {0}".format(source))
        _log.debug("dir list: {0}".format(dir_list))
        file_list = [os.path.join(source, x) for x in dir_list if not x.lower().endswith(TaggerUtils.FILE_TYPE) and
                     os.path.isfile(os.path.join(source, x))]
        copy_files.extend(file_list)

        for fname in copy_files:
            if not fname.endswith(".m3u"):
                _log.debug("source: {0}".format(fname))
                _log.debug("target: {0}".format(os.path.join(dest_dir_name, os.path.basename(fname))))
                shutil.copyfile(fname, os.path.join(dest_dir_name, os.path.basename(fname)))

    # remove source directory, if configured as such.
    if not cfg.keep_original:
        _log.info("Deleting source directory '{0}'".format(source))
        shutil.rmtree(source)

    _log.info("Tagging complete.")
Beispiel #37
0
 def test_write_initial_key_tag(self):
     self.modify(u"initial_key=C#m")
     item = self.lib.items().get()
     mediafile = MediaFile(syspath(item.path))
     self.assertEqual(mediafile.initial_key, u'C#m')
Beispiel #38
0
 def test_write_custom_tags(self):
     item = self.add_item_fixture(artist='old artist')
     item.write(tags={'artist': 'new artist'})
     self.assertNotEqual(item.artist, 'new artist')
     self.assertEqual(MediaFile(syspath(item.path)).artist, 'new artist')
Beispiel #39
0
 def test_invalid_descriptor(self):
     with self.assertRaises(ValueError) as cm:
         MediaFile.add_field('somekey', True)
     self.assertIn(u'must be an instance of MediaField',
                   unicode(cm.exception))