Exemplo n.º 1
0
    def _test_cover_art(self, filename):
        self._set_up(filename)
        try:
            # Use reasonable large data > 64kb.
            # This checks a mutagen error with ASF files.
            tests = {
                'jpg': {
                    'mime': 'image/jpeg',
                    'data': self.jpegdata + "a" * 1024 * 128
                },
                'png': {
                    'mime': 'image/png',
                    'data': self.pngdata + "a" * 1024 * 128
                },
            }
            for t in tests:
                f = picard.formats.open(self.filename)
                metadata = Metadata()
                imgdata = tests[t]['data']
                metadata.append_image(
                    CoverArtImage(
                        data=imgdata
                    )
                )
                f._save(self.filename, metadata)

                f = picard.formats.open(self.filename)
                loaded_metadata = f._load(self.filename)
                image = loaded_metadata.images[0]
                self.assertEqual(image.mimetype, tests[t]['mime'])
                self.assertEqual(image.data, imgdata)
        finally:
            self._tear_down()
Exemplo n.º 2
0
    def _test_cover_art(self, filename):
        self._set_up(filename)
        try:
            # Use reasonable large data > 64kb.
            # This checks a mutagen error with ASF files.
            tests = {
                'jpg': {
                    'mime': 'image/jpeg',
                    'data': self.jpegdata + b"a" * 1024 * 128
                },
                'png': {
                    'mime': 'image/png',
                    'data': self.pngdata + b"a" * 1024 * 128
                },
            }
            for t in tests:
                f = picard.formats.open_(self.filename)
                metadata = Metadata()
                imgdata = tests[t]['data']
                metadata.append_image(CoverArtImage(data=imgdata))
                f._save(self.filename, metadata)

                f = picard.formats.open_(self.filename)
                loaded_metadata = f._load(self.filename)
                image = loaded_metadata.images[0]
                self.assertEqual(image.mimetype, tests[t]['mime'])
                self.assertEqual(image.data, imgdata)
        finally:
            self._tear_down()
Exemplo n.º 3
0
    def _load(self, filename):
        log.debug("Loading file %r", filename)
        file = ASF(encode_filename(filename))
        metadata = Metadata()
        for name, values in file.tags.items():
            if name == 'WM/Picture':
                for image in values:
                    (mime, data, type, description) = unpack_image(image.value)
                    try:
                        coverartimage = TagCoverArtImage(
                            file=filename,
                            tag=name,
                            types=types_from_id3(type),
                            comment=description,
                            support_types=True,
                            data=data,
                        )
                    except CoverArtImageError as e:
                        log.error('Cannot load image from %r: %s' %
                                  (filename, e))
                    else:
                        metadata.append_image(coverartimage)

                continue
            elif name not in self.__RTRANS:
                continue
            elif name == 'WM/SharedUserRating':
                # Rating in WMA ranges from 0 to 99, normalize this to the range 0 to 5
                values[0] = int(round(int(unicode(values[0])) / 99.0 * (config.setting['rating_steps'] - 1)))
            name = self.__RTRANS[name]
            values = filter(bool, map(unicode, values))
            if values:
                metadata[name] = values
        self._info(metadata, file)
        return metadata
Exemplo n.º 4
0
    def _load(self, filename):
        log.debug("Loading file %r", filename)
        file = self._File(encode_filename(filename))
        metadata = Metadata()
        if file.tags:
            for origname, values in file.tags.items():
                if origname.lower().startswith(
                        "cover art") and values.kind == mutagen.apev2.BINARY:
                    if b'\0' in values.value:
                        descr, data = values.value.split(b'\0', 1)
                        try:
                            coverartimage = TagCoverArtImage(
                                file=filename,
                                tag=origname,
                                data=data,
                            )
                        except CoverArtImageError as e:
                            log.error('Cannot load image from %r: %s' %
                                      (filename, e))
                        else:
                            metadata.append_image(coverartimage)

                # skip EXTERNAL and BINARY values
                if values.kind != mutagen.apev2.TEXT:
                    continue
                for value in values:
                    name = origname
                    if name == "Year":
                        name = "date"
                        value = sanitize_date(value)
                    elif name == "Track":
                        name = "tracknumber"
                        track = value.split("/")
                        if len(track) > 1:
                            metadata["totaltracks"] = track[1]
                            value = track[0]
                    elif name == "Disc":
                        name = "discnumber"
                        disc = value.split("/")
                        if len(disc) > 1:
                            metadata["totaldiscs"] = disc[1]
                            value = disc[0]
                    elif name == 'Performer' or name == 'Comment':
                        name = name.lower() + ':'
                        if value.endswith(')'):
                            start = value.rfind(' (')
                            if start > 0:
                                name += value[start + 2:-1]
                                value = value[:start]
                    elif name in self.__translate:
                        name = self.__translate[name]
                    else:
                        name = name.lower()
                    metadata.add(name, value)
        self._info(metadata, file)
        return metadata
Exemplo n.º 5
0
    def _load(self, filename):
        log.debug("Loading file %r", filename)
        file = self._File(encode_filename(filename))
        metadata = Metadata()
        if file.tags:
            for origname, values in file.tags.items():
                if origname.lower().startswith("cover art") and values.kind == mutagen.apev2.BINARY:
                    if '\0' in values.value:
                        descr, data = values.value.split('\0', 1)
                        try:
                            coverartimage = TagCoverArtImage(
                                file=filename,
                                tag=origname,
                                data=data,
                            )
                        except CoverArtImageError as e:
                            log.error('Cannot load image from %r: %s' %
                                      (filename, e))
                        else:
                            metadata.append_image(coverartimage)

                # skip EXTERNAL and BINARY values
                if values.kind != mutagen.apev2.TEXT:
                    continue
                for value in values:
                    name = origname
                    if name == "Year":
                        name = "date"
                        value = sanitize_date(value)
                    elif name == "Track":
                        name = "tracknumber"
                        track = value.split("/")
                        if len(track) > 1:
                            metadata["totaltracks"] = track[1]
                            value = track[0]
                    elif name == "Disc":
                        name = "discnumber"
                        disc = value.split("/")
                        if len(disc) > 1:
                            metadata["totaldiscs"] = disc[1]
                            value = disc[0]
                    elif name == 'Performer' or name == 'Comment':
                        name = name.lower() + ':'
                        if value.endswith(')'):
                            start = value.rfind(' (')
                            if start > 0:
                                name += value[start + 2:-1]
                                value = value[:start]
                    elif name in self.__translate:
                        name = self.__translate[name]
                    else:
                        name = name.lower()
                    metadata.add(name, value)
        self._info(metadata, file)
        return metadata
