class TWave(TestCase): def setUp(self): fn_wav_pcm_2s_16000_08_id3v23 = \ os.path.join(DATA_DIR, "silence-2s-PCM-16000-08-ID3v23.wav") self.wav_pcm_2s_16000_08_ID3v23 = \ WAVE(fn_wav_pcm_2s_16000_08_id3v23) self.tmp_fn_pcm_2s_16000_08_ID3v23 = \ get_temp_copy(fn_wav_pcm_2s_16000_08_id3v23) self.tmp_wav_pcm_2s_16000_08_ID3v23 = \ WAVE(self.tmp_fn_pcm_2s_16000_08_ID3v23) self.fn_wav_pcm_2s_16000_08_notags = \ os.path.join(DATA_DIR, "silence-2s-PCM-16000-08-notags.wav") self.wav_pcm_2s_16000_08_notags = \ WAVE(self.fn_wav_pcm_2s_16000_08_notags) self.tmp_fn_pcm_2s_16000_08_notag = \ get_temp_copy(self.fn_wav_pcm_2s_16000_08_notags) self.tmp_wav_pcm_2s_16000_08_notag = \ WAVE(self.tmp_fn_pcm_2s_16000_08_notag) fn_wav_pcm_2s_44100_16_id3v23 = \ os.path.join(DATA_DIR, "silence-2s-PCM-44100-16-ID3v23.wav") self.wav_pcm_2s_44100_16_ID3v23 = WAVE(fn_wav_pcm_2s_44100_16_id3v23) def test_channels(self): self.failUnlessEqual(self.wav_pcm_2s_16000_08_ID3v23.info.channels, 2) self.failUnlessEqual(self.wav_pcm_2s_44100_16_ID3v23.info.channels, 2) def test_sample_rate(self): self.failUnlessEqual(self.wav_pcm_2s_16000_08_ID3v23.info.sample_rate, 16000) self.failUnlessEqual(self.wav_pcm_2s_44100_16_ID3v23.info.sample_rate, 44100) def test_number_of_samples(self): self.failUnlessEqual( self.wav_pcm_2s_16000_08_ID3v23.info._number_of_samples, 32000) self.failUnlessEqual( self.wav_pcm_2s_44100_16_ID3v23.info._number_of_samples, 88200) def test_bits_per_sample(self): self.failUnlessEqual( self.wav_pcm_2s_16000_08_ID3v23.info.bits_per_sample, 8) self.failUnlessEqual( self.wav_pcm_2s_44100_16_ID3v23.info.bits_per_sample, 16) def test_bitrate(self): self.failUnlessEqual(self.wav_pcm_2s_16000_08_ID3v23.info.bitrate, 64000) self.failUnlessEqual(self.wav_pcm_2s_44100_16_ID3v23.info.bitrate, 352800) def test_length(self): self.failUnlessAlmostEqual(self.wav_pcm_2s_16000_08_ID3v23.info.length, 2.0, 2) self.failUnlessAlmostEqual(self.wav_pcm_2s_44100_16_ID3v23.info.length, 2.0, 2) def test_not_my_file(self): self.failUnlessRaises(InvalidChunk, WAVE, os.path.join(DATA_DIR, "empty.ogg")) def test_pprint(self): self.wav_pcm_2s_44100_16_ID3v23.pprint() def test_mime(self): self.failUnless("audio/wav" in self.wav_pcm_2s_44100_16_ID3v23.mime) self.failUnless("audio/wave" in self.wav_pcm_2s_44100_16_ID3v23.mime) def test_id3_tags(self): id3 = self.wav_pcm_2s_44100_16_ID3v23.tags self.assertEquals(id3["TALB"], "Quod Libet Test Data") self.assertEquals(id3["TCON"], "Silence") self.assertEquals(id3["TIT2"], "Silence") self.assertEquals(id3["TPE1"], ["piman / jzig"]) def test_id3_tags_uppercase_chunk(self): id3 = self.wav_pcm_2s_16000_08_ID3v23 self.assertEquals(id3["TALB"], "Quod Libet Test Data") self.assertEquals(id3["TCON"], "Silence") self.assertEquals(id3["TIT2"], "Silence") self.assertEquals(id3["TPE1"], ["piman / jzig"]) def test_delete(self): self.tmp_wav_pcm_2s_16000_08_ID3v23.delete() self.failIf(self.tmp_wav_pcm_2s_16000_08_ID3v23.tags) self.failUnless(WAVE(self.tmp_fn_pcm_2s_16000_08_ID3v23).tags is None) def test_save_no_tags(self): self.tmp_wav_pcm_2s_16000_08_ID3v23.tags = None self.tmp_wav_pcm_2s_16000_08_ID3v23.save() self.assertTrue(self.tmp_wav_pcm_2s_16000_08_ID3v23.tags is None) def test_add_tags_already_there(self): self.failUnless(self.tmp_wav_pcm_2s_16000_08_ID3v23.tags) self.failUnlessRaises(Exception, self.tmp_wav_pcm_2s_16000_08_ID3v23.add_tags) def test_roundtrip(self): self.failUnlessEqual(self.tmp_wav_pcm_2s_16000_08_ID3v23["TIT2"], ["Silence"]) self.tmp_wav_pcm_2s_16000_08_ID3v23.save() new = WAVE(self.tmp_wav_pcm_2s_16000_08_ID3v23.filename) self.failUnlessEqual(new["TIT2"], ["Silence"]) def test_save_tags(self): from mutagen.id3 import TIT1 tags = self.tmp_wav_pcm_2s_16000_08_ID3v23.tags tags.add(TIT1(encoding=3, text="foobar")) tags.save() new = WAVE(self.tmp_wav_pcm_2s_16000_08_ID3v23.filename) self.failUnlessEqual(new["TIT1"], ["foobar"]) """" Simulate the way Picard writes and update tags """ def test_picard_lifecycle(self): path_tmp_wav_file = \ get_temp_copy(self.fn_wav_pcm_2s_16000_08_notags) from mutagen.id3 import ID3 wav = WAVE(path_tmp_wav_file) id3 = wav.tags """" Picard WaveFile._get_tags: """ self.assertIsNone(id3, "Ensure ID3-tag-header does not exist") """" Picard WaveFile._get_tags: initialize tags """ wav.add_tags() id3 = wav.tags self.assertIsInstance(id3, ID3) """ ID3v2.3 separator """ separator = '/' """ Initialize Picard like metadata tags """ self.__init_id3_tags(id3, major=3) """ Write the Picard like metadata to the empty WAVE-file """ id3.save(path_tmp_wav_file, v23_sep=separator) """ Tags (metadata) have been added; now load the file again """ wav = WAVE(path_tmp_wav_file) id3 = wav.tags self.assertIsInstance(id3, ID3) self.assertEquals(id3["TRCK"], "1/10") self.assertEquals(id3["TPOS"], "1/1") self.assertEquals(id3["TXXX:MusicBrainz Release Group Id"], "e00305af-1c72-469b-9a7c-6dc665ca9adc") self.assertEquals(id3["TXXX:MusicBrainz Album Artist Id"], [ "3fe817fc-966e-4ece-b00a-76be43e7e73c", "984f8239-8fe1-4683-9c54-10ffb14439e9" ]) self.assertEquals(id3["TXXX:CATALOGNUMBER"], ["PRAR931391"]) self.assertEquals(id3["TSRC"], ["NLB931100460", "USMH51100098"]) @staticmethod def __init_id3_tags(id3, major=3): """ Attributes: id3 ID3 Tag object major ID3 major version, e.g.: 3 for ID3v2.3 """ from mutagen.id3 import TRCK, TPOS, TXXX, TPUB, TALB, UFID, TPE2, \ TSO2, TMED, TIT2, TPE1, TSRC, IPLS, TORY, TDAT, TYER id3.add(TRCK(encoding=major, text="1/10")) id3.add(TPOS(encoding=major, text="1/1")) id3.add( TXXX(encoding=major, desc="MusicBrainz Release Group Id", text="e00305af-1c72-469b-9a7c-6dc665ca9adc")) id3.add(TXXX(encoding=major, desc="originalyear", text="2011")) id3.add( TXXX(encoding=major, desc="MusicBrainz Album Type", text="album")) id3.add( TXXX(encoding=major, desc="MusicBrainz Album Id", text="e7050302-74e6-42e4-aba0-09efd5d431d8")) id3.add(TPUB(encoding=major, text="J&R Adventures")) id3.add(TXXX(encoding=major, desc="CATALOGNUMBER", text="PRAR931391")) id3.add(TALB(encoding=major, text="Don\'t Explain")) id3.add( TXXX(encoding=major, desc="MusicBrainz Album Status", text="official")) id3.add(TXXX(encoding=major, desc="SCRIPT", text="Latn")) id3.add( TXXX(encoding=major, desc="MusicBrainz Album Release Country", text="US")) id3.add(TXXX(encoding=major, desc="BARCODE", text="804879313915")) id3.add( TXXX(encoding=major, desc="MusicBrainz Album Artist Id", text=[ "3fe817fc-966e-4ece-b00a-76be43e7e73c", "984f8239-8fe1-4683-9c54-10ffb14439e9" ])) id3.add(TPE2(encoding=major, text="Beth Hart & Joe Bonamassa")) id3.add(TSO2(encoding=major, text="Hart, Beth & Bonamassa, Joe")) id3.add(TXXX(encoding=major, desc="ASIN", text="B005NPEUB2")) id3.add(TMED(encoding=major, text="CD")) id3.add( UFID(encoding=major, owner="http://musicbrainz.org", data=b"f151cb94-c909-46a8-ad99-fb77391abfb8")) id3.add(TIT2(encoding=major, text="Sinner's Prayer")) id3.add( TXXX(encoding=major, desc="MusicBrainz Artist Id", text=[ "3fe817fc-966e-4ece-b00a-76be43e7e73c", "984f8239-8fe1-4683-9c54-10ffb14439e9" ])) id3.add(TPE1(encoding=major, text=["Beth Hart & Joe Bonamassa"])) id3.add( TXXX(encoding=major, desc="Artists", text=["Beth Hart", "Joe Bonamassa"])) id3.add(TSRC(encoding=major, text=["NLB931100460", "USMH51100098"])) id3.add( TXXX(encoding=major, desc="MusicBrainz Release Track Id", text="d062f484-253c-374b-85f7-89aab45551c7")) id3.add( IPLS(encoding=major, people=[["engineer", "James McCullagh"], ["engineer", "Jared Kvitka"], ["arranger", "Jeff Bova"], ["producer", "Roy Weisman"], ["piano", "Beth Hart"], ["guitar", "Blondie Chaplin"], ["guitar", "Joe Bonamassa"], ["percussion", "Anton Fig"], ["drums", "Anton Fig"], ["keyboard", "Arlan Schierbaum"], ["bass guitar", "Carmine Rojas"], ["orchestra", "The Bovaland Orchestra"], ["vocals", "Beth Hart"], ["vocals", "Joe Bonamassa"]])), id3.add(TORY(encoding=major, text="2011")) id3.add(TYER(encoding=major, text="2011")) id3.add(TDAT(encoding=major, text="2709"))
def initiateWAVE(filename, directory, thumbnails, options): audio = WAVE(str(directory) + "/" + str(filename)) # verify artist information is present before preceeding if ' - ' not in filename and str(audio['TCON']) == '': messagebox.showinfo("No artist information found, aborting procedure") return False, False, False # transcribe formal tagnames into informal counterpart formalTagDict = { 'TPE1': 'Artist', 'TALB': 'Album', 'TPE2': 'Album Artist', 'TBPM': 'BPM', 'COMM::eng': 'Comment', 'TCMP': 'Compilation', 'TCOP': 'Copyright', 'TPOS': 'Discnumber', 'TCON': 'Genre', 'APIC:': 'Image', 'TKEY': 'Key', 'TDRC': 'Release_Date', 'TIT2': 'Title', 'TXXX:replaygain_track_gain': 'ReplayGain', } # transcribe informal tagnames into formal counterpart informalTagDict = {v: k for k, v in formalTagDict.items()} ID3Frames = { 'TPE1': TPE1, 'TALB': TALB, 'TPE2': TPE2, 'TBPM': TBPM, 'COMM': COMM, 'TCMP': TCMP, 'TCOP': TCOP, 'TPOS': TPOS, 'TCON': TCON, 'APIC:': APIC, 'TKEY': TKEY, 'TDRC': TDRC, 'TIT2': TIT2, 'TXXX': TXXX, } fileParameters = [] tagList = list(audio.keys()) for tag in tagList: # delete extraneous tags if the tag is not in the list of selected tags and the delete unselected tags option is activated if (tag not in formalTagDict or formalTagDict[tag] not in options["Selected Tags (L)"] ) and options["Delete Unselected Tags (B)"].get() == True: audio.pop(tag) audio.save() else: fileParameters.append(tag) for tag in options["Selected Tags (L)"]: if tag in informalTagDict: tag = informalTagDict[tag] # add tags of interest if missing if tag not in fileParameters: try: if "COMM" in tag: audio[tag] = COMM(encoding=3, lang="eng", text="") elif "TXXX" in tag: audio[tag] = TXXX(encoding=3, desc="replaygain_track_gain", text="") else: audio[tag] = ID3Frames[tag](encoding=3, text="") audio.save() except: messagebox.showinfo( "Permission Error", "Unable to save tags, file may be open somewhere") return False, False, False # check for discrepancies between tags and filename # check both artist and title tags if ' - ' in filename: artist = filename.split(' - ')[0] title = filename[filename.index(filename.split(' - ')[1]):filename. rfind('.')] if artist != str(audio["TPE1"]) or title != str(audio["TIT2"]): # save artist and title to tag if both are empty if str(audio["TPE1"]) == '' and str(audio["TIT2"]) == '': audio["TPE1"] = TPE1(encoding=3, text=artist) audio["TIT2"] = TIT2(encoding=3, text=title) audio.save() else: audio, filename = compareArtistAndTitle( audio, artist, title, filename, directory, options) # only check title tag else: title = filename[:filename.rfind('.')] if title != str(audio["TIT2"]): # save title to tag if tag is empty if str(audio["TIT2"]) == '': audio["TIT2"] = TIT2(encoding=3, text=title) audio.save() else: audio, filename = compareTitle(audio, title, filename, directory, options) # handle naming format and typo check if options["Audio naming format (S)"].get() == "Artist - Title" or options[ 'Audio naming format (S)'].get() == 'Title': namingConvention = options['Audio naming format (S)'].get() artist = str(audio["TPE1"]) audio, filename = handleStaticNamingConvention(audio, filename, artist, title, directory, namingConvention) if options["Scan Filename and Tags (B)"].get( ) == True and type(audio) != bool: audio, filename, options = extractArtistAndTitle( audio, filename, directory, options, namingConvention) if type(audio) != bool: # save thumbnail to list image = audio["APIC:"] if image.data != b'': stream = BytesIO(image.data) image = Image.open(stream).convert("RGBA") thumbnails = saveThumbnail(image, thumbnails) stream.close() else: thumbnails = saveThumbnail("NA", thumbnails) return audio, filename, informalTagDict, thumbnails, options
def rename(directory, filename, artist, title, extension, namingConvention): if namingConvention == "Artist - Title" or (namingConvention == 'Dynamic' and ' - ' in filename): try: os.rename( directory + '/' + filename, directory + '/' + str(artist) + ' - ' + str(title) + extension) filename = str(artist) + ' - ' + str(title) + extension except PermissionError: messagebox.showinfo( "Permission Error", "File cannot be renamed, it may still be open") return False, False elif namingConvention == "Title" or (namingConvention == 'Dynamic' and ' - ' not in filename): try: os.rename(directory + '/' + filename, str(directory) + '/' + str(title) + extension) filename = str(title) + extension except PermissionError: messagebox.showinfo( "Permission Error", "File cannot be renamed, it may still be open") return False, False if extension == ".wav": audio = WAVE(str(directory) + '/' + filename) audio["TPE1"] = TPE1(encoding=3, text=artist) audio["TIT2"] = TIT2(encoding=3, text=title) audio.save() return audio, filename elif extension == ".flac": audio = FLAC(str(directory) + '/' + filename) audio['artist'] = artist audio['title'] = title audio.save() return audio, filename elif extension == ".aiff": audio = AIFF(str(directory) + '/' + filename) audio["TPE1"] = TPE1(encoding=3, text=artist) audio["TIT2"] = TIT2(encoding=3, text=title) audio.save() return audio, filename elif extension == ".m4a": audio = MP4(str(directory) + '/' + filename) audio["\xa9ART"] = artist audio["\xa9nam"] = title audio.save() return audio, filename elif extension == ".mp3": audio = MP3(str(directory) + '/' + filename) audio["TPE1"] = TPE1(encoding=3, text=artist) audio["TIT2"] = TIT2(encoding=3, text=title) audio.save() return audio, filename elif extension == ".ogg": audio = OggVorbis(str(directory) + '/' + filename) audio['artist'] = artist audio['title'] = title audio.save() return audio, filename