import os import urllib.request import subprocess import sys import chardet import mutagen import imghdr RootDir = 'MuziekBibliotheek' def getoption(option): r = None if option.endswith('='): # expects the results after the option for arg in sys.argv[1:]: if not r and arg.startswith(option): r = arg[len(option):] else: # expects True or False r = option in sys.argv[1:] return r def needupdate(): command = 'ls -rtLa1' cd = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout results = cd.readlines() result = results[-1][:-1] != b'.muziekinfo' return result def getcoverfile(f): coverfile = '' if f.endswith('.mp3'): from mutagen.mp3 import MP3 #from mutagen.id3 import ID3, APIC, error md = MP3(f) try: imageData = md.tags.getall("APIC")[0].data ext = imghdr.what('', imageData) if ext == 'jpeg': ext = 'jpg' coverfile = 'Cover.' + ext with open(coverfile, "wb") as f: f.write(imageData) except: coverfile = '' elif f.endswith('.flac'): md = mutagen.File(f, easy=True) if hasattr(md, 'pictures'): for pic in md.pictures: if pic.type == 3: ext = pic.mime[6:] if ext == 'jpeg': ext = 'jpg' coverfile = 'Cover.' + ext with open(coverfile, "wb") as f: f.write(pic.data) break return coverfile def gettrackinfo(f): r = {} md = mutagen.File(f) if not 'tracknumber' in md.keys(): md = mutagen.easyid3.EasyID3(f) if 'tracknumber' in md.keys(): r['TRACKNUMBER'] = md['tracknumber'][0].split('/')[0] if 'title' in md.keys(): r['TITLE'] = md['title'][0] return r if 'TRACKNUMBER' not in r: r['TRACKNUMBER'] = '1' if 'TITLE' not in r: e = f.rfind('.') s = f.find(' ') if s == -1 or s > 5: s = f.find('. ') if s == -1 or s > 5: s = f.find(' ') if s == -1 or s > 5: s1 = f.find('(') s = f.find(') ') if s1 != 0 or s == -1 or s > 12: s = 0 else: s += 12 else: s += 6 else: s += 7 else: s += 13 r['TITLE'] = f[s:e] return r def gettracknum(flac): if flac[0] == '(': s = i = 1 else: s = i = 0 while (flac[i] in "0123456789" and i < len(flac)): i += 1 if i: try: i = int(flac[s:i]) except Exception as e: # print(e) i = 0 return i def getduration(f): try: tt = mutagen.File(f).info.length except Exception as e: print('Exception in getduration for file "%s"\nMessage: %s' % (os.path.join(os.getcwd(), f), e)) sys.exit(1) m = int(tt / 60.0) s = int(tt - m * 60 + 0.5) return '"%d:%2.2d"' % (m, s) def sortcoverfiles(coverfiles): r = [] for cf in coverfiles: if "cover" in cf.lower(): r.append(cf) coverfiles.remove(cf) elif "front" in cf.lower(): r.append(cf) coverfiles.remove(cf) for cf in coverfiles: r.append(cf) return r def flacname(file): ext= os.path.splitext(file)[-1] if len(ext) <= len('.flac') or len(file) < 252: return file[:-len(ext)] + '.flac' else: return file[:-len('.flac')] + '.flac' def wma2flac(wma): flac = flacname(wma) command = 'ffmpeg -loglevel 16 -i "%(wma)s" -f flac "%(flac)s"' % vars() cd = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout lines = cd.readlines() if lines: print(cd.readlines()) return flac def wav2flac(wav): flac = flacname(wav) command = 'ffmpeg -loglevel 16 -i "%(wav)s" "%(flac)s"' % vars() cd = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout lines = cd.readlines() if lines: print(cd.readlines()) return flac def renametrackfile(f): if f.startswith('('): oldf = f e = f.index(')') f = f[1:e] + '.' + f[e+1:] os.rename(oldf, f) return f def ape2flac(ape): flac = flacname(ape) command = 'ffmpeg -loglevel 16 -i "%(ape)s" -c:a flac "%(flac)s"' % vars() cd = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout lines = cd.readlines() if lines: print(cd.readlines()) return flac def findcdneighbours(): ok = True dir = os.getcwd() prevcd = None nextcd = None prevvol = None nextvol = None prevcdvol = None nextcdvol = None cdlist = [os.path.join(os.path.dirname(dir), d) for d in sorted(os.listdir(os.path.dirname(dir))) if d.startswith("CD") and os.path.isdir(os.path.join(os.path.dirname(dir), d))] cdnum = cdlist.index(dir) if cdnum: prevcd = os.path.join(RootDir, cdlist[cdnum-1].split(RootDir)[1][1:]) if (cdnum + 1) < len(cdlist): nextcd = os.path.join(RootDir, cdlist[cdnum+1].split(RootDir)[1][1:]) vollist = [os.path.join(os.path.dirname(dir), d) for d in sorted(os.listdir(os.path.dirname(dir))) if d.startswith("VOL") and os.path.isdir(os.path.join(os.path.dirname(dir), d))] # print(vollist) if vollist: # on this level links so we have to sort out which volume this CD belongs to firstcd = [] thisvol = -1 thiscdvol = -1 for volnum, vol in enumerate(vollist): # cdlist = [os.path.join(vol, d) for d in sorted(os.listdir(vol)) if d.startswith("CD") and os.path.islink(os.path.join(vol, d))] cdlist = [os.path.join(vol, d) for d in sorted(os.listdir(vol)) if d.startswith("CD") and os.path.isdir(os.path.join(vol, d))] if not cdlist: print('empty VOLume! "%s"' % (vol)) print(vollist) print(sorted(os.listdir(vol))) return (prevcd, nextcd, prevvol, nextvol, prevcdvol, nextcdvol) # return None reallist = [os.path.realpath(cd) for cd in cdlist] firstcd.append(cdlist[0]) try: cdnum = reallist.index(os.path.realpath(dir)) except: cdnum = -1 if cdnum != -1: thisvol = volnum thiscdvol = cdnum if thisvol != -1: if thisvol: prevvol = os.path.join(RootDir, vollist[thisvol-1].split(RootDir)[1][1:], os.path.basename(firstcd[thisvol-1])) if (thisvol + 1) < len(vollist): nextvol = os.path.join(RootDir, vollist[thisvol+1].split(RootDir)[1][1:], os.path.basename(firstcd[thisvol+1])) cdlist = [os.path.join(vollist[thisvol], d) for d in sorted(os.listdir(vollist[thisvol])) if d.startswith("CD") and os.path.islink(os.path.join(vollist[thisvol], d))] if thiscdvol: prevcdvol = os.path.join(RootDir, cdlist[thiscdvol-1].split(RootDir)[1][1:]) if (thiscdvol + 1) < len(cdlist): nextcdvol = os.path.join(RootDir, cdlist[thiscdvol+1].split(RootDir)[1][1:]) return (prevcd, nextcd, prevvol, nextvol, prevcdvol, nextcdvol) def dodir(curdir, force, recursive): prevcd = None nextcd = None prevvol = None nextvol = None prevcdvol = None nextcdvol = None os.chdir(curdir) curdir = os.path.realpath(os.getcwd()) infofile = os.path.join(curdir, '.muziekinfo') skipfile = os.path.join(curdir, '.muziekinfo_skip') playfile = os.path.join(curdir, 'muziekinfo.m3u') # if infofile.find('MuziekBibliotheek') == -1: if infofile.find(RootDir) == -1: raise Exception('Not a path I should look at: %(infofile)s' % vars()) sys.exit(0) if os.path.exists(skipfile): print('Skipping directory "%(curdir)s"!' % vars()) return 0 if os.path.exists(infofile): print('FILE "%(infofile)s" already present!' % vars()) if os.path.islink(curdir): return 0 if not force and not needupdate(): if recursive: dirfiles = [d for d in os.listdir('.') if not d.startswith('.') and os.path.isdir(d)] for dir in dirfiles: result = dodir(os.path.join(curdir, dir), force, recursive) return 0 print('FILE "%(infofile)s" will be overwritten!' % vars()) try: infofd = open(infofile, 'w'); except Exception as e: print('Exception %(e)s creating file "%(infofile)s"'% vars()) return 1 dirfiles = [] coverfiles = [] soundfiles = [] tracknames = [] for f in os.listdir('.'): if f.startswith('.'): continue t = -1 if os.path.isdir(f): newdir = os.path.join(curdir, f) if not os.path.exists(os.path.join(newdir, os.path.basename(skipfile))): dirfiles.append(f) else: print('Skipping directory "%(newdir)s"!' % vars()) elif f.endswith('.flac'): t = gettracknum(f) if t: soundfiles.append((f, t)) elif f.endswith('.ape'): # if f.startswith('(') and f[1].isdigit(): # f = renametrackfile(f) t = gettracknum(f) if t and not os.path.exists(flacname(f)): soundfiles.append((ape2flac(f), t)) elif f.endswith('.wav'): t = gettracknum(f) if t and not os.path.exists(flacname(f)): soundfiles.append((wav2flac(f), t)) elif f.endswith('.wma'): t = gettracknum(f) if not os.path.exists(flacname(f)) : soundfiles.append((wma2flac(f), t)) elif f.endswith('.mp3'): t = gettracknum(f) if not t: t = len(soundfiles) + 1 soundfiles.append((f, t)) elif f.endswith('.jpg') or f.endswith('.png') or f.endswith('.jpeg') or f.endswith('.gif'): coverfiles.append(f) if t == 0: print('Skipping "%(f)s"' % vars()) if soundfiles and os.path.basename(curdir).startswith("CD"): # part of box? (prevcd, nextcd, prevvol, nextvol, prevcdvol, nextcdvol) = findcdneighbours() if not coverfiles: for (soundfile, t) in soundfiles: coverfile = getcoverfile(soundfile) if coverfile: coverfiles.append(coverfile) break; if coverfiles: coverfiles = sortcoverfiles(coverfiles) infofd.write(' var howler_cover = "%s";\n' % (urllib.request.pathname2url(coverfiles[0]))) else: # infofd.write(' var howler_cover = "/MuziekBibliotheek/.images/noimageavailable.png";\n') infofd.write(' var howler_cover = "/%s/.images/noimageavailable.png";\n' % (RootDir)) if prevcd: infofd.write(' var howler_prevcd = "%s";\n' % (urllib.request.pathname2url(prevcd))) else: infofd.write(' var howler_prevcd = undefined;\n') if nextcd: infofd.write(' var howler_nextcd = "%s";\n' % (urllib.request.pathname2url(nextcd))) else: infofd.write(' var howler_nextcd = undefined;\n') if prevvol: infofd.write(' var howler_prevvol = "%s";\n' % (urllib.request.pathname2url(prevvol))) else: infofd.write(' var howler_prevvol = undefined;\n') if nextvol: infofd.write(' var howler_nextvol = "%s";\n' % (urllib.request.pathname2url(nextvol))) else: infofd.write(' var howler_nextvol = undefined;\n') if prevcdvol: infofd.write(' var howler_prevcdvol = "%s";\n' % (urllib.request.pathname2url(prevcdvol))) else: infofd.write(' var howler_prevcdvol = undefined;\n') if nextcdvol: infofd.write(' var howler_nextcdvol = "%s";\n' % (urllib.request.pathname2url(nextcdvol))) else: infofd.write(' var howler_nextcdvol = undefined;\n') cwd = os.path.realpath(os.getcwd()) (dummy, rest) = cwd.split(RootDir) # template = os.path.join(dummy, 'MuziekBibliotheek', '.muziekinfo.shtml_template') template = os.path.join(dummy, RootDir, '.templates/template.shtml') if not os.path.exists(template): print('NO SUCH FILE: "%(template)s"' % vars()) return 1 if rest: infofd.write(' var howler_curdir = "%s%s";\n' % (RootDir, urllib.request.pathname2url(rest))) else: infofd.write(' var howler_curdir = "%s";\n' % (RootDir)) x = [d for d in sorted(dirfiles) if os.path.isdir(d)] def myfunc(s): r = 0 l = len(s) while r < l: if not s[r].isalpha() or s[r].islower(): r += 1 else: break return s[r:] + s[0:r] realdirs = sorted(x, key=myfunc) dirs = ['"'+urllib.request.pathname2url(d)+'"' for d in realdirs] if dirs: infofd.write(' var howler_dirs = [\n' + ',\n'.join(dirs) + '\n];\n') else: infofd.write(' var howler_dirs = [];\n') soundtracks = soundfiles #[(f, int(gettracknum(f))) for f in soundfiles if gettracknum(f)] soundtracks_sorted = sorted(soundtracks, key=lambda tup: tup[1]) duration = [getduration(f[0]) for f in soundtracks_sorted] sounds = ['"'+urllib.request.pathname2url(f)+'"' for (f, t) in soundtracks_sorted] tracknames = [] for soundtrack in soundtracks_sorted: trackinfo = gettrackinfo(soundtrack[0]) if 'TITLE' not in trackinfo.keys(): trackname = soundtrack[0] else: trackname = trackinfo['TITLE'] tracknames.append('"%s"' % trackname.replace('"', '\\"')) # tracknames = ['"%s"' % gettrackinfo(soundtrack[0])['TITLE'].replace('"', '\\"').encode('utf8').replace(b'\x83\xc2', b'').decode() for soundtrack in soundtracks_sorted] # tracknames = ['"%s"' % gettrackinfo(soundtrack[0])['TITLE'].replace('"', '\\"').encode('ascii').replace(b'\x83\xc2', b'').decode() for soundtrack in soundtracks_sorted] # tracknames = ['"%s"' % gettrackinfo(soundtrack[0])['TITLE'].replace('"', '\\"') for soundtrack in soundtracks_sorted] # print([ord(tracknames[1][i]) for i in range(len(tracknames[1]))]) # print(tracknames[1].encode('utf8').replace(b'\x83\xc2', b'').decode()) # print([ord(tracknames[1][i]) for i in range(len(tracknames[1]))]) tracks = ['%d' % (t) for (f, t) in soundtracks_sorted] if sounds: infofd.write(' var howler_sounds = [\n' + ',\n'.join(sounds) + ',\n];\n') infofd.write(' var howler_tracknames = [\n' + ',\n'.join(tracknames) + ',\n];\n') infofd.write(' var howler_tracknums = [\n' + ',\n'.join(tracks) + ',\n];\n') infofd.write(' var howler_time = [\n' + ',\n'.join(duration) + ',\n];\n') playfd = open(playfile, 'w') playfd.write('#EXTM3U\n') for (fil, nam, num, tim) in zip(sounds, tracknames, tracks, duration): m, s = tuple(tim[1:-1].split(':')) tim = int(m) * 60 + int(s) num = int(num) nam = nam[1:-1] playfd.write('#EXTINF:%1d,%s [%d] - %s\n%s\n' % (tim, curdir[17:], num, nam, fil)) playfd.close() else: infofd.write(' var howler_sounds = [];\n') infofd.write(' var howler_tracknums = [];\n') infofd.write(' var howler_tracknames = [];\n') infofd.write(' var howler_time = [];\n') infofd.close() if not os.path.exists('.muziekinfo.shtml'): os.symlink(template, '.muziekinfo.shtml') if recursive: for dir in dirfiles: result = dodir(os.path.join(curdir, dir), force, recursive) return 0 if __name__ == '__main__': force = getoption('--force') curdir = getoption('--dir=') if curdir in [None, '']: curdir = os.getcwd() recursive = getoption('--recursive') if not os.path.exists(curdir): raise Exception('No such directory: "%(curdir)s"' % vars()) dodir(curdir, force, recursive)