Beispiel #1
0
    def _remove_deleted_tags(self, metadata, tags):
        """Remove the tags from the file that were deleted in the UI"""
        config = get_config()

        for name in metadata.deleted_tags:
            real_name = self._get_tag_name(name)
            try:
                if name.startswith('performer:'):
                    role = name.split(':', 1)[1]
                    _remove_people_with_role(tags, ['TMCL', 'TIPL', 'IPLS'],
                                             role)
                elif name.startswith('comment:') or name == 'comment':
                    (lang, desc) = parse_comment_tag(name)
                    for key, frame in list(tags.items()):
                        if (frame.FrameID == 'COMM' and frame.desc == desc
                                and frame.lang == lang):
                            del tags[key]
                elif name.startswith('lyrics:') or name == 'lyrics':
                    if ':' in name:
                        desc = name.split(':', 1)[1]
                    else:
                        desc = ''
                    for key, frame in list(tags.items()):
                        if frame.FrameID == 'USLT' and frame.desc == desc:
                            del tags[key]
                elif name in self._rtipl_roles:
                    role = self._rtipl_roles[name]
                    _remove_people_with_role(tags, ['TIPL', 'IPLS'], role)
                elif name == 'musicbrainz_recordingid':
                    for key, frame in list(tags.items()):
                        if frame.FrameID == 'UFID' and frame.owner == 'http://musicbrainz.org':
                            del tags[key]
                elif real_name == 'POPM':
                    user_email = config.setting['rating_user_email']
                    for key, frame in list(tags.items()):
                        if frame.FrameID == 'POPM' and frame.email == user_email:
                            del tags[key]
                elif real_name in self.__translate:
                    del tags[real_name]
                elif name.lower() in self.__rtranslate_freetext_ci:
                    delall_ci(
                        tags,
                        'TXXX:' + self.__rtranslate_freetext_ci[name.lower()])
                elif real_name in self.__translate_freetext:
                    tags.delall('TXXX:' + real_name)
                    if real_name in self.__rrename_freetext:
                        tags.delall('TXXX:' +
                                    self.__rrename_freetext[real_name])
                elif not name.startswith(
                        "~id3:") and name not in self.__other_supported_tags:
                    tags.delall('TXXX:' + name)
                elif name.startswith("~id3:"):
                    frameid = name[5:]
                    tags.delall(frameid)
                elif name in self.__other_supported_tags:
                    del tags[real_name]
            except KeyError:
                pass
Beispiel #2
0
 def test_delall_ci(self):
     tags = {
         'TAGNAME:ABC': 'a',
         'tagname:abc': 'a',
         'TagName:Abc': 'a',
         'OtherTag': 'a'
     }
     mutagenext.delall_ci(tags, 'tagname:Abc')
     self.assertEqual({'OtherTag': 'a'}, tags)
Beispiel #3
0
    def _save(self, filename, metadata):
        log.debug("Saving file %r", filename)
        config = get_config()
        file = ASF(encode_filename(filename))
        tags = file.tags

        if config.setting['clear_existing_tags']:
            cover = tags['WM/Picture'] if config.setting[
                'preserve_images'] else None
            tags.clear()
            if cover:
                tags['WM/Picture'] = cover
        cover = []
        for image in metadata.images.to_be_saved_to_tags():
            tag_data = pack_image(image.mimetype, image.data,
                                  image_type_as_id3_num(image.maintype),
                                  image.comment)
            cover.append(ASFByteArrayAttribute(tag_data))
        if cover:
            tags['WM/Picture'] = cover
        for name, values in metadata.rawitems():
            if name.startswith('lyrics:'):
                name = 'lyrics'
            elif name == '~rating':
                values = [
                    int(values[0]) * 99 // (config.setting['rating_steps'] - 1)
                ]
            elif name == 'discnumber' and 'totaldiscs' in metadata:
                values = [
                    '%s/%s' % (metadata['discnumber'], metadata['totaldiscs'])
                ]
            if name in self.__TRANS:
                name = self.__TRANS[name]
            elif name in self.__TRANS_CI:
                if name in self.__casemap:
                    name = self.__casemap[name]
                else:
                    name = self.__TRANS_CI[name]
                delall_ci(tags, name)
            else:
                continue
            tags[name] = values

        self._remove_deleted_tags(metadata, tags)

        file.save()
