Example #1
0
 def setFirstComment(self, _value):
     self.isSave = True
     keys = self.tags.getall("COMM")
     for key in keys:
         self.tags[key.HashKey] = id3.COMM(
             encoding=3,
             text=self.correctValuesForMusicTagType(_value),
             lang=key.lang,
             desc=key.desc)
     if len(keys) == 0:
         #self.tags["COMM::'XXX'"] = id3.COMM(encoding=3, text=self.correctValuesForMusicTagType(_value), lang="XXX")
         self.tags["COMM::'eng'"] = id3.COMM(
             encoding=3,
             text=self.correctValuesForMusicTagType(_value),
             lang="eng")
Example #2
0
        def set_id3_tags(audio):
            # add ID3 tag if it doesn't exist
            audio.add_tags()

            def embed_image(data):
                audio.tags.add(
                    id3.APIC(
                        encoding=3,
                        mime='image/jpeg',
                        type=3,
                        desc='Front Cover',
                        data=data
                    )
                )

            save_cover_image(embed_image)

            if album is not None:
                audio.tags.add(
                    id3.TALB(text=[tag_to_ascii(track.album.name, album)],
                             encoding=3))
            audio.tags.add(
                id3.TIT2(text=[tag_to_ascii(track.name, title)],
                         encoding=3))
            audio.tags.add(
                id3.TPE1(text=[tag_to_ascii(artists, artists_ascii)],
                         encoding=3))
            if album_artist is not None:
                audio.tags.add(
                    id3.TPE2(text=[tag_to_ascii(
                                       track.album.artist.name, album_artist)],
                             encoding=3))
            audio.tags.add(id3.TDRC(text=[str(track.album.year)],
                                    encoding=3))
            audio.tags.add(
                id3.TPOS(text=[idx_of_total_str(track.disc, num_discs)],
                         encoding=3))
            audio.tags.add(
                id3.TRCK(text=[idx_of_total_str(track.index, num_tracks)],
                         encoding=3))
            if args.comment is not None:
                audio.tags.add(
                    id3.COMM(text=[tag_to_ascii(comment, comment_ascii)],
                             encoding=3))
            if args.grouping is not None:
                audio.tags.add(
                    id3.TIT1(text=[tag_to_ascii(grouping, grouping_ascii)],
                             encoding=3))
            if genres is not None and genres:
                tcon_tag = id3.TCON(encoding=3)
                tcon_tag.genres = genres if args.ascii_path_only \
                    else genres_ascii
                audio.tags.add(tcon_tag)

            if args.id3_v23:
                audio.tags.update_to_v23()
                audio.save(v2_version=3, v23_sep='/')
                audio.tags.version = (2, 3, 0)
            else:
                audio.save()
Example #3
0
    def _set_tag(self, raw, tag, data):
        if tag not in self.tag_mapping.itervalues():
            tag = "TXXX:" + tag

        if raw.tags is not None:
            raw.tags.delall(tag)

        # FIXME: Properly set and retrieve multiple values
        if tag == 'USLT':
            data = data[0]

        if tag == 'APIC':
            frames = [
                id3.Frames[tag](encoding=3,
                                mime=info.mime,
                                type=info.type,
                                desc=info.desc,
                                data=info.data) for info in data
            ]
        elif tag == 'COMM':
            frames = [
                id3.COMM(encoding=3, text=d, desc='', lang='\x00\x00\x00')
                for d in data
            ]
        else:
            frames = [id3.Frames[tag](encoding=3, text=data)]

        if raw.tags is not None:
            for frame in frames:
                raw.tags.add(frame)
Example #4
0
        def set_id3_tags_raw(audio, audio_file):
            try:
                id3_dict = id3.ID3(audio_file)
            except id3.ID3NoHeaderError:
                id3_dict = id3.ID3()

            def embed_image(data):
                id3_dict.add(
                    id3.APIC(encoding=3,
                             mime='image/jpeg',
                             type=3,
                             desc='Front Cover',
                             data=data))

            save_cover_image(embed_image)

            if album is not None:
                id3_dict.add(
                    id3.TALB(text=[tag_to_ascii(track.album.name, album)],
                             encoding=3))
            id3_dict.add(
                id3.TIT2(text=[tag_to_ascii(track.name, title)], encoding=3))
            id3_dict.add(
                id3.TPE1(text=[tag_to_ascii(artists, artists_ascii)],
                         encoding=3))
            if album_artist is not None:
                id3_dict.add(
                    id3.TPE2(text=[
                        tag_to_ascii(track.album.artist.name, album_artist)
                    ],
                             encoding=3))
            id3_dict.add(id3.TDRC(text=[str(track.album.year)], encoding=3))
            id3_dict.add(
                id3.TPOS(text=[idx_of_total_str(track.disc, num_discs)],
                         encoding=3))
            id3_dict.add(
                id3.TRCK(text=[idx_of_total_str(track.index, num_tracks)],
                         encoding=3))
            if args.comment is not None:
                id3_dict.add(
                    id3.COMM(text=[tag_to_ascii(comment, comment_ascii)],
                             encoding=3))
            if args.grouping is not None:
                audio.tags.add(
                    id3.TIT1(text=[tag_to_ascii(grouping, grouping_ascii)],
                             encoding=3))
            if genres is not None and genres:
                tcon_tag = id3.TCON(encoding=3)
                tcon_tag.genres = genres if args.ascii_path_only \
                    else genres_ascii
                id3_dict.add(tcon_tag)

            if args.id3_v23:
                id3_dict.update_to_v23()
                id3_dict.save(audio_file, v2_version=3, v23_sep='/')
                id3_dict.version = (2, 3, 0)
            else:
                id3_dict.save(audio_file)
            audio.tags = id3_dict