Exemplo n.º 6
0
Arquivo: mp4.py Projeto: Freso/picard
    def _load(self, filename):
        log.debug("Loading file %r", filename)
        file = MP4(encode_filename(filename))
        tags = file.tags
        if tags is None:
            file.add_tags()

        metadata = Metadata()
        for name, values in tags.items():
            if name in self.__text_tags:
                for value in values:
                    metadata.add(self.__text_tags[name], value)
            elif name in self.__bool_tags:
                metadata.add(self.__bool_tags[name], values and '1' or '0')
            elif name in self.__int_tags:
                for value in values:
                    metadata.add(self.__int_tags[name], unicode(value))
            elif name in self.__freeform_tags:
                for value in values:
                    value = value.strip("\x00").decode("utf-8", "replace")
                    metadata.add(self.__freeform_tags[name], value)
            elif name == "----:com.apple.iTunes:fingerprint":
                for value in values:
                    value = value.strip("\x00").decode("utf-8", "replace")
                    if value.startswith("MusicMagic Fingerprint"):
                        metadata.add("musicip_fingerprint", value[22:])
            elif name == "trkn":
                metadata["tracknumber"] = str(values[0][0])
                metadata["totaltracks"] = str(values[0][1])
            elif name == "disk":
                metadata["discnumber"] = str(values[0][0])
                metadata["totaldiscs"] = str(values[0][1])
            elif name == "covr":
                for value in values:
                    if value.imageformat not in (value.FORMAT_JPEG,
                                                 value.FORMAT_PNG):
                        continue
                    try:
                        coverartimage = TagCoverArtImage(
                            file=filename,
                            tag=name,
                            data=value,
                        )
                    except CoverArtImageError as e:
                        log.error('Cannot load image from %r: %s' %
                                  (filename, e))
                    else:
                        metadata.append_image(coverartimage)

        self._info(metadata, file)
        return metadata
Exemplo n.º 7
0
    def _load(self, filename):
        log.debug("Loading file %r", filename)
        file = MP4(encode_filename(filename))
        tags = file.tags
        if tags is None:
            file.add_tags()

        metadata = Metadata()
        for name, values in tags.items():
            if name in self.__text_tags:
                for value in values:
                    metadata.add(self.__text_tags[name], value)
            elif name in self.__bool_tags:
                metadata.add(self.__bool_tags[name], values and '1' or '0')
            elif name in self.__int_tags:
                for value in values:
                    metadata.add(self.__int_tags[name], string_(value))
            elif name in self.__freeform_tags:
                for value in values:
                    value = value.decode("utf-8", "replace").strip("\x00")
                    metadata.add(self.__freeform_tags[name], value)
            elif name == "----:com.apple.iTunes:fingerprint":
                for value in values:
                    value = value.decode("utf-8", "replace").strip("\x00")
                    if value.startswith("MusicMagic Fingerprint"):
                        metadata.add("musicip_fingerprint", value[22:])
            elif name == "trkn":
                metadata["tracknumber"] = string_(values[0][0])
                metadata["totaltracks"] = string_(values[0][1])
            elif name == "disk":
                metadata["discnumber"] = string_(values[0][0])
                metadata["totaldiscs"] = string_(values[0][1])
            elif name == "covr":
                for value in values:
                    if value.imageformat not in (value.FORMAT_JPEG,
                                                 value.FORMAT_PNG):
                        continue
                    try:
                        coverartimage = TagCoverArtImage(
                            file=filename,
                            tag=name,
                            data=value,
                        )
                    except CoverArtImageError as e:
                        log.error('Cannot load image from %r: %s' %
                                  (filename, e))
                    else:
                        metadata.append_image(coverartimage)

        self._info(metadata, file)
        return metadata
Exemplo n.º 8
0
    def _load(self, filename):
        log.debug("Loading file %r", filename)
        file = ASF(encode_filename(filename))
        metadata = Metadata()
        for name, values in file.tags.items():
            if name == 'WM/Picture':
                for image in values:
                    (mime, data, type_,
                     description) = unpack_image(image.value)
                    try:
                        coverartimage = TagCoverArtImage(
                            file=filename,
                            tag=name,
                            types=types_from_id3(type_),
                            comment=description,
                            support_types=True,
                            data=data,
                        )
                    except CoverArtImageError as e:
                        log.error('Cannot load image from %r: %s' %
                                  (filename, e))
                    else:
                        metadata.append_image(coverartimage)

                continue
            elif name not in self.__RTRANS:
                continue
            elif name == 'WM/SharedUserRating':
                # Rating in WMA ranges from 0 to 99, normalize this to the range 0 to 5
                values[0] = int(
                    round(
                        int(string_(values[0])) / 99.0 *
                        (config.setting['rating_steps'] - 1)))
            elif name == 'WM/PartOfSet':
                disc = string_(values[0]).split("/")
                if len(disc) > 1:
                    metadata["totaldiscs"] = disc[1]
                    values[0] = disc[0]
            name = self.__RTRANS[name]
            values = [string_(value) for value in values if value]
            if values:
                metadata[name] = values
        self._info(metadata, file)
        return metadata
