Example #1
0
    def _load(self, filename):
        log.debug("Loading file %r", filename)
        self.__casemap = {}
        file = self._File(encode_filename(filename))
        metadata = Metadata()
        if file.tags:
            for origname, values in file.tags.items():
                name_lower = origname.lower()
                if (values.kind == mutagen.apev2.BINARY
                        and name_lower.startswith("cover art")):
                    if b'\0' in values.value:
                        descr, data = values.value.split(b'\0', 1)
                        try:
                            coverartimage = TagCoverArtImage(
                                file=filename,
                                tag=name_lower,
                                data=data,
                            )
                        except CoverArtImageError as e:
                            log.error('Cannot load image from %r: %s' %
                                      (filename, e))
                        else:
                            metadata.images.append(coverartimage)

                # skip EXTERNAL and BINARY values
                if values.kind != mutagen.apev2.TEXT:
                    continue
                for value in values:
                    name = name_lower
                    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 + ':'
                        if value.endswith(')'):
                            start = value.rfind(' (')
                            if start > 0:
                                name += value[start + 2:-1]
                                value = value[:start]
                    elif name in self.__rtranslate:
                        name = self.__rtranslate[name]
                    self.__casemap[name] = origname
                    metadata.add(name, value)
        self._info(metadata, file)
        return metadata
Example #2
0
 def _load(self, filename):
     self.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 = value.rfind(' (')
                     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 != self.config.setting['rating_user_email']:
                     continue
                 name = '~rating'
                 value = unicode(
                     int(
                         round(
                             (float(value) *
                              (self.config.setting['rating_steps'] - 1)))))
             elif name == "fingerprint" and value.startswith(
                     "MusicMagic Fingerprint"):
                 name = "musicip_fingerprint"
                 value = value[22:]
             elif name == "tracktotal" and "totaltracks" not in file.tags:
                 name = "totaltracks"
             elif name == "metadata_block_picture":
                 image = mutagen.flac.Picture(
                     base64.standard_b64decode(value))
                 metadata.add_image(image.mime, image.data)
                 continue
             metadata.add(name, value)
     if self._File == mutagen.flac.FLAC:
         for image in file.pictures:
             metadata.add_image(image.mime, image.data)
     # Read the unofficial COVERART tags, for backward compatibillity only
     if not "metadata_block_picture" in file.tags:
         try:
             for index, data in enumerate(file["COVERART"]):
                 metadata.add_image(file["COVERARTMIME"][index],
                                    base64.standard_b64decode(data))
         except KeyError:
             pass
     self._info(metadata, file)
     return metadata