Example #5
0
    def set_tags(self, audio):
        try:
            id3_dict = id3.ID3(self.audio_file)
        except id3.ID3NoHeaderError:
            id3_dict = id3.ID3()

        def embed_image(data):
            id3_dict.add(
                id3.APIC(encoding=3,
                         mime='image/jpeg',
                         type=3,
                         desc='Front Cover',
                         data=data))

        self.save_cover_image(embed_image)

        if self.album() is not None:
            id3_dict.add(id3.TALB(text=[self.album()], encoding=3))

        id3_dict.add(id3.TIT2(text=[self.title()], encoding=3))
        id3_dict.add(id3.TPE1(text=[self.artists()], encoding=3))

        if self.album_artist() is not None:
            id3_dict.add(id3.TPE2(text=[self.album_artist()], encoding=3))

        id3_dict.add(id3.TDRC(text=[self.year()], encoding=3))
        id3_dict.add(id3.TPOS(text=[self.disc_idx_and_total()], encoding=3))
        id3_dict.add(id3.TRCK(text=[self.track_idx_and_total()], encoding=3))

        if self.comment() is not None:
            id3_dict.add(id3.COMM(text=[self.comment()], encoding=3))

        if self.grouping() is not None:
            id3_dict.add(id3.TIT1(text=[self.grouping()], encoding=3))

        if self.genres() is not None:
            tcon_tag = id3.TCON(encoding=3)
            tcon_tag.genres = self.genres()
            id3_dict.add(tcon_tag)

        if self.args.id3_v23:
            id3_dict.update_to_v23()
            id3_dict.save(self.audio_file, v2_version=3, v23_sep='/')
            id3_dict.version = (2, 3, 0)
        else:
            id3_dict.save(self.audio_file)
        audio.tags = id3_dict
Example #6
0
def attach_mp3_idv3(doc, file):
    """
    :type doc: Mp3Model
    :param doc:
    :param file:
    :return:
    """
    artists = [('["%s",%d]' % (x['name'], x.id)) for x in doc.artists]
    artists_str = reduce(lambda x, y: x + "," + y, artists)

    authors = reduce(lambda x, y: x + ',' + y,
                     [x['name'] for x in doc.artists])
    data = {
        'title': doc['name'],
        'artist': authors,
        'album': doc['album']['name'],
        'album_artist': authors,
        'track_num': str(doc['no']),
    }

    if Config().get_media_tag_163():
        comment_plaintext = u'music:{"musicId":%d,"musicName":"%s","bitrate":320000,' \
                            u'"albumId":%d,"album":"%s", "artist":[%s]}' \
                            % (doc.id, doc['name'], doc.album.id, doc.album['name'],
                               artists_str)
        comment = "163 key(Don't modify):" + aes_ecb(comment_plaintext,
                                                     aes_code).decode()
        data['comment'] = comment

    try:
        mp3 = ID3(file, v2_version=3)
        for k, v in data.items():
            if k not in mutagen_idv3_key_map:
                continue

            if k == 'comment':
                mp3.add(id3.COMM(lang='XXX', text=v))
                continue

            attr_type = getattr(id3, mutagen_idv3_key_map[k], None)
            if attr_type:
                mp3.add(attr_type(text=v))

        mp3.save(v2_version=3)
    except Exception as e:
        return False
    return True