Exemplo n.º 9
0
    def _test_cover_art(self, filename):
        self._set_up(filename)
        try:
            source_types = ["front", "booklet"]
            # Use reasonable large data > 64kb.
            # This checks a mutagen error with ASF files.
            tests = [
                CoverArtImage(data=self.jpegdata + b"a" * 1024 * 128, types=source_types),
                CoverArtImage(data=self.pngdata + b"a" * 1024 * 128, types=source_types),
            ]
            for test in tests:
                f = picard.formats.open_(self.filename)
                metadata = Metadata()
                metadata.append_image(test)
                f._save(self.filename, metadata)

                f = picard.formats.open_(self.filename)
                loaded_metadata = f._load(self.filename)
                image = loaded_metadata.images[0]
                self.assertEqual(test.mimetype, image.mimetype)
                self.assertEqual(test, image)
        finally:
            self._tear_down()
Exemplo n.º 10
0
 def _cover_metadata(self):
     imgdata = self.jpegdata
     metadata = Metadata()
     metadata.append_image(
         TagCoverArtImage(file="a", tag="a", data=imgdata + "a", support_types=True, types=[u"booklet", u"front"])
     )
     metadata.append_image(
         TagCoverArtImage(file="b", tag="b", data=imgdata + "b", support_types=True, types=[u"back"])
     )
     metadata.append_image(
         TagCoverArtImage(file="c", tag="c", data=imgdata + "c", support_types=True, types=[u"front"])
     )
     metadata.append_image(TagCoverArtImage(file="d", tag="d", data=imgdata + "d"))
     metadata.append_image(TagCoverArtImage(file="e", tag="e", data=imgdata + "e", is_front=False))
     metadata.append_image(TagCoverArtImage(file="f", tag="f", data=imgdata + "f", types=[u"front"]))
     metadata.append_image(TagCoverArtImage(file="g", tag="g", data=imgdata + "g", types=[u"back"], is_front=True))
     return metadata
Exemplo n.º 11
0
    def _load(self, filename):
        log.debug("Loading file %r", filename)
        file = self._get_file(encode_filename(filename))
        tags = file.tags or {}
        # upgrade custom 2.3 frames to 2.4
        for old, new in self.__upgrade.items():
            if old in tags and new not in tags:
                f = tags.pop(old)
                tags.add(getattr(id3, new)(encoding=f.encoding, text=f.text))
        metadata = Metadata()
        for frame in tags.values():
            frameid = frame.FrameID
            if frameid in self.__translate:
                name = self.__translate[frameid]
                if frameid.startswith('T') or frameid == 'GRP1':
                    for text in frame.text:
                        if text:
                            metadata.add(name, string_(text))
                elif frameid == 'COMM':
                    for text in frame.text:
                        if text:
                            metadata.add('%s:%s' % (name, frame.desc),
                                         string_(text))
                else:
                    metadata.add(name, string_(frame))
            elif frameid == 'TIT1':
                itunes_compatible = config.setting[
                    'itunes_compatible_grouping']
                name = 'work' if itunes_compatible else 'grouping'
                for text in frame.text:
                    if text:
                        metadata.add(name, string_(text))
            elif frameid == "TMCL":
                for role, name in frame.people:
                    if role or name:
                        metadata.add('performer:%s' % role, name)
            elif frameid == "TIPL":
                # If file is ID3v2.3, TIPL tag could contain TMCL
                # so we will test for TMCL values and add to TIPL if not TMCL
                for role, name in frame.people:
                    if role in self._tipl_roles and name:
                        metadata.add(self._tipl_roles[role], name)
                    else:
                        metadata.add('performer:%s' % role, name)
            elif frameid == 'TXXX':
                name = frame.desc
                if name in self.__translate_freetext:
                    name = self.__translate_freetext[name]
                elif ((name in self.__rtranslate) !=
                      (name in self.__rtranslate_freetext)):
                    # If the desc of a TXXX frame conflicts with the name of a
                    # Picard tag, load it into ~id3:TXXX:desc rather than desc.
                    #
                    # This basically performs an XOR, making sure that 'name'
                    # is in __rtranslate or __rtranslate_freetext, but not
                    # both. (Being in both implies we support reading it both
                    # ways.) Currently, the only tag in both is license.
                    name = '~id3:TXXX:' + name
                for text in frame.text:
                    metadata.add(name, string_(text))
            elif frameid == 'USLT':
                name = 'lyrics'
                if frame.desc:
                    name += ':%s' % frame.desc
                metadata.add(name, string_(frame.text))
            elif frameid == 'UFID' and frame.owner == 'http://musicbrainz.org':
                metadata['musicbrainz_recordingid'] = frame.data.decode(
                    'ascii', 'ignore')
            elif frameid in self.__tag_re_parse.keys():
                m = self.__tag_re_parse[frameid].search(frame.text[0])
                if m:
                    for name, value in m.groupdict().items():
                        if value is not None:
                            metadata[name] = value
                else:
                    log.error("Invalid %s value '%s' dropped in %r", frameid,
                              frame.text[0], filename)
            elif frameid == 'APIC':
                try:
                    coverartimage = TagCoverArtImage(
                        file=filename,
                        tag=frameid,
                        types=types_from_id3(frame.type),
                        comment=frame.desc,
                        support_types=True,
                        data=frame.data,
                    )
                except CoverArtImageError as e:
                    log.error('Cannot load image from %r: %s' % (filename, e))
                else:
                    metadata.append_image(coverartimage)
            elif frameid == 'POPM':
                # Rating in ID3 ranges from 0 to 255, normalize this to the range 0 to 5
                if frame.email == config.setting['rating_user_email']:
                    rating = string_(
                        int(
                            round(frame.rating / 255.0 *
                                  (config.setting['rating_steps'] - 1))))
                    metadata.add('~rating', rating)

        if 'date' in metadata:
            sanitized = sanitize_date(metadata.getall('date')[0])
            if sanitized:
                metadata['date'] = sanitized

        self._info(metadata, file)
        return metadata