Beispiel #4
0
    def _save(self, filename, metadata):
        log.debug("Saving file %r", filename)
        config = get_config()
        file = MP4(encode_filename(self.filename))
        if file.tags is None:
            file.add_tags()
        tags = file.tags

        if config.setting["clear_existing_tags"]:
            tags.clear()

        for name, values in metadata.rawitems():
            if name.startswith('lyrics:'):
                name = 'lyrics'
            if name == 'comment:':
                name = 'comment'
            if name in self.__r_text_tags:
                tags[self.__r_text_tags[name]] = values
            elif name in self.__r_bool_tags:
                tags[self.__r_bool_tags[name]] = (values[0] == '1')
            elif name in self.__r_int_tags:
                try:
                    tags[self.__r_int_tags[name]] = [
                        int(value) for value in values
                    ]
                except ValueError:
                    pass
            elif name in self.__r_freeform_tags:
                values = [v.encode("utf-8") for v in values]
                tags[self.__r_freeform_tags[name]] = values
            elif name in self.__r_freeform_tags_ci:
                values = [v.encode("utf-8") for v in values]
                delall_ci(tags, self.__r_freeform_tags_ci[name])
                if name in self.__casemap:
                    name = self.__casemap[name]
                else:
                    name = self.__r_freeform_tags_ci[name]
                tags[name] = values
            elif name == "musicip_fingerprint":
                tags["----:com.apple.iTunes:fingerprint"] = [
                    b"MusicMagic Fingerprint%s" % v.encode('ascii')
                    for v in values
                ]
            elif self.supports_tag(
                    name) and name not in self.__other_supported_tags:
                values = [v.encode("utf-8") for v in values]
                name = self.__casemap.get(name, name)
                tags['----:com.apple.iTunes:' + name] = values

        if "tracknumber" in metadata:
            try:
                tracknumber = int(metadata["tracknumber"])
            except ValueError:
                pass
            else:
                totaltracks = 0
                if "totaltracks" in metadata:
                    try:
                        totaltracks = int(metadata["totaltracks"])
                    except ValueError:
                        pass
                tags["trkn"] = [(tracknumber, totaltracks)]

        if "discnumber" in metadata:
            try:
                discnumber = int(metadata["discnumber"])
            except ValueError:
                pass
            else:
                totaldiscs = 0
                if "totaldiscs" in metadata:
                    try:
                        totaldiscs = int(metadata["totaldiscs"])
                    except ValueError:
                        pass
                tags["disk"] = [(discnumber, totaldiscs)]

        covr = []
        for image in metadata.images.to_be_saved_to_tags():
            if image.mimetype == "image/jpeg":
                covr.append(MP4Cover(image.data, MP4Cover.FORMAT_JPEG))
            elif image.mimetype == "image/png":
                covr.append(MP4Cover(image.data, MP4Cover.FORMAT_PNG))
        if covr:
            tags["covr"] = covr

        self._remove_deleted_tags(metadata, tags)

        file.save()