Example #7
0
        def set_id3_tags_raw(audio, audio_file):
            try:
                id3_dict = id3.ID3(audio_file)
            except id3.ID3NoHeaderError:
                id3_dict = id3.ID3()

            def embed_image():
                id3_dict.add(
                    id3.APIC(encoding=3,
                             mime='image/jpeg',
                             type=3,
                             desc='Front Cover',
                             data=image.data))

            save_cover_image(embed_image)

            if album is not None:
                id3_dict.add(
                    id3.TALB(text=[tag_to_ascii(track.album.name, album)],
                             encoding=3))
            id3_dict.add(
                id3.TIT2(text=[tag_to_ascii(track.name, title)], encoding=3))
            id3_dict.add(
                id3.TPE1(text=[tag_to_ascii(track.artists[0].name, artist)],
                         encoding=3))
            id3_dict.add(id3.TDRC(text=[str(track.album.year)], encoding=3))
            id3_dict.add(
                id3.TPOS(text=[idx_of_total_str(track.disc, num_discs)],
                         encoding=3))
            id3_dict.add(
                id3.TRCK(text=[idx_of_total_str(track.index, num_tracks)],
                         encoding=3))
            if args.comment is not None:
                id3_dict.add(
                    id3.COMM(text=[tag_to_ascii(args.comment[0], comment)],
                             encoding=3))
            if genres is not None and genres:
                tcon_tag = id3.TCON(encoding=3)
                tcon_tag.genres = genres if args.ascii_path_only \
                    else genres_ascii
                id3_dict.add(tcon_tag)

            id3_dict.save(audio_file)
            audio.tags = id3_dict
Example #8
0
    def set_tags(self, audio):
        # add ID3 tag if it doesn't exist
        audio.add_tags()

        def embed_image(data):
            audio.tags.add(
                id3.APIC(encoding=3,
                         mime='image/jpeg',
                         type=3,
                         desc='Front Cover',
                         data=data))

        self.save_cover_image(embed_image)

        if self.album() is not None:
            audio.tags.add(id3.TALB(text=[self.album()], encoding=3))

        audio.tags.add(id3.TIT2(text=[self.title()], encoding=3))
        audio.tags.add(id3.TPE1(text=[self.artists()], encoding=3))

        if self.album_artist() is not None:
            audio.tags.add(id3.TPE2(text=[self.album_artist()], encoding=3))

        audio.tags.add(id3.TDRC(text=[self.year()], encoding=3))
        audio.tags.add(id3.TPOS(text=[self.disc_idx_and_total()], encoding=3))
        audio.tags.add(id3.TRCK(text=[self.track_idx_and_total()], encoding=3))

        if self.comment() is not None:
            audio.tags.add(id3.COMM(text=[self.comment()], encoding=3))

        if self.grouping() is not None:
            audio.tags.add(id3.TIT1(text=[self.grouping()], encoding=3))

        if self.genres() is not None:
            tcon_tag = id3.TCON(encoding=3)
            tcon_tag.genres = self.genres()
            audio.tags.add(tcon_tag)

        if self.args.id3_v23:
            audio.tags.update_to_v23()
            audio.save(v2_version=3, v23_sep='/')
            audio.tags.version = (2, 3, 0)
        else:
            audio.save()
Example #9
0
def attach_mp3_idv3(filename, data):

    try:
        mp3 = ID3(filename, v2_version=3)
        for k, v in data.items():
            if k not in mutagen_idv3_key_map:
                continue

            if k == 'comment':
                mp3.add(id3.COMM(lang='XXX', text=v))
                continue

            attr_type = getattr(id3, mutagen_idv3_key_map[k], None)
            if attr_type:
                mp3.add(attr_type(text=v))

        mp3.save()
    except Exception as e:
        return False
    return True
Example #10
0
        def set_id3_tags(audio):
            # add ID3 tag if it doesn't exist
            audio.add_tags()

            def embed_image():
                audio.tags.add(
                    id3.APIC(encoding=3,
                             mime='image/jpeg',
                             type=3,
                             desc='Front Cover',
                             data=image.data))

            save_cover_image(embed_image)

            if album is not None:
                audio.tags.add(
                    id3.TALB(text=[tag_to_ascii(track.album.name, album)],
                             encoding=3))
            audio.tags.add(
                id3.TIT2(text=[tag_to_ascii(track.name, title)], encoding=3))
            audio.tags.add(
                id3.TPE1(text=[tag_to_ascii(track.artists[0].name, artist)],
                         encoding=3))
            audio.tags.add(id3.TDRC(text=[str(track.album.year)], encoding=3))
            audio.tags.add(
                id3.TPOS(text=[idx_of_total_str(track.disc, num_discs)],
                         encoding=3))
            audio.tags.add(
                id3.TRCK(text=[idx_of_total_str(track.index, num_tracks)],
                         encoding=3))
            if args.comment is not None:
                audio.tags.add(
                    id3.COMM(text=[tag_to_ascii(args.comment[0], comment)],
                             encoding=3))
            if genres is not None and genres:
                tcon_tag = id3.TCON(encoding=3)
                tcon_tag.genres = genres if args.ascii_path_only \
                    else genres_ascii
                audio.tags.add(tcon_tag)

            audio.save()