Exemplo n.º 12
0
    def _load(self, filename):
        log.debug("Loading file %r", filename)
        file = self._File(encode_filename(filename))
        file.tags = file.tags or {}
        metadata = Metadata()
        for origname, values in file.tags.items():
            for value in values:
                name = origname
                if name == "date" or name == "originaldate":
                    # YYYY-00-00 => YYYY
                    value = sanitize_date(value)
                elif name == 'performer' or name == 'comment':
                    # transform "performer=Joe Barr (Piano)" to "performer:Piano=Joe Barr"
                    name += ':'
                    if value.endswith(')'):
                        start = len(value) - 2
                        count = 1
                        while count > 0 and start > 0:
                            if value[start] == ')':
                                count += 1
                            elif value[start] == '(':
                                count -= 1
                            start -= 1
                        if start > 0:
                            name += value[start + 2:-1]
                            value = value[:start]
                elif name.startswith('rating'):
                    try:
                        name, email = name.split(':', 1)
                    except ValueError:
                        email = ''
                    if email != config.setting['rating_user_email']:
                        continue
                    name = '~rating'
                    value = string_(
                        int(
                            round((float(value) *
                                   (config.setting['rating_steps'] - 1)))))
                elif name == "fingerprint" and value.startswith(
                        "MusicMagic Fingerprint"):
                    name = "musicip_fingerprint"
                    value = value[22:]
                elif name == "tracktotal":
                    if "totaltracks" in file.tags:
                        continue
                    name = "totaltracks"
                elif name == "disctotal":
                    if "totaldiscs" in file.tags:
                        continue
                    name = "totaldiscs"
                elif name == "metadata_block_picture":
                    image = mutagen.flac.Picture(
                        base64.standard_b64decode(value))
                    try:
                        coverartimage = TagCoverArtImage(
                            file=filename,
                            tag=name,
                            types=types_from_id3(image.type),
                            comment=image.desc,
                            support_types=True,
                            data=image.data,
                        )
                    except CoverArtImageError as e:
                        log.error('Cannot load image from %r: %s' %
                                  (filename, e))
                    else:
                        metadata.append_image(coverartimage)

                    continue
                elif name in self.__translate:
                    name = self.__translate[name]
                metadata.add(name, value)
        if self._File == mutagen.flac.FLAC:
            for image in file.pictures:
                try:
                    coverartimage = TagCoverArtImage(
                        file=filename,
                        tag='FLAC/PICTURE',
                        types=types_from_id3(image.type),
                        comment=image.desc,
                        support_types=True,
                        data=image.data,
                    )
                except CoverArtImageError as e:
                    log.error('Cannot load image from %r: %s' % (filename, e))
                else:
                    metadata.append_image(coverartimage)

        # Read the unofficial COVERART tags, for backward compatibillity only
        if "metadata_block_picture" not in file.tags:
            try:
                for data in file["COVERART"]:
                    try:
                        coverartimage = TagCoverArtImage(
                            file=filename,
                            tag='COVERART',
                            data=base64.standard_b64decode(data))
                    except CoverArtImageError as e:
                        log.error('Cannot load image from %r: %s' %
                                  (filename, e))
                    else:
                        metadata.append_image(coverartimage)
            except KeyError:
                pass
        self._info(metadata, file)
        return metadata
Exemplo n.º 13
0
 def _cover_metadata(self):
     imgdata = self.jpegdata
     metadata = Metadata()
     metadata.append_image(
         TagCoverArtImage(
             file='a',
             tag='a',
             data=imgdata + 'a',
             support_types=True,
             types=[u'booklet', u'front'],
         )
     )
     metadata.append_image(
         TagCoverArtImage(
             file='b',
             tag='b',
             data=imgdata + 'b',
             support_types=True,
             types=[u'back'],
         )
     )
     metadata.append_image(
         TagCoverArtImage(
             file='c',
             tag='c',
             data=imgdata + 'c',
             support_types=True,
             types=[u'front'],
         )
     )
     metadata.append_image(
         TagCoverArtImage(
             file='d',
             tag='d',
             data=imgdata + 'd',
         )
     )
     metadata.append_image(
         TagCoverArtImage(
             file='e',
             tag='e',
             data=imgdata + 'e',
             is_front=False
         )
     )
     metadata.append_image(
         TagCoverArtImage(
             file='f',
             tag='f',
             data=imgdata + 'f',
             types=[u'front']
         )
     )
     metadata.append_image(
         TagCoverArtImage(
             file='g',
             tag='g',
             data=imgdata + 'g',
             types=[u'back'],
             is_front=True
         )
     )
     return metadata
Exemplo n.º 14
0
 def _file_save_image(filename, image):
     f = picard.formats.open_(filename)
     metadata = Metadata()
     metadata.append_image(image)
     f._save(filename, metadata)
Exemplo n.º 15
0
 def _cover_metadata(self):
     imgdata = self.jpegdata
     metadata = Metadata()
     metadata.append_image(
         TagCoverArtImage(
             file='a',
             tag='a',
             data=imgdata + b'a',
             support_types=True,
             types=[u'booklet', u'front'],
         ))
     metadata.append_image(
         TagCoverArtImage(
             file='b',
             tag='b',
             data=imgdata + b'b',
             support_types=True,
             types=[u'back'],
         ))
     metadata.append_image(
         TagCoverArtImage(
             file='c',
             tag='c',
             data=imgdata + b'c',
             support_types=True,
             types=[u'front'],
         ))
     metadata.append_image(
         TagCoverArtImage(
             file='d',
             tag='d',
             data=imgdata + b'd',
         ))
     metadata.append_image(
         TagCoverArtImage(file='e',
                          tag='e',
                          data=imgdata + b'e',
                          is_front=False))
     metadata.append_image(
         TagCoverArtImage(file='f',
                          tag='f',
                          data=imgdata + b'f',
                          types=[u'front']))
     metadata.append_image(
         TagCoverArtImage(file='g',
                          tag='g',
                          data=imgdata + b'g',
                          types=[u'back'],
                          is_front=True))
     return metadata
