def fix_cover(audio: File): """ Transfers album cover from audio key APIC:XXXX to APIC: Example audio['APIC: Payday 2.jpg'] = APIC() becomes audio['APIC:'] = APIC() """ restart = False for k in audio.keys(): if k.startswith('APIC:') and k != 'APIC:': apic = audio.pop(k) apic.desc = '' apic.encoding = 0 audio['APIC:'] = apic audio.save() restart = True break elif k == 'APIC:' and audio[k].encoding != 0: apic = audio.pop(k) apic.desc = '' apic.encoding = 0 audio[k] = apic audio.save() restart = True break if restart: fix_cover(audio)
def writeAlbumCover(songPath: str, coverPath: str, picData=None): """ 给音频文件写入封面 Parameters ---------- songPath: 音频文件路径\n coverPath: 封面图片路径\n picData: 封面图片二进制数据 """ id_card = File(songPath) # 读取封面数据 if not picData: with open(coverPath, 'rb') as f: picData = f.read() # 获取音频数据和图片数据后缀名 audioSuffix = id_card.mime[0].split('/')[-1] try: picSuffix = imghdr.what(None, picData) except: picSuffix = 'jpeg' mimeType = 'image/' + picSuffix # 开始写入封面 if audioSuffix == 'mp3': keyName = 'APIC:' keyName_list = [] # 获取可能已经存在的封面键名 for key in id_card.tags.keys(): if key.startswith('APIC'): keyName = key keyName_list.append(key) # 弹出所有旧标签才能写入新数据 for key in keyName_list: id_card.pop(key) id_card[keyName] = APIC(encoding=0, mime=mimeType, type=3, desc='', data=picData) elif audioSuffix == 'flac': # 创建Picture实例 picture = Picture() # 设置属性值 picture.mime = mimeType picture.data = picData picture.type = 0 # 清除原来的图片数据 id_card.clear_pictures() # 添加图片 id_card.add_picture(picture) elif audioSuffix == 'mp4': try: id_card['covr'][0] = picData except: id_card['covr'] = [picData] # 没有键时需要创建一个 id_card.save()
def fix_cover(audio: File): """ Transfers album cover from audio key APIC:XXXX to APIC: Example audio['APIC: Payday 2.jpg'] = APIC() becomes audio['APIC:'] = APIC() """ for k in audio.keys(): if k.startswith('APIC:') and k != 'APIC:': audio['APIC:'] = audio.pop(k) audio.save() break
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!')