Example #11
0
 def _write_id3v2_tag(self, file_name, step, obj):
     song = self.active_class(file_name)
     song.delete()
     song['TPE1'] = id3.TPE1(encoding=3, text=[obj.artist[step]])
     song['TALB'] = id3.TALB(encoding=3, text=[obj.album])
     if obj.genre and not obj.tgenre:
         song['TCON'] = id3.TCON(encoding=3, text=[obj.genre])
     elif obj.tgenre:
         song['TCON'] = id3.TCON(encoding=3, text=[obj.tgenre[step]])
     song['TIT2'] = id3.TIT2(encoding=3, text=[obj.title[step]])
     number = '{0}/{1}'.format(int(obj.track[step]), int(obj.track[-1]))
     song['TRCK'] = id3.TRCK(encoding=3, text=[number])
     if obj.year and not obj.tdate:
         song['TDRC'] = id3.TDRC(encoding=3, text=[obj.year])
     elif obj.tdate:
         song['TDRC'] = id3.TDRC(encoding=3, text=[obj.tdate[step]])
     song['COMM::XXX'] = id3.COMM(encoding=3,
                                  lang='XXX',
                                  desc='',
                                  text=[obj.comment])
     song.save(file_name)
Example #12
0
    def id3edit(self):
        """ ID3タグを編集 """

        # ID3タグ書き換え encoding: UTF-16 with BOM (1)
        self.tags['TIT2'] = id3.TIT2(encoding=1, text=self.title)
        self.tags['TALB'] = id3.TALB(encoding=1, text=self.album)
        self.tags['TPE1'] = id3.TPE1(encoding=1, text=self.artist)
        self.tags['TCON'] = id3.TCON(encoding=1, text=self.genre)
        #self.tags['TPE2'] = TPE2(encoding=1, text=self.albumartist)

        self.tags.delall('COMM')
        self.tags['COMM'] = id3.COMM(encoding=1, lang='eng', text=self.comment)

        # アートワーク書き換え
        if not self.artwork_url == '':   # アートワーク画像のURLがある場合
            # 画像読み込み
            try:
                artwork_read = urlopen(self.artwork_url).read()
            except:
                raise URLOpenError("画像を取得できません")

            # アートワーク初期化
            self.tags.delall('APIC')

            # 画像設定
            self.tags['APIC'] = id3.APIC(
                encoding=1, mime='image/jpeg', type=3, desc='Cover', data=artwork_read)

        # 保存
        self.tags.save(self.filepath)

        # 表示用アートワーク更新
        artworks = self.tags.getall('APIC')  # list
        artwork = None
        for artwork in artworks:    # 抽出
            pass
        if artwork:     # アートワーク画像が存在するか
            self.artwork = artwork.data  # type: bytes
        else:
            self.artwork = None
Example #13
0
def create_comment(desc, value):
    frame = id3.COMM(encoding, 'XXX', desc, value)
    frame.get_value = lambda: get_text(frame)
    frame.set_value = partial(set_text, frame)
    return {'comment:' + frame.desc: frame}