Exemplo n.º 16
0
class Cluster(QtCore.QObject, Item):

    # Weights for different elements when comparing a cluster to a release
    comparison_weights = {
        'album': 17,
        'albumartist': 6,
        'totaltracks': 5,
        'releasecountry': 2,
        'format': 2,
    }

    def __init__(self,
                 name,
                 artist="",
                 special=False,
                 related_album=None,
                 hide_if_empty=False):
        QtCore.QObject.__init__(self)
        self.item = None
        self.metadata = Metadata()
        self.metadata['album'] = name
        self.metadata['albumartist'] = artist
        self.metadata['totaltracks'] = 0
        self.special = special
        self.hide_if_empty = hide_if_empty
        self.related_album = related_album
        self.files = []
        self.lookup_task = None

    def __repr__(self):
        if self.related_album:
            return '<Cluster %s %r>' % (self.related_album.id,
                                        self.related_album.metadata[u"album"] +
                                        '/' + self.metadata['album'])
        return '<Cluster %r>' % self.metadata['album']

    def __len__(self):
        return len(self.files)

    def _update_related_album(self):
        if self.related_album:
            self.related_album.update_metadata_images()
            self.related_album.update()

    def add_files(self, files):
        for file in files:
            self.metadata.length += file.metadata.length
            file._move(self)
            file.update(signal=False)
            cover = file.metadata.get_single_front_image()
            if cover and cover[0] not in self.metadata.images:
                self.metadata.append_image(cover[0])
        self.files.extend(files)
        self.metadata['totaltracks'] = len(self.files)
        self.item.add_files(files)
        self._update_related_album()

    def add_file(self, file):
        self.metadata.length += file.metadata.length
        self.files.append(file)
        self.metadata['totaltracks'] = len(self.files)
        file._move(self)
        file.update(signal=False)
        cover = file.metadata.get_single_front_image()
        if cover and cover[0] not in self.metadata.images:
            self.metadata.append_image(cover[0])
        self.item.add_file(file)
        self._update_related_album()

    def remove_file(self, file):
        self.metadata.length -= file.metadata.length
        self.files.remove(file)
        self.metadata['totaltracks'] = len(self.files)
        self.item.remove_file(file)
        if not self.special and self.get_num_files() == 0:
            self.tagger.remove_cluster(self)
        self.update_metadata_images()
        self._update_related_album()

    def update(self):
        if self.item:
            self.item.update()

    def get_num_files(self):
        return len(self.files)

    def iterfiles(self, save=False):
        for file in self.files:
            yield file

    def can_save(self):
        """Return if this object can be saved."""
        if self.files:
            return True
        else:
            return False

    def can_remove(self):
        """Return if this object can be removed."""
        return not self.special

    def can_edit_tags(self):
        """Return if this object supports tag editing."""
        return True

    def can_analyze(self):
        """Return if this object can be fingerprinted."""
        return any([_file.can_analyze() for _file in self.files])

    def can_autotag(self):
        return True

    def can_refresh(self):
        return False

    def can_browser_lookup(self):
        return not self.special

    def can_view_info(self):
        if self.files:
            return True
        else:
            return False

    def is_album_like(self):
        return True

    def column(self, column):
        if column == 'title':
            return '%s (%d)' % (self.metadata['album'], len(self.files))
        elif (column == '~length' and self.special) or column == 'album':
            return ''
        elif column == '~length':
            return format_time(self.metadata.length)
        elif column == 'artist':
            return self.metadata['albumartist']
        return self.metadata[column]

    def _lookup_finished(self, document, http, error):
        self.lookup_task = None

        try:
            releases = document['releases']
        except (KeyError, TypeError):
            releases = None

        mparms = {'album': self.metadata['album']}

        # no matches
        if not releases:
            self.tagger.window.set_statusbar_message(
                N_("No matching releases for cluster %(album)s"),
                mparms,
                timeout=3000)
            return

        # multiple matches -- calculate similarities to each of them
        match = sorted((self.metadata.compare_to_release(
            release, Cluster.comparison_weights) for release in releases),
                       reverse=True,
                       key=itemgetter(0))[0]

        if match[0] < config.setting['cluster_lookup_threshold']:
            self.tagger.window.set_statusbar_message(
                N_("No matching releases for cluster %(album)s"),
                mparms,
                timeout=3000)
            return
        self.tagger.window.set_statusbar_message(
            N_("Cluster %(album)s identified!"), mparms, timeout=3000)
        self.tagger.move_files_to_album(self.files, match[1]['id'])

    def lookup_metadata(self):
        """Try to identify the cluster using the existing metadata."""
        if self.lookup_task:
            return
        self.tagger.window.set_statusbar_message(
            N_("Looking up the metadata for cluster %(album)s..."),
            {'album': self.metadata['album']})
        self.lookup_task = self.tagger.mb_api.find_releases(
            self._lookup_finished,
            artist=self.metadata['albumartist'],
            release=self.metadata['album'],
            tracks=string_(len(self.files)),
            limit=QUERY_LIMIT)

    def clear_lookup_task(self):
        if self.lookup_task:
            self.tagger.webservice.remove_task(self.lookup_task)
            self.lookup_task = None

    @staticmethod
    def cluster(files, threshold):
        artistDict = ClusterDict()
        albumDict = ClusterDict()
        tracks = []
        for file in files:
            artist = file.metadata["albumartist"] or file.metadata["artist"]
            album = file.metadata["album"]
            # Improve clustering from directory structure if no existing tags
            # Only used for grouping and to provide cluster title / artist - not added to file tags.
            filename = file.filename
            if config.setting[
                    "windows_compatibility"] or sys.platform == "win32":
                filename = ntpath.splitdrive(filename)[1]
            album, artist = album_artist_from_path(filename, album, artist)
            # For each track, record the index of the artist and album within the clusters
            tracks.append((artistDict.add(artist), albumDict.add(album)))

        artist_cluster_engine = ClusterEngine(artistDict)
        artist_cluster_engine.cluster(threshold)

        album_cluster_engine = ClusterEngine(albumDict)
        album_cluster_engine.cluster(threshold)

        # Arrange tracks into albums
        albums = {}
        for i, track in enumerate(tracks):
            cluster = album_cluster_engine.getClusterFromId(track[1])
            if cluster is not None:
                albums.setdefault(cluster, []).append(i)

        # Now determine the most prominent names in the cluster and build the
        # final cluster list
        for album_id, album in albums.items():
            album_name = album_cluster_engine.getClusterTitle(album_id)

            artist_max = 0
            artist_id = None
            artist_hist = {}
            for track_id in album:
                cluster = artist_cluster_engine.getClusterFromId(
                    tracks[track_id][0])
                if cluster is not None:
                    cnt = artist_hist.get(cluster, 0) + 1
                    if cnt > artist_max:
                        artist_max = cnt
                        artist_id = cluster
                    artist_hist[cluster] = cnt

            if artist_id is None:
                artist_name = "Various Artists"
            else:
                artist_name = artist_cluster_engine.getClusterTitle(artist_id)

            yield album_name, artist_name, (files[i] for i in album)

    def update_metadata_images(self):
        update_metadata_images(self)
