def write_tags(f, meta, tag_cfg, cov, ext, embed_cov): if ext == ".flac": audio = FLAC(f) for k, v in meta.items(): if tag_cfg[k]: audio[k] = str(v) if embed_cov and cov: with open(cov, 'rb') as cov_obj: image = Picture() image.type = 3 image.mime = "image/jpeg" image.data = cov_obj.read() audio.add_picture(image) audio.save() elif ext == ".mp3": try: audio = id3.ID3(f) except ID3NoHeaderError: audio = id3.ID3() if tag_cfg['TRACKNUMBER'] and tag_cfg['TRACKTOTAL']: audio['TRCK'] = id3.TRCK(encoding=3, text=str(meta['TRACKNUMBER']) + "/" + str(meta['TRACKTOTAL'])) elif tag_cfg['TRACKNUMBER']: audio['TRCK'] = id3.TRCK(encoding=3, text=str(meta['TRACKNUMBER'])) if tag_cfg['DISCNUMBER']: audio['TPOS'] = id3.TPOS(encoding=3, text=str(meta['DISCNUMBER']) + "/" + str(meta['DISCTOTAL'])) legend = { "ALBUM": id3.TALB, "ALBUMARTIST": id3.TPE2, "ARTIST": id3.TPE1, "COMMENT": id3.COMM, "COMPOSER": id3.TCOM, "COPYRIGHT": id3.TCOP, "DATE": id3.TDAT, "GENRE": id3.TCON, "ISRC": id3.TSRC, "LABEL": id3.TPUB, "PERFORMER": id3.TOPE, "TITLE": id3.TIT2, # Not working. "URL": id3.WXXX, "YEAR": id3.TYER } for k, v in meta.items(): try: if tag_cfg[k]: id3tag = legend[k] audio[id3tag.__name__] = id3tag(encoding=3, text=v) except KeyError: pass if embed_cov and cov: with open(cov, 'rb') as cov_obj: audio.add(id3.APIC(3, 'image/jpeg', 3, '', cov_obj.read())) audio.save(f, 'v2_version=3')
def write_id3(mp3_file, track, cover=None): t = mp3.Open(mp3_file) if not t.tags: t.add_tags() album = track['albums'][0] t_add = t.tags.add t_add(id3.TIT2(encoding=3, text=track['title'])) t_add(id3.TPE1(encoding=3, text=track['artists'])) t_add(id3.TCOM(encoding=3, text=track[FLD_COMPOSERS])) t_add(id3.TALB(encoding=3, text=album['title'])) if 'labels' in album: t_add(id3.TPUB(encoding=3, text=', '.join(l['name'] for l in album['labels']))) if FLD_TRACKNUM in track: tnum = '{}/{}'.format(track[FLD_TRACKNUM], album['trackCount']) t_add(id3.TRCK(encoding=3, text=tnum)) if FLD_VOLUMENUM in album: t_add(id3.TPOS(encoding=3, text=str(album[FLD_VOLUMENUM]))) if 'year' in album: t_add(id3.TDRC(encoding=3, text=str(album['year']))) if args.genre: t_add(id3.TCON(encoding=3, text=album['genre'].title())) if cover: t_add(id3.APIC(encoding=3, desc='', mime=cover.mime, type=3, data=cover.data)) t.tags.update_to_v23() #id3.ID3v1SaveOptions.CREATE = 2 t.save(v1=2, v2_version=3)
def fix_tags(abs, ext, f_meta): """ :param abs: Path of the file we're tagging :param ext: Extension of the file we're tagging :param f_meta: Dict containing the metadata of the track we're tagging. """ if ext == "mp3": try: audio = id3.ID3(abs) except ID3NoHeaderError: audio = id3.ID3() audio['TRCK'] = id3.TRCK(text=str(audio['TRCK']) + "/" + str(f_meta['track_total'])) audio['TPOS'] = id3.TPOS(text=str(audio['TPOS']) + "/" + str(f_meta['disc_total'])) audio['TDRC'] = id3.TPOS(text=f_meta['release_date']) audio['TPUB'] = id3.TPOS(text=f_meta['planning']) audio['TPE1'] = id3.TPE1(text=f_meta['track_artist']) audio['TPE2'] = id3.TPE2(text=f_meta['album_artist']) audio.save(abs, "v2_version=3") else: audio = FLAC(abs) audio['TRACKTOTAL'] = str(f_meta['track_total']) audio['DISCTOTAL'] = str(f_meta['disc_total']) audio['DATE'] = f_meta['release_date'] audio['LABEL'] = f_meta['planning'] audio['ARTIST'] = f_meta['track_artist'] audio['ALBUMARTIST'] = f_meta['album_artist'] audio.save()
def add_mp3_tags(filename, metadata): metadata = metadata.copy() try: audio = id3.ID3(filename) except ID3NoHeaderError: audio = id3.ID3() # ID3 is weird about the track number and total so we have to set that manually audio["TRCK"] = id3.TRCK( encoding=3, text=f"{metadata.pop('TRACKNUMBER')}/{metadata.pop('TRACKTOTAL')}") legend = { "ALBUM": id3.TALB, "ALBUMARTIST": id3.TPE2, "ARTIST": id3.TPE1, "COMMENT": id3.COMM, "COMPOSER": id3.TCOM, "COPYRIGHT": id3.TCOP, "GENRE": id3.TCON, "ORGANIZATION": id3.TPUB, "TITLE": id3.TIT2, "ISRC": id3.TSRC, "DATE": id3.TYER } for tag, value in metadata.items(): if value: id3tag = legend[tag] audio[id3tag.__name__] = id3tag(encoding=3, text=value) audio.save(filename, 'v2_version=3')
def write_tags(f, meta, cov): if f.endswith('.mp3'): try: audio = id3.ID3(f) except ID3NoHeaderError: audio = id3.ID3() audio['TRCK'] = id3.TRCK(encoding=3, text=str(meta['TRACK']) + "/" + str(meta['TRACKTOTAL'])) legend = { "ALBUM": id3.TALB, "ALBUMARTIST": id3.TPE2, "ARTIST": id3.TPE1, "COPYRIGHT": id3.TCOP, "TITLE": id3.TIT2, "YEAR": id3.TYER } for tag, value in meta.items(): if not tag in ['UPC', 'TRACK', 'TRACKTOTAL']: id3tag = legend[tag] audio[id3tag.__name__] = id3tag(encoding=3, text=value) with open(cov, 'rb') as cov_obj: audio.add(id3.APIC(3, 'image/jpeg', 3, '', cov_obj.read())) audio.save(f, 'v2_version=3') else: audio = FLAC(f) for tag, value in meta.items(): audio[tag] = str(value) image = Picture() image.type = 3 image.mime = "image/jpeg" with open(cov, 'rb') as cov_obj: image.data = cov_obj.read() audio.add_picture(image) audio.save()
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_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 write_tags(pre_abs, meta, fmt, cov_abs): if fmt == "FLAC": audio = FLAC(pre_abs) del meta['track_padded'] for k, v in meta.items(): if v: audio[k] = str(v) if cov_abs: with open(cov_abs, "rb") as f: image = Picture() image.type = 3 image.mime = "image/jpeg" image.data = f.read() audio.add_picture(image) elif fmt == "MP3": try: audio = id3.ID3(pre_abs) except ID3NoHeaderError: audio = id3.ID3() audio['TRCK'] = id3.TRCK(encoding=3, text="{}/{}".format(meta['track'], meta['tracktotal'])) legend = { "album": id3.TALB, "albumartist": id3.TPE2, "artist": id3.TPE1, #"comment": id3.COMM, "copyright": id3.TCOP, "isrc": id3.TSRC, "label": id3.TPUB, "title": id3.TIT2, "year": id3.TYER } for k, v in meta.items(): id3tag = legend.get(k) if v and id3tag: audio[id3tag.__name__] = id3tag(encoding=3, text=v) if cov_abs: with open(cov_abs, "rb") as cov_obj: audio.add(id3.APIC(3, "image/jpeg", 3, None, cov_obj.read())) else: audio = MP4(pre_abs) audio['\xa9nam'] = meta['title'] audio['\xa9alb'] = meta['album'] audio['aART'] = meta['albumartist'] audio['\xa9ART'] = meta['artist'] audio['trkn'] = [(meta['track'], meta['tracktotal'])] audio['\xa9day'] = meta['year'] audio['cprt'] = meta['copyright'] if cov_abs: with open(cov_abs, "rb") as f: audio['covr'] = [ MP4Cover(f.read(), imageformat=MP4Cover.FORMAT_JPEG) ] audio.save(pre_abs)
def setTrackNum(self, _value): self.isSave = True values = _value.split("/") try: self.tags["TRCK"] = id3.TRCK( encoding=3, text=str(int(self.correctValuesForMusicTagType(values[0])))) if len(values) > 1: self.tags["TPOS"] = id3.TPOS( encoding=3, text=str(int(self.correctValuesForMusicTagType( values[1])))) elif len(values) == 1 and self.tags["TPOS"] is not None: del self.tags["TPOS"] except: pass
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 compiletracl(tracl): rawfile = File("%s.mp3" % tracl['id']) rawfile['TIT2'] = id3.TIT2(encoding=3, text=tracl['title']) rawfile['TALB'] = id3.TALB(encoding=3, text=tracl['album']) rawfile['TPE1'] = id3.TPE1(encoding=3, text=tracl['artist']) rawfile['TCON'] = id3.TCON(encoding=3, text=tracl['genre']) rawfile['TYER'] = id3.TYER(encoding=3, text=tracl['year']) rawfile['TRCK'] = id3.TRCK(encoding=3, text=tracl['len']) rawfile['WOAS'] = id3.WOAS(encoding=3, text=tracl['link']) rawfile['APIC'] = id3.APIC(encoding=3, type=3, mime='image/jpeg', desc='Cover', data=downloadcover(tracl)) rawfile.save(v2_version=3)
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 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 __get_tags(metadata: MetaData) -> id3.ID3: tags = id3.ID3() if metadata.podcast_title: tags.add( id3.TPE1(encoding=id3.Encoding.LATIN1, text=metadata.podcast_title)) if metadata.episode_title: tags.add( id3.TIT2(encoding=id3.Encoding.LATIN1, text=metadata.episode_title)) if metadata.episode_number: tags.add( id3.TRCK(encoding=id3.Encoding.LATIN1, text=str(metadata.episode_number))) if metadata.chapters: toc: List[str] = [ f"chp{index}" for index in range(len(metadata.chapters)) ] tags.add( id3.CTOC(encoding=id3.Encoding.LATIN1, element_id="toc", flags=id3.CTOCFlags.TOP_LEVEL | id3.CTOCFlags.ORDERED, child_element_ids=toc, sub_frames=[])) for idx, chapter in enumerate(metadata.chapters): tags.add( id3.CHAP(encoding=id3.Encoding.LATIN1, element_id=f"chp{idx}", start_time=chapter.start, end_time=chapter.end, sub_frames=[ id3.TIT2(encoding=id3.Encoding.LATIN1, text=chapter.name) ])) return tags
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 tag_mp3(path_audio,path_pic,lrc,title,artist,album,disc,track,**kw): ''' ref: http://id3.org/id3v2.3.0 https://github.com/quodlibet/mutagen/blob/master/mutagen/id3/_frames.py http://help.mp3tag.de/main_tags.html http://code.activestate.com/recipes/577138-embed-lyrics-into-mp3-files-using-mutagen-uslt-tag/ ''' tags=id3.ID3() if path_pic: with open(path_pic,'rb') as f: tags['APIC']=id3.APIC(mime='image/jpeg',type=id3.PictureType.COVER_FRONT,data=f.read()) for x in lrc: tags["USLT::"+x['lang']]=id3.USLT(text=x['text'],lang=x['lang']) tags['TIT2']=id3.TIT2(text=title) tags['TPE1']=id3.TPE1(text=artist) if album!=None: tags['TALB']=id3.TALB(text=album) tags['TPOS']=id3.TPOS(text=disc) tags['TRCK']=id3.TRCK(text=str(track)) tags.save(path_audio)
def xx_total_correct(tag_thing): """ id3 tags combine the 'track/disc-number' and 'track/disc-total' fields in one frame (e.g. 3/14). :param tag_thing: mutagen.id3.ID3 object (= mp3.MP3.tags) or flac_like dict :return: corrected tags """ if type(tag_thing) == id3.ID3: if all(x in tag_thing for x in ('TXXX:tracktotal', 'TRCK')): track_numb = tag_thing['TRCK'].text[0] track_tot = tag_thing['TXXX:tracktotal'].text[0] tag_thing.add(id3.TRCK(encoding=3, text=f'{track_numb}/{track_tot}')) tag_thing.delall('TXXX:tracktotal') if all(x in tag_thing for x in('TXXX:disctotal', 'TPOS')): track_numb = tag_thing['TPOS'].text[0] track_tot = tag_thing['TXXX:disctotal'].text[0] tag_thing.add(id3.TPOS(encoding=3, text=f'{track_numb}/{track_tot}')) tag_thing.delall('TXXX:disctotal') return tag_thing if type(tag_thing) == dict: if 'tracknumber' in tag_thing and '/' in tag_thing['tracknumber'][0]: track_numb, track_tot = tag_thing['tracknumber'][0].split('/') tag_thing['tracknumber'] = [track_numb] tag_thing['tracktotal'] = [track_tot] if 'discnumber' in tag_thing and '/' in tag_thing['discnumber'][0]: disc_numb, track_tot = tag_thing['discnumber'][0].split('/') tag_thing['discnumber'] = [disc_numb] tag_thing['disctotal'] = [track_tot] return tag_thing else: raise Exception('What is this thing?')
def tag(self, target, track): mp3 = MP3(os.path.join(self.workdir, target)) if not mp3.tags: mp3.add_tags() tags = mp3.tags tags.add(id3.TALB(encoding=id3.Encoding.UTF8, text=self.disc.album)) tags.add(id3.TPE1(encoding=id3.Encoding.UTF8, text=self.disc.artist)) tags.add(id3.TIT2(encoding=id3.Encoding.UTF8, text=track.title)) tags.add(id3.TRCK(encoding=id3.Encoding.UTF8, text=str(track.trackno))) tags.add(id3.TDRC(encoding=id3.Encoding.UTF8, text=str(self.disc.year))) tpos = str(self.disc.discno) if self.disc.set_size > 1: tpos = f"{self.disc.discno}/{self.disc.set_size}" tags.add(id3.TPOS(encoding=id3.Encoding.UTF8, text=tpos)) if self.disc.cover_art: tags.add( id3.APIC(encoding=id3.Encoding.UTF8, data=self.disc.cover_art)) mp3.save()
def save(self): if self.clr: for file in self.audiofiles: tmp = mid3.ID3(file) tmp.clear() tmp.add(mid3.TIT2(text="")) # title frame tmp.add(mid3.TPE1(text="")) # artist frame tmp.add(mid3.TALB(text="")) # album frame tmp.add(mid3.TPE2(text="")) # album artist frame tmp.add(mid3.TCOM(text="")) # composer frame tmp.add(mid3.TCON(text="")) # genre frame tmp.add(mid3.TDRC(text="")) # date frame tmp.add(mid3.TRCK(text="")) # tracknumber frame tmp.add(mid3.TPOS(text="")) # discnumber frame tmp.add(mid3.TBPM(text="")) # bpm frame tmp.save() if self.artwork: with open(self.artwork, 'rb') as albumart: for file in self.audiofiles: tmp = mid3.ID3(file) tmp['APIC'] = mid3.APIC( encoding=3, mime='image/jpeg', type=3, desc=u'Cover', data=albumart.read() ) tmp.save() albumart.seek(0) for file in self.audiofiles: tmp = EasyID3(file) for tag in self.__sets: if self.__sets[tag]: tmp[tag] = self.__sets[tag] tmp.save()
def load_mp3(dir, file, _numtracks = None, _disk = None, _numdisks = None): m = id3.ID3(os.path.join(dir, file)) changed = [] # Don't want this taking up space if t3['encodedby'] in m: del(m[t3['encodedby']]) changed.append('encodedby') # Adjust num tracks try: numtracks = 0 track = m[t3['track']].text[0] if '/' in track: (track, numtracks) = track.split('/') except: track = numtracks = 0 if numtracks == 0: if _numtracks: numtracks = _numtracks m[t3['track']] = id3.TRCK(0, '{0}/{1}'.format(track,numtracks)) changed.append('track') else: print " no numtracks for {0}".format(file) sys.exit(1) # Try to detect disk numbers try: numdisks = 0 disk = m[t3['disk']].text[0] if '/' in disk: (disk, numdisks) = disk.split('/') except: disk = numdisks = 0 if _disk and _numdisks and not int(disk) and not int(numdisks): disk = str(_disk).zfill(len(str(_numdisks))) numdisks = _numdisks m[t3['disk']] = id3.TPOS(0, '{0}/{1}'.format(disk,numdisks)) changed.append('disk') elif str(_disk) != '0' and str(disk) != '0' and str(disk) != str(_disk): print " Disknum mismatch {0} != {1} on {2}".format(disk, _disk, file) sys.exit(1) elif int(disk) and not int(numdisks): if _numdisks: disk = str(disk).zfill(len(str(_numdisks))) numdisks = _numdisks m[t3['disk']] = id3.TPOS(0, '{0}/{1}'.format(disk,numdisks)) changed.append('disk') else: print " disknum but no numdisks on {0}".format(file) sys.exit(1) # Clean up the album name if t3['album'] in m: album = fix_album(m[t3['album']].text[0].strip()) if album != m[t3['album']].text[0]: m[t3['album']].text[0] = album changed.append('album') # Clean up the title #print m[t3['title']].text[0] if t3['title'] in m: title = m[t3['title']].text[0].strip() title = fix_title(title) if title != m[t3['title']].text[0]: m[t3['title']].text = [title] changed.append('title') else: title = '' changed.append('title') # Generate a new name new = fix_name_full(u"{0} {1}.mp3".format( str(track).zfill(max(2,len(str(numtracks)))), title )) if int(disk) > 0 and int(numdisks) > 1: new = "{0}-{1}".format( str(disk).zfill(len(str(numdisks))), new) try: if file.decode("utf-8") == new.decode("utf-8"): new = None except: print " FILE: "+os.path.join(dirname, file) raise # Return return (m, new, changed)
def add_episode_number(tags: id3.ID3, episode_number: int) -> None: tags.add(id3.TRCK(encoding=id3.Encoding.LATIN1, text=str(episode_number)))
def tag_mp3(filename, root_dir, final_name, d, album, istrack=True, em_image=False): """ Tag an mp3 file :param str filename: mp3 temporary file path :param str root_dir: Root dir used to get the cover art :param str final_name: Final name of the mp3 file (complete path) :param dict d: Track dictionary from Qobuz_client :param bool istrack :param bool em_image: Embed cover art into file """ try: audio = id3.ID3(filename) except ID3NoHeaderError: audio = id3.ID3() # temporarily holds metadata tags = dict() tags["title"] = _get_title(d) try: tags["label"] = album["label"]["name"] except KeyError: pass artist_ = d.get("performer", {}).get("name") # TRACK ARTIST if istrack: tags["artist"] = artist_ or d["album"]["artist"][ "name"] # TRACK ARTIST else: tags["artist"] = artist_ or album["artist"]["name"] if istrack: tags["genre"] = _format_genres(d["album"]["genres_list"]) tags["albumartist"] = d["album"]["artist"]["name"] tags["album"] = d["album"]["title"] tags["date"] = d["album"]["release_date_original"] tags["copyright"] = _format_copyright(d["copyright"]) tracktotal = str(d["album"]["tracks_count"]) else: tags["genre"] = _format_genres(album["genres_list"]) tags["albumartist"] = album["artist"]["name"] tags["album"] = album["title"] tags["date"] = album["release_date_original"] tags["copyright"] = _format_copyright(album["copyright"]) tracktotal = str(album["tracks_count"]) tags["year"] = tags["date"][:4] audio["TRCK"] = id3.TRCK(encoding=3, text=f'{d["track_number"]}/{tracktotal}') audio["TPOS"] = id3.TPOS(encoding=3, text=str(d["media_number"])) # write metadata in `tags` to file for k, v in tags.items(): id3tag = ID3_LEGEND[k] audio[id3tag.__name__] = id3tag(encoding=3, text=v) if em_image: _embed_id3_img(root_dir, audio) audio.save(filename, "v2_version=3") os.rename(filename, final_name)
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))
continue if 'TALB' in tags and str( tags['TALB']) and str(tags['TALB']) != 'Unknown Album': result['album_title'] = str(tags['TALB']) tags['TIT2'] = id3.TIT2(encoding=3, text=u'{0}'.format(music)) tags['TALB'] = id3.TALB(encoding=3, text=u'{0}'.format(result['album_title'])) tags['TPE1'] = id3.TPE1(encoding=3, text=u'{0}'.format(real_artist)) tags['TCON'] = id3.TCON(encoding=3, text=u'{0}'.format(result['genre'])) tags['TDRC'] = id3.TDRC(encoding=3, text=u'{0}'.format(result['album_year'])) tags['TRCK'] = id3.TRCK(encoding=3, text=u'{0}'.format(result['track_number'])) if album_art: tags['APIC'] = id3.APIC( encoding=3, mime='image/png' if '.png' in result['album_art_url'] else 'image/jpeg', type=3, desc=u'Cover', data=album_art) tags.save(music_path) print("Artist: {} | Album: {} ({}) | {} Music: {} ({})".format( real_artist, result['album_title'], result['album_year'], result['track_number'], music, result['genre'])) if album is not result['album_title']:
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 tag_mp3(filename, root_dir, final_name, d, album, istrack=True, em_image=False): """ Tag an mp3 file :param str filename: mp3 temporary file path :param str root_dir: Root dir used to get the cover art :param str final_name: Final name of the mp3 file (complete path) :param dict d: Track dictionary from Qobuz_client :param bool istrack :param bool em_image: Embed cover art into file """ id3_legend = { "album": id3.TALB, "albumartist": id3.TPE2, "artist": id3.TPE1, "comment": id3.COMM, "composer": id3.TCOM, "copyright": id3.TCOP, "date": id3.TDAT, "genre": id3.TCON, "isrc": id3.TSRC, "label": id3.TPUB, "performer": id3.TOPE, "title": id3.TIT2, "year": id3.TYER, } try: audio = id3.ID3(filename) except ID3NoHeaderError: audio = id3.ID3() # temporarily holds metadata tags = dict() tags["title"] = get_title(d) try: tags["label"] = album["label"]["name"] except KeyError: pass try: tags["artist"] = d["performer"]["name"] except KeyError: if istrack: tags["artist"] = d["album"]["artist"]["name"] else: tags["artist"] = album["artist"]["name"] if istrack: tags["genre"] = _format_genres(d["album"]["genres_list"]) tags["albumartist"] = d["album"]["artist"]["name"] tags["album"] = d["album"]["title"] tags["date"] = d["album"]["release_date_original"] tags["copyright"] = _format_copyright(d["copyright"]) tracktotal = str(d["album"]["tracks_count"]) else: tags["genre"] = _format_genres(album["genres_list"]) tags["albumartist"] = album["artist"]["name"] tags["album"] = album["title"] tags["date"] = album["release_date_original"] tags["copyright"] = _format_copyright(album["copyright"]) tracktotal = str(album["tracks_count"]) tags["year"] = tags["date"][:4] audio["TRCK"] = id3.TRCK(encoding=3, text=f'{d["track_number"]}/{tracktotal}') audio["TPOS"] = id3.TPOS(encoding=3, text=str(d["media_number"])) # write metadata in `tags` to file for k, v in tags.items(): id3tag = id3_legend[k] audio[id3tag.__name__] = id3tag(encoding=3, text=v) if em_image: emb_image = os.path.join(root_dir, "cover.jpg") multi_emb_image = os.path.join( os.path.abspath(os.path.join(root_dir, os.pardir)), "cover.jpg" ) if os.path.isfile(emb_image): cover_image = emb_image else: cover_image = multi_emb_image with open(cover_image, "rb") as cover: audio.add(id3.APIC(3, "image/jpeg", 3, "", cover.read())) audio.save(filename, "v2_version=3") os.rename(filename, final_name)
def __update_tag(self, download_dir, audio_file, image_file, song_title=None, album_title=None, album_artist=None, album_composer=None, track_number=-1, process_index=-1, process_total=-1): """ The function that update audio metadata for each song. :param str download_dir: Download directory :param str audio_file: Path to audio file :param str image_file: Path to image file :param str song_title: Song title :param str album_title: Album title to be saved in metadata :param str album_artist: Album artist to be saved in metadata :param str album_composer: Album composer to be saved in metadata :param int track_number: track number to be saved in metadata :param int process_index: Current process index displayed in log message :param int process_total: Total number of process displayed in log message """ if audio_file is None: logger.warning('[Process:{}/{}][Track:{}] Could not update metadata because there is no data found on the playlist. The video may be private or deleted.'.format(process_index, process_total, track_number)) return if process_index > 0 and process_total > 0: if track_number > 0: log_prefix = '[Process:{}/{}][Track:{}]'.format(process_index, process_total, track_number) else: log_prefix = '[Process:{}/{}]'.format(process_index, process_total) else: log_prefix = '' audio_filename = os.path.basename(audio_file) try: # Validate audio data if not os.path.isfile(audio_file): raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), audio_file) audio_mime_type = mimetypes.guess_type(audio_file) if contains_at_least(audio_mime_type, ['audio/x-mp4', 'audio/x-m4a', 'audio/mp4a-latm']): # For more info about mp4 tag is available at # https://github.com/quodlibet/mutagen/blob/cf399dc58940fb1356f672809d763be9e2af0033/mutagen/mp4/__init__.py # http://atomicparsley.sourceforge.net/mpeg-4files.html mp4_data = mp4.MP4(audio_file) # Track Number if not self.no_track_number and track_number > 0: mp4_data['trkn'] = [(track_number, 0)] # Cover image if not self.no_artwork: image_data = self.__get_tag_image(image_file, audio_mime_type) if image_data: mp4_data['covr'] = [image_data] # Album title if not self.no_album_title and album_title is not None: mp4_data['\xa9alb'] = album_title # Album artist if not self.no_album_artist and album_artist is not None: mp4_data['aART'] = album_artist # Composer if not self.no_composer and album_composer is not None: mp4_data['\xa9wrt'] = album_composer # Part of compilation if not self.no_compilation: mp4_data['cpil'] = True # Save mp4_data.save() elif contains_at_least(audio_mime_type, ['audio/x-mp3', 'audio/mpeg']): # For more info about ID3v2 tag is available at # https://github.com/quodlibet/mutagen/blob/4a5d7d17f1a611280cc52d229aa70b77ca3c55dd/mutagen/id3/_frames.py # https://help.mp3tag.de/main_tags.html mp3_data = id3.ID3(audio_file) # Cover image if not self.no_artwork: image_data = self.__get_tag_image(image_file, audio_mime_type) if image_data: mp3_data['APIC'] = image_data # Track number if not self.no_track_number and track_number > 0: mp3_data.add(id3.TRCK(encoding=3, text=['{}/{}'.format(track_number, 0)])) # Album title if not self.no_album_title and album_title is not None: mp3_data["TALB"] = id3.TALB(encoding=0, text=album_title) # Album artist if not self.no_album_artist and album_artist is not None: mp3_data["TPE2"] = id3.TPE2(encoding=0, text=album_artist) # Composer if not self.no_composer and album_composer is not None: mp3_data["TCOM"] = id3.TCOM(encoding=0, text=album_composer) # Part of compilation if not self.no_compilation: mp3_data['TCMP'] = id3.TCMP(encoding=0, text=['1']) # Save mp3_data.save() elif contains_at_least(audio_mime_type, ['audio/x-aac']): # TODO: Add AAC support pass # image_data = __get_tag_image(image_file, audio_mime_type) # aac_data = aac.AAC(audio_file) # if not self.no_track_number: # if track_number > 0 and track_total > 0: # aac_data.add_tags(id3.TRCK(encoding=3, text=['{}/{}'.format(track_number, track_total)])) # # mp3_data['TRCK'] = id3.TRCK(encoding=3, text=[str(track_number)]) # if image_data: # mp3_data['APIC'] = image_data # aac_data.save() elif contains_at_least(audio_mime_type, ['audio/x-flac']): # https://github.com/quodlibet/mutagen/blob/a1db79ece62c4e86259f15825e360d1ce0986a22/mutagen/flac.py # https://github.com/quodlibet/mutagen/blob/4a5d7d17f1a611280cc52d229aa70b77ca3c55dd/tests/test_flac.py flac_data = flac.FLAC(audio_file) # Artwork if not self.no_artwork: image_data = self.__get_tag_image(image_file, audio_mime_type) if image_data: flac_data.add_picture(image_data) # Save flac_data.save() flac_data = File(audio_file) # Track number if not self.no_track_number and track_number > 0: flac_data.tags['tracknumber'] = str(track_number) # Album title if not self.no_album_title and album_title is not None: flac_data.tags['album'] = album_title # Album artist if not self.no_album_artist and album_artist is not None: flac_data.tags['albumartist'] = album_artist # Composer if not self.no_composer and album_composer is not None: flac_data.tags['composer'] = album_composer # Part of compilation if not self.no_compilation: pass # Save flac_data.save() # audio = File(audio_file, easy=True) else: raise InvalidMimeTypeException("Invalid audio format.", audio_mime_type) # Remove artwork if succeeded if os.path.exists(image_file): os.remove(image_file) # Rename filename from id to title dest_audio_file = os.path.join(download_dir, '{}.{}'.format(song_title, self.audio_codec)) os.rename(audio_file, dest_audio_file) dest_audio_filename = os.path.basename(dest_audio_file) logger.info('{}[File:{}] Updated.'.format(log_prefix, dest_audio_filename)) except FileNotFoundError: message = 'File not found. Skipped.' logger.warning('{}[File:{}] {}'.format(log_prefix, audio_filename, message)) except InvalidDataException as e: message = e.message + ' Skipped.' logger.warning('{}[File:{}] {}'.format(log_prefix, audio_filename, message)) except InvalidMimeTypeException as e: message = e.message + ' Skipped.' logger.warning('{}[File:{}] {}'.format(log_prefix, audio_filename, message)) except Exception as e: message = 'Error {}: {} Skipped.'.format(type(e), str(e)) logger.error('{}[File:{}] {}'.format(log_prefix, audio_filename, message))
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 _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