Example #14
0
    def _save(self, filename, metadata):
        """Save metadata to the file."""
        log.debug("Saving file %r", filename)
        try:
            tags = compatid3.CompatID3(encode_filename(filename))
        except mutagen.id3.ID3NoHeaderError:
            tags = compatid3.CompatID3()

        if config.setting['clear_existing_tags']:
            tags.clear()
        if config.setting['save_images_to_tags'] and metadata.images:
            tags.delall('APIC')

        if config.setting['write_id3v1']:
            v1 = 2
        else:
            v1 = 0
        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=text))

        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=text))

        if config.setting['save_images_to_tags']:
            # 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 metadata.images:
                desc = desctag = image['desc']
                if not save_this_image_to_tags(image):
                    continue
                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["mime"],
                             type=image_type_as_id3_num(image['type']),
                             desc=desctag,
                             data=image["data"]))

        tmcl = mutagen.id3.TMCL(encoding=encoding, people=[])
        tipl = mutagen.id3.TIPL(encoding=encoding, people=[])

        tags.delall('TCMP')
        for name, values in metadata.rawitems():
            if name.startswith('performer:'):
                role = name.split(':', 1)[1]
                for value in values:
                    tmcl.people.append([role, value])
            elif name.startswith('comment:'):
                desc = name.split(':', 1)[1]
                if desc.lower()[:4] == "itun":
                    tags.delall('COMM:' + desc)
                    tags.add(
                        id3.COMM(encoding=0,
                                 desc=desc,
                                 lang='eng',
                                 text=[v + u'\x00' for v in values]))
                else:
                    tags.add(
                        id3.COMM(encoding=encoding,
                                 desc=desc,
                                 lang='eng',
                                 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=str(values[0])))
            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 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(
                                id3.TXXX(encoding=encoding,
                                         desc=self.__rtranslate_freetext[name],
                                         text=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'):
                    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 in self.__rtranslate_freetext:
                tags.add(
                    id3.TXXX(encoding=encoding,
                             desc=self.__rtranslate_freetext[name],
                             text=values))
            elif name.startswith('~id3:'):
                name = name[5:]
                if name.startswith('TXXX:'):
                    tags.add(
                        id3.TXXX(encoding=encoding, desc=name[5:],
                                 text=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 not name in self.__other_supported_tags:
                tags.add(id3.TXXX(encoding=encoding, desc=name, text=values))

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

        if config.setting['write_id3v23']:
            tags.update_to_v23(join_with=config.setting['id3v23_join_with'])
            tags.save(encode_filename(filename), v2=3, v1=v1)
        else:
            tags.update_to_v24()
            tags.save(encode_filename(filename), v2=4, v1=v1)

        if self._IsMP3 and config.setting["remove_ape_from_mp3"]:
            try:
                mutagen.apev2.delete(encode_filename(filename))
            except:
                pass
Example #15
0
 def _write_tags(self, tags):
     for tag, value in tags.items():
         if tag == "releaseName" or tag == "release_name" or tag == "TALB":
             self._add_tag(id3.TALB(encoding=UTF_8, text=value))
         elif tag == "front_cover" or tag == "APIC_FRONT":
             mime, desc, data = value
             self._add_tag(id3.APIC(UTF_8, mime, FRONT_COVER, desc, data))
         elif tag == "backCover" or tag == "APIC_BACK":
             mime, desc, data = value
             self._add_tag(id3.APIC(UTF_8, mime, BACK_COVER, desc, data))
         elif tag == "lead_performer" or tag == "TPE1":
             self._add_tag(id3.TPE1(encoding=UTF_8, text=value))
         elif tag == "lead_performer_region" or tag == "TXXX_LEAD_PERFORMER_REGION":
             self._add_tag(
                 id3.TXXX(encoding=UTF_8,
                          desc="LEAD-PERFORMER-REGION:UN/LOCODE",
                          text=value))
         elif tag == "lead_performer_date_of_birth" or tag == "TXXX_LEAD_PERFORMER_DATE_OF_BIRTH":
             self._add_tag(
                 id3.TXXX(encoding=UTF_8,
                          desc="LEAD-PERFORMER-DATE-OF-BIRTH",
                          text=value))
         elif tag == "guestPerformers" or tag == "TMCL":
             self._add_tag(id3.TMCL(encoding=UTF_8, people=value))
         elif tag == "label_name" or tag == "TOWN":
             self._add_tag(id3.TOWN(encoding=UTF_8, text=value))
         elif tag == "catalogNumber" or tag == "TXXX_CATALOG_NUMBER":
             self._add_tag(
                 id3.TXXX(encoding=UTF_8, desc="Catalog Number",
                          text=value))
         elif tag == "upc" or tag == "TXXX_UPC":
             """ Deprecated and replaced with BARCODE """
             self._add_tag(id3.TXXX(encoding=UTF_8, desc="UPC", text=value))
         elif tag == "barcode" or tag == "TXXX_BARCODE":
             self._add_tag(
                 id3.TXXX(encoding=UTF_8, desc="BARCODE", text=value))
         elif tag == "recording_time" or tag == "TDRC":
             self._add_tag(
                 id3.TDRC(encoding=UTF_8, text=[id3.ID3TimeStamp(value)]))
         elif tag == "releaseTime" or tag == "TDRL":
             self._add_tag(
                 id3.TDRL(encoding=UTF_8, text=[id3.ID3TimeStamp(value)]))
         elif tag == "originalReleaseTime" or tag == "TDOR":
             self._add_tag(
                 id3.TDOR(encoding=UTF_8, text=[id3.ID3TimeStamp(value)]))
         elif tag == "tagging_time" or tag == "TDTG":
             self._add_tag(
                 id3.TDTG(encoding=UTF_8, text=[id3.ID3TimeStamp(value)]))
         elif tag == "recordingStudios" or tag == "TXXX_RECORDING_STUDIO":
             self._add_tag(
                 id3.TXXX(encoding=UTF_8,
                          desc="Recording Studio",
                          text=value))
         elif tag == "TIPL":
             self._add_tag(id3.TIPL(encoding=UTF_8, people=value))
         elif tag == "comments" or tag == "COMM":
             text, lang = value
             self._add_tag(
                 id3.COMM(encoding=UTF_8, text=text, desc="", lang=lang))
         elif tag == "track_title" or tag == "TIT2":
             self._add_tag(id3.TIT2(encoding=UTF_8, text=value))
         elif tag == "version_info" or tag == "TPE4":
             self._add_tag(id3.TPE4(encoding=UTF_8, text=value))
         elif tag == "featured_guest" or tag == "TXXX_FEATURED_GUEST":
             self._add_tag(
                 id3.TXXX(encoding=UTF_8, desc="Featured Guest",
                          text=value))
         elif tag == "lyricist" or tag == "TEXT":
             self._add_tag(id3.TEXT(encoding=UTF_8, text=value))
         elif tag == "composer" or tag == "TCOM":
             self._add_tag(id3.TCOM(encoding=UTF_8, text=value))
         elif tag == "publisher" or tag == "TPUB":
             self._add_tag(id3.TPUB(encoding=UTF_8, text=value))
         elif tag == "isrc" or tag == "TSRC":
             self._add_tag(id3.TSRC(encoding=UTF_8, text=value))
         elif tag == "labels" or tag == "TXXX_TAGS":
             self._add_tag(id3.TXXX(encoding=UTF_8, desc="Tags",
                                    text=value))
         elif tag == "isni" or tag == "TXXX_ISNI_Joel_Miller":
             self._add_tag(
                 id3.TXXX(encoding=UTF_8,
                          desc="ISNI:Joel Miller",
                          text=value))
         elif tag == "TXXX_ISNI_Rebecca_Ann_Maloy":
             self._add_tag(
                 id3.TXXX(encoding=UTF_8,
                          desc="ISNI:Rebecca Ann Maloy",
                          text=value))
         elif tag == "TXXX_IPI_Joel_Miller":
             self._add_tag(
                 id3.TXXX(encoding=UTF_8,
                          desc="IPI:Joel Miller",
                          text=value))
         elif tag == "TXXX_IPI_Rebecca_Ann_Maloy":
             self._add_tag(
                 id3.TXXX(encoding=UTF_8,
                          desc="IPI:Rebecca Ann Maloy",
                          text=value))
         elif tag == "TXXX_ISNI":
             self._add_tag(id3.TXXX(encoding=UTF_8, desc="ISNI",
                                    text=value))
         elif tag == "iswc" or tag == "TXXX_ISWC":
             self._add_tag(id3.TXXX(encoding=UTF_8, desc="ISWC",
                                    text=value))
         elif tag == "lyrics" or tag == "USLT":
             text, lang = value
             self._add_tag(
                 id3.USLT(encoding=UTF_8, text=text, desc="", lang=lang))
         elif tag == "language" or tag == "TLAN":
             self._add_tag(id3.TLAN(encoding=UTF_8, text=value))
         elif tag == "primary_style" or tag == "TCON":
             self._add_tag(id3.TCON(encoding=UTF_8, text=value))
         elif tag == "compilation" or tag == "TCMP":
             self._add_tag(id3.TCMP(encoding=UTF_8, text=value))
         elif tag == "tagger" or tag == "TXXX_TAGGER":
             self._add_tag(
                 id3.TXXX(encoding=UTF_8, desc="TAGGER", text=value))
         elif tag == "TXXX_Tagger":
             """ Deprecated and replaced with separate TAGGER and VERSION tags """
             self._add_tag(
                 id3.TXXX(encoding=UTF_8, desc="Tagger", text=value))
         elif tag == "tagger_version" or tag == "TXXX_TAGGER_VERSION":
             self._add_tag(
                 id3.TXXX(encoding=UTF_8, desc="TAGGER_VERSION",
                          text=value))
         elif tag == "TXXX_Tagging_Time":
             """ Deprecated and replaced with TAGGING_TIME """
             self._add_tag(
                 id3.TXXX(encoding=UTF_8, desc="Tagging Time", text=value))
         elif tag == "TXXX_TAGGING_TIME":
             """ Deprecated and replaced with TDTG """
             self._add_tag(
                 id3.TXXX(encoding=UTF_8, desc="TAGGING_TIME", text=value))
         elif tag == "TXXX_PRODUCTION_COMPANY":
             self._add_tag(
                 id3.TXXX(encoding=UTF_8,
                          desc="PRODUCTION-COMPANY",
                          text=value))
         elif tag == "TXXX_PRODUCTION_COMPANY_REGION":
             self._add_tag(
                 id3.TXXX(encoding=UTF_8,
                          desc="PRODUCTION-COMPANY-REGION:UN/LOCODE",
                          text=value))
         elif tag == "TXXX_RECORDING_STUDIO_REGION":
             self._add_tag(
                 id3.TXXX(encoding=UTF_8,
                          desc="RECORDING-STUDIO-REGION:UN/LOCODE",
                          text=value))
         elif tag == "TXXX_RECORDING_STUDIO_ADDRESS":
             self._add_tag(
                 id3.TXXX(encoding=UTF_8,
                          desc="RECORDING-STUDIO-ADDRESS",
                          text=value))
         elif tag == "TRCK":
             self._add_tag(id3.TRCK(encoding=UTF_8, text=value))
         else:
             raise AssertionError("Knows nothing about '{0}'".format(tag))
def add_id3_data_to_mp3(mp3_path,
                        track_name,
                        artist_name,
                        album_name,
                        genres,
                        cover_art_path=None,
                        remove_cover_art=True):
    mp3_path = Path(mp3_path).resolve()
    cover_art_path = Path(cover_art_path).resolve()

    try:
        mp3_meta = id3.ID3(mp3_path)
    except mutagen.id3.ID3NoHeaderError:
        mp3_meta = mutagen.File(mp3_path, easy=True)
        mp3_meta.add_tags()
        mp3_meta.save()
        mp3_meta = id3.ID3(mp3_path)

    # track
    mp3_meta.add(id3.TIT2(text=track_name, encoding=3))
    mp3_meta.add(id3.TSOT(text=track_name, encoding=3))

    # artist
    mp3_meta.add(id3.TOPE(text=artist_name, encoding=3))
    mp3_meta.add(id3.TPE1(text=artist_name, encoding=3))
    mp3_meta.add(id3.TSO2(text=artist_name, encoding=3))

    # album
    mp3_meta.add(id3.TALB(text=album_name, encoding=3))
    mp3_meta.add(id3.TOAL(text=album_name, encoding=3))
    mp3_meta.add(id3.TSOA(text=album_name, encoding=3))

    # genre
    if len(genres) > 0:
        mp3_meta.add(id3.TCON(text=genres[0], encoding=3))

    # comment
    mp3_meta.add(
        id3.COMM(text='finessed', desc=u'finessed', encoding=3, lang='ENG'))

    # cover art
    if cover_art_path:
        ext = Path(cover_art_path).suffix[1:]
        just_name = Path(cover_art_path).stem
        parent_dir = Path(cover_art_path).parent

        if ext == 'jpg' or ext == 'jpeg' or ext == 'webp' or ext == 'png':
            if ext == 'jpg' or ext == 'jpeg' or ext == 'webp':
                with Image.open(cover_art_path).convert("RGB") as cover_art:
                    cover_art.save(f"{parent_dir / just_name}.png", "png")
                    og_cover_art = str(cover_art_path)
                    cover_art_path = f"{parent_dir / just_name}.png"

            with open(cover_art_path, 'rb') as cover_art:
                mp3_meta.add(
                    id3.APIC(encoding=3,
                             mime=f'image/png',
                             type=3,
                             desc='youtube thumbnail',
                             data=cover_art.read()))

            if remove_cover_art:
                Path(cover_art_path).unlink()
                Path(og_cover_art).unlink()

    mp3_meta.save()
Example #17
0
def tag_audio_file(settings, sub, jar, entry):
    '''Metadata tagging using mutagen'''
    # id3 settings
    id3v1 = id3v1_dic[settings.id3removev1.text]
    id3v2 = int(settings.id3v2version)
    id3encoding = encodings[id3v2]
    # overrides
    frames = sub.xpath('./metadata/*')
    overrides = [(override.tag, override.text) for override in frames]
    key_errors = {}
    # track numbering
    tracks = sub.find('./track_numbering')
    tracks = tracks.text if tracks else 'no'
    if not overrides and tracks == 'no':
        return Outcome(True, 'Tagging skipped')
    # get 'easy' access to metadata
    try:
        audio = mutagen.File(entry['poca_abspath'], easy=True)
    except mutagen.MutagenError:
        return Outcome(
            False, '%s not found or invalid file type for tagging' %
            entry['poca_abspath'])
    except mutagen.mp3.HeaderNotFoundError:
        return Outcome(False, '%s is a bad mp3' % entry['poca_abspath'])
    if audio is None:
        return Outcome(
            False,
            '%s is invalid file type for tagging' % entry['poca_abspath'])
    # add_tags is undocumented for easy but seems to work
    if audio.tags is None:
        audio.add_tags()
    # tracks
    if tracks == 'yes' or (tracks == 'if missing'
                           and 'tracknumber' not in audio):
        track_no = jar.track_no if hasattr(jar, 'track_no') else 0
        track_no += 1
        overrides.append(('tracknumber', str(track_no)))
        jar.track_no = track_no
        jar.save()
    # run overrides and save
    while overrides:
        tag, text = overrides.pop()
        if not text and tag in audio:
            _text = audio.pop(tag)
            continue
        try:
            audio[tag] = text
        except (easyid3.EasyID3KeyError, easymp4.EasyMP4KeyError, \
                ValueError) as e:
            key_errors[tag] = text
    audio.save()
    # ONLY for ID3
    if isinstance(audio, mutagen.mp3.EasyMP3):
        audio = mutagen.File(entry['poca_abspath'], easy=False)
        if 'comment' in key_errors:
            audio.tags.delall('COMM')
            comm_txt = key_errors.pop('comment')
            if comm_txt:
                comm = id3.COMM(encoding=id3encoding, lang='eng', \
                                desc='desc', text=comm_txt)
                audio.tags.add(comm)
        if 'chapters' in key_errors:
            _toc = key_errors.pop('chapters')
            audio.tags.delall('CTOC')
            audio.tags.delall('CHAP')
        if id3v2 == 3:
            audio.tags.update_to_v23()
        elif id3v2 == 4:
            audio.tags.update_to_v24()
        audio.save(v1=id3v1, v2_version=id3v2)
    # invalid keys
    invalid_keys = list(key_errors.keys())
    if not invalid_keys:
        return Outcome(True, 'Metadata successfully updated')
    else:
        return Outcome(
            False, '%s is set to add invalid tags: %s' %
            (sub.title.text, ', '.join(invalid_keys)))
Example #18
0
    def _save(self, filename, metadata, settings):
        """Save metadata to the file."""
        self.log.debug("Saving file %r", filename)
        try:
            tags = compatid3.CompatID3(encode_filename(filename))
        except mutagen.id3.ID3NoHeaderError:
            tags = compatid3.CompatID3()

        if settings['clear_existing_tags']:
            tags.clear()
        if settings['save_images_to_tags'] and metadata.images:
            tags.delall('APIC')

        if settings['write_id3v1']:
            v1 = 2
        else:
            v1 = 0
        encoding = {'utf-8': 3, 'utf-16': 1}.get(settings['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=text))

        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=text))

        if settings['save_images_to_tags']:
            for mime, data in metadata.images:
                tags.add(
                    id3.APIC(encoding=0, mime=mime, type=3, desc='',
                             data=data))

        tmcl = mutagen.id3.TMCL(encoding=encoding, people=[])
        tipl = mutagen.id3.TIPL(encoding=encoding, people=[])

        id3.TCMP = compatid3.TCMP
        tags.delall('TCMP')
        for name, values in metadata.rawitems():
            if name.startswith('performer:'):
                role = name.split(':', 1)[1]
                for value in values:
                    tmcl.people.append([role, value])
            elif name.startswith('comment:'):
                desc = name.split(':', 1)[1]
                if desc.lower()[:4] == "itun":
                    tags.delall('COMM:' + desc)
                    tags.add(
                        id3.COMM(encoding=0,
                                 desc=desc,
                                 lang='eng',
                                 text=[v + u'\x00' for v in values]))
                else:
                    tags.add(
                        id3.COMM(encoding=encoding,
                                 desc=desc,
                                 lang='eng',
                                 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_trackid':
                tags.add(
                    id3.UFID(owner='http://musicbrainz.org',
                             data=str(values[0])))
            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 == settings[
                            '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 /
                        (settings['rating_steps'] - 1)))
                tags.add(
                    id3.POPM(email=settings['rating_user_email'],
                             rating=rating,
                             count=count))
            elif name in self.__rtranslate:
                frameid = self.__rtranslate[name]
                if frameid.startswith('W'):
                    tags.add(getattr(id3, frameid)(url=values[0]))
                elif frameid.startswith('T'):
                    tags.add(
                        getattr(id3, frameid)(encoding=encoding, text=values))
            elif name in self.__rtranslate_freetext:
                tags.add(
                    id3.TXXX(encoding=encoding,
                             desc=self.__rtranslate_freetext[name],
                             text=values))
            elif name.startswith('~id3:'):
                name = name[5:]
                if name.startswith('TXXX:'):
                    tags.add(
                        id3.TXXX(encoding=encoding, desc=name[5:],
                                 text=values))
                else:
                    frameclass = getattr(id3, name[:4], None)
                    if frameclass:
                        tags.add(frameclass(encoding=encoding, text=values))

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

        if settings['write_id3v23']:
            tags.update_to_v23()
            tags.save(encode_filename(filename), v2=3, v1=v1)
        else:
            # remove all custom 2.3 frames
            for old in self.__upgrade.keys():
                tags.delall(old)
            tags.update_to_v24()
            tags.save(encode_filename(filename), v2=4, v1=v1)

        if self._IsMP3 and settings["remove_ape_from_mp3"]:
            try:
                mutagen.apev2.delete(encode_filename(filename))
            except:
                pass
Example #19
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()
        if metadata.images_to_be_saved_to_tags:
            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)))

        # 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 metadata.images_to_be_saved_to_tags:
            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=[])

        tags.delall('TCMP')
        for name, values in metadata.rawitems():
            values = [id3text(v, encoding) for v in values]
            name = id3text(name, encoding)

            if name.startswith('performer:'):
                role = name.split(':', 1)[1]
                for value in values:
                    tmcl.people.append([role, value])
            elif name.startswith('comment:'):
                desc = name.split(':', 1)[1]
                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='eng',
                                 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':
                if config.setting['itunes_compatible_grouping']:
                    tags.add(id3.TIT1(encoding=encoding, text=values))
                    tags.delall('TXXX:Work')
                else:
                    tags.add(self.build_TXXX(encoding, 'Work', values))
            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'):
                    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 in self.__rtranslate_freetext:
                tags.add(
                    self.build_TXXX(encoding, self.__rtranslate_freetext[name],
                                    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))

        tags.add(tmcl)
        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:
                pass
Example #20
0
def comments(metadata):
    if keys.NOTES in metadata:
        return id3.COMM(text=metadata[keys.NOTES], desc='Notes', lang='eng')
    return None