Exemplo n.º 17
0
    def _load(self, filename):
        log.debug("Loading file %r", filename)
        file = self._get_file(encode_filename(filename))
        tags = file.tags or {}
        # upgrade custom 2.3 frames to 2.4
        for old, new in self.__upgrade.items():
            if old in tags and new not in tags:
                f = tags.pop(old)
                tags.add(getattr(id3, new)(encoding=f.encoding, text=f.text))
        metadata = Metadata()
        for frame in tags.values():
            frameid = frame.FrameID
            if frameid in self.__translate:
                name = self.__translate[frameid]
                if frameid.startswith('T'):
                    for text in frame.text:
                        if text:
                            metadata.add(name, unicode(text))
                elif frameid == 'COMM':
                    for text in frame.text:
                        if text:
                            metadata.add('%s:%s' % (name, frame.desc), unicode(text))
                else:
                    metadata.add(name, unicode(frame))
            elif frameid == "TMCL":
                for role, name in frame.people:
                    if role or name:
                        metadata.add('performer:%s' % role, name)
            elif frameid == "TIPL":
                # If file is ID3v2.3, TIPL tag could contain TMCL
                # so we will test for TMCL values and add to TIPL if not TMCL
                for role, name in frame.people:
                    if role in self._tipl_roles and name:
                        metadata.add(self._tipl_roles[role], name)
                    else:
                        metadata.add('performer:%s' % role, name)
            elif frameid == 'TXXX':
                name = frame.desc
                if name in self.__translate_freetext:
                    name = self.__translate_freetext[name]
                elif ((name in self.__rtranslate) !=
                        (name in self.__rtranslate_freetext)):
                    # If the desc of a TXXX frame conflicts with the name of a
                    # Picard tag, load it into ~id3:TXXX:desc rather than desc.
                    #
                    # This basically performs an XOR, making sure that 'name'
                    # is in __rtranslate or __rtranslate_freetext, but not
                    # both. (Being in both implies we support reading it both
                    # ways.) Currently, the only tag in both is license.
                    name = '~id3:TXXX:' + name
                for text in frame.text:
                    metadata.add(name, unicode(text))
            elif frameid == 'USLT':
                name = 'lyrics'
                if frame.desc:
                    name += ':%s' % frame.desc
                metadata.add(name, unicode(frame.text))
            elif frameid == 'UFID' and frame.owner == 'http://musicbrainz.org':
                metadata['musicbrainz_recordingid'] = frame.data.decode('ascii', 'ignore')
            elif frameid in self.__tag_re_parse.keys():
                m = self.__tag_re_parse[frameid].search(frame.text[0])
                if m:
                    for name, value in m.groupdict().iteritems():
                        if value is not None:
                            metadata[name] = value
                else:
                    log.error("Invalid %s value '%s' dropped in %r", frameid, frame.text[0], filename)
            elif frameid == 'APIC':
                try:
                    coverartimage = TagCoverArtImage(
                        file=filename,
                        tag=frameid,
                        types=types_from_id3(frame.type),
                        comment=frame.desc,
                        support_types=True,
                        data=frame.data,
                    )
                except CoverArtImageError as e:
                    log.error('Cannot load image from %r: %s' % (filename, e))
                else:
                    metadata.append_image(coverartimage)
            elif frameid == 'POPM':
                # Rating in ID3 ranges from 0 to 255, normalize this to the range 0 to 5
                if frame.email == config.setting['rating_user_email']:
                    rating = unicode(int(round(frame.rating / 255.0 * (config.setting['rating_steps'] - 1))))
                    metadata.add('~rating', rating)

        if 'date' in metadata:
            sanitized = sanitize_date(metadata.getall('date')[0])
            if sanitized:
                metadata['date'] = sanitized

        self._info(metadata, file)
        return metadata
