def getsubmission(tfile): data = open(tfile).read() torrent = decode(data) tname = torrent['info']['name'] for file in torrent["info"]["files"]: name = "/".join(file["path"]) flac_re = re.compile(".flac") if flac_re.search(name) != None: flacname=name log_re = re.compile(".log") if log_re.search(name) !=None: logname=name fpath = os.path.join(tname,flacname) lpath = os.path.join(tname,logname) audio = FLAC(fpath) print audio.keys() if not audio.has_key('musicbrainz_albumid'): print "ReleaseID tag is not set. Has this flac been tagged by picard?" return(-1) albumid = audio['musicbrainz_albumid'][0] print albumid q = ws.Query() try: inc = ws.ReleaseIncludes(artist=True, releaseEvents=True, labels=True, discs=True, tracks=True) release = q.getReleaseById(albumid, inc) except ws.WebServiceError, e: print 'Error:', e return(-1)
def _extract(self, path: Path): """ Extract meta-data from music file like name and artists. This method uses mutagen lib to open the file. :param path: Path object with filename :return: List with meta-data """ # Parameters file = None title_tag = "" artist_tag = "" # Return data = { 'title': "", 'artists': "", 'duration': 0.0 } # Check sufix and open music file if path.suffix == '.mp3': # MP3 File file = MP3(path) title_tag = "TIT2" artist_tag = "TPE1" elif path.suffix in ['.m4a', '.mp4']: # MPEG4 File file = MP4(path) title_tag = chr(169) + "nam" artist_tag = chr(169) + "ART" elif path.suffix == '.flac': # FLAC File file = FLAC(path) title_tag = "title" artist_tag = "artist" # Check opening success and read data if file is not None: # Get music title if title_tag in file.keys(): title = file.get(title_tag) data['title'] = (str(title[0] if len(title) > 0 else "")) if isinstance(title, list) else str(title) # Get music artists (separated with '/') if artist_tag in file.keys(): artist = file.get(artist_tag) data['artists'] = "/".join(artist) if isinstance(artist, list) else str(artist) # Get music lenght (seconds) data['duration'] = float(file.info.length) return data
def get_album_and_artist(self): """ Return FLAC tags for album and artist""" self.audio_files.sort() for file in self.audio_files: try: tags = FLAC(file) if tags: if "album" in tags.keys() and "artist" in tags.keys(): return (tags["album"][0], tags["artist"][0]) break # If we found ID3 tag info from a file, no reason to query the rest in a directory. except mutagen.flac.FLACNoHeaderError: continue return (None, None)
def check_flac_tags(full_path, e): t = {} # "proper" tags tags = FLAC(full_path) unallowed = set(tags.keys()).difference(flac_allow) if unallowed: for item in unallowed: e.append("Unallowed tag: '" + item + "' - remove") # "minimal" tags for item in 'album', 'tracknumber', 'title', 'date': t[item] = tags[item][0] # Handle multiple artist tags in single track if len(tags['artist']) > 1: t['artist'] = ", ".join(tags['artist']) else: t['artist'] = tags['artist'][0] # "optional" tags for item in 'tracktotal', 'genre', 'albumartist', 'discnumber', 'disctotal': if item in tags: t[item] = tags[item][0] else: t[item] = None return t, e
def media_info(f): aid_re = re.compile("brainz.*album.*id",re.I) has_info = 0 ext = os.path.splitext(f)[-1] if ext == ".mp3": has_info = 1 info = ID3(f) format = "MP3" if ext == ".flac": has_info = 1 info = FLAC(f) format = "FLAC" if ext == ".ogg": has_info = 1 info = OggVorbis(f) format = "OGG" if not has_info: return {} for k in info.keys(): if re.search(aid_re,k) != None: aid = info[k] if type(aid) ==mutagen.id3.TXXX: aid = aid.__unicode__() if type(aid) == type([0]): aid = aid[0] info_d = {} info_d['mbid'] = aid return info_d
def get_album_and_artist(self): """ Return FLAC tags for album and artist""" self.audio_files.sort() for file in self.audio_files: try: tags = FLAC(file) if tags: if "album" in tags.keys() and "artist" in tags.keys(): logger.debug(u'album -> {album}, artist -> {artist}'.format(album=tags["album"][0], artist=tags["artist"][0])) return (tags["album"][0], tags["artist"][0]) break # If we found ID3 tag info from a file, no reason to query the rest in a directory. except mutagen.flac.FLACNoHeaderError: logger.error(u'No FLAC Header data') continue return (None, None)
def copy_id3tag(src, des): from subprocess import call f = FLAC(src) args = ["neroAacTag", des] for key in f.keys(): args.extend([_rectify(key) + "=" + f[key][0],]) print args call(args)
def get_artwork(filename): if not filename.endswith(".flac"): print "This program is strictly intended for use with flac files" exit(1) flacinfo = FLAC(filename) if 'album' not in flacinfo.keys() or 'album' not in flacinfo.keys(): print("Missing album or artist" + filename) artist=flacinfo["artist"][0].encode('utf-8') album=flacinfo["album"][0].encode('utf-8') print artist,album folder = os.path.dirname(os.path.realpath(filename)) coverpath = "".join([folder, os.path.sep, "cover.jpg"]) if os.path.isfile(coverpath): return cmd = "glyrc cover --artist \'{0}\' --album \'{1}\' --write \'{2}\'".format(artist, album, coverpath) out = get_shell_cmd_output(cmd) print(out)
def extract_taginfo(self): taginfo = { } try : tagproxy = FLAC(self.filename) except ValueError : return { } for key in tagproxy.keys(): taginfo[key.lower()] = tagproxy[key][0] return taginfo
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 get_artwork(filename): if not filename.endswith(".flac"): print "This program is strictly intended for use with flac files" exit(1) flacinfo = FLAC(filename) if 'album' not in flacinfo.keys() or 'album' not in flacinfo.keys(): print("Missing album or artist" + filename) artist = flacinfo["artist"][0].encode('utf-8') album = flacinfo["album"][0].encode('utf-8') print artist, album folder = os.path.dirname(os.path.realpath(filename)) coverpath = "".join([folder, os.path.sep, "cover.jpg"]) if os.path.isfile(coverpath): return cmd = "glyrc cover --artist \'{0}\' --album \'{1}\' --write \'{2}\'".format( artist, album, coverpath) out = get_shell_cmd_output(cmd) print(out)
def printFLAC(flacFile): from mutagen.flac import FLAC, Picture audio = FLAC(flacFile) print "--FLAC--------------------------------------------" for tag in audio.keys(): for text in audio[tag]: if 'APIC' in tag: print("Tag %s" % (tag) ) else: print("Tag %s: %s" % (tag, text) ) pics = audio.pictures for p in pics: print("Bild gefunden. Typ %s: %s" % (p.type, p.desc) )
def _get_tag(self): if self.suffix == '.flac': try: audio = FLAC(self.path) self.artist = audio['artist'][0] self.title = audio['title'][0] if audio.get('genre'): self.local_genres = audio['genre'][0] if audio.get('date'): self.local_year = audio['date'][0] if audio.pictures: self.cover_embedded = True except (FLACNoHeaderError, Exception) as e: pass if self.suffix == '.mp3': try: audio = EasyID3(self.path) self.artist = audio['artist'][0] self.title = audio['title'][0] if audio.get('genre'): self.local_genres = audio['genre'][0] if audio.get('date'): self.local_year = audio['date'][0] audio = MP3(self.path) for k in audio.keys(): if u'covr' in k or u'APIC' in k: self.cover_embedded = True except (HeaderNotFoundError, MutagenError, KeyError) as e: pass if self.suffix == '.m4a': try: audio = MP4(self.path) self.artist = audio['\xa9ART'][0] self.title = audio['\xa9nam'][0] if audio.get('\xa9gen'): self.local_genres = audio['\xa9gen'][0] if audio.get('\xa9day'): self.local_year = audio['\xa9day'][0] if audio.get('covr'): self.cover_embedded = True except (KeyError, MP4StreamInfoError, MutagenError) as e: pass
class TFLAC(TestCase): SAMPLE = os.path.join(DATA_DIR, "silence-44-s.flac") def setUp(self): self.NEW = get_temp_copy(self.SAMPLE) self.flac = FLAC(self.NEW) def tearDown(self): os.unlink(self.NEW) def test_zero_samples(self): # write back zero sample count and load again self.flac.info.total_samples = 0 self.flac.save() new = FLAC(self.flac.filename) assert new.info.total_samples == 0 assert new.info.bitrate == 0 assert new.info.length == 0.0 def test_bitrate(self): assert self.flac.info.bitrate == 101430 old_file_size = os.path.getsize(self.flac.filename) self.flac.save(padding=lambda x: 9999) new_flac = FLAC(self.flac.filename) assert os.path.getsize(new_flac.filename) > old_file_size assert new_flac.info.bitrate == 101430 def test_padding(self): for pad in [0, 42, 2**24 - 1, 2**24]: self.flac.save(padding=lambda x: pad) new = FLAC(self.flac.filename) expected = min(2**24 - 1, pad) self.assertEqual(new.metadata_blocks[-1].length, expected) def test_save_multiple_padding(self): # we don't touch existing padding blocks on save, but will # replace them in the file with one at the end def num_padding(f): blocks = f.metadata_blocks return len([b for b in blocks if isinstance(b, Padding)]) num_blocks = num_padding(self.flac) self.assertEqual(num_blocks, 1) block = Padding() block.length = 42 self.flac.metadata_blocks.append(block) block = Padding() block.length = 24 self.flac.metadata_blocks.append(block) self.flac.save() self.assertEqual(num_padding(self.flac), num_blocks + 2) new = FLAC(self.flac.filename) self.assertEqual(num_padding(new), 1) self.assertTrue(isinstance(new.metadata_blocks[-1], Padding)) def test_increase_size_new_padding(self): self.assertEqual(self.flac.metadata_blocks[-1].length, 3060) value = u"foo" * 100 self.flac[u"foo"] = [value] self.flac.save() new = FLAC(self.NEW) self.assertEqual(new.metadata_blocks[-1].length, 2752) self.assertEqual(new[u"foo"], [value]) def test_delete(self): self.failUnless(self.flac.tags) self.flac.delete() self.assertTrue(self.flac.tags is not None) self.assertFalse(self.flac.tags) flac = FLAC(self.NEW) self.assertTrue(flac.tags is None) def test_module_delete(self): delete(self.NEW) flac = FLAC(self.NEW) self.failIf(flac.tags) def test_info(self): self.failUnlessAlmostEqual(FLAC(self.NEW).info.length, 3.7, 1) def test_keys(self): self.failUnlessEqual(list(self.flac.keys()), list(self.flac.tags.keys())) def test_values(self): self.failUnlessEqual(list(self.flac.values()), list(self.flac.tags.values())) def test_items(self): self.failUnlessEqual(list(self.flac.items()), list(self.flac.tags.items())) def test_vc(self): self.failUnlessEqual(self.flac['title'][0], 'Silence') def test_write_nochange(self): f = FLAC(self.NEW) f.save() with open(self.SAMPLE, "rb") as a: with open(self.NEW, "rb") as b: self.failUnlessEqual(a.read(), b.read()) def test_write_changetitle(self): f = FLAC(self.NEW) if PY3: self.assertRaises(TypeError, f.__setitem__, b'title', b"A New Title") else: f[b"title"] = b"A New Title" f.save() f = FLAC(self.NEW) self.failUnlessEqual(f[b"title"][0], b"A New Title") def test_write_changetitle_unicode_value(self): f = FLAC(self.NEW) if PY3: self.assertRaises(TypeError, f.__setitem__, b'title', u"A Unicode Title \u2022") else: f[b"title"] = u"A Unicode Title \u2022" f.save() f = FLAC(self.NEW) self.failUnlessEqual(f[b"title"][0], u"A Unicode Title \u2022") def test_write_changetitle_unicode_key(self): f = FLAC(self.NEW) f[u"title"] = b"A New Title" if PY3: self.assertRaises(ValueError, f.save) else: f.save() f = FLAC(self.NEW) self.failUnlessEqual(f[u"title"][0], b"A New Title") def test_write_changetitle_unicode_key_and_value(self): f = FLAC(self.NEW) f[u"title"] = u"A Unicode Title \u2022" f.save() f = FLAC(self.NEW) self.failUnlessEqual(f[u"title"][0], u"A Unicode Title \u2022") def test_force_grow(self): f = FLAC(self.NEW) f["faketag"] = ["a" * 1000] * 1000 f.save() f = FLAC(self.NEW) self.failUnlessEqual(f["faketag"], ["a" * 1000] * 1000) def test_force_shrink(self): self.test_force_grow() f = FLAC(self.NEW) f["faketag"] = "foo" f.save() f = FLAC(self.NEW) self.failUnlessEqual(f["faketag"], ["foo"]) def test_add_vc(self): f = FLAC(os.path.join(DATA_DIR, "no-tags.flac")) self.failIf(f.tags) f.add_tags() self.failUnless(f.tags == []) self.failUnlessRaises(ValueError, f.add_tags) def test_add_vc_implicit(self): f = FLAC(os.path.join(DATA_DIR, "no-tags.flac")) self.failIf(f.tags) f["foo"] = "bar" self.failUnless(f.tags == [("foo", "bar")]) self.failUnlessRaises(ValueError, f.add_tags) def test_ooming_vc_header(self): # issue 112: Malformed FLAC Vorbis header causes out of memory error # https://github.com/quodlibet/mutagen/issues/112 self.assertRaises(error, FLAC, os.path.join(DATA_DIR, 'ooming-header.flac')) def test_with_real_flac(self): if not have_flac: return self.flac["faketag"] = "foobar" * 1000 self.flac.save() self.failIf(call_flac("-t", self.flac.filename) != 0) def test_save_unknown_block(self): block = MetadataBlock(b"test block data") block.code = 99 self.flac.metadata_blocks.append(block) self.flac.save() def test_load_unknown_block(self): self.test_save_unknown_block() flac = FLAC(self.NEW) self.failUnlessEqual(len(flac.metadata_blocks), 7) self.failUnlessEqual(flac.metadata_blocks[5].code, 99) self.failUnlessEqual(flac.metadata_blocks[5].data, b"test block data") def test_two_vorbis_blocks(self): self.flac.metadata_blocks.append(self.flac.metadata_blocks[1]) self.flac.save() self.failUnlessRaises(error, FLAC, self.NEW) def test_missing_streaminfo(self): self.flac.metadata_blocks.pop(0) self.flac.save() self.failUnlessRaises(error, FLAC, self.NEW) def test_load_invalid_flac(self): self.failUnlessRaises(error, FLAC, os.path.join(DATA_DIR, "xing.mp3")) def test_save_invalid_flac(self): self.failUnlessRaises(error, self.flac.save, os.path.join(DATA_DIR, "xing.mp3")) def test_pprint(self): self.failUnless(self.flac.pprint()) def test_double_load(self): blocks = list(self.flac.metadata_blocks) self.flac.load(self.flac.filename) self.failUnlessEqual(blocks, self.flac.metadata_blocks) def test_seektable(self): self.failUnless(self.flac.seektable) def test_cuesheet(self): self.failUnless(self.flac.cuesheet) def test_pictures(self): self.failUnless(self.flac.pictures) def test_add_picture(self): f = FLAC(self.NEW) c = len(f.pictures) f.add_picture(Picture()) f.save() f = FLAC(self.NEW) self.failUnlessEqual(len(f.pictures), c + 1) def test_clear_pictures(self): f = FLAC(self.NEW) c1 = len(f.pictures) c2 = len(f.metadata_blocks) f.clear_pictures() f.save() f = FLAC(self.NEW) self.failUnlessEqual(len(f.metadata_blocks), c2 - c1) def test_ignore_id3(self): id3 = ID3() id3.add(TIT2(encoding=0, text='id3 title')) id3.save(self.NEW) f = FLAC(self.NEW) f['title'] = 'vc title' f.save() id3 = ID3(self.NEW) self.failUnlessEqual(id3['TIT2'].text, ['id3 title']) f = FLAC(self.NEW) self.failUnlessEqual(f['title'], ['vc title']) def test_delete_id3(self): id3 = ID3() id3.add(TIT2(encoding=0, text='id3 title')) id3.save(self.NEW, v1=2) f = FLAC(self.NEW) f['title'] = 'vc title' f.save(deleteid3=True) self.failUnlessRaises(ID3NoHeaderError, ID3, self.NEW) f = FLAC(self.NEW) self.failUnlessEqual(f['title'], ['vc title']) def test_save_on_mp3(self): path = os.path.join(DATA_DIR, "silence-44-s.mp3") self.assertRaises(error, self.flac.save, path) def test_mime(self): self.failUnless("audio/x-flac" in self.flac.mime) def test_variable_block_size(self): FLAC(os.path.join(DATA_DIR, "variable-block.flac")) def test_load_flac_with_application_block(self): FLAC(os.path.join(DATA_DIR, "flac_application.flac"))
except: print("wav to flac error") # convert tag file to flac cli_args = [ 'ffmpeg', '-i', tag_path[i], '-ac', '2', '-acodec', 'flac', 'tag.flac' ] try: subprocess.check_output(cli_args) except: print("convert tag flac error") # read tag info tag_info = FLAC('tag.flac') key_list = tag_info.keys() # write tag info audio_flac = FLAC(flac_path[i]) audio_flac.clear() audio_flac.clear_pictures() for key in key_list: audio_flac[key] = tag_info[key] # write cover image info if os.path.exists('./cover.jpg'): img = fimg() img.type = 3 img.mime = 'image/jpg' img.desc = 'front cover' img.colors = 0
class TFLAC(TestCase): SAMPLE = os.path.join("tests", "data", "silence-44-s.flac") NEW = SAMPLE + ".new" def setUp(self): shutil.copy(self.SAMPLE, self.NEW) self.failUnlessEqual(open(self.SAMPLE, "rb").read(), open(self.NEW, "rb").read()) self.flac = FLAC(self.NEW) def test_delete(self): self.failUnless(self.flac.tags) self.flac.delete() self.failIf(self.flac.tags) flac = FLAC(self.NEW) self.failIf(flac.tags) def test_module_delete(self): delete(self.NEW) flac = FLAC(self.NEW) self.failIf(flac.tags) def test_info(self): self.failUnlessAlmostEqual(FLAC(self.NEW).info.length, 3.7, 1) def test_keys(self): self.failUnlessEqual( list(self.flac.keys()), list(self.flac.tags.keys())) def test_values(self): self.failUnlessEqual( list(self.flac.values()), list(self.flac.tags.values())) def test_items(self): self.failUnlessEqual( list(self.flac.items()), list(self.flac.tags.items())) def test_vc(self): self.failUnlessEqual(self.flac['title'][0], 'Silence') def test_write_nochange(self): f = FLAC(self.NEW) f.save() self.failUnlessEqual(open(self.SAMPLE, "rb").read(), open(self.NEW, "rb").read()) def test_write_changetitle(self): f = FLAC(self.NEW) if PY3: self.assertRaises( TypeError, f.__setitem__, b'title', b"A New Title") else: f[b"title"] = b"A New Title" f.save() f = FLAC(self.NEW) self.failUnlessEqual(f[b"title"][0], b"A New Title") def test_write_changetitle_unicode_value(self): f = FLAC(self.NEW) if PY3: self.assertRaises( TypeError, f.__setitem__, b'title', u"A Unicode Title \u2022") else: f[b"title"] = u"A Unicode Title \u2022" f.save() f = FLAC(self.NEW) self.failUnlessEqual(f[b"title"][0], u"A Unicode Title \u2022") def test_write_changetitle_unicode_key(self): f = FLAC(self.NEW) f[u"title"] = b"A New Title" if PY3: self.assertRaises(ValueError, f.save) else: f.save() f = FLAC(self.NEW) self.failUnlessEqual(f[u"title"][0], b"A New Title") def test_write_changetitle_unicode_key_and_value(self): f = FLAC(self.NEW) f[u"title"] = u"A Unicode Title \u2022" f.save() f = FLAC(self.NEW) self.failUnlessEqual(f[u"title"][0], u"A Unicode Title \u2022") def test_force_grow(self): f = FLAC(self.NEW) f["faketag"] = ["a" * 1000] * 1000 f.save() f = FLAC(self.NEW) self.failUnlessEqual(f["faketag"], ["a" * 1000] * 1000) def test_force_shrink(self): self.test_force_grow() f = FLAC(self.NEW) f["faketag"] = "foo" f.save() f = FLAC(self.NEW) self.failUnlessEqual(f["faketag"], ["foo"]) def test_add_vc(self): f = FLAC(os.path.join("tests", "data", "no-tags.flac")) self.failIf(f.tags) f.add_tags() self.failUnless(f.tags == []) self.failUnlessRaises(ValueError, f.add_tags) def test_add_vc_implicit(self): f = FLAC(os.path.join("tests", "data", "no-tags.flac")) self.failIf(f.tags) f["foo"] = "bar" self.failUnless(f.tags == [("foo", "bar")]) self.failUnlessRaises(ValueError, f.add_tags) def test_ooming_vc_header(self): # issue 112: Malformed FLAC Vorbis header causes out of memory error # http://code.google.com/p/mutagen/issues/detail?id=112 self.assertRaises(IOError, FLAC, os.path.join('tests', 'data', 'ooming-header.flac')) def test_with_real_flac(self): if not have_flac: return self.flac["faketag"] = "foobar" * 1000 self.flac.save() badval = os.system("tools/notarealprogram 2> %s" % devnull) value = os.system("flac -t %s 2> %s" % (self.flac.filename, devnull)) self.failIf(value and value != badval) def test_save_unknown_block(self): block = MetadataBlock(b"test block data") block.code = 99 self.flac.metadata_blocks.append(block) self.flac.save() def test_load_unknown_block(self): self.test_save_unknown_block() flac = FLAC(self.NEW) self.failUnlessEqual(len(flac.metadata_blocks), 7) self.failUnlessEqual(flac.metadata_blocks[5].code, 99) self.failUnlessEqual(flac.metadata_blocks[5].data, b"test block data") def test_two_vorbis_blocks(self): self.flac.metadata_blocks.append(self.flac.metadata_blocks[1]) self.flac.save() self.failUnlessRaises(IOError, FLAC, self.NEW) def test_missing_streaminfo(self): self.flac.metadata_blocks.pop(0) self.flac.save() self.failUnlessRaises(IOError, FLAC, self.NEW) def test_load_invalid_flac(self): self.failUnlessRaises( IOError, FLAC, os.path.join("tests", "data", "xing.mp3")) def test_save_invalid_flac(self): self.failUnlessRaises( IOError, self.flac.save, os.path.join("tests", "data", "xing.mp3")) def test_pprint(self): self.failUnless(self.flac.pprint()) def test_double_load(self): blocks = list(self.flac.metadata_blocks) self.flac.load(self.flac.filename) self.failUnlessEqual(blocks, self.flac.metadata_blocks) def test_seektable(self): self.failUnless(self.flac.seektable) def test_cuesheet(self): self.failUnless(self.flac.cuesheet) def test_pictures(self): self.failUnless(self.flac.pictures) def test_add_picture(self): f = FLAC(self.NEW) c = len(f.pictures) f.add_picture(Picture()) f.save() f = FLAC(self.NEW) self.failUnlessEqual(len(f.pictures), c + 1) def test_clear_pictures(self): f = FLAC(self.NEW) c1 = len(f.pictures) c2 = len(f.metadata_blocks) f.clear_pictures() f.save() f = FLAC(self.NEW) self.failUnlessEqual(len(f.metadata_blocks), c2 - c1) def test_ignore_id3(self): id3 = ID3() id3.add(TIT2(encoding=0, text='id3 title')) id3.save(self.NEW) f = FLAC(self.NEW) f['title'] = 'vc title' f.save() id3 = ID3(self.NEW) self.failUnlessEqual(id3['TIT2'].text, ['id3 title']) f = FLAC(self.NEW) self.failUnlessEqual(f['title'], ['vc title']) def test_delete_id3(self): id3 = ID3() id3.add(TIT2(encoding=0, text='id3 title')) id3.save(self.NEW, v1=2) f = FLAC(self.NEW) f['title'] = 'vc title' f.save(deleteid3=True) self.failUnlessRaises(ID3NoHeaderError, ID3, self.NEW) f = FLAC(self.NEW) self.failUnlessEqual(f['title'], ['vc title']) def test_mime(self): self.failUnless("audio/x-flac" in self.flac.mime) def test_variable_block_size(self): FLAC(os.path.join("tests", "data", "variable-block.flac")) def test_load_flac_with_application_block(self): FLAC(os.path.join("tests", "data", "flac_application.flac")) def tearDown(self): os.unlink(self.NEW)
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)
class FlacFile(audiofile.AudioFile): def __init__(self, filename): super(FlacFile, self).__init__(filename) def loadFile(self): try: self.audio = FLAC(self.filename) self.loadMetaData() self.fileOpen = True except BaseException: self.fileOpen = False logging.error("FEHLER bei %s" % (self.filename)) exc_type, exc_value, exc_traceback = sys.exc_info() lines = traceback.format_exception(exc_type, exc_value, exc_traceback) for line in lines: logging.error(line) return def loadMetaData(self): """ Die FLAC-metadata werden eingelesen. Ursprünglich wurden die Daten einzeln behandelt. Mitlerweile wird das alles durch Ersatzvariablen in der Config- Datei behandelt. Für ein paar Sachen kann es aber noch ganz nütlich sein, die Felder einzeln aufzubereiten (z.B. TrackNr) """ for tag in self.audio.keys(): self.tags[tag] = self.audio[tag] for text in self.audio[tag]: if tag == 'title': if self.debug: logging.debug('Title? {0}: {1}'.format(tag, text.encode('UTF-8'))) self.setTitle(text) elif tag == 'artist': if self.debug: logging.debug('Artist? {0}: {1}'.format(tag, text.encode('UTF-8'))) self.setArtist(text) elif tag == 'album': if self.debug: logging.debug('Album? {0}: {1}'.format(tag, text.encode('UTF-8'))) self.setAlbum(text) elif tag == 'tracknumber': if self.debug: logging.debug('Track? {0}: {1}'.format(tag, text.encode('UTF-8'))) self.setTrack(text) elif tag == 'tracktotal': if self.debug: logging.debug('Tracks? {0}: {1}'.format(tag, text.encode('UTF-8'))) self.setTrackTotal(text) elif tag == 'discnumber': if self.debug: logging.debug('Discnumber? {0}: {1}'.format(tag, text.encode('UTF-8'))) self.setDiscNo(text) elif tag == 'cddb': if self.debug: logging.debug('CDDB? {0}: {1}'.format(tag, text.encode('UTF-8'))) self.setCDDB(text) elif tag == 'date': if self.debug: logging.debug('Year? {0}: {1}'.format(tag, text.encode('UTF-8'))) self.setYear(text) elif tag == 'lyrics': if self.debug: logging.debug('Lyrics? {0}: {1}'.format(tag, text.encode('UTF-8'))) elif 'comment' in tag: if self.debug: logging.debug('Comment? {0}: {1}'.format(tag, text.encode('UTF-8'))) self.setComments(text) else: if self.debug: logging.debug('Unhandled Tag {0}: {1}'.format(tag, text.encode('UTF-8'))) if self.debug: logging.debug("LENGTH? %s" % (self.audio.info.length)) def loadFrontCover(self): pics = self.audio.pictures if self.debug: logging.debug('Insgesamt %s Bilder' % (len(pics))) datei = None logo = None if len(pics) > 0: # Versuchen ein Frontcover laut Deklaration zu finden # oder, wenn es nur ein Bild gibt, dieses nehmen for p in pics: if p.type == 3 or len(pics) == 1: datei = self.getTempPic(p.data) break # Wenn nix gefunden wurde, nehmen wir das erste Bild if not datei: for p in pics: datei = self.getTempPic(p.data) break if datei: self.setCover(pygame.image.load(datei)) else: self.setCover(pygame.image.load(self.LEERCD)) return def loadLogo(self): pics = self.audio.pictures if self.debug: logging.debug('Insgesamt %s Bilder' % (len(pics))) logo = None if len(pics) > 0: # try to fetch a logo-pic if self.debug: logging.debug('Try to fetch bandlogo from %s pics ' % (len(pics))) for p in pics: if p.type == 19: if self.debug: logging.debug('Bandlogo found! ') logo = self.getTempPic(p.data) break if logo: self.setLogo(pygame.image.load(logo)) return def loadStoredPictures(self): diaMode = self.guiPlayer.slide_mode if self.debug: logging.debug('DiaMode: %s' % (diaMode)) pics = self.audio.pictures if self.debug: logging.info('Insgesamt %s Bilder' % (len(pics))) # wenn nur ein Bild vorhanden -> # abbrechen, denn dies wurde als Frontcover # behandelt if len(pics) <= 1: return foundBackcover = False for p in pics: if self.debug: logging.debug('Bild gefunden. Typ {0}: {1}'.format(p.type, p.desc.encode('UTF-8'))) if not p.type == 3 and not p.type == 19: if (diaMode == 3 or diaMode == 5) or (p.type > 3 and p.type < 7): datei = self.getTempPic(p.data) if p.type == 4 and not foundBackcover and diaMode > 3: foundBackcover = True self.setBackcover(pygame.image.load(datei)) else: self.setMiscPic(pygame.image.load(datei)) if self.debug: logging.info('done.') return
def copyFLAC(flacFile, mpgFile): from mutagen.flac import FLAC, Picture from mutagen.mp3 import MP3 from mutagen.id3 import ID3 bChange = False # audioMP3 = MP3('smiley.mp3', ID3=EasyID3) audioMP3 = MP3() audioMP3.add_tags(ID3=CompatID3) bNew = True audioFLAC = FLAC(flacFile) if audioFLAC.has_key('tracktotal'): tracktotal = audioFLAC["tracktotal"][0] else: tracktotal = 0 if not audioFLAC.has_key('albumartist'): audioFLAC["albumartist"] = audioFLAC["artist"] if not audioFLAC.has_key('compilation'): audioFLAC["compilation"] = 0 print "--FLAC--------------------------------------------" for tag in audioFLAC.keys(): # print >> sys.stdout, "--> verarbeite Key: ", tag # if 'APIC' in tag: # print 'Tag {0}'.format(tag) # else: # print >> sys.stdout, "--> verarbeite Key: Tag {0}: {1}".format(tag, textTag.encode('UTF-8')) if tag == "tracktotal": pass else: # id3.add(mutagen.id3.COMM(encoding=3,text=relationshipLink, lang="eng", desc="MusicGrabberSig")) # Tag COMM:Kommentar:'DEU': von Vinyl if tag == "tracknumber": audioFLAC[tag] = '{0}/{1}'.format(audioFLAC[tag][0], tracktotal) # audioFLAC[tag]='{0}/{1}'.format(audioFLAC[tag], tracktotal) searchTag = tag if "comment" in tag: searchTag = 'COMMENTS' if "description" in tag: searchTag = 'COMMENTS' # if not str.upper(tag) in id3Trans.flacFrames: if not hasFrame(searchTag): if "replaygain" in tag: continue print >> sys.stderr, "Key nicht gefunden: ", tag continue id3Frame = getFrame(searchTag) # audioMP3[id3Frame] = audioFLAC[tag] # for textTag in audioFLAC[tag]: # print >> sys.stderr, "tag: %s frame: %s " % (tag, id3Frame.__name__) if "comment" in tag: # audioMP3.add(id3Frame(encoding=3, text= audioFLAC[tag], lang="DEU", desc="Kommentar")) try: audioMP3[id3Frame.__name__] = id3Frame(3, text=audioFLAC[tag], lang="DEU", desc="Kommentar") except: pass else: audioMP3[id3Frame.__name__] = id3Frame(3, text=audioFLAC[tag]) # audioMP3.add(id3Frame(encoding=3, text= audioFLAC[tag])) bChange = True # print u'Tag {0}: {1} --> MP3 zugefuegt'.format(id3Frame, audioFLAC[tag]) if bChange == True: # print dir(audioMP3) # if bNew : audioMP3.save(mpgFile,v1=2) # else : audioMP3.save(v1=2) audioMP3.tags.update_to_v23() if bNew: audioMP3.tags.save(filename=mpgFile, v2=3) else: audioMP3.tags.save(v2=3) print '-' * 40
class flac(TagParser): """ Class for processing Ogg FLAC file tags """ def __init__(self, codec, path): super(flac, self).__init__(codec, path, tag_map=FLAC_STANDARD_TAGS) try: self.entry = FLAC(path) except IOError as e: raise TagError('Error opening {}: {}'.format(path, str(e))) except FLACNoHeaderError as e: raise TagError('Error opening {}: {}'.format(path, str(e))) self.albumart_obj = None self.track_numbering = FLACNumberingTag(self, 'TRACKNUMBER') self.disk_numbering = FLACNumberingTag(self, 'DISKNUMBER') def __getitem__(self, item): if item == 'tracknumber': return [format_unicode_string_value('{:d}'.format(self.track_numbering.value))] if item == 'totaltracks': return [format_unicode_string_value('{:d}'.format(self.track_numbering.total))] if item == 'disknumber': return [format_unicode_string_value('{:d}'.format(self.disk_numbering.value))] if item == 'totaldisks': return [format_unicode_string_value('{:d}'.format(self.disk_numbering.total))] return super(flac, self).__getitem__(item) def __delitem__(self, item): try: item = item.split('=', 1)[0] except ValueError: pass fields = self.__tag2fields__(item) for tag in fields: if tag not in self.entry.keys(): continue del self.entry[tag] self.modified = True def __field2tag__(self, field): return super(flac, self).__field2tag__(field.upper()) def keys(self): """ Return tag names sorted with self.sort_keys() """ keys = super(flac, self).keys() if 'TOTALTRACKS' in keys: keys.remove('TOTALTRACKS') if 'TOTALDISKS' in keys: keys.remove('TOTALDISKS') if 'TRACKNUMBER' in [x.upper() for x in keys]: if self.track_numbering.total is not None: keys.append('totaltracks') if 'DISKNUMBER' in [x.upper() for x in keys]: if self.disk_numbering.total is not None: keys.append('totaldisks') if FLAC_ALBUMART_TAG in [x.upper() for x in keys]: keys.remove(FLAC_ALBUMART_TAG) for replaygain_tag_fields in FLAC_REPLAYGAIN_TAGS.values(): for tag in replaygain_tag_fields: if tag in keys: keys.remove(tag) return [x.lower() for x in self.sort_keys(keys)] def has_key(self, tag): return tag.lower() in self.keys() def set_tag(self, item, value): """ All flac tags are str strings, and there can be multiple tags with same name. We do special precessing for track and disk numbering. """ if item == 'tracknumber': self.track_numbering.value = value return if item == 'totaltracks': self.track_numbering.total = value return if item == 'disknumber': self.disk_numbering.value = value return if item == 'totaldisks': self.disk_numbering.total = value return if not isinstance(value, list): value = [value] tags = self.__tag2fields__(item) item = tags[0] for tag in tags: if tag in self.entry: if tag in OGG_MULTIPLE_VALUES_TAGS and value not in self.entry[tag]: value = self.entry[tag] + value del self.entry[tag] entries = [] for v in value: if item in FLAC_TAG_FORMATTERS: entries.append(FLAC_TAG_FORMATTERS[item](v)) else: entries.append(format_unicode_string_value(v)) self.entry[item] = entries self.modified = True
def copyFLAC(flacFile, mpgFile): from mutagen.flac import FLAC, Picture from mutagen.mp3 import MP3 from mutagen.id3 import ID3 bChange = False # audioMP3 = MP3('smiley.mp3', ID3=EasyID3) audioMP3 = MP3() audioMP3.add_tags(ID3=CompatID3) bNew = True audioFLAC = FLAC(flacFile) if audioFLAC.has_key('tracktotal'): tracktotal = audioFLAC["tracktotal"][0] else: tracktotal = 0 if not audioFLAC.has_key('albumartist'): audioFLAC["albumartist"] = audioFLAC["artist"] if not audioFLAC.has_key('compilation'): audioFLAC["compilation"] = 0 print "--FLAC--------------------------------------------" for tag in audioFLAC.keys(): # print >> sys.stdout, "--> verarbeite Key: ", tag # if 'APIC' in tag: # print 'Tag {0}'.format(tag) # else: # print >> sys.stdout, "--> verarbeite Key: Tag {0}: {1}".format(tag, textTag.encode('UTF-8')) if tag == "tracktotal": pass else: # id3.add(mutagen.id3.COMM(encoding=3,text=relationshipLink, lang="eng", desc="MusicGrabberSig")) # Tag COMM:Kommentar:'DEU': von Vinyl if tag == "tracknumber": audioFLAC[tag]='{0}/{1}'.format(audioFLAC[tag][0], tracktotal) # audioFLAC[tag]='{0}/{1}'.format(audioFLAC[tag], tracktotal) searchTag = tag if "comment" in tag: searchTag = 'COMMENTS' if "description" in tag: searchTag = 'COMMENTS' # if not str.upper(tag) in id3Trans.flacFrames: if not hasFrame(searchTag): if "replaygain" in tag: continue print >> sys.stderr, "Key nicht gefunden: ", tag continue id3Frame = getFrame(searchTag) # audioMP3[id3Frame] = audioFLAC[tag] # for textTag in audioFLAC[tag]: # print >> sys.stderr, "tag: %s frame: %s " % (tag, id3Frame.__name__) if "comment" in tag: # audioMP3.add(id3Frame(encoding=3, text= audioFLAC[tag], lang="DEU", desc="Kommentar")) try: audioMP3[id3Frame.__name__] = id3Frame(3, text=audioFLAC[tag], lang="DEU", desc="Kommentar") except: pass else: audioMP3[id3Frame.__name__] = id3Frame(3, text=audioFLAC[tag]) # audioMP3.add(id3Frame(encoding=3, text= audioFLAC[tag])) bChange = True # print u'Tag {0}: {1} --> MP3 zugefuegt'.format(id3Frame, audioFLAC[tag]) if bChange == True: # print dir(audioMP3) # if bNew : audioMP3.save(mpgFile,v1=2) # else : audioMP3.save(v1=2) audioMP3.tags.update_to_v23() if bNew : audioMP3.tags.save(filename=mpgFile, v2=3) else : audioMP3.tags.save(v2=3) print '-'*40
def create_library(self, source_path, source_type="usb"): # Todo - add regex to remove numbers from the begining of filenames to get song name (\d{1,3}|)( - |)(?P<song_Name>.+) self.library_ready = False new_library = [] for root, d_names, f_names in os.walk(str(source_path)): for fileName in f_names: audio = None foundType = [ musicType for musicType in MUSIC_TYPES if (musicType.lower() in fileName.lower()) ] if bool(foundType): song_path = str(root) + "/" + str(fileName) if True: # try: # Removed to find error if "flac" in str(foundType[0]): # add flac filter audio = FLAC(song_path) # LOG.info("Checking FLAC Tags" + str(audio)) elif "aac" in str(foundType[0]): # add flac filter: audio = AAC(song_path) # LOG.info("Checking aac Tags" + str(audio)) elif "mp3" in str(foundType[0]): # add flac filter: try: audio = EasyID3(song_path) except ID3NoHeaderError: LOG.info("No ID Tags Found... " + song_path) audio = {} # LOG.info("Checking mp3 Tags" + str(audio)) elif "m4a" in str(foundType[0]): # add flac filter: audio = MP4(song_path) # LOG.info("Checking m4a Tags" + str(audio)) if audio is not None: # An ID3 tag found if 'title' not in audio.keys(): trim_length = (len(str(foundType[0])) + 1) * -1 self.song_label = str(fileName)[:trim_length] else: self.song_label = audio['title'][0] #LOG.info("Validating title: " + self.song_label) if 'artist' not in audio.keys(): if 'Contributing artists' in audio.keys(): self.song_artist = audio[ 'Contributing artists'][0] else: self.song_artist = "" else: self.song_artist = audio['artist'][0] #LOG.info("Validating artist: " + self.song_artist) if 'album' not in audio.keys(): self.song_album = "" else: self.song_album = audio['album'][0] else: # There was no ID3 Tag found, use filename as song title trim_length = (len(str(foundType[0])) + 1) * -1 self.song_label = str(fileName)[:trim_length] self.song_artist = "" self.song_album = "" info = { "location": song_path, "label": self.song_label, "artist": self.song_artist, "album": self.song_album, "source": str(source_type) } new_library.append(info) song_count = len(new_library) if song_count == 0: self.speak_dialog('no.files', data={"source": str(source_type)}, expect_response=False) else: self.speak_dialog('scan.complete', data={ "count": str(song_count), "source": str(source_type) }, expect_response=False) wait_while_speaking() LOG.info("Added: " + str(song_count) + " to the library from the " + str(source_type) + " Device") self.library_ready = True return new_library
class MusicFile(object): """ initialisation function @param the absolute path of the file @raise A NotAMusicFileException if the file cannot be read as a musicFile """ def __init__(self, path): try: logging.debug("Trying mp3") self.tags = MP3(path, ID3=EasyID3) #self.tags = EasyID3(path) except mutagen.mp3.HeaderNotFoundError: try: logging.debug("Trying flac") self.tags = FLAC(path) except FLACNoHeaderError: try: logging.debug("Trying general") self.tags = EasyID3(path) except ID3NoHeaderError: raise NotAMusicFileException self.path = path """ sanitize method, clean the tag using the following technique just idea for the moment """ def sanitize_with_musicBrainz(self): titleName = self['title'] artistName = self['artist'] albumName = self['album'] q = Query() try: logging.debug("Querying with title:{},artist:{},album:{}" .format(titleName, artistName, albumName)) f = TrackFilter(title=titleName, artistName=artistName, releaseTitle=albumName, limit=2) results = q.getTracks(f) logging.debug("results are " + str(results)) except WebServiceError as e: logging.error("Failed to contact musicbrainz server.") if str(e)[:15]=="HTTP Error 503:": logging.info("Pausing for a moment, \ the musicbrainz server doesn't handle too much request") sleep(60) return self.sanitize_with_musicBrainz() self.treat_musicbrainz_result(results) """ A simple way to sort the results obtain by a query to the musicbrainz database and put them into the correct tag @return True if the results can be put into the corresponding tag False otherwise """ def treat_musicbrainz_result(self, results): if len(results) == 0: logging.debug("No result found") return False for result in results: logging.debug("A result was found") if result.score == 100: track = result.track logging.debug("A perfect match seems to be found !!") possible_releases = track.getReleases() if len(possible_releases) != 1: logging.debug("Multiple album, I will try to guess") for rel in possible_releases: print(dir(rel)) exit(0) return False else: release = possible_releases[0] logging.debug(dir(track)) logging.debug("tags are :" + str(track.getTags())) dir(track) self['title'] = track.title self['artist'] = track.artist.name self['album'] = release.getTitle() #self.tags['album'] = return """ guess the title from the sound using the sound fingerprint using acoustID api """ def guess_sound(self): if 'title' in self and 'artist' in self: logging.debug("We already have title and artist for this one") return logging.debug(self.path) logging.debug(ACOUSTID_KEY) try: match = acoustid.match(ACOUSTID_KEY, self.path) except: return for score, recording_id, title, artist in match: if score > 0.99: #we are quite sure this result match logging.debug("Match Found score:%s, title:%s, artist:%s", score, title, artist) self['title'] = title self['artist'] = artist else: #the result are not sure enough logging.debug("The result doesn't seems sure enough.") """ guess the tags from the path """ def guess_path(self): if self['artist'] and self['album'] and self['title']: logging.debug("Cannot guess any further with only the title") return #let's try to find album name or artist name from the path, if it's #not already set clean_path = path_split(self.path) logging.debug("Trying to guess on %s" % str(clean_path)) logging.debug("Clean title:%s" % clean_title(clean_path[-1])) #try to guess the album if not already set if not self['title']: # Our worst case #we remove all trailing number, trailing '-' #and the extension and try to find it in our database possible_title = clean_path[-1] logging.debug("Title could be {}".format(possible_title)) """ get the tag, the name of the tag must be in the value of USEFUL_TAG @param the name of the tag @return the value of the tag """ def __getitem__(self, key): try: #return the first item as default tag = self.tags[key] except KeyError: return None if not tag: # if tag is empty return None else: return tag[0] """ set the tag, the name of the tag must be in the value of USEFUL_TAG @param the key of the tag the new value of the tag """ def __setitem__(self, key, value): #set the tag without writing them self.tags[key] = str(value) def keys(self): return self.tags.keys() """ test if the music file contain a certain tag """ def has_key(self, tag): return tag in self.tags """ print all tags that mutagen can find @param maximum length of all the content """ def __str__(self, maxsize=100): ret = [self.path] for k in self.tags.keys(): ret += ["\t %s:%s" % (k, self[k])] return "\n".join(ret) """ move this music file in the good place """ def move(self, new_basedir, path_format): formatted_path = path_format.format(**self.tags) new_dir = os.join(new_basedir, formatted_path) try: mkdir_p(new_dir) except OSError: logging.info("Directory %s already exist" % new_dir) #extract the extension try: ext = self.get_extension() except: # TODO specify error logging.error("Moving file %s failed, extension is undefined" % self.path) return formatted_path = path_format.format(self.tags) new_filename = os.path.join(new_basedir, formatted_path + ext) try: os.mv(self.path, new_filename) except OSError: logging.error("Moving file %s failed, cannot move the file") """ return the extension of this file according to it's name it can be not accurate if the extention doesn't correspond to the real file type. """ def get_extension(self): return os.path.splitext(self.path)[-1] """ capitalize all tag by default or a list of specific tag """ def capitalize_tag(self, tag=None): for tag in self.tags: self.tags[tag] = [string.capwords(i) for i in self.tags[tag]] """ save modification to the id3 tag """ def save(self): self.tags.save() """ Get the length of the track in milisecond """ def length(self): return int(self.tags.info.length) """ Get the bitrate of the track """ def bitrate(self): return int(self.tags.info.bitrate) """ """ def has_mandatory_tag(self): for tag in OBLIGATORY_TAG: if tag not in self: return False return True
class TFLAC(TestCase): SAMPLE = os.path.join(DATA_DIR, "silence-44-s.flac") def setUp(self): self.NEW = get_temp_copy(self.SAMPLE) self.flac = FLAC(self.NEW) def tearDown(self): os.unlink(self.NEW) def test_zero_samples(self): # write back zero sample count and load again self.flac.info.total_samples = 0 self.flac.save() new = FLAC(self.flac.filename) assert new.info.total_samples == 0 assert new.info.bitrate == 0 assert new.info.length == 0.0 def test_bitrate(self): assert self.flac.info.bitrate == 101430 old_file_size = os.path.getsize(self.flac.filename) self.flac.save(padding=lambda x: 9999) new_flac = FLAC(self.flac.filename) assert os.path.getsize(new_flac.filename) > old_file_size assert new_flac.info.bitrate == 101430 def test_padding(self): for pad in [0, 42, 2**24 - 1, 2 ** 24]: self.flac.save(padding=lambda x: pad) new = FLAC(self.flac.filename) expected = min(2**24 - 1, pad) self.assertEqual(new.metadata_blocks[-1].length, expected) def test_save_multiple_padding(self): # we don't touch existing padding blocks on save, but will # replace them in the file with one at the end def num_padding(f): blocks = f.metadata_blocks return len([b for b in blocks if isinstance(b, Padding)]) num_blocks = num_padding(self.flac) self.assertEqual(num_blocks, 1) block = Padding() block.length = 42 self.flac.metadata_blocks.append(block) block = Padding() block.length = 24 self.flac.metadata_blocks.append(block) self.flac.save() self.assertEqual(num_padding(self.flac), num_blocks + 2) new = FLAC(self.flac.filename) self.assertEqual(num_padding(new), 1) self.assertTrue(isinstance(new.metadata_blocks[-1], Padding)) def test_increase_size_new_padding(self): self.assertEqual(self.flac.metadata_blocks[-1].length, 3060) value = u"foo" * 100 self.flac[u"foo"] = [value] self.flac.save() new = FLAC(self.NEW) self.assertEqual(new.metadata_blocks[-1].length, 2752) self.assertEqual(new[u"foo"], [value]) def test_delete(self): self.failUnless(self.flac.tags) self.flac.delete() self.assertTrue(self.flac.tags is not None) self.assertFalse(self.flac.tags) flac = FLAC(self.NEW) self.assertTrue(flac.tags is None) def test_delete_change_reload(self): self.flac.delete() self.flac.tags["FOO"] = ["BAR"] self.flac.save() assert FLAC(self.flac.filename)["FOO"] == ["BAR"] # same with delete failing due to IO etc. with pytest.raises(MutagenError): self.flac.delete(os.devnull) self.flac.tags["FOO"] = ["QUUX"] self.flac.save() assert FLAC(self.flac.filename)["FOO"] == ["QUUX"] def test_module_delete(self): delete(self.NEW) flac = FLAC(self.NEW) self.failIf(flac.tags) def test_info(self): self.failUnlessAlmostEqual(FLAC(self.NEW).info.length, 3.7, 1) def test_keys(self): self.failUnlessEqual( list(self.flac.keys()), list(self.flac.tags.keys())) def test_values(self): self.failUnlessEqual( list(self.flac.values()), list(self.flac.tags.values())) def test_items(self): self.failUnlessEqual( list(self.flac.items()), list(self.flac.tags.items())) def test_vc(self): self.failUnlessEqual(self.flac['title'][0], 'Silence') def test_write_nochange(self): f = FLAC(self.NEW) f.save() with open(self.SAMPLE, "rb") as a: with open(self.NEW, "rb") as b: self.failUnlessEqual(a.read(), b.read()) def test_write_changetitle(self): f = FLAC(self.NEW) if PY3: self.assertRaises( TypeError, f.__setitem__, b'title', b"A New Title") else: f[b"title"] = b"A New Title" f.save() f = FLAC(self.NEW) self.failUnlessEqual(f[b"title"][0], b"A New Title") def test_write_changetitle_unicode_value(self): f = FLAC(self.NEW) if PY3: self.assertRaises( TypeError, f.__setitem__, b'title', u"A Unicode Title \u2022") else: f[b"title"] = u"A Unicode Title \u2022" f.save() f = FLAC(self.NEW) self.failUnlessEqual(f[b"title"][0], u"A Unicode Title \u2022") def test_write_changetitle_unicode_key(self): f = FLAC(self.NEW) f[u"title"] = b"A New Title" if PY3: self.assertRaises(ValueError, f.save) else: f.save() f = FLAC(self.NEW) self.failUnlessEqual(f[u"title"][0], b"A New Title") def test_write_changetitle_unicode_key_and_value(self): f = FLAC(self.NEW) f[u"title"] = u"A Unicode Title \u2022" f.save() f = FLAC(self.NEW) self.failUnlessEqual(f[u"title"][0], u"A Unicode Title \u2022") def test_force_grow(self): f = FLAC(self.NEW) f["faketag"] = ["a" * 1000] * 1000 f.save() f = FLAC(self.NEW) self.failUnlessEqual(f["faketag"], ["a" * 1000] * 1000) def test_force_shrink(self): self.test_force_grow() f = FLAC(self.NEW) f["faketag"] = "foo" f.save() f = FLAC(self.NEW) self.failUnlessEqual(f["faketag"], ["foo"]) def test_add_vc(self): f = FLAC(os.path.join(DATA_DIR, "no-tags.flac")) self.failIf(f.tags) f.add_tags() self.failUnless(f.tags == []) self.failUnlessRaises(ValueError, f.add_tags) def test_add_vc_implicit(self): f = FLAC(os.path.join(DATA_DIR, "no-tags.flac")) self.failIf(f.tags) f["foo"] = "bar" self.failUnless(f.tags == [("foo", "bar")]) self.failUnlessRaises(ValueError, f.add_tags) def test_ooming_vc_header(self): # issue 112: Malformed FLAC Vorbis header causes out of memory error # https://github.com/quodlibet/mutagen/issues/112 self.assertRaises(error, FLAC, os.path.join(DATA_DIR, 'ooming-header.flac')) def test_with_real_flac(self): if not have_flac: return self.flac["faketag"] = "foobar" * 1000 self.flac.save() self.failIf(call_flac("-t", self.flac.filename) != 0) def test_save_unknown_block(self): block = MetadataBlock(b"test block data") block.code = 99 self.flac.metadata_blocks.append(block) self.flac.save() def test_load_unknown_block(self): self.test_save_unknown_block() flac = FLAC(self.NEW) self.failUnlessEqual(len(flac.metadata_blocks), 7) self.failUnlessEqual(flac.metadata_blocks[5].code, 99) self.failUnlessEqual(flac.metadata_blocks[5].data, b"test block data") def test_two_vorbis_blocks(self): self.flac.metadata_blocks.append(self.flac.metadata_blocks[1]) self.flac.save() self.failUnlessRaises(error, FLAC, self.NEW) def test_missing_streaminfo(self): self.flac.metadata_blocks.pop(0) self.flac.save() self.failUnlessRaises(error, FLAC, self.NEW) def test_load_invalid_flac(self): self.failUnlessRaises( error, FLAC, os.path.join(DATA_DIR, "xing.mp3")) def test_save_invalid_flac(self): self.failUnlessRaises( error, self.flac.save, os.path.join(DATA_DIR, "xing.mp3")) def test_pprint(self): self.failUnless(self.flac.pprint()) def test_double_load(self): blocks = list(self.flac.metadata_blocks) self.flac.load(self.flac.filename) self.failUnlessEqual(blocks, self.flac.metadata_blocks) def test_seektable(self): self.failUnless(self.flac.seektable) def test_cuesheet(self): self.failUnless(self.flac.cuesheet) def test_pictures(self): self.failUnless(self.flac.pictures) def test_add_picture(self): f = FLAC(self.NEW) c = len(f.pictures) f.add_picture(Picture()) f.save() f = FLAC(self.NEW) self.failUnlessEqual(len(f.pictures), c + 1) def test_clear_pictures(self): f = FLAC(self.NEW) c1 = len(f.pictures) c2 = len(f.metadata_blocks) f.clear_pictures() f.save() f = FLAC(self.NEW) self.failUnlessEqual(len(f.metadata_blocks), c2 - c1) def test_ignore_id3(self): id3 = ID3() id3.add(TIT2(encoding=0, text='id3 title')) id3.save(self.NEW) f = FLAC(self.NEW) f['title'] = 'vc title' f.save() id3 = ID3(self.NEW) self.failUnlessEqual(id3['TIT2'].text, ['id3 title']) f = FLAC(self.NEW) self.failUnlessEqual(f['title'], ['vc title']) def test_delete_id3(self): id3 = ID3() id3.add(TIT2(encoding=0, text='id3 title')) id3.save(self.NEW, v1=2) f = FLAC(self.NEW) f['title'] = 'vc title' f.save(deleteid3=True) self.failUnlessRaises(ID3NoHeaderError, ID3, self.NEW) f = FLAC(self.NEW) self.failUnlessEqual(f['title'], ['vc title']) def test_save_on_mp3(self): path = os.path.join(DATA_DIR, "silence-44-s.mp3") self.assertRaises(error, self.flac.save, path) def test_mime(self): self.failUnless("audio/x-flac" in self.flac.mime) def test_variable_block_size(self): FLAC(os.path.join(DATA_DIR, "variable-block.flac")) def test_load_flac_with_application_block(self): FLAC(os.path.join(DATA_DIR, "flac_application.flac"))
# insertion des infos en tables InsertSQLAudio(AllInfo) # ----------------------------------------- # insertion des fichiers FLAC en table # ----------------------------------------- elif ".FLAC" in filename or ".flac" in filename: i = i + 1 MutaFLAC = FLAC(path + "/" + filename) AllInfo = {'Duree': MutaFLAC.info.length} #print filename," - ",MutaFLAC['title'][0], MutaFLAC['album'][0] if 'artist' in MutaFLAC.keys(): AllInfo['Artist'] = MutaFLAC['artist'][0] else: AllInfo['Artist'] = "Unknown" if 'album' in MutaFLAC.keys(): AllInfo['Album'] = MutaFLAC['album'][0] else: AllInfo['Album'] = "Unknown" if 'title' in MutaFLAC.keys(): AllInfo['Titre'] = MutaFLAC['title'][0] else: AllInfo['Titre'] = "Unknown" if 'tracknumber' in MutaFLAC.keys():
class TFLAC(TestCase): SAMPLE = os.path.join("tests", "data", "silence-44-s.flac") NEW = SAMPLE + ".new" def setUp(self): shutil.copy(self.SAMPLE, self.NEW) self.failUnlessEqual(open(self.SAMPLE).read(), open(self.NEW).read()) self.flac = FLAC(self.NEW) def test_delete(self): self.failUnless(self.flac.tags) self.flac.delete() self.failIf(self.flac.tags) flac = FLAC(self.NEW) self.failIf(flac.tags) def test_module_delete(self): delete(self.NEW) flac = FLAC(self.NEW) self.failIf(flac.tags) def test_info(self): self.failUnlessAlmostEqual(FLAC(self.NEW).info.length, 3.7, 1) def test_keys(self): self.failUnlessEqual(self.flac.keys(), self.flac.tags.keys()) def test_values(self): self.failUnlessEqual(self.flac.values(), self.flac.tags.values()) def test_items(self): self.failUnlessEqual(self.flac.items(), self.flac.tags.items()) def test_vc(self): self.failUnlessEqual(self.flac['title'][0], 'Silence') def test_write_nochange(self): f = FLAC(self.NEW) f.save() self.failUnlessEqual(open(self.SAMPLE).read(), open(self.NEW).read()) def test_write_changetitle(self): f = FLAC(self.NEW) f["title"] = "A New Title" f.save() f = FLAC(self.NEW) self.failUnlessEqual(f["title"][0], "A New Title") def test_write_changetitle_unicode_value(self): f = FLAC(self.NEW) f["title"] = u"A Unicode Title \u2022" f.save() f = FLAC(self.NEW) self.failUnlessEqual(f["title"][0], u"A Unicode Title \u2022") def test_write_changetitle_unicode_key(self): f = FLAC(self.NEW) f[u"title"] = "A New Title" f.save() f = FLAC(self.NEW) self.failUnlessEqual(f[u"title"][0], "A New Title") def test_write_changetitle_unicode_key_and_value(self): f = FLAC(self.NEW) f[u"title"] = u"A Unicode Title \u2022" f.save() f = FLAC(self.NEW) self.failUnlessEqual(f[u"title"][0], u"A Unicode Title \u2022") def test_force_grow(self): f = FLAC(self.NEW) f["faketag"] = ["a" * 1000] * 1000 f.save() f = FLAC(self.NEW) self.failUnlessEqual(f["faketag"], ["a" * 1000] * 1000) def test_force_shrink(self): self.test_force_grow() f = FLAC(self.NEW) f["faketag"] = "foo" f.save() f = FLAC(self.NEW) self.failUnlessEqual(f["faketag"], ["foo"]) def test_add_vc(self): f = FLAC(os.path.join("tests", "data", "no-tags.flac")) self.failIf(f.tags) f.add_tags() self.failUnless(f.tags == []) self.failUnlessRaises(ValueError, f.add_tags) def test_add_vc_implicit(self): f = FLAC(os.path.join("tests", "data", "no-tags.flac")) self.failIf(f.tags) f["foo"] = "bar" self.failUnless(f.tags == [("foo", "bar")]) self.failUnlessRaises(ValueError, f.add_tags) def test_ooming_vc_header(self): # issue 112: Malformed FLAC Vorbis header causes out of memory error # http://code.google.com/p/mutagen/issues/detail?id=112 self.assertRaises(IOError, FLAC, os.path.join('tests', 'data', 'ooming-header.flac')) def test_with_real_flac(self): if not have_flac: return self.flac["faketag"] = "foobar" * 1000 self.flac.save() badval = os.system("tools/notarealprogram 2> %s" % devnull) value = os.system("flac -t %s 2> %s" % (self.flac.filename, devnull)) self.failIf(value and value != badval) def test_save_unknown_block(self): block = MetadataBlock("test block data") block.code = 99 self.flac.metadata_blocks.append(block) self.flac.save() def test_load_unknown_block(self): self.test_save_unknown_block() flac = FLAC(self.NEW) self.failUnlessEqual(len(flac.metadata_blocks), 7) self.failUnlessEqual(flac.metadata_blocks[5].code, 99) self.failUnlessEqual(flac.metadata_blocks[5].data, "test block data") def test_two_vorbis_blocks(self): self.flac.metadata_blocks.append(self.flac.metadata_blocks[1]) self.flac.save() self.failUnlessRaises(IOError, FLAC, self.NEW) def test_missing_streaminfo(self): self.flac.metadata_blocks.pop(0) self.flac.save() self.failUnlessRaises(IOError, FLAC, self.NEW) def test_load_invalid_flac(self): self.failUnlessRaises( IOError, FLAC, os.path.join("tests", "data", "xing.mp3")) def test_save_invalid_flac(self): self.failUnlessRaises( IOError, self.flac.save, os.path.join("tests", "data", "xing.mp3")) def test_pprint(self): self.failUnless(self.flac.pprint()) def test_double_load(self): blocks = list(self.flac.metadata_blocks) self.flac.load(self.flac.filename) self.failUnlessEqual(blocks, self.flac.metadata_blocks) def test_seektable(self): self.failUnless(self.flac.seektable) def test_cuesheet(self): self.failUnless(self.flac.cuesheet) def test_pictures(self): self.failUnless(self.flac.pictures) def test_add_picture(self): f = FLAC(self.NEW) c = len(f.pictures) f.add_picture(Picture()) f.save() f = FLAC(self.NEW) self.failUnlessEqual(len(f.pictures), c + 1) def test_clear_pictures(self): f = FLAC(self.NEW) c1 = len(f.pictures) c2 = len(f.metadata_blocks) f.clear_pictures() f.save() f = FLAC(self.NEW) self.failUnlessEqual(len(f.metadata_blocks), c2 - c1) def test_ignore_id3(self): id3 = ID3() id3.add(TIT2(encoding=0, text='id3 title')) id3.save(self.NEW) f = FLAC(self.NEW) f['title'] = 'vc title' f.save() id3 = ID3(self.NEW) self.failUnlessEqual(id3['TIT2'].text, ['id3 title']) f = FLAC(self.NEW) self.failUnlessEqual(f['title'], ['vc title']) def test_delete_id3(self): id3 = ID3() id3.add(TIT2(encoding=0, text='id3 title')) id3.save(self.NEW, v1=2) f = FLAC(self.NEW) f['title'] = 'vc title' f.save(deleteid3=True) self.failUnlessRaises(ID3NoHeaderError, ID3, self.NEW) f = FLAC(self.NEW) self.failUnlessEqual(f['title'], ['vc title']) def test_mime(self): self.failUnless("audio/x-flac" in self.flac.mime) def test_variable_block_size(self): FLAC(os.path.join("tests", "data", "variable-block.flac")) def test_load_flac_with_application_block(self): FLAC(os.path.join("tests", "data", "flac_application.flac")) def tearDown(self): os.unlink(self.NEW)
class FlacTrack(Track): """Expose canonical metadata from a single FLAC file""" def __init__(self, path ): super(FlacTrack, self).__init__(path) """ Load metadata from path """ self._flac = FLAC( self.PathName ) @property def Album(self): return self._flac["album"][0] @property def AlbumArtist(self): if "album artist" in self._flac.keys(): return self._flac["album artist"][0] #print "No AlbumArtist" return self.Artist @property def Artist(self): return self._flac["artist"][0] @property def Title(self): # print type( self._flac["title"][0].text ), self._flac["title"][0].text return self._flac["title"][0] @property def TrackNumber(self): return self._flac["tracknumber"][0] @property def AudioMD5(self): md5 = unicode( hex(self._flac.info.md5_signature) ) #print md5 return md5 @property def Wife(self): return "custom1" in self._flac and self._flac["custom1"][0] == u'Wife' @property def IsPartOfCompilation(self): artist = self.Artist.lower() albumArtist = self.AlbumArtist.lower() return ( artist == 'various' or artist == 'various artists' or albumArtist == 'various' or albumArtist == 'various artists' or not artist == albumArtist ) @property def AlbumArtwork(self): # should be a jpeg #print "pictures", self._flac.pictures if len( self._flac.pictures ) > 0: cover = self._flac.pictures[0] if cover.mime == "image/jpeg": #print cover.data return AlbumArtwork( io.BytesIO( cover.data ) ) # else: # print "no jpeg cover" # else: # print "no cover" #print type (self.Path), self.Path #print type (self.Album), self.Album path = None if isinstance(self.Path, unicode): path = self.Path.encode('utf-8') else: path = self.Path artPath = os.path.join( path, self.Album.encode('utf-8') ) + ".jpg" if os.path.isfile(artPath): return AlbumArtwork( io.BytesIO( open(artPath).read() ) ) return None