def tag(self): data = self.get_parent_instance().metadata() with tempfile.NamedTemporaryFile(suffix='-musicdb.mp3') as f: # Download with default_storage.open(self.file.location) as g: contents = g.read() f.write(contents) f.flush() f.seek(0) audio = MutagenFile(f.name) audio.delete() if isinstance(audio, mp3.MP3): audio.tags = easyid3.EasyID3() audio.update(data) audio.save() self.length = int(audio.info.length) # Copy it back default_storage.delete(self.file.location) dst = default_storage.save(self.file.location, DjangoFile(f)) assert dst == self.file.location
def tag(filename, data): audio = File(filename) audio.delete() if isinstance(audio, mp3.MP3): audio.tags = easyid3.EasyID3() audio.update(data) audio.save()
def apply_tags(file_path, tag_list): """ Using mutagen, apply the tags to the file. """ if file_path.endswith(".mp3"): muta = EasyID3(file_path) elif file_path.endswith(".m4a"): muta = EasyMP4(file_path) else: muta = File(file_path) for key in tag_list: muta.update({key: tag_list[key]}) muta.save()
def tag(self): try: data = self.get_parent_instance().metadata() filename = os.path.join(settings.MEDIA_LOCATION, self.file.location) audio = MutagenFile(filename) audio.delete() if isinstance(audio, mp3.MP3): audio.tags = easyid3.EasyID3() audio.update(data) audio.save() except: self.tags_dirty = None self.save() raise finally: self.tags_dirty = False self.save()
def transfer_tags(flacfile, wavpackfile): """docstring for transfer_tags""" fl = File(flacfile) wv = File(wavpackfile) wv.update(fl) try: wv["track"] = wv["tracknumber"] del wv["tracknumber"] if wv["totaltracks"]: wv["track"] = wv["track"][0] + "/" + wv["totaltracks"][0] del wv["totaltracks"] wv["disc"] = wv["discnumber"] del wv["discnumber"] if wv["totaldiscs"]: wv["disc"] = wv["disc"][0] + "/" + wv["totaldiscs"][0] del wv["totaldiscs"] except KeyError: pass wv.save()
def download_playlist(playlist: Playlist, force_redownload=False): for short_track in playlist.tracks: track = short_track.track print(f"Downloading `{track.artists[0]['name']} - {track.title}`", end="... ") if not track.available: print("not available") continue track_path = os.path.normpath( os.path.join(strip_bad_symbols(track.artists[0]['name']), strip_bad_symbols(track.albums[0]['title']))) os.makedirs(track_path, exist_ok=True) os.chdir(track_path) file_name = strip_bad_symbols(f'{track.title}') used_codec = None for info in sorted(track.get_download_info(), key=lambda x: x['bitrate_in_kbps'], reverse=True): codec = info['codec'] bitrate = info['bitrate_in_kbps'] full_file_name = f'{file_name}.{codec}' # file is not created 'til all bytes of it recieved, # so we can check if track was already downloaded # by just checking its existence on disk if os.path.exists(full_file_name) and not force_redownload: used_codec = codec # to prevent wrong `unknown downloading error` message break try: track.download(full_file_name, codec=codec, bitrate_in_kbps=bitrate) used_codec = codec break except (YandexMusicError, TimeoutError): continue if not used_codec: print("unknown downloading error") continue cover_filename = file_name + ".jpg" track.download_cover(cover_filename, size="300x300") file = File(f'{file_name}.{used_codec}') file.update({ # Title 'TIT2': TIT2(encoding=3, text=track.title), # Artist 'TPE1': TPE1(encoding=3, text=DELIMITER.join(i['name'] for i in track.artists)), # Album 'TALB': TALB(encoding=3, text=DELIMITER.join(i['title'] for i in track.albums)), # Year 'TDRC': TDRC(encoding=3, text=str(track.albums[0]['year'])), # Picture 'APIC': APIC(encoding=3, text=cover_filename, data=open(cover_filename, 'rb').read()) }) lyrics = client.track_supplement(track.track_id).lyrics if lyrics: # Song lyrics file.tags.add(USLT(encoding=3, text=lyrics.full_lyrics)) file.save() os.chdir(pwd) print("done")
def process_a_file(self, input_path, output_dir): au_id = input_path.name if not au_id.isdigit(): print(f'unexpected file: {input_path}') return au_id = int(au_id) file_kind = File(input_path) if isinstance(file_kind, MP4): suffix = '.m4a' elif isinstance(file_kind, FLAC): suffix = '.flac' elif file_kind is None: raise TypeError('unknown file kind') else: raise TypeError( f'unsupport file kind {file_kind.__class__.__name__}') audio_info = self.audio_cache.get(au_id) filename = f'{spilt_artist_str(audio_info["author"])[0]} - {audio_info["title"]}{suffix}' output_path = output_dir / filename if not self.overwrite and output_path.is_file(): print(f'{filename} exists, skipped') return print(f'processing: {filename}') shutil.copyfile(input_path, output_path) album_id = audio_info['pgc_info']['pgc_menu']['menuId'] album_info = self.album_cache.get(album_id) album_cover_url = album_info['menusRespones']['coverUrl'] album_au_ids = list(map(lambda x: x['id'], album_info['songsList'])) track_id = album_au_ids.index(au_id) assert album_cover_url == album_info['songsList'][track_id][ 'cover_url'] album_cover_path = self.cover_cache.get(album_cover_url) if album_cover_path.suffix == '.jpg': image_format = AtomDataType.JPEG elif album_cover_path.suffix == '.png': image_format = AtomDataType.PNG else: raise TypeError( f'Unsupport format: {album_cover_path.suffix}, Only support jpg and png.' ) with album_cover_path.open('rb') as fp: cover_image = fp.read() audio_file = File(output_path) if isinstance(audio_file, MP4): tags = { '\xa9nam': audio_info['title'], '\xa9alb': album_info['menusRespones']['title'], '\xa9ART': format_artist_list(spilt_artist_str(audio_info['author'])), '\xa9day': str( get_year_from_timestamp( album_info['menusRespones']['pbtime'])), 'aART': format_artist_list( spilt_artist_str(album_info['menusRespones']['mbnames'])), 'trkn': [(track_id + 1, album_info['menusRespones']['songNum'])], 'disk': [(1, 1)], 'covr': [MP4Cover(cover_image, imageformat=image_format)], } elif isinstance(audio_file, FLAC): tags = { 'ALBUM': album_info['menusRespones']['title'], 'ARTIST': format_artist_list(spilt_artist_str(audio_info['author'])), 'ALBUMARTIST': format_artist_list( spilt_artist_str(album_info['menusRespones']['mbnames'])), 'DATE': str( get_year_from_timestamp( album_info['menusRespones']['pbtime'])), 'TITLE': audio_info['title'], 'DISCNUMBER': '1', 'DISCTOTAL': '1', 'TRACKTOTAL': str(album_info['menusRespones']['songNum']), 'TRACKNUMBER': str(track_id + 1), } picture = Picture() picture.data = cover_image picture.type = PictureType.COVER_FRONT audio_file.add_picture(picture) audio_file.update(tags) audio_file.save()
def main(): args = sys.argv err = 0 if 'id3help' in args: from mutagen.easyid3 import EasyID3 for key in EasyID3.valid_keys.keys(): print(key, ) from optparse import OptionParser as OP OP = OP() OP.usage = ("%prog [options] filenames") OP.epilog = '%s id3help: for help with id3 tags' % os.path.basename( args[0]) OP.add_option('-t', '--tag', dest='tag', action='append', help="set a tag", metavar='tag=value') OP.add_option( '-a', '--add', dest='add', action='append', help='set/add values to a tag, without removing any existing values', metavar='tag=value') OP.add_option('-p', '--pattern', dest='pattern', action='store', help='substitution pattern from filename', metavar="'%n %t.flac'") OP.add_option('--fn2tag', dest='pattern', action='store', help='same as -p | --pattern') OP.add_option('-r', '--remove', dest='remove', action='append', help='remove a tag value or entire tag', metavar="'tag' or 'tag=value'") OP.add_option('-j', '--justify', dest='justify', action='store_true', help='zero-justify tracknumbers') OP.add_option('--clear', dest='clear', action='store_true', help='clear all tags') OP.add_option('-n', '--noact', dest='noact', action='store_true', help="just show what changes would be made") OP.add_option('-c', '--confirm', dest='confirm', action='store_true', help='show changes and prompt for confirmation to save') OP.add_option('-f', '--files', dest='filenames', action='append', help='one or more filenames/globs') OP.add_option('-q', '--quiet', dest='quiet', action='store_true', help='no output to stdout') OP.add_option('--tag2fn', dest='tag2fn', action='store', help='substitution pattern from tags', metavar="'%n %t.flac'") OP.add_option( '-s', '--filter', dest='symbols', action='store', help= 'one or more characters to filter from tags used to build filenames', metavar="'!@$&*/\?'") OP.add_option( '-m', '--map', dest='map', action='store', help= 'replace all instances of a char with another char\nin conjunction with --tag2fn', metavar="/ -") OP.add_option('-i', '--index', dest='idx', action='store_true', help='index files by filename order (persistent file order)') OP.add_option('-v', '--version', dest='vers', action='store_true', help='show version') argstr = ' '.join(args) if len(args) < 2: OP.print_usage() # print("version %s" % __version__) print('-h|--help for help') sys.exit(1) p = '(-t|--tag|-a|--add|-p|--pattern|-r|--remove|-f|--files)\ +?\-[^\ ]*' mo = re.search(p, argstr) if mo: print('illegal option combination: ', mo.group()) sys.exit(1) (opt, fnames) = OP.parse_args() if opt.vers: print('%s %s' % (OP.get_prog_name(), __version__)) if opt.filenames: fnames += opt.filenames for fname in fnames: if not os.path.exists(fname): print('%s: no such file' % fname) err += 1 if err: sys.exit(err) cfmr = Confirmer(opt) fnum = 0 idx = 0 if opt.pattern: subster = Subster(opt.pattern) elif opt.tag2fn: subster = Subster(opt.tag2fn, 'tag2fn') else: subster = Subster('', '') modded = any( [opt.clear, opt.remove, opt.add, opt.tag, opt.pattern, opt.justify]) spkr = Speaker(opt.quiet) top_length = 0 for fname in fnames: bfname = os.path.basename(fname) top_length = len(bfname) if len(bfname) > top_length else top_length for fname in fnames: fnum += 1 vals = {} keys = [] origfn = fname if os.path.splitext(fname)[1] == '.mp3': try: mf = MP3(fname) except IOError: spkr.speak("\ncan't open %s" % fname) continue spkr.speak("processing %s" % fname) if opt.clear: mf.clear() for action in opt.remove or []: k, v = (action.split('=', 1) + [''])[:2] vals[k] = mf.pop(k, []) if k and not v: vals[k] = [] elif v and v in vals[k]: vals[k].remove(v) for action in opt.tag or []: k, v = (action.split('=', 1) + [''])[:2] vals[k] = [v] for action in opt.add or []: k, v = (action.split('=', 1) + [''])[:2] if vals.get(k, []): vals[k] += mf.pop(k, []) else: vals[k] = mf.pop(k, []) vals[k].extend([v]) if subster.pattern: d = subster.getdict(fname) for k in d: values = d.get(k, []) if not isinstance(values, list): values = [values] try: vals[k].extend(values) except KeyError: vals[k] = values if opt.justify: if not vals.get('tracknumber'): vals['tracknumber'] = fnum width = len(str(len(fnames))) n = width - len(str(vals['tracknumber'])) vals['tracknumber'] = [n * '0' + str(vals['tracknumber'])] if not modded: if not opt.quiet: print(mf.pprint()) continue if opt.noact or opt.confirm: for k in vals: print(k + '=' + str(vals[k])) if opt.noact: continue if opt.confirm and not cfmr.confirm(): continue for k in vals: try: mf.update({k: vals[k]}) # mf.save( ) except ValueError: pass mf.save() else: try: # print(fname) mf = File(fname) except IOError: spkr.speak("can't open %s" % fname) continue spkr.speak(os.path.basename(fname)) if opt.idx: trn = mf.get('tracknumber', None) mf['idx'] = unicode(fnum) if trn: mf['idx'] += trn mf.save() print(' indexed') if opt.clear: mf.clear() spkr.speak('\n\ttags cleared..') for action in opt.remove or []: k, v = (action.split('=', 1) + [''])[:2] t = mf.pop(k, []) if v and v in t: t.remove(v) spkr.speak(str(k) + ' removes ' + str(v)) if v and t: mf.update({k: t}) for action in opt.tag or []: if '=' in action: k, v = action.split('=', 1) if k and v: mf.update({k: [v]}) spkr.speak('\t\ttag set: ' + k + '=' + v) for action in opt.add or []: if '=' in action: k, v = action.split('=', 1) mf.update({k: mf.get(k, []) + [v]}) spkr.speak('\n\ttag appended: ' + k + '=' + v) if subster.mode == 'fn2tag': d = subster.getdict(fname) for k in d: mf.update({k: d[k]}) spkr.speak('\n\tfrom filename: ' + k + '=' + d[k]) if subster.mode == 'tag2fn': fname = '' fnlist = subster.getfnlist() if 'tracknumber' in fnlist: tn = 1 else: tn = 0 lit = True for item in fnlist: lit = not lit if lit: if not tn and item == 'tracknumber': item = 'track' if tn and item == 'track': item = 'tracknumber' if item.startswith('track') and opt.justify: subst = mf[item][0].rjust(2, '0') else: subst = mf[item][0] if opt.symbols: pat = '[' + opt.symbols + ']' subst = re.sub(pat, '', subst) subst = subst.strip() fname += subst else: fname += item if '/' in fname: fname = re.sub('/', '-', fname) # if opt.map: # fname = map(fname,opt.map) if opt.noact or opt.confirm: pass if not any([modded, opt.tag2fn, opt.quiet]): print(mf.pprint(), ) if cfmr.confirm(): if opt.tag2fn: if opt.map: a, b = opt.map.split() fname = re.sub(a, b, fname) pth = os.path.join(os.path.dirname(origfn), fname) second_column = top_length + 2 tab = (second_column - len(os.path.basename(origfn))) * ' ' try: os.rename(origfn, pth) print(tab + '--> ' + fname), # spkr.speak( 'renamed... ' + fname ) except IOError: raise IOError else: mf.save() spkr.speak('\tsaved!')
os.makedirs(dirName, exist_ok=True) trackFileName = f'{dirName}\\{i+1:02d}-{trackName}.mp3' if not os.path.isfile(trackFileName) : print("%3d %s" % (i+1, trackFileName)) try: track.download(filename=trackFileName) except: print("Error!!") file = File(f'{trackFileName}') file.update({ # Title 'TIT2': TIT2(encoding=3, text=track.title), # Artist 'TPE1': TPE1(encoding=3, text=DELIMITER.join(i['name'] for i in track.artists)), # Album 'TALB': TALB(encoding=3, text=DELIMITER.join(i['title'] for i in track.albums)), # Year 'TDRC': TDRC(encoding=3, text=str(track.albums[0]['year'])), # Picture #'APIC': APIC(encoding=3, text=cover_filename, data=open(cover_filename, 'rb').read()) }) file.save() # else: # # print("%3d %s file exist!" % (i+1, trackFileName)) # client.users_playlists_change(pl.kind, diff.to_json(), pl.revision)