Exemplo n.º 18
0
    def _load(self, filename):
        self.log.debug("Loading file %r", filename)
        file = self._File(encode_filename(filename), ID3=compatid3_dsf.CompatID3)
        tags = file.tags or {}
        # upgrade custom 2.3 frames to 2.4
        for old, new in self.__upgrade.items():
            if old in tags and new not in tags:
                f = tags.pop(old)
                tags.add(getattr(id3, new)(encoding=f.encoding, text=f.text))
        metadata = Metadata()
        for frame in tags.values():
            frameid = frame.FrameID
            if frameid in self.__translate:
                name = self.__translate[frameid]
                if frameid.startswith('T'):
                    for text in frame.text:
                        if text:
                            metadata.add(name, unicode(text))
                elif frameid == 'COMM':
                    for text in frame.text:
                        if text:
                            metadata.add('%s:%s' % (name, frame.desc), unicode(text))
                else:
                    metadata.add(name, unicode(frame))
            elif frameid == "TMCL":
                for role, name in frame.people:
                    if role or name:
                        metadata.add('performer:%s' % role, name)
            elif frameid == "TIPL":
                for role, name in frame.people:
                    if role in self.__tipl_roles and name:
                        metadata.add(self.__tipl_roles[role], name)
            elif frameid == 'TXXX':
                if frame.desc in self.__translate_freetext:
                    name = self.__translate_freetext[frame.desc]
                else:
                    name = str(frame.desc.lower())
                for text in frame.text:
                    metadata.add(name, unicode(text))
            elif frameid == 'USLT':
                name = 'lyrics'
                if frame.desc:
                    name += ':%s' % frame.desc
                metadata.add(name, unicode(frame.text))
            elif frameid == 'UFID' and frame.owner == 'http://musicbrainz.org':
                metadata['musicbrainz_trackid'] = unicode(frame.data)
            elif frameid == 'TRCK':
                value = frame.text[0].split('/')
                if len(value) > 1:
                    metadata['tracknumber'], metadata['totaltracks'] = value[:2]
                else:
                    metadata['tracknumber'] = value[0]
            elif frameid == 'TPOS':
                value = frame.text[0].split('/')
                if len(value) > 1:
                    metadata['discnumber'], metadata['totaldiscs'] = value[:2]
                else:
                    metadata['discnumber'] = value[0]
            elif frameid == 'APIC':
                coverartimage = TagCoverArtImage(file=filename, tag=frameid, data=frame.data)
                metadata.append_image(coverartimage)
            elif frameid == 'POPM':
                # Rating in ID3 ranges from 0 to 255, normalize this to the range 0 to 5
                if frame.email == self.config.setting['rating_user_email']:
                    rating = unicode(int(round(frame.rating / 255.0 * (self.config.setting['rating_steps'] - 1))))
                    metadata.add('~rating', rating)

        if 'date' in metadata:
            metadata['date'] = sanitize_date(metadata.getall('date')[0])

        self._info(metadata, file)
        return metadata
Exemplo n.º 19
0
class Cluster(QtCore.QObject, Item):

    # Weights for different elements when comparing a cluster to a release
    comparison_weights = {
        'album': 17,
        'albumartist': 6,
        'totaltracks': 5,
        'releasecountry': 2,
        'format': 2,
    }

    def __init__(self, name, artist="", special=False, related_album=None, hide_if_empty=False):
        QtCore.QObject.__init__(self)
        self.item = None
        self.metadata = Metadata()
        self.metadata['album'] = name
        self.metadata['albumartist'] = artist
        self.metadata['totaltracks'] = 0
        self.special = special
        self.hide_if_empty = hide_if_empty
        self.related_album = related_album
        self.files = []
        self.lookup_task = None

    def __repr__(self):
        return '<Cluster %r>' % self.metadata['album']

    def __len__(self):
        return len(self.files)

    def _update_related_album(self):
        if self.related_album:
            self.related_album.update_metadata_images()
            self.related_album.update()

    def add_files(self, files):
        for file in files:
            self.metadata.length += file.metadata.length
            file._move(self)
            file.update(signal=False)
            cover = file.metadata.get_single_front_image()
            if cover and cover[0] not in self.metadata.images:
                self.metadata.append_image(cover[0])
        self.files.extend(files)
        self.metadata['totaltracks'] = len(self.files)
        self.item.add_files(files)
        self._update_related_album()

    def add_file(self, file):
        self.metadata.length += file.metadata.length
        self.files.append(file)
        self.metadata['totaltracks'] = len(self.files)
        file._move(self)
        file.update(signal=False)
        cover = file.metadata.get_single_front_image()
        if cover and cover[0] not in self.metadata.images:
            self.metadata.append_image(cover[0])
        self.item.add_file(file)
        self._update_related_album()

    def remove_file(self, file):
        self.metadata.length -= file.metadata.length
        self.files.remove(file)
        self.metadata['totaltracks'] = len(self.files)
        self.item.remove_file(file)
        if not self.special and self.get_num_files() == 0:
            self.tagger.remove_cluster(self)
        self.update_metadata_images()
        self._update_related_album()

    def update(self):
        if self.item:
            self.item.update()

    def get_num_files(self):
        return len(self.files)

    def iterfiles(self, save=False):
        for file in self.files:
            yield file

    def can_save(self):
        """Return if this object can be saved."""
        if self.files:
            return True
        else:
            return False

    def can_remove(self):
        """Return if this object can be removed."""
        return not self.special

    def can_edit_tags(self):
        """Return if this object supports tag editing."""
        return True

    def can_analyze(self):
        """Return if this object can be fingerprinted."""
        return any([_file.can_analyze() for _file in self.files])

    def can_autotag(self):
        return True

    def can_refresh(self):
        return False

    def can_browser_lookup(self):
        return not self.special

    def can_view_info(self):
        if self.files:
            return True
        else:
            return False

    def is_album_like(self):
        return True

    def column(self, column):
        if column == 'title':
            return '%s (%d)' % (self.metadata['album'], len(self.files))
        elif (column == '~length' and self.special) or column == 'album':
            return ''
        elif column == '~length':
            return format_time(self.metadata.length)
        elif column == 'artist':
            return self.metadata['albumartist']
        return self.metadata[column]

    def _lookup_finished(self, document, http, error):
        self.lookup_task = None

        try:
            releases = document.metadata[0].release_list[0].release
        except (AttributeError, IndexError):
            releases = None

        mparms = {
            'album': self.metadata['album']
        }

        # no matches
        if not releases:
            self.tagger.window.set_statusbar_message(
                N_("No matching releases for cluster %(album)s"),
                mparms,
                timeout=3000
            )
            return

        # multiple matches -- calculate similarities to each of them
        match = sorted((self.metadata.compare_to_release(
            release, Cluster.comparison_weights) for release in releases),
            reverse=True, key=itemgetter(0))[0]

        if match[0] < config.setting['cluster_lookup_threshold']:
            self.tagger.window.set_statusbar_message(
                N_("No matching releases for cluster %(album)s"),
                mparms,
                timeout=3000
            )
            return
        self.tagger.window.set_statusbar_message(
            N_("Cluster %(album)s identified!"),
            mparms,
            timeout=3000
        )
        self.tagger.move_files_to_album(self.files, match[1].id)

    def lookup_metadata(self):
        """Try to identify the cluster using the existing metadata."""
        if self.lookup_task:
            return
        self.tagger.window.set_statusbar_message(
            N_("Looking up the metadata for cluster %(album)s..."),
            {'album': self.metadata['album']}
        )
        self.lookup_task = self.tagger.xmlws.find_releases(self._lookup_finished,
            artist=self.metadata['albumartist'],
            release=self.metadata['album'],
            tracks=string_(len(self.files)),
            limit=QUERY_LIMIT)

    def clear_lookup_task(self):
        if self.lookup_task:
            self.tagger.xmlws.remove_task(self.lookup_task)
            self.lookup_task = None

    @staticmethod
    def cluster(files, threshold):
        artistDict = ClusterDict()
        albumDict = ClusterDict()
        tracks = []
        for file in files:
            artist = file.metadata["albumartist"] or file.metadata["artist"]
            album = file.metadata["album"]
            # Improve clustering from directory structure if no existing tags
            # Only used for grouping and to provide cluster title / artist - not added to file tags.
            filename = file.filename
            if config.setting["windows_compatibility"] or sys.platform == "win32":
                filename = ntpath.splitdrive(filename)[1]
            album, artist = album_artist_from_path(filename, album, artist)
            # For each track, record the index of the artist and album within the clusters
            tracks.append((artistDict.add(artist),
                           albumDict.add(album)))

        artist_cluster_engine = ClusterEngine(artistDict)
        artist_cluster_engine.cluster(threshold)

        album_cluster_engine = ClusterEngine(albumDict)
        album_cluster_engine.cluster(threshold)

        # Arrange tracks into albums
        albums = {}
        for i, track in enumerate(tracks):
            cluster = album_cluster_engine.getClusterFromId(track[1])
            if cluster is not None:
                albums.setdefault(cluster, []).append(i)

        # Now determine the most prominent names in the cluster and build the
        # final cluster list
        for album_id, album in albums.items():
            album_name = album_cluster_engine.getClusterTitle(album_id)

            artist_max = 0
            artist_id = None
            artist_hist = {}
            for track_id in album:
                cluster = artist_cluster_engine.getClusterFromId(
                    tracks[track_id][0])
                if cluster is not None:
                    cnt = artist_hist.get(cluster, 0) + 1
                    if cnt > artist_max:
                        artist_max = cnt
                        artist_id = cluster
                    artist_hist[cluster] = cnt

            if artist_id is None:
                artist_name = "Various Artists"
            else:
                artist_name = artist_cluster_engine.getClusterTitle(artist_id)

            yield album_name, artist_name, (files[i] for i in album)

    def update_metadata_images(self):
        update_metadata_images(self)
