def test_delete_id3_with_ape(self): ID3(self.filename).save(v1=2) ape_tag = APEv2() ape_tag["oh"] = ["no"] ape_tag.save(self.filename) id3.delete(self.filename, delete_v2=False) self.assertEqual(APEv2(self.filename)["oh"], "no")
def test_save_id3_over_ape(self): id3.delete(self.filename, delete_v2=False) ape_tag = APEv2() ape_tag["oh"] = ["no"] ape_tag.save(self.filename) ID3(self.filename).save() self.assertEqual(APEv2(self.filename)["oh"], "no")
def test_ape_id3_lookalike(self): # mp3 with apev2 tag that parses as id3v1 (at least with ParseID3v1) id3.delete(self.filename, delete_v2=False) ape_tag = APEv2() ape_tag["oh"] = [ "noooooooooo0000000000000000000000000000000000ooooooooooo"] ape_tag.save(self.filename) ID3(self.filename).save() self.assertTrue(APEv2(self.filename))
def get_gain(mime, music_file): gain = None try: if mime == 'audio/mpeg': # mp3gain stores replay gain info in an APEv2 tag, not ID3. apev2 = APEv2(music_file) gain = apev2['REPLAYGAIN_TRACK_GAIN'].value elif mime == 'application/ogg': tags = OggVorbis(music_file) gain = tags['replaygain_track_gain'][0] elif mime == 'audio/mp4': tags = MP4(music_file) gain = tags['----:com.apple.iTunes:replaygain_track_gain'][ 0] # Oh, how I wish I were kidding elif mime == 'audio/x-flac': tags = FLAC(music_file) gain = tags['replaygain_track_gain'][0] except: pass # Lazily we assume that if anything went wrong it's because the tag was not there if gain: # We have e.g. -3.100000 dB. Remove the "dB" and convert to float gain = float(gain.split(" ")[0]) return gain
def GetMetadata(self, fpath): from mutagen import MutagenError from mutagen.mp3 import EasyMP3 from mutagen.easymp4 import EasyMP4 from mutagen.flac import FLAC from mutagen.apev2 import APEv2 try: if (fpath.endswith("mp3") or fpath.endswith("ogg") or fpath.endswith("wma")): audio = EasyMP3(fpath) elif (fpath.endswith("m4a") or fpath.endswith("m4v") or fpath.endswith("mp4")): audio = EasyMP4(fpath) elif fpath.endswith("flac"): audio = FLAC(fpath) elif fpath.endswith("ape"): audio = APEv2(fpath) elif fpath.endswith("wav"): audio = dict() except MutagenError as e: logger.warning("Mutagen parse metadata failed, ignore.\n" "file: {}, exception: {}".format(fpath, str(e))) return None metadata_dict = dict(audio) for key in metadata_dict.keys(): metadata_dict[key] = metadata_dict[key][0] if "title" not in metadata_dict: title = os.path.split(fpath)[-1].split(".")[0] metadata_dict["title"] = title return metadata_dict
def copy_replaygain_tags(self, filename): self.log.filename = filename self.log.debug("begin processing file") try: apev2 = APEv2(filename) except mutagen.apev2.error: self.log.info("no APEv2 tag found, skipping file") return except IOError: e = sys.exc_info() self.log.error("%s" % e[1]) return try: id3 = ID3(filename) except mutagen.id3.error: self.log.info("no ID3 tag found, creating one") id3 = ID3() modified = False for name, converter in REPLAYGAIN_TAGS: copied = self.copy_replaygain_tag(apev2, id3, name, converter) if copied: modified = True if modified: self.log.debug("saving modified ID3 tag") id3.save(filename) self.log.debug("done processing file") self.log.filename = None
def read_mp3(self): try: for k, v in APEv2(self.filename).items(): self[k.lower()] = str(v) except: pass try: tags = mp3.Open(self.filename) for k, v in tags.items(): if k == 'TPE1': k, v = 'artist', v.text[0] elif k == 'TALB': k, v = 'album', v.text[0] elif k == 'TIT2': k, v = 'title', v.text[0] elif k == 'TRCK': k, v = 'tracknumber', v.text[0] elif k.startswith('TXXX:'): k, v = k[5:].lower(), v.text[0] elif k.startswith('COMM:'): k, v = 'comment', v.text[0] else: continue if k.startswith('quodlibet::'): k = k[11:] self[k] = v self['length'] = int(tags.info.length) self['sample_rate'] = tags.info.sample_rate except: pass
def __init__(self, pathname, extension): MutagenTagger.__init__(self, pathname) try: self.tag = self.opener[extension](pathname) except KeyError: try: self.tag = APEv2(pathname) except: print("ape tag not found") self.tag = None return else: print("ape tag found on non-native format") except: print("failed to create tagger for native format") self.tag = None return else: try: self.tag.add_tags() except: print("ape tag found on native format") else: print("no existing ape tags found") self.tag_frame = FreeTagFrame() self.add(self.tag_frame) self.tag_frame.show()
def __init__(self, file, threshold=60, duration_distance_threshold=10000): self.file = file self.threshold = threshold self.ddt = duration_distance_threshold self.cleaner = re.compile(r"[^A-Za-z0-9 ]").sub try: self.audio = MP3(file, ID3=EasyID3) except: try: self.audio = FLAC(file) except: try: self.audio = OggVorbis(file) except: try: self.audio = OggFLAC(file) except: try: self.audio = OggTheora(file) except: try: self.audio = APEv2(file) except: try: self.audio = ASF(file) except: try: self.audio = MP4(file) except: try: self.audio = Musepack(file) except: try: self.audio = TrueAudio(file) except: try: self.audio = WavPack(file) except: raise FileTypeException( 'Unknown file type, no metadata, or file not found.' ) try: [title] = self.audio['title'] self.title = self.__clean_literal(str(title)) except: self.title = None try: [artist] = self.audio['artist'] self.artist = self.__clean_literal(str(artist)) except: self.artist = None try: [album] = self.audio['album'] self.album = self.__clean_literal(str(album)) except: self.album = None self.mbzQuery()
def getAPELyrics(bfile, getlrc): try: tags = APEv2(bfile) if 'lyrics' in tags: lyr = tags['lyrics'][0] match = isLRC(lyr) if (getlrc and match) or ((not getlrc) and (not match)): return lyr except: return
def set_tag_info_ape(f_path, tag_info, cover_data=None): if tag_info.get('date'): tag_info['year'] = tag_info['date'] + 'Z' if tag_info.get('tracknumber'): tag_info['track'] = tag_info.pop('tracknumber') audio = APEv2(f_path) audio.delete() for key in tag_info.keys(): audio[key] = tag_info[key] audio.save()
def _load_ape(self, track): try: ape = APEv2(track) except apev2_error: info("No APEv2 on file '%s', skipping", track) raise except BaseException: error("Error: %s", sys.exc_info()[1]) raise else: return ape
def loadmeta(self): for i in range(0,self.tracktotal): if self.tracktotal>99: ii="{:03d}".format(i+1) else: ii="{:02d}".format(i+1) self.trackmeta.append({}) if self.format == 'DTS': # DTS f=os.path.join(self.icache.getroot(), self.albumdir, ii + " " + self.trackname[i] + ".dts") dts = APEv2(f) # guard if 'Track' in dts.keys(): tmp = dts['Track'] (tracknumber, tracktotal) = str(tmp).split('/') numnumber = int(tracknumber, 10) numtotal = int(tracktotal, 10) if numtotal != self.tracktotal or numnumber != (i+1): raise Exception("TUNE-CHAOS: %s" % self.albumdir) else: raise Exception("TUNE-CHAOS: %s" % self.albumdir) # album if 'Album' in dts.keys(): self.trackmeta[i]['album'] = [ str(xx) for xx in dts['Album'] ] # artist if 'Artist' in dts.keys(): self.trackmeta[i]['artist'] = [ str(xx) for xx in dts['Artist'] ] # year if 'Year' in dts.keys(): self.trackmeta[i]['date'] = [ str(xx) for xx in dts['Year'] ] # title if 'Title' in dts.keys(): self.trackmeta[i]['title'] = [ str(xx) for xx in dts['Title'] ] else: # FLAC f=os.path.join(self.icache.getroot(), self.albumdir, ii + " " + self.trackname[i] + ".flac") ff = FLAC(f) for t in ff.keys(): self.trackmeta[i][t.lower()]=ff[t] for t in SUPPRESS_TAGS: if t in self.trackmeta[i].keys(): del self.trackmeta[i][t] if self.tracktotal>99: self.trackmeta[i]['tracknumber'] = [ "{:03d}".format(i+1) ] self.trackmeta[i]['tracktotal'] = [ "{:03d}".format(self.tracktotal) ] else: self.trackmeta[i]['tracknumber'] = [ "{:02d}".format(i+1) ] self.trackmeta[i]['tracktotal'] = [ "{:02d}".format(self.tracktotal) ] return None
def load(self, filething): super().load(filething) try: self.tags = APEv2(filething) # Correct the calculated length if not hasattr(self.info, 'bitrate') or self.info.bitrate == 0: return ape_data = _APEv2Data(filething.fileobj) if ape_data.size is not None: # Remove APEv2 data length from calculated track length extra_length = (8.0 * ape_data.size) / self.info.bitrate self.info.length = max(self.info.length - extra_length, 0.001) except APENoHeaderError: self.tags = None
def set_tag_info_mp3(f_path, tag_info, cover_data=None): try: # 有些歌曲会有APE格式的标签 audio = APEv2(f_path) audio.delete() except Exception as e: logger.debug(e) audio = EasyMP3(f_path) audio.delete() for key in tag_info.keys(): audio[key] = tag_info[key] audio.save() if cover_data: audio = mutagen.mp3.MP3(f_path) audio['APIC'] = mutagen.id3.APIC( encoding=3, # 3 is for utf-8 mime='image/jpeg', # image/jpeg or image/png type=3, # 3 is for the cover image desc='', data=cover_data ) audio.save()
def getTag(file, tag1, tag2, tag3): tag = '' if ".flac" in file: flacFile = FLAC(file) # tag - common one tag = flacFile[tag1][0] if ".mp3" in file: mp3File = MP3(file) # tag - common one tag = mp3File[tag1][0] if ".m4a" in file: m4aFile = MP4(file) # tag - common one tag = m4aFile[tag1][0] if ".ape" in file: apeFile = APEv2(file) # tag - starts with a capital tag = apeFile[tag2][0] if ".ogg" in file: oggFile = OggVorbis(file) # tag - CAPS tag = oggFile.tags[tag3][0] return tag
def getImageFromTagAPE(self, pathfile, pathimage=None, nameimage='cover'): """Extract Cover from tag APE file media.""" mediafile = APEv2(pathfile) for tag in mediafile.keys(): if 'COVER' in tag.upper(): tagdatapicture = mediafile[tag] text_delimiter_index = tagdatapicture.value.find(b'\x00') if text_delimiter_index > 0: comment = tagdatapicture.value[0:text_delimiter_index] comment = comment.decode('utf-8', 'replace') ext = comment.split('.')[1] else: comment = None ext = 'jpg' if pathimage is None: imagefinal = path.join(path.dirname(pathfile), 'cover.' + ext) else: imagefinal = path.join(pathimage, nameimage + '.' + ext) image_data = tagdatapicture.value[text_delimiter_index + 1:] with open(imagefinal, "wb") as h: h.write(image_data) return True return False
def add_tags(self): if self.tags is None: self.tags = APEv2() else: raise APEError("%r already has tags: %r" % (self, self.tags))
def load(self, filething): super().load(filething) try: self.tags = APEv2(filething) except APENoHeaderError: self.tags = None
def add_song(fpath, g_songs, g_artists, g_albums, lans='auto', delimiter='', expand_artist_songs=False): """ parse music file metadata with Easymp3 and return a song model. """ try: if fpath.endswith('mp3') or fpath.endswith('ogg') or fpath.endswith( 'wma'): metadata = EasyMP3(fpath) elif fpath.endswith('m4a') or fpath.endswith('m4v') or fpath.endswith( 'mp4'): metadata = EasyMP4(fpath) elif fpath.endswith('flac'): metadata = FLAC(fpath) elif fpath.endswith('ape'): metadata = APEv2(fpath) elif fpath.endswith('wav'): metadata = dict() except MutagenError as e: logger.warning('Mutagen parse metadata failed, ignore.\n' 'file: {}, exception: {}'.format(fpath, str(e))) return None metadata_dict = dict(metadata) for key in metadata.keys(): metadata_dict[key] = core_lans(metadata_dict[key][0], lans) if 'title' not in metadata_dict: title = os.path.split(fpath)[-1].split('.')[0] metadata_dict['title'] = core_lans(title, lans) metadata_dict.update( dict( url=fpath, duration=metadata.info.length * 1000 # milesecond )) try: if fpath.endswith('flac'): data = FLACMetadataSongSchema().load(metadata_dict) elif fpath.endswith('ape'): data = APEMetadataSongSchema().load(metadata_dict) else: data = EasyMP3MetadataSongSchema().load(metadata_dict) except ValidationError: logger.exception('解析音乐文件({}) 元数据失败'.format(fpath)) return # NOTE: use {title}-{artists_name}-{album_name} as song identifier title = data['title'] album_name = data['album_name'] artist_name_list = [ name.strip() for name in re.split(r'[,&]', data['artists_name']) ] artists_name = ','.join(artist_name_list) duration = data['duration'] album_artist_name = data['album_artist_name'] # 生成 song model # 用来生成 id 的字符串应该尽量减少无用信息,这样或许能减少 id 冲突概率 # 加入分隔符'-'在一定概率上更能确保不发生哈希值重复 song_id_str = delimiter.join( [title, artists_name, album_name, str(int(duration))]) song_id = gen_id(song_id_str) if song_id not in g_songs: # 剩下 album, lyric 三个字段没有初始化 song = LSongModel( identifier=song_id, artists=[], title=title, url=fpath, duration=duration, comments=[], # 下面这些字段不向外暴露 genre=data['genre'], cover=data['cover'], date=data['date'], desc=data['desc'], disc=data['disc'], track=data['track']) g_songs[song_id] = song else: song = g_songs[song_id] logger.warning('Duplicate song: %s %s', song.url, fpath) return # 生成 album artist model album_artist_id = gen_id(album_artist_name) if album_artist_id not in g_artists: album_artist = create_artist(album_artist_id, album_artist_name) g_artists[album_artist_id] = album_artist else: album_artist = g_artists[album_artist_id] # 生成 album model album_id_str = delimiter.join([album_name, album_artist_name]) album_id = gen_id(album_id_str) # cover_data, cover_fmt = read_audio_cover(fpath) # if cover_data is None: # cover = None # else: # cover = Media(reverse(song, '/cover/data'), type_=MediaType.image) if album_id not in g_albums: album = create_album(album_id, album_name, None) g_albums[album_id] = album else: album = g_albums[album_id] # 处理专辑的歌手信息和歌曲信息,专辑歌手的专辑列表信息 if album not in album_artist.albums: album_artist.albums.append(album) if album_artist not in album.artists: album.artists.append(album_artist) if song not in album.songs: album.songs.append(song) # 处理歌曲的歌手和专辑信息,以及歌手的歌曲列表和参与作品 song.album = album for artist_name in artist_name_list: artist_id = gen_id(artist_name) if artist_id in g_artists: artist = g_artists[artist_id] else: artist = create_artist(identifier=artist_id, name=artist_name) g_artists[artist_id] = artist if artist not in song.artists: song.artists.append(artist) if song not in artist.songs: artist.songs.append(song) # 处理歌曲歌手的参与作品信息(不与前面的重复) if album not in artist.albums and album not in artist.contributed_albums: artist.contributed_albums.append(album) # 处理专辑歌手的歌曲信息: 有些作词人出合辑很少出现在歌曲歌手里(可选) if expand_artist_songs and song not in album_artist.songs: album_artist.songs.append(song)
def setUp(self): super(TAPEv2ThenID3v1, self).setUp() f = open(self.filename, "ab+") f.write(b"TAG" + b"\x00" * 125) f.close() self.audio = APEv2(self.filename)
def setUp(self): fd, self.filename = mkstemp(".apev2") os.close(fd) shutil.copy(OLD, self.filename) self.audio = APEv2(self.filename)
def setUp(self): self.filename = get_temp_copy(OLD) self.audio = APEv2(self.filename)
elif ftype == 'ape' or ftype == 'apl': m_audio = None try: f = open(filename, 'rb') m_audio = MonkeysAudioInfo(f) f.close() if m_audio.length <> 0: bitrate = int( os.path.getsize(filename) * 8 / 1000 / m_audio.length) except IOError: print 'm_audio.error', filename, ' ---> time and avrg bitrate will not be availble' pass try: audio = APEv2(filename) except IOError: return { "title": filename[filename.rfind('\\') + 1:-(len(ftype) + 1)], "artist": 'No Artist', "album": 'No Album', "bitrate": bitrate, 'time': '00:00', 'time_sec': 0, 'ftype': ftype } if audio == None: return { "title": filename[filename.rfind('\\') + 1:-(len(ftype) + 1)],
def process(fullpath, config, rcontext, columns=None): # The whole parse data method is in a try block to catch any exception try: # Read the audio file and get the two data # classes from it (Tag and AudioInfo) track = eyed3.load(fullpath) track_tag = track.tag track_info = track.info # A variable to store mutiple strings in a list list_of_strings = [] # Init list assorted = [] # Store the title in the list assorted.append(track_tag.title) ''' To get a tag from the audio file we need to set a frame in the eyed3 lib. In return we get mutiple frames or NoneType. For each frame we get the correct variable and this is stored in a list. After the loop we parse all found data into one string, this string for example look like: subtitle / subtitle / subtitle *The eyed3 lib says that it will never occur that there are multiple frames, that is why we store all data in one string, but to be sure of this, we still irritate through all frames* Because a NoneType can not be irritated we use the "or", so when we get a NoneType the for loop uses an empty list. Wich will result in an empty string because nothing is added to the list. ''' # Store the subtitle in the list for subtitle_frame in track_tag.frame_set["TIT3"] or []: list_of_strings.append(subtitle_frame.text) assorted.append(' / '.join(list_of_strings)) # Store the artist in the list assorted.append(track_tag.artist) # Store the album artist in the list list_of_strings = [] for album_artist_frame in track_tag.frame_set["TPE2"] or []: list_of_strings.append(album_artist_frame.text) assorted.append(' / '.join(list_of_strings)) # Store the album in the list assorted.append(track_tag.album) ''' Some tags return an array or list of items. To get the correct data of these lists we first need to know if the list is not a NoneType or empty. If we don't check this an empty list will result in an exception, "[0]" can not be used an such list If the list is empty we still need to give something in return to the database, so we first define the variable to None. ''' # Store the track_number in the list track_number = None track_total = None if (track_tag.track_num): track_number = track_tag.track_num[0] track_total = track_tag.track_num[1] assorted.append(track_number) assorted.append(track_total) # Store the disc_number in the list disc_number = None disc_total = None if (track_tag.disc_num): disc_number = track_tag.disc_num[0] disc_total = track_tag.disc_num[1] assorted.append(disc_number) assorted.append(disc_total) # delete variables del track_number, track_total, disc_number, disc_total # Store the cd_id in the list assorted.append(track_tag.cd_id) # Store the publisher in the list assorted.append(track_tag.publisher) # Store the composer in the list list_of_strings = [] for composer_frame in track_tag.frame_set["TCOM"] or []: list_of_strings.append(composer_frame.text) assorted.append(' / '.join(list_of_strings)) # Store the conductor in the list list_of_strings = [] for conductor_frame in track_tag.frame_set["TPE3"] or []: list_of_strings.append(conductor_frame.text) assorted.append(' / '.join(list_of_strings)) # Store the group content in the list list_of_strings = [] for group_content_frame in track_tag.frame_set["TIT1"] or []: list_of_strings.append(group_content_frame.text) assorted.append(' / '.join(list_of_strings)) # Store the releasedate and the recording year in the list assorted.append(track_tag.release_date) assorted.append(track_tag.recording_date) # Store beats per minute in the list assorted.append(track_tag.bpm) # Store the duration of the song in the list assorted.append(track_info.time_secs) # Store the play count of the song in the list assorted.append(track_tag.play_count) # Store the terms of use in the list assorted.append(track_tag.terms_of_use) # Store the language in the list list_of_strings = [] for language_frame in track_tag.frame_set["TLAN"] or []: list_of_strings.append(language_frame.text) assorted.append(' / '.join(list_of_strings)) # Store the rating in the list rating = 0 for rating in track_tag.popularities: rating = rating.rating assorted.append(rating) # Store the genre in the list genre = None if (track_tag.genre): genre = track_tag.genre.name assorted.append(genre) # Store the comment data in the list comment_description = None comment_lang = None comment_text = None for comment in track_tag.comments: comment_description = comment.description comment_lang = comment.lang comment_text = comment.text assorted.append(comment_text) assorted.append(comment_description) assorted.append(comment_lang) # delete variables del rating, genre, comment_description, comment_lang, comment_text # Store the encoded by in the list list_of_strings = [] for encoded_by_frame in track_tag.frame_set["TENC"] or []: list_of_strings.append(encoded_by_frame.text) assorted.append(' / '.join(list_of_strings)) # Store the copyright in the list list_of_strings = [] for copyright_frame in track_tag.frame_set["TCOP"] or []: list_of_strings.append(copyright_frame.text) assorted.append(' / '.join(list_of_strings)) # Store the mood in the list list_of_strings = [] for mood_frame in track_tag.frame_set["TMOO"] or []: list_of_strings.append(mood_frame.text) assorted.append(' / '.join(list_of_strings)) # Store the compilation in the list list_of_strings = [] for compitation_frame in track_tag.frame_set["TPE4"] or []: list_of_strings.append(compitation_frame.text) assorted.append(' / '.join(list_of_strings)) # Store the user text data in the list user_text_description = None user_text_text = None for user_text in track_tag._user_texts: user_text_description = user_text.description user_text_text = user_text.text assorted.append(user_text_text) assorted.append(user_text_description) # Store the lyrics data in the list lyrics_description = None lyrics_text = None lyrics_lang = None for lyric in track_tag.lyrics: lyrics_description = lyric.description lyrics_text = lyric.text lyrics_lang = lyric.lang assorted.append(lyrics_text) assorted.append(lyrics_description) assorted.append(lyrics_lang) # Store the image data in the list image_description = None image_url = None image_picturetype = None for image in track_tag.images: image_description = image.description image_url = image.image_url image_picturetype = image.picTypeToString(image.picture_type) assorted.append(image_description) assorted.append(image_picturetype) assorted.append(image_url) # delete variables del user_text_description, user_text_text, lyrics_description del lyrics_lang, lyrics_text, image_description, image_url del image_picturetype # Store the chapter data in the list chapter_title = None chapter_subtitle = None chapter_starttime = None chapter_endtime = None chapter_startoffset = None chapter_endoffset = None for chapter in track_tag.chapters: chapter_title = chapter.title chapter_subtitle = chapter.subtitle if (chapter.times): chapter_starttime = chapter.times[0] chapter_endtime = chapter.times[1] if (chapter.offsets): chapter_startoffset = chapter.offsets[0] chapter_endoffset = chapter.offsets[1] assorted.append(chapter_title) assorted.append(chapter_subtitle) assorted.append(chapter_starttime) assorted.append(chapter_endtime) assorted.append(chapter_startoffset) assorted.append(chapter_endoffset) # delete variables del chapter_title, chapter_subtitle, chapter_starttime del chapter_endtime, chapter_startoffset, chapter_endoffset # Store all URL's in the list assorted.append(track_tag.commercial_url) assorted.append(track_tag.copyright_url) assorted.append(track_tag.artist_url) assorted.append(track_tag.audio_file_url) assorted.append(track_tag.audio_source_url) assorted.append(track_tag.internet_radio_url) assorted.append(track_tag.payment_url) assorted.append(track_tag.publisher_url) user_url = None for user_url_tag in track_tag._user_urls: user_url = user_url_tag.url assorted.append(user_url) # Delete all variables that are not used anymore del user_url, list_of_strings, track_info, track_tag, track # Get APEv2 tag from MP3, # because we don't know the key:value we store all data in one column try: apev2_tag = APEv2(fullpath) except: apev2_tag = None assorted.append(apev2_tag) # Delete variable del apev2_tag # Store the embedded XMP metadata if config.ENABLEXMP: import libxmp xmpfile = libxmp.XMPFiles(file_path=fullpath) assorted.append(str(xmpfile.get_xmp())) xmpfile.close_file() else: assorted.append(None) # Make sure we stored exactly the same amount of columns as # specified!! assert len(assorted) == len(columns) # Print some data that is stored in the database if debug is true if config.DEBUG: print "\nMPEG file data:" for i in range(0, len(assorted)): print "%-18s %s" % (columns[i], assorted[i]) print # Store in database return assorted except: traceback.print_exc(file=sys.stderr) return None