Beispiel #5
0
    def _save(self, filename, metadata):
        """Save metadata to the file."""
        log.debug("Saving file %r", filename)
        tags = self._get_tags(filename)

        if config.setting['clear_existing_tags']:
            tags.clear()
        images_to_save = list(metadata.images.to_be_saved_to_tags())
        if images_to_save:
            tags.delall('APIC')

        encoding = {
            'utf-8': 3,
            'utf-16': 1
        }.get(config.setting['id3v2_encoding'], 0)

        if 'tracknumber' in metadata:
            if 'totaltracks' in metadata:
                text = '%s/%s' % (metadata['tracknumber'],
                                  metadata['totaltracks'])
            else:
                text = metadata['tracknumber']
            tags.add(id3.TRCK(encoding=0, text=id3text(text, 0)))

        if 'discnumber' in metadata:
            if 'totaldiscs' in metadata:
                text = '%s/%s' % (metadata['discnumber'],
                                  metadata['totaldiscs'])
            else:
                text = metadata['discnumber']
            tags.add(id3.TPOS(encoding=0, text=id3text(text, 0)))

        if 'movementnumber' in metadata:
            if 'movementtotal' in metadata:
                text = '%s/%s' % (metadata['movementnumber'],
                                  metadata['movementtotal'])
            else:
                text = metadata['movementnumber']
            tags.add(id3.MVIN(encoding=0, text=id3text(text, 0)))

        # This is necessary because mutagens HashKey for APIC frames only
        # includes the FrameID (APIC) and description - it's basically
        # impossible to save two images, even of different types, without
        # any description.
        counters = defaultdict(lambda: 0)
        for image in images_to_save:
            desc = desctag = image.comment
            if counters[desc] > 0:
                if desc:
                    desctag = "%s (%i)" % (desc, counters[desc])
                else:
                    desctag = "(%i)" % counters[desc]
            counters[desc] += 1
            tags.add(
                id3.APIC(encoding=0,
                         mime=image.mimetype,
                         type=image_type_as_id3_num(image.maintype),
                         desc=id3text(desctag, 0),
                         data=image.data))

        tmcl = mutagen.id3.TMCL(encoding=encoding, people=[])
        tipl = mutagen.id3.TIPL(encoding=encoding, people=[])
        for name, values in metadata.rawitems():
            values = [id3text(v, encoding) for v in values]
            name = id3text(name, encoding)
            name_lower = name.lower()

            if not self.supports_tag(name):
                continue
            elif name.startswith('performer:'):
                role = name.split(':', 1)[1]
                for value in values:
                    if config.setting['write_id3v23']:
                        # TIPL will be upgraded to IPLS
                        tipl.people.append([role, value])
                    else:
                        tmcl.people.append([role, value])
            elif name == 'comment' or name.startswith('comment:'):
                (lang, desc) = parse_comment_tag(name)
                if desc.lower()[:4] == 'itun':
                    tags.delall('COMM:' + desc)
                    tags.add(
                        id3.COMM(encoding=0,
                                 desc=desc,
                                 lang='eng',
                                 text=[v + '\x00' for v in values]))
                else:
                    tags.add(
                        id3.COMM(encoding=encoding,
                                 desc=desc,
                                 lang=lang,
                                 text=values))
            elif name.startswith('lyrics:') or name == 'lyrics':
                if ':' in name:
                    desc = name.split(':', 1)[1]
                else:
                    desc = ''
                for value in values:
                    tags.add(id3.USLT(encoding=encoding, desc=desc,
                                      text=value))
            elif name in self._rtipl_roles:
                for value in values:
                    tipl.people.append([self._rtipl_roles[name], value])
            elif name == 'musicbrainz_recordingid':
                tags.add(
                    id3.UFID(owner='http://musicbrainz.org',
                             data=bytes(values[0], 'ascii')))
            elif name == '~rating':
                # Search for an existing POPM frame to get the current playcount
                for frame in tags.values():
                    if frame.FrameID == 'POPM' and frame.email == config.setting[
                            'rating_user_email']:
                        count = getattr(frame, 'count', 0)
                        break
                else:
                    count = 0

                # Convert rating to range between 0 and 255
                rating = int(
                    round(
                        float(values[0]) * 255 /
                        (config.setting['rating_steps'] - 1)))
                tags.add(
                    id3.POPM(email=config.setting['rating_user_email'],
                             rating=rating,
                             count=count))
            elif name == 'grouping':
                if config.setting['itunes_compatible_grouping']:
                    tags.add(id3.GRP1(encoding=encoding, text=values))
                else:
                    tags.add(id3.TIT1(encoding=encoding, text=values))
            elif name == 'work' and config.setting[
                    'itunes_compatible_grouping']:
                tags.add(id3.TIT1(encoding=encoding, text=values))
                tags.delall('TXXX:Work')
                tags.delall('TXXX:WORK')
            elif name in self.__rtranslate:
                frameid = self.__rtranslate[name]
                if frameid.startswith('W'):
                    valid_urls = all([all(urlparse(v)[:2]) for v in values])
                    if frameid == 'WCOP':
                        # Only add WCOP if there is only one license URL, otherwise use TXXX:LICENSE
                        if len(values) > 1 or not valid_urls:
                            tags.add(
                                self.build_TXXX(
                                    encoding, self.__rtranslate_freetext[name],
                                    values))
                        else:
                            tags.add(id3.WCOP(url=values[0]))
                    elif frameid == 'WOAR' and valid_urls:
                        for url in values:
                            tags.add(id3.WOAR(url=url))
                elif frameid.startswith('T') or frameid == 'MVNM':
                    if config.setting['write_id3v23']:
                        if frameid == 'TMOO':
                            tags.add(self.build_TXXX(encoding, 'mood', values))
                    # No need to care about the TMOO tag being added again as it is
                    # automatically deleted by Mutagen if id2v23 is selected
                    tags.add(
                        getattr(id3, frameid)(encoding=encoding, text=values))
                    if frameid == 'TSOA':
                        tags.delall('XSOA')
                    elif frameid == 'TSOP':
                        tags.delall('XSOP')
                    elif frameid == 'TSO2':
                        tags.delall('TXXX:ALBUMARTISTSORT')
            elif name_lower in self.__rtranslate_freetext_ci:
                if name_lower in self.__casemap:
                    description = self.__casemap[name_lower]
                else:
                    description = self.__rtranslate_freetext_ci[name_lower]
                delall_ci(tags, 'TXXX:' + description)
                tags.add(self.build_TXXX(encoding, description, values))
            elif name in self.__rtranslate_freetext:
                description = self.__rtranslate_freetext[name]
                if description in self.__rrename_freetext:
                    tags.delall('TXXX:' + self.__rrename_freetext[description])
                tags.add(self.build_TXXX(encoding, description, values))
            elif name.startswith('~id3:'):
                name = name[5:]
                if name.startswith('TXXX:'):
                    tags.add(self.build_TXXX(encoding, name[5:], values))
                else:
                    frameclass = getattr(id3, name[:4], None)
                    if frameclass:
                        tags.add(frameclass(encoding=encoding, text=values))
            # don't save private / already stored tags
            elif not name.startswith(
                    "~") and name not in self.__other_supported_tags:
                tags.add(self.build_TXXX(encoding, name, values))

        if tmcl.people:
            tags.add(tmcl)
        if tipl.people:
            tags.add(tipl)

        self._remove_deleted_tags(metadata, tags)

        self._save_tags(tags, encode_filename(filename))

        if self._IsMP3 and config.setting["remove_ape_from_mp3"]:
            try:
                mutagen.apev2.delete(encode_filename(filename))
            except BaseException:
                pass