Exemplo n.º 20
0
    def _load(self, filename):
        log.debug("Loading file %r", filename)
        file = self._File(encode_filename(filename))
        file.tags = file.tags or {}
        metadata = Metadata()
        for origname, values in file.tags.items():
            for value in values:
                name = origname
                if name == "date" or name == "originaldate":
                    # YYYY-00-00 => YYYY
                    value = sanitize_date(value)
                elif name == 'performer' or name == 'comment':
                    # transform "performer=Joe Barr (Piano)" to "performer:Piano=Joe Barr"
                    name += ':'
                    if value.endswith(')'):
                        start = len(value) - 2
                        count = 1
                        while count > 0 and start > 0:
                            if value[start] == ')':
                                count += 1
                            elif value[start] == '(':
                                count -= 1
                            start -= 1
                        if start > 0:
                            name += value[start + 2:-1]
                            value = value[:start]
                elif name.startswith('rating'):
                    try:
                        name, email = name.split(':', 1)
                    except ValueError:
                        email = ''
                    if email != config.setting['rating_user_email']:
                        continue
                    name = '~rating'
                    value = unicode(int(round((float(value) * (config.setting['rating_steps'] - 1)))))
                elif name == "fingerprint" and value.startswith("MusicMagic Fingerprint"):
                    name = "musicip_fingerprint"
                    value = value[22:]
                elif name == "tracktotal":
                    if "totaltracks" in file.tags:
                        continue
                    name = "totaltracks"
                elif name == "disctotal":
                    if "totaldiscs" in file.tags:
                        continue
                    name = "totaldiscs"
                elif name == "metadata_block_picture":
                    image = mutagen.flac.Picture(base64.standard_b64decode(value))
                    try:
                        coverartimage = TagCoverArtImage(
                            file=filename,
                            tag=name,
                            types=types_from_id3(image.type),
                            comment=image.desc,
                            support_types=True,
                            data=image.data,
                        )
                    except CoverArtImageError as e:
                        log.error('Cannot load image from %r: %s' % (filename, e))
                    else:
                        metadata.append_image(coverartimage)

                    continue
                elif name in self.__translate:
                    name = self.__translate[name]
                metadata.add(name, value)
        if self._File == mutagen.flac.FLAC:
            for image in file.pictures:
                try:
                    coverartimage = TagCoverArtImage(
                        file=filename,
                        tag='FLAC/PICTURE',
                        types=types_from_id3(image.type),
                        comment=image.desc,
                        support_types=True,
                        data=image.data,
                    )
                except CoverArtImageError as e:
                    log.error('Cannot load image from %r: %s' % (filename, e))
                else:
                     metadata.append_image(coverartimage)

        # Read the unofficial COVERART tags, for backward compatibillity only
        if not "metadata_block_picture" in file.tags:
            try:
                for data in file["COVERART"]:
                    try:
                        coverartimage = TagCoverArtImage(
                            file=filename,
                            tag='COVERART',
                            data=base64.standard_b64decode(data)
                        )
                    except CoverArtImageError as e:
                        log.error('Cannot load image from %r: %s' % (filename, e))
                    else:
                        metadata.append_image(coverartimage)
            except KeyError:
                pass
        self._info(metadata, file)
        return metadata