Last active
September 30, 2019 20:31
-
-
Save dfaker/655591dc995246a0394bdfbfd22b5f82 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import os | |
| import glob | |
| import subprocess as sp | |
| import itertools | |
| import sys | |
| """ | |
| REQUIRES: | |
| MPV https://mpv.io/ | |
| FFMPEG https://www.ffmpeg.org/ | |
| Can be run from the command line like: | |
| interest.py | |
| (to browse starting at the folder the script is located in) | |
| or | |
| interest.py DIR/PATH | |
| (to browse starting at a custom folder) | |
| or | |
| interest.py FILE/PATH | |
| (to open a file directly) | |
| After a file is selected it will open in mplayer. | |
| tap x rapidly to mark current frames as 'interesting' to mark them for later extract. | |
| press c to start cropping, click once to start the crop and second time to complete it. | |
| """ | |
| def selectFromEnumerated(itemList,headerPrompt='Please select your option by the number in square brackets:',inputPrompt='>>> ',bulletStyle=' [{number}] - {entry}'): | |
| print('') | |
| print(headerPrompt) | |
| selected = None | |
| il=[] | |
| while selected is None: | |
| for i,e in enumerate(itemList): | |
| il.append(i) | |
| print( bulletStyle.format(entry=str(e),number=i)) | |
| try: | |
| ind = int(input(inputPrompt)) | |
| selected=itemList[ind] | |
| return selected,ind | |
| except Exception as e: | |
| print(e) | |
| root = '.\\' | |
| for arg in sys.argv: | |
| if '.py' not in arg and os.path.exists(arg): | |
| root=arg | |
| while not os.path.isfile(root): | |
| path = os.path.join(root,'*') | |
| print(path) | |
| folders = list(glob.glob(path)) | |
| selected,ind = selectFromEnumerated(folders) | |
| root = selected | |
| print(root) | |
| os.path.exists('shotsTemp') or os.mkdir('shotsTemp') | |
| playerProc = sp.Popen(['mpv',root,'--script', 'easycrop.lua','--input-conf','customMpvConf.conf'],stdout=sp.PIPE,stderr=sp.DEVNULL) | |
| timeStamps=[] | |
| buf=b'' | |
| crops = {} | |
| while 1: | |
| c = playerProc.stdout.read(1) | |
| if len(c)==0: | |
| break | |
| buf+=c | |
| markerCount=buf.count(b']@[') | |
| if markerCount>0 and markerCount%2==0: | |
| for e in buf.split(b']@['): | |
| if b'CROP,' in e: | |
| cropTS=0 | |
| if len(timeStamps)>0: | |
| cropTS=timeStamps[-1] | |
| name,w,h,x,y = e.split(b',') | |
| print(name,w,h,x,y) | |
| w,h,x,y = int(w),int(h),int(x),int(y) | |
| print(name,w,h,x,y) | |
| crops[cropTS]=(w,h,x,y) | |
| try: | |
| e=float(e) | |
| timeStamps.append(e) | |
| except: | |
| pass | |
| buf=b'' | |
| maxw,maxh,maxx,maxy,minx,miny=0,0,0,0,0,0 | |
| for h,w,x,y in crops.values(): | |
| maxh = max(maxh,h) | |
| maxw = max(maxw,w) | |
| maxx = max(maxx,x+w) | |
| maxy = max(maxy,y+h) | |
| minx = max(minx,x) | |
| miny = max(miny,y) | |
| for k,(h,w,x,y) in list(crops.items()): | |
| h2=maxh | |
| w2=maxw | |
| x2 = max( min(x ,minx - h2 ) ,minx) | |
| y2 = max( min(y ,miny - w2 ) ,miny) | |
| crops[k] = (h2,w2,x2,y2) | |
| playerProc.communicate() | |
| timeStamps=sorted(timeStamps) | |
| if len(timeStamps)==0: | |
| exit() | |
| tempTimestamps = [] | |
| for value,group in itertools.groupby(timeStamps,lambda x:int(x)): | |
| listGroup = list(group) | |
| groupLen = len(listGroup) | |
| print('Processed', groupLen ,'tags at around second',value) | |
| for v in listGroup: | |
| tempTimestamps.append( (v, min(4,groupLen) ) ) | |
| timeStamps = tempTimestamps | |
| print('Found a total of', len(timeStamps), 'tagged frames. Will now merge into sections.' ) | |
| spans=[ (timeStamps[0][0],timeStamps[0][0]) ] | |
| lastTimeStamp=timeStamps[0][0] | |
| for ts,bias in timeStamps: | |
| head = spans[:-1] | |
| tails,taile=spans[-1] | |
| if abs(ts-taile)<0.5+(0.3*bias): | |
| taile=ts | |
| spans = head+[(tails,taile)] | |
| else: | |
| spans = head+[(tails,taile),(ts,ts)] | |
| def diverge(s,e): | |
| if s<e: | |
| return (s,e) | |
| elif e<s: | |
| return (e,e) | |
| else: | |
| return (s-1,s+1) | |
| spans = [ diverge(s,e) for s,e in sorted(spans)] | |
| spanToCropLookup = {} | |
| if len(crops)>0: | |
| for s,e in spans: | |
| mid = (s+e)/2 | |
| closest = sorted([ (k,(h,w,x,y)) for k,(h,w,x,y) in crops.items()],key=lambda x: abs(x[0]-mid) )[0][1] | |
| spanToCropLookup[(s,e)]=closest | |
| os.path.exists('cutsTemp') or os.mkdir('cutsTemp') | |
| cutBaseNum = len(os.listdir('cutsTemp')) | |
| print('Starting on ',len(spans),'sections starting cuts:') | |
| with open(os.path.join('cutsTemp','concatInst.txt'),'w') as concatInst: | |
| for i,(s,e) in enumerate(spans): | |
| print('Making cut',i+1,' ar start ',s ,'seconds through to ',e,' seconds') | |
| outfn = os.path.join('cutsTemp',str(cutBaseNum)+'_'+str(i)+'.mp4') | |
| if len(spanToCropLookup)==0: | |
| cutProc = sp.Popen(['ffmpeg','-ss',str(s),'-i',root,'-t',str(e-s),outfn],stdout=sp.DEVNULL,stderr=sp.DEVNULL) | |
| else: | |
| w,h,x,y = spanToCropLookup[(s,e)] | |
| cmd = ['ffmpeg','-ss',str(s),'-i',root,'-t',str(e-s), "-vf", "crop={}:{}:{}:{}".format(w,h,x,y) ,outfn] | |
| print(cmd) | |
| cutProc = sp.Popen(cmd,stdout=sp.DEVNULL,stderr=sp.DEVNULL) | |
| cutProc.communicate() | |
| concatInst.write('file '+outfn.replace('\\','\\\\')+'\n') | |
| print('Cut ',i,' complete') | |
| os.path.exists('joinsTemp') or os.mkdir('joinsTemp') | |
| joinBaseNum = str(len(os.listdir('joinsTemp'))) +os.path.splitext(os.path.basename(root))[0] | |
| outfn = os.path.join('joinsTemp',joinBaseNum+'_'+str(i)+'.mp4') | |
| print('Starting join into ',outfn) | |
| joinProc = sp.Popen(['ffmpeg', '-f', 'concat', '-safe', '0', '-i', os.path.join('cutsTemp','concatInst.txt'), '-c', 'copy', outfn],stdout=sp.DEVNULL,stderr=sp.DEVNULL) | |
| joinProc.communicate() | |
| print('Join complete into ',outfn) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment