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")
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()
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)
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
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
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
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
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()
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
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()
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)
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
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}
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
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()
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)))
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
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
def comments(metadata): if keys.NOTES in metadata: return id3.COMM(text=metadata[keys.NOTES], desc='Notes', lang='eng') return None