Code: Select all
#!/usr/bin/env python2
#MythTV commercial removal -- by Daraden
#usage userjob = /path to script/script.py --jobid %JOBID%
#/path to script/script.py --chanid --starttime can also be used
from MythTV import Job, Program, Recorded, System, MythDB, findfile, MythError, MythLog, datetime
import argparse
import subprocess
import os
import sys
import shutil
from glob import glob
import time
from datetime import timedelta
import logging
db=MythDB()
#set this to True to use commflag results as cut list
use_commflag = True
#save copy of original file as file.old if not using output file
save_old = True
#user needs write access to this directory to save logfile
logdir ='/tmp/'
#logging setup for file and console
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
lf= logging.Formatter('%(asctime)s:%(levelname)s:%(message)s')
if os.access(logdir,os.W_OK):
fh = logging.FileHandler(filename='%scut.log'%(logdir),mode='w')
fh.setLevel(logging.DEBUG)
fh.setFormatter(lf)
logger.addHandler(fh)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
ch.setFormatter(lf)
logger.addHandler(ch)
def run_cut(jobid=None,chanid=None,starttime=None,outfile=None):
logging.info('Started')
#check for ffmpeg or mythffmpeg
ffmpeg = prog_check()[0]
logging.debug('ffmpeg=%s',ffmpeg)
if jobid:
job = Job(jobid, db=db)
chanid = job.chanid
starttime = job.starttime
logging.debug('chanid=%s starttime=%s',chanid,starttime)
if not jobid:
chanid = chanid
starttime = starttime
logging.debug('chanid=%s starttime=%s',chanid,starttime)
rec = find_rec(chanid,starttime)
logging.debug('DB recording entry=%s',rec)
sg = findfile('/'+rec.basename, rec.storagegroup, db=db)
infile = os.path.join(sg.dirname, rec.basename)
tmpdir = sg.dirname +'crtmp/'
tmp_chk(tmpdir)
if save_old and not outfile:
shutil.copyfile(infile,'%s.old' % infile)
if outfile != None:
Outfile = outfile
elif outfile == None:
Outfile = infile
if rec.cutlist == 1 or use_commflag == True:
if jobid:
job.update({'status':job.RUNNING, 'comment':'Removing Cutlist'})
logging.info('getting cutlist')
if rec.cutlist == 1:
cut_list =''.join(str(rec.markup.getcutlist())).replace('[','').replace(']','').replace('(','').replace(')','').replace(' ','')
elif rec.cutlist == 0 and rec.commflagged == 1:
cut_list =''.join(str(rec.markup.getskiplist())).replace('[','').replace(']','').replace('(','').replace(')','').replace(' ','')
else:
print 'No cut/skip list found'
logging.debug('No cut/skip list found')
sys.exit(1)
cutlist = cut_list
if cutlist.startswith('0,'):
cutlist = cutlist.replace("0,",'',1)
if cutlist.endswith(',9999999'):
cutlist = cutlist.replace(",9999999",'',-1)
logging.debug('Myth cutlist:%s',cut_list)
logging.debug('FFmpeg cutlist:%s',cutlist)
logging.info('cutting started')
cut =subprocess.Popen([ffmpeg,'-i',infile,'-c','copy','-map','0','-f','segment','-segment_list','%scut.ffcat'%(tmpdir),'-segment_frames',cutlist,'%scut%%03d.ts'%(tmpdir)],stdout = subprocess.PIPE, stderr = subprocess.STDOUT,universal_newlines=True)
output = cut.communicate()[0]
if cut.wait() == 0:
if jobid:
job.update({'status':job.RUNNING, 'comment':'Cuting Finished'})
logging.info('Cuting Finished')
if cut.wait() != 0:
if jobid:
job.update({'status':job.ERRORED, 'comment':'FFMPEG ERROR: cutiing'})
logging.error('FFMPEG ERROR: cutiing')
logging.error('%s',output)
sys.exit(1)
if jobid:
job.update({'status':job.RUNNING, 'comment':'Merging Cuts'})
logging.info('Merging Cuts')
j = []
for root, dirs, files in os.walk(tmpdir):
for files in files:
if files.endswith('.ts') and files.startswith('cut'):
if os.path.isfile(os.path.join(root,files)):
j.append(os.path.join(root,files))
if cut_list.startswith('0,'):
ls = j[1::2]
if not cut_list.startswith('0,'):
ls = j[0::2]
q =','.join(ls).replace(',','|')
logging.debug('concat list:%s' % q)
join =subprocess.Popen([ffmpeg,'-y','-i','concat:%s'%q,'-map','0','-c','copy','-f','mpegts',Outfile],stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
output = join.communicate()[0]
if join.wait() == 0:
if jobid:
job.update({'status':job.RUNNING, 'comment':'Merging Finished'})
logging.info('Merging Finished')
if join.wait() != 0:
if jobid:
job.update({'status':job.ERRORED, 'comment':'FFMPEG ERROR: merging'})
logging.error('FFMPEG ERROR: merging')
logging.error('%s',output)
sys.exit(1)
rem_tmp(tmpdir)
if outfile == None:
clear_markup(rec,infile,outfile)
def clear_markup(rec,infile,outfile):
logging.info('Started Clearing markup')
logging.debug('rec=%s infile=%s outfile=%s',rec,infile,outfile)
chanid = rec.chanid
utcstarttime = rec.starttime
starttime = str(utcstarttime.utcisoformat().replace(u':', '').replace(u' ', '').replace(u'T', '').replace('-', ''))
logging.debug('chanid=%s starttime=%s',chanid,starttime)
logging.info('start clearing markup')
try:
rcl = subprocess.Popen(['mythutil','--chanid',str(chanid),'--starttime',str(starttime),'--clearcutlist'], stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
output1 = rcl.communicate()
if rcl.wait() == 0:
logging.info('Cutlist removed for:%s_%s',chanid,starttime)
logging.debug('%s',output1)
if rcl.wait() != 0:
logging.error('MYTHUTIL ERROR: clearing cutlist for:%s_%s',chanid,starttime)
logging.error('%s',output1)
except Exception as e:
logging.error('Mythutil exception clearing cutlist%s',e)
try:
rsl = subprocess.Popen(['mythutil','--chanid',str(chanid),'--starttime',str(starttime),'--clearskiplist'], stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
output2 = rsl.communicate()[0]
if rsl.wait() == 0:
logging.info('Skiplist removed for:%s_%s',chanid,starttime)
if rsl.wait() != 0:
logging.error('MYTHUTIL ERROR: clearing skiplist for:%s_%s',chanid,starttime)
logging.error('%s',output2)
except Exception as e:
logging.error('Mythutil exception clearing skiplist:%s',e)
for index,mark in reversed(list(enumerate(rec.markup))):
if mark.type in (rec.markup.MARK_COMM_START, rec.markup.MARK_COMM_END):
del rec.markup[index]
rec.bookmark = 0
rec.cutlist = 0
rec.commflagged = 0
rec.markup.commit()
rec.basename = os.path.basename(infile)
rec.filesize = os.path.getsize(infile)
rec.transcoded = 1
rec.seek.clean()
rec.update()
try:
logging.info('Removing PNG files')
for png in glob('%s*.png' % infile):
os.remove(png)
except Exception as e:
logging.error('Error removing png files',e)
try:
logging.info('Removing JPG files')
for jpg in glob('%s*.jpg' % infile):
os.remove(jpg)
except Exception as e:
logging.error('Error removing jpg files',e)
try:
logging.info('Rebuilding seektable')
rst = subprocess.Popen(['mythcommflag','--chanid',str(chanid),'--starttime',str(starttime),'--rebuild'], stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
output2 = rst.communicate()[0]
if rst.wait() == 0:
logging.info('Seektable Rebuilt for:%s_%s',chanid,starttime)
logging.info('%s',output2)
if rst.wait() != 0:
logging.error('MYTHcommflag ERROR: Rebuilding Seektable for:%s_%s',chanid,starttime)
logging.error('%s',output2)
except Exception as e:
logging.error('Mythcommflag ERROR clearing skiplist:%s',e)
def tmp_chk(tmpdir):
try:
if os.path.isdir(tmpdir):
logging.info('temp Folder found:')
if os.listdir(tmpdir) != 0:
logging.warning('Temp folder not empty!:Removing Files:')
shutil.rmtree(tmpdir)
os.makedirs(tmpdir)
if not os.path.isdir(tmpdir):
logging.info('no temp folder found:')
os.makedirs(tmpdir)
logging.info('Temp folder created:')
except Exception as e:
logging.error('%s',e)
def rem_tmp(tmpdir):
try:
if os.path.isdir(tmpdir):
logging.info('temp Folder found')
shutil.rmtree(tmpdir)
logging.info('Temp Folder Removed')
if not os.path.isdir(tmpdir):
logging.info('no temp folder found')
except Exception as e:
logging.error('%s',e)
def find_rec(chanid,starttime):
def local_time_offset(t=None):
if t is None:
t = time.time()
if time.localtime(t).tm_isdst and time.daylight:
return -time.altzone
else:
return -time.timezone
def RecordedFromBasename(chanid,starttime):
bnts = '%s_%s.ts' % (chanid,starttime)
bnmpg = '%s_%s.mpg' % (chanid,starttime)
x = list(db.searchRecorded(basename=bnmpg))
if len(x) == 1:
for recorded in x:
return recorded
if len(x) != 1:
x = list(db.searchRecorded(basename=bnts))
if len(x) == 1:
for recorded in x:
return recorded
if len(x) != 1:
raise LookupError\
('unable to find Recorded entry for ChanID %s StartTime %s'\
% (chanid,starttime))
try:
rec = Recorded((chanid,starttime), db=db)
except:
try:
tzoffset = local_time_offset() / (60*60)
utcstarttime = datetime.strptime(starttime,"%Y%m%d%H%M%S")
utcstarttime = utcstarttime + timedelta(hours=tzoffset)
rec = Recorded((chanid,utcstarttime), db=db)
except:
rec = RecordedFromBasename(chanid,starttime)
return rec
def prog_check():
if os.access('/usr/bin/ffmpeg', os.X_OK):
ffmpeg = '/usr/bin/ffmpeg'
elif os.access('/usr/bin/mythffmpeg', os.X_OK):
ffmpeg = '/usr/bin/mythffmpeg'
else:
ffmpeg = None
if os.access('/usr/bin/ffprobe', os.X_OK):
ffprobe ='/usr/bin/ffprobe'
elif os.access('/usr/bin/mythffprobe', os.X_OK):
ffprobe = '/usr/bin/mythffprobe'
else:
ffprobe = None
if ffmpeg == None or ffprobe == None:
raise LookupError ('Unable to find ffmpeg/ffprobe')
return ffmpeg,ffprobe
def main():
parser = argparse.ArgumentParser(description='MythTV Commercial removal and closed caption extraction tool.\nNOTE:Having .srt file in same dir as media files breaks playback in some media players(VLC)')
parser.add_argument('--chanid',action='store',type=str,dest='chanid',help='Channel-Id of Recording')
parser.add_argument('--starttime',action='store',type=str,dest='starttime',help='Starttime of recording in utc format')
parser.add_argument('--jobid',action='store',type=int,dest='jobid',help='JOBID')
parser.add_argument('-o',action='store',type=str,dest='outfile',help='Output file to be created')
args = parser.parse_args()
if args.jobid:
run_cut(jobid=args.jobid,outfile=args.outfile)
sys.exit(0)
if args.chanid and args.starttime:
run_cut(chanid=args.chanid,starttime=args.starttime,outfile=args.outfile)
sys.exit(0)
else:
print 'chanid and starttime or jobid'
main()