Example #3
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
Example #4
0
 def _load(self, filename):
     self.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 = value.rfind(' (')
                     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 != self.config.setting['rating_user_email']:
                     continue
                 name = '~rating'
                 value = unicode(int(round((float(value) * (self.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))
                 metadata.add_image(image.mime, image.data)
                 continue
             metadata.add(name, value)
     if self._File == mutagen.flac.FLAC:
         for image in file.pictures:
             metadata.add_image(image.mime, image.data)
     # Read the unofficial COVERART tags, for backward compatibillity only
     if not "metadata_block_picture" in file.tags:
         try:
             for index, data in enumerate(file["COVERART"]):
                 metadata.add_image(file["COVERARTMIME"][index], base64.standard_b64decode(data))
         except KeyError:
             pass
     self._info(metadata, file)
     return metadata
Example #5
0
 def test_correct(self):
     self.assertEqual(util.sanitize_date("2006--"), "2006")
     self.assertEqual(util.sanitize_date("2006--02"), "2006")
     self.assertEqual(util.sanitize_date("2006   "), "2006")
     self.assertEqual(util.sanitize_date("2006 02"), "")
     self.assertEqual(util.sanitize_date("2006.02"), "")
     self.assertEqual(util.sanitize_date("2006-02"), "2006-02")
Example #6
0
 def test_correct(self):
     self.failUnlessEqual(util.sanitize_date("2006--"), "2006")
     self.failUnlessEqual(util.sanitize_date("2006--02"), "2006")
     self.failUnlessEqual(util.sanitize_date("2006   "), "2006")
     self.failUnlessEqual(util.sanitize_date("2006 02"), "")
     self.failUnlessEqual(util.sanitize_date("2006.02"), "")
     self.failUnlessEqual(util.sanitize_date("2006-02"), "2006-02")
Example #7
0
 def test_correct(self):
     self.assertEqual(util.sanitize_date("2006--"), "2006")
     self.assertEqual(util.sanitize_date("2006--02"), "2006")
     self.assertEqual(util.sanitize_date("2006   "), "2006")
     self.assertEqual(util.sanitize_date("2006 02"), "")
     self.assertEqual(util.sanitize_date("2006.02"), "")
     self.assertEqual(util.sanitize_date("2006-02"), "2006-02")
Example #8
0
 def test_correct(self):
     self.failUnlessEqual(util.sanitize_date("2006--"), "2006")
     self.failUnlessEqual(util.sanitize_date("2006--02"), "2006")
     self.failUnlessEqual(util.sanitize_date("2006   "), "2006")
     self.failUnlessEqual(util.sanitize_date("2006 02"), "")
     self.failUnlessEqual(util.sanitize_date("2006.02"), "")
     self.failUnlessEqual(util.sanitize_date("2006-02"), "2006-02")
Example #9
0
 def _load(self, filename):
     self.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)
                     mime = mimetype.get_from_data(data, descr,
                                                   'image/jpeg')
                     metadata.add_image(mime, data)
             # 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
Example #10
0
    def _save(self, filename, metadata, settings):
        """Save metadata to the file."""
        self.log.debug("Saving file %r", filename)
        file = self._File(encode_filename(filename))
        if file.tags is None:
            file.add_tags()
        if settings["clear_existing_tags"]:
            file.tags.clear()
        if self._File == mutagen.flac.FLAC and (
            settings["clear_existing_tags"] or
            (settings['save_images_to_tags'] and metadata.images)):
            file.clear_pictures()
        tags = {}
        for name, value in metadata.items():
            if name == '~rating':
                # Save rating according to http://code.google.com/p/quodlibet/wiki/Specs_VorbisComments
                if settings['rating_user_email']:
                    name = 'rating:%s' % settings['rating_user_email']
                else:
                    name = 'rating'
                value = unicode(float(value) / (settings['rating_steps'] - 1))
            # don't save private tags
            elif name.startswith("~"):
                continue
            if name.startswith('lyrics:'):
                name = 'lyrics'
            elif name == "date" or name == "originaldate":
                # YYYY-00-00 => YYYY
                value = sanitize_date(value)
            elif name.startswith('performer:') or name.startswith('comment:'):
                # transform "performer:Piano=Joe Barr" to "performer=Joe Barr (Piano)"
                name, desc = name.split(':', 1)
                if desc:
                    value += ' (%s)' % desc
            elif name == "musicip_fingerprint":
                name = "fingerprint"
                value = "MusicMagic Fingerprint%s" % value
            tags.setdefault(name.upper().encode('utf-8'), []).append(value)

        if "totaltracks" in metadata:
            tags.setdefault(u"TRACKTOTAL", []).append(metadata["totaltracks"])
        if "totaldiscs" in metadata:
            tags.setdefault(u"DISCTOTAL", []).append(metadata["totaldiscs"])

        if settings['save_images_to_tags']:
            for mime, data in metadata.images:
                image = mutagen.flac.Picture()
                image.type = 3 # Cover image
                image.data = data
                image.mime = mime
                if self._File == mutagen.flac.FLAC:
                    file.add_picture(image)
                else:
                    tags.setdefault(u"METADATA_BLOCK_PICTURE", []).append(
                        base64.standard_b64encode(image.write()))
        file.tags.update(tags)
        kwargs = {}
        if self._File == mutagen.flac.FLAC and settings["remove_id3_from_flac"]:
            kwargs["deleteid3"] = True
        try:
            file.save(**kwargs)
        except TypeError:
            file.save()
Example #11
0
    def _save(self, filename, metadata):
        """Save metadata to the file."""
        log.debug("Saving file %r", filename)
        is_flac = self._File == mutagen.flac.FLAC
        file = self._File(encode_filename(filename))
        if file.tags is None:
            file.add_tags()
        if config.setting["clear_existing_tags"]:
            channel_mask = file.tags.get('waveformatextensible_channel_mask',
                                         None)
            file.tags.clear()
            if channel_mask:
                file.tags['waveformatextensible_channel_mask'] = channel_mask
        images_to_save = list(metadata.images.to_be_saved_to_tags())
        if is_flac and (config.setting["clear_existing_tags"]
                        or images_to_save):
            file.clear_pictures()
        tags = {}
        for name, value in metadata.items():
            if name == '~rating':
                # Save rating according to http://code.google.com/p/quodlibet/wiki/Specs_VorbisComments
                user_email = sanitize_key(config.setting['rating_user_email'])
                if user_email:
                    name = 'rating:%s' % user_email
                else:
                    name = 'rating'
                value = str(
                    float(value) / (config.setting['rating_steps'] - 1))
            # don't save private tags
            elif name.startswith("~") or not self.supports_tag(name):
                continue
            elif name.startswith('lyrics:'):
                name = 'lyrics'
            elif name == "date" or name == "originaldate":
                # YYYY-00-00 => YYYY
                value = sanitize_date(value)
            elif name.startswith('performer:') or name.startswith('comment:'):
                # transform "performer:Piano=Joe Barr" to "performer=Joe Barr (Piano)"
                name, desc = name.split(':', 1)
                if desc:
                    value += ' (%s)' % desc
            elif name == "musicip_fingerprint":
                name = "fingerprint"
                value = "MusicMagic Fingerprint%s" % value
            elif name in self.__rtranslate:
                name = self.__rtranslate[name]
            tags.setdefault(name.upper(), []).append(value)

        if "totaltracks" in metadata:
            tags.setdefault("TRACKTOTAL", []).append(metadata["totaltracks"])
        if "totaldiscs" in metadata:
            tags.setdefault("DISCTOTAL", []).append(metadata["totaldiscs"])

        for image in images_to_save:
            picture = mutagen.flac.Picture()
            picture.data = image.data
            picture.mime = image.mimetype
            picture.desc = image.comment
            picture.width = image.width
            picture.height = image.height
            picture.type = image_type_as_id3_num(image.maintype)
            if is_flac:
                # See https://xiph.org/flac/format.html#metadata_block_picture
                expected_block_size = (8 * 4 + len(picture.data) +
                                       len(picture.mime) +
                                       len(picture.desc.encode('UTF-8')))
                if expected_block_size > FLAC_MAX_BLOCK_SIZE:
                    log.error(
                        'Failed saving image to %r: Image size of %d bytes exceeds maximum FLAC block size of %d bytes',
                        filename, expected_block_size, FLAC_MAX_BLOCK_SIZE)
                    continue
                file.add_picture(picture)
            else:
                tags.setdefault("METADATA_BLOCK_PICTURE", []).append(
                    base64.b64encode(picture.write()).decode('ascii'))

        file.tags.update(tags)

        self._remove_deleted_tags(metadata, file.tags)

        if is_flac:
            flac_sort_pics_after_tags(file.metadata_blocks)

        kwargs = {}
        if is_flac and config.setting["remove_id3_from_flac"]:
            kwargs["deleteid3"] = True
        try:
            file.save(**kwargs)
        except TypeError:
            file.save()
Example #12
0
    def _load(self, filename):
        log.debug("Loading file %r", filename)
        file = self._File(encode_filename(filename), ID3=compatid3.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":
                # 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 == "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":
                extras = {"desc": frame.desc, "type": image_type_from_id3_num(frame.type)}
                metadata.add_image(frame.mime, frame.data, extras=extras)
            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
Example #13
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))
                 imagetype = ID3_REVERSE_IMAGE_TYPE_MAP.get(image.type, "other")
                 metadata.add_image(image.mime, image.data, description=image.desc, type_=imagetype)
                 continue
             metadata.add(name, value)
     if self._File == mutagen.flac.FLAC:
         for image in file.pictures:
             imagetype = ID3_REVERSE_IMAGE_TYPE_MAP.get(image.type, "other")
             metadata.add_image(image.mime, image.data, description=image.desc, type_=imagetype)
     # Read the unofficial COVERART tags, for backward compatibillity only
     if not "metadata_block_picture" in file.tags:
         try:
             for index, data in enumerate(file["COVERART"]):
                 metadata.add_image(file["COVERARTMIME"][index], base64.standard_b64decode(data))
         except KeyError:
             pass
     self._info(metadata, file)
     return metadata
Example #14
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
Example #15
0
    def _load(self, filename):
        log.debug("Loading file %r", filename)
        file = self._File(encode_filename(filename), ID3=compatid3.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":
                # 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 == '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':
                extras = {
                    'desc': frame.desc,
                    'type': image_type_from_id3_num(frame.type)
                }
                metadata.add_image(frame.mime, frame.data, extras=extras)
            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
Example #16
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
Example #17
0
    def _save(self, filename, metadata):
        """Save metadata to the file."""
        log.debug("Saving file %r", filename)
        is_flac = self._File == mutagen.flac.FLAC
        file = self._File(encode_filename(filename))
        if file.tags is None:
            file.add_tags()
        if config.setting["clear_existing_tags"]:
            file.tags.clear()
        if (is_flac and (config.setting["clear_existing_tags"]
                         or metadata.images_to_be_saved_to_tags)):
            file.clear_pictures()
        tags = {}
        for name, value in metadata.items():
            if name == '~rating':
                # Save rating according to http://code.google.com/p/quodlibet/wiki/Specs_VorbisComments
                if config.setting['rating_user_email']:
                    name = 'rating:%s' % config.setting['rating_user_email']
                else:
                    name = 'rating'
                value = string_(
                    float(value) / (config.setting['rating_steps'] - 1))
            # don't save private tags
            elif name.startswith("~"):
                continue
            elif name.startswith('lyrics:'):
                name = 'lyrics'
            elif name == "date" or name == "originaldate":
                # YYYY-00-00 => YYYY
                value = sanitize_date(value)
            elif name.startswith('performer:') or name.startswith('comment:'):
                # transform "performer:Piano=Joe Barr" to "performer=Joe Barr (Piano)"
                name, desc = name.split(':', 1)
                if desc:
                    value += ' (%s)' % desc
            elif name == "musicip_fingerprint":
                name = "fingerprint"
                value = "MusicMagic Fingerprint%s" % value
            elif name in self.__rtranslate:
                name = self.__rtranslate[name]
            tags.setdefault(name.upper(), []).append(value)

        if "totaltracks" in metadata:
            tags.setdefault("TRACKTOTAL", []).append(metadata["totaltracks"])
        if "totaldiscs" in metadata:
            tags.setdefault("DISCTOTAL", []).append(metadata["totaldiscs"])

        for image in metadata.images_to_be_saved_to_tags:
            picture = mutagen.flac.Picture()
            picture.data = image.data
            picture.mime = image.mimetype
            picture.desc = image.comment
            picture.type = image_type_as_id3_num(image.maintype)
            if self._File == mutagen.flac.FLAC:
                file.add_picture(picture)
            else:
                tags.setdefault("METADATA_BLOCK_PICTURE", []).append(
                    base64.b64encode(picture.write()).decode('ascii'))

        file.tags.update(tags)

        self._remove_deleted_tags(metadata, file.tags)

        kwargs = {}
        if is_flac and config.setting["remove_id3_from_flac"]:
            kwargs["deleteid3"] = True
        try:
            file.save(**kwargs)
        except TypeError:
            file.save()
Example #18
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
Example #19
0
 def test_incorrect(self):
     self.failIfEqual(util.sanitize_date("2006--02"), "2006-02")
     self.failIfEqual(util.sanitize_date("2006.03.02"), "2006-03-02")
Example #20
0
    def _load(self, filename):
        self.log.debug("Loading file %r", filename)
        file = self._File(encode_filename(filename), ID3=compatid3.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' and frame.desc in self.__translate_freetext:
                name = self.__translate_freetext[frame.desc]
                for text in frame.text:
                    metadata.add(name, unicode(text))
            elif frameid == 'USLT':
                name = 'lyrics'
                if frame.desc:
                    name += 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':
                metadata.add_image(frame.mime, frame.data)
            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
Example #21
0
    def _load(self, filename):
        self.log.debug("Loading file %r", filename)
        file = self._File(encode_filename(filename), ID3=compatid3.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' and frame.desc in self.__translate_freetext:
                name = self.__translate_freetext[frame.desc]
                for text in frame.text:
                    metadata.add(name, unicode(text))
            elif frameid == 'USLT':
                name = 'lyrics'
                if frame.desc:
                    name += 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':
                metadata.add_image(frame.mime, frame.data)
            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
Example #22
0
    def _save(self, filename, metadata, settings):
        """Save metadata to the file."""
        self.log.debug("Saving file %r", filename)
        file = self._File(encode_filename(filename))
        if file.tags is None:
            file.add_tags()
        if settings["clear_existing_tags"]:
            file.tags.clear()
        if self._File == mutagen.flac.FLAC and (
                settings["clear_existing_tags"] or
            (settings['save_images_to_tags'] and metadata.images)):
            file.clear_pictures()
        tags = {}
        for name, value in metadata.items():
            if name == '~rating':
                # Save rating according to http://code.google.com/p/quodlibet/wiki/Specs_VorbisComments
                if settings['rating_user_email']:
                    name = 'rating:%s' % settings['rating_user_email']
                else:
                    name = 'rating'
                value = unicode(float(value) / (settings['rating_steps'] - 1))
            # don't save private tags
            elif name.startswith("~"):
                continue
            if name.startswith('lyrics:'):
                name = 'lyrics'
            elif name == "date" or name == "originaldate":
                # YYYY-00-00 => YYYY
                value = sanitize_date(value)
            elif name.startswith('performer:') or name.startswith('comment:'):
                # transform "performer:Piano=Joe Barr" to "performer=Joe Barr (Piano)"
                name, desc = name.split(':', 1)
                if desc:
                    value += ' (%s)' % desc
            elif name == "musicip_fingerprint":
                name = "fingerprint"
                value = "MusicMagic Fingerprint%s" % value
            tags.setdefault(name.upper().encode('utf-8'), []).append(value)

        if "totaltracks" in metadata:
            tags.setdefault(u"TRACKTOTAL", []).append(metadata["totaltracks"])
        if "totaldiscs" in metadata:
            tags.setdefault(u"DISCTOTAL", []).append(metadata["totaldiscs"])

        if settings['save_images_to_tags']:
            for mime, data in metadata.images:
                image = mutagen.flac.Picture()
                image.type = 3  # Cover image
                image.data = data
                image.mime = mime
                if self._File == mutagen.flac.FLAC:
                    file.add_picture(image)
                else:
                    tags.setdefault(u"METADATA_BLOCK_PICTURE", []).append(
                        base64.standard_b64encode(image.write()))
        file.tags.update(tags)
        kwargs = {}
        if self._File == mutagen.flac.FLAC and settings["remove_id3_from_flac"]:
            kwargs["deleteid3"] = True
        try:
            file.save(**kwargs)
        except TypeError:
            file.save()
Example #23
0
    def _save(self, filename, metadata):
        """Save metadata to the file."""
        log.debug("Saving file %r", filename)
        file = self._File(encode_filename(filename))
        if file.tags is None:
            file.add_tags()
        if config.setting["clear_existing_tags"]:
            file.tags.clear()
        if self._File == mutagen.flac.FLAC and (
            config.setting["clear_existing_tags"] or (config.setting["save_images_to_tags"] and metadata.images)
        ):
            file.clear_pictures()
        tags = {}
        for name, value in metadata.items():
            if name == "~rating":
                # Save rating according to http://code.google.com/p/quodlibet/wiki/Specs_VorbisComments
                if config.setting["rating_user_email"]:
                    name = "rating:%s" % config.setting["rating_user_email"]
                else:
                    name = "rating"
                value = unicode(float(value) / (config.setting["rating_steps"] - 1))
            # don't save private tags
            elif name.startswith("~"):
                continue
            if name.startswith("lyrics:"):
                name = "lyrics"
            elif name == "date" or name == "originaldate":
                # YYYY-00-00 => YYYY
                value = sanitize_date(value)
            elif name.startswith("performer:") or name.startswith("comment:"):
                # transform "performer:Piano=Joe Barr" to "performer=Joe Barr (Piano)"
                name, desc = name.split(":", 1)
                if desc:
                    value += " (%s)" % desc
            elif name == "musicip_fingerprint":
                name = "fingerprint"
                value = "MusicMagic Fingerprint%s" % value
            tags.setdefault(name.upper().encode("utf-8"), []).append(value)

        if "totaltracks" in metadata:
            tags.setdefault(u"TRACKTOTAL", []).append(metadata["totaltracks"])
        if "totaldiscs" in metadata:
            tags.setdefault(u"DISCTOTAL", []).append(metadata["totaldiscs"])

        if config.setting["save_images_to_tags"]:
            for image in metadata.images:
                if config.setting["save_only_front_images_to_tags"] and image["type"] != "front":
                    continue
                picture = mutagen.flac.Picture()
                picture.data = image["data"]
                picture.mime = image["mime"]
                picture.desc = image["description"]
                picture.type = ID3_IMAGE_TYPE_MAP.get(image["type"], 0)
                if self._File == mutagen.flac.FLAC:
                    file.add_picture(picture)
                else:
                    tags.setdefault(u"METADATA_BLOCK_PICTURE", []).append(base64.standard_b64encode(picture.write()))
        file.tags.update(tags)
        kwargs = {}
        if self._File == mutagen.flac.FLAC and config.setting["remove_id3_from_flac"]:
            kwargs["deleteid3"] = True
        try:
            file.save(**kwargs)
        except TypeError:
            file.save()
Example #24
0
 def test_incorrect(self):
     self.failIfEqual(util.sanitize_date("2006--02"), "2006-02")
     self.failIfEqual(util.sanitize_date("2006.03.02"), "2006-03-02")
Example #25
0
 def test_incorrect(self):
     self.assertNotEqual(util.sanitize_date("2006--02"), "2006-02")
     self.assertNotEqual(util.sanitize_date("2006.03.02"), "2006-03-02")
Example #26
0
 def test_incorrect(self):
     self.assertNotEqual(util.sanitize_date("2006--02"), "2006-02")
     self.assertNotEqual(util.sanitize_date("2006.03.02"), "2006-03-02")
Example #27
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
Example #28
0
    def _save(self, filename, metadata):
        """Save metadata to the file."""
        log.debug("Saving file %r", filename)
        is_flac = self._File == mutagen.flac.FLAC
        file = self._File(encode_filename(filename))
        if file.tags is None:
            file.add_tags()
        if config.setting["clear_existing_tags"]:
            channel_mask = file.tags.get('waveformatextensible_channel_mask', None)
            file.tags.clear()
            if channel_mask:
                file.tags['waveformatextensible_channel_mask'] = channel_mask
        if (is_flac and (config.setting["clear_existing_tags"] or
                         metadata.images_to_be_saved_to_tags)):
            file.clear_pictures()
        tags = {}
        for name, value in metadata.items():
            if name == '~rating':
                # Save rating according to http://code.google.com/p/quodlibet/wiki/Specs_VorbisComments
                if config.setting['rating_user_email']:
                    name = 'rating:%s' % config.setting['rating_user_email']
                else:
                    name = 'rating'
                value = str(float(value) / (config.setting['rating_steps'] - 1))
            # don't save private tags
            elif name.startswith("~") or not self.supports_tag(name):
                continue
            elif name.startswith('lyrics:'):
                name = 'lyrics'
            elif name == "date" or name == "originaldate":
                # YYYY-00-00 => YYYY
                value = sanitize_date(value)
            elif name.startswith('performer:') or name.startswith('comment:'):
                # transform "performer:Piano=Joe Barr" to "performer=Joe Barr (Piano)"
                name, desc = name.split(':', 1)
                if desc:
                    value += ' (%s)' % desc
            elif name == "musicip_fingerprint":
                name = "fingerprint"
                value = "MusicMagic Fingerprint%s" % value
            elif name in self.__rtranslate:
                name = self.__rtranslate[name]
            tags.setdefault(name.upper(), []).append(value)

        if "totaltracks" in metadata:
            tags.setdefault("TRACKTOTAL", []).append(metadata["totaltracks"])
        if "totaldiscs" in metadata:
            tags.setdefault("DISCTOTAL", []).append(metadata["totaldiscs"])

        for image in metadata.images_to_be_saved_to_tags:
            picture = mutagen.flac.Picture()
            picture.data = image.data
            picture.mime = image.mimetype
            picture.desc = image.comment
            picture.type = image_type_as_id3_num(image.maintype)
            if self._File == mutagen.flac.FLAC:
                file.add_picture(picture)
            else:
                tags.setdefault("METADATA_BLOCK_PICTURE", []).append(
                    base64.b64encode(picture.write()).decode('ascii'))

        file.tags.update(tags)

        self._remove_deleted_tags(metadata, file.tags)

        kwargs = {}
        if is_flac and config.setting["remove_id3_from_flac"]:
            kwargs["deleteid3"] = True
        try:
            file.save(**kwargs)
        except TypeError:
            file.save()
Example #29
0
    def _load(self, filename):
        log.debug("Loading file %r", filename)
        file = self._File(encode_filename(filename), ID3=compatid3.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":
                # 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 == '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':
                extras = {
                    'desc': frame.desc,
                    'type': image_type_from_id3_num(frame.type)
                }
                metadata.add_image(frame.mime, frame.data, extras=extras)
            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
Example #30
0
    def _load(self, filename):
        self.log.debug("Loading file %r", filename)
        file = self._File(encode_filename(filename), ID3=compatid3.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" and frame.desc in self.__translate_freetext:
                name = self.__translate_freetext[frame.desc]
                for text in frame.text:
                    metadata.add(name, unicode(text))
            elif frameid == "USLT":
                name = "lyrics"
                if frame.desc:
                    name += 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":
                metadata.add_image(frame.mime, frame.data)
            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