Cutting commercials without transcoding
Moderator: Forum Moderators
-
- Newcomer
- Posts: 11
- Joined: Sun May 31, 2015 4:26 pm
Cutting commercials without transcoding
Just a query. I'm using Mythtv v29. On Mythtv v0.28 I could set mythtranscoder to cut commercials without transcoding. I'm not seeing this in v29 mythtv-setup, has this been removed or am I just missing it in setup?
Re: Cutting commercials without transcoding
Hi,
There is a "Run transcode jobs before auto commercial detection" setting, but
I may be misunderstanding your question. if you don't want to transcode, then
in your recording rules, turn it off. Make sure it's off in the Default template
if you want new rules created that way.
There is a "Run transcode jobs before auto commercial detection" setting, but
I may be misunderstanding your question. if you don't want to transcode, then
in your recording rules, turn it off. Make sure it's off in the Default template
if you want new rules created that way.
-
- Newcomer
- Posts: 11
- Joined: Sun May 31, 2015 4:26 pm
Re: Cutting commercials without transcoding
Thank you for responding. It seems I was not clear with my question. In mythtv-setup in 0.28 under Recording Profiles > Transcoders you can choose different quality settings, under each there's a box you can check for "Lossless Transcoding". That is missing in v29. I want to remove commercials without effecting the quality. I know that can be done from the terminal but that's more time consuming.
-
- Senior
- Posts: 122
- Joined: Tue Nov 11, 2014 11:03 am
Re: Cutting commercials without transcoding
You cannot completely remove portions of a recording without transcoding.
"Lossless Transcoding" is transcoding but just not transcoding to a different quality or format of file.
I am unsure why this might be missing from mythtv 29 but if you have a look at the mythtranscode options you may be able to create an equivalent job that would do the same without having to do any magic on the command line.
See https://www.mythtv.org/wiki/Mythtranscode
Using the --mpeg2 Option for (Virtually) Lossless MPEG2 Transcode
is a virtually lossless MPEG2 transcode option - (only for SD recordings I assume)
Also see https://www.mythtv.org/wiki/User_Jobs
For some examples and scripts.
I am sure there is a good example script using mythtranscode and the fifo option somewhere in the wiki but I can't find it at the moment.
There's one here :- https://www.mythtv.org/wiki/Example_Scr ... fodir_mode But I recall something simpler than that.
Anyhow, even though I do use my own scripts to remove commercials and transcode most of my recordings one should understand that this is quite optional and you will often get advice that it is really not worth it if all you are aiming to do is reduce the size of your recordings.
Creating a cutlist and keeping the recordings as they are will preserve the quality and storage space is cheap.
"Lossless Transcoding" is transcoding but just not transcoding to a different quality or format of file.
I am unsure why this might be missing from mythtv 29 but if you have a look at the mythtranscode options you may be able to create an equivalent job that would do the same without having to do any magic on the command line.
See https://www.mythtv.org/wiki/Mythtranscode
Using the --mpeg2 Option for (Virtually) Lossless MPEG2 Transcode
is a virtually lossless MPEG2 transcode option - (only for SD recordings I assume)
Also see https://www.mythtv.org/wiki/User_Jobs
For some examples and scripts.
I am sure there is a good example script using mythtranscode and the fifo option somewhere in the wiki but I can't find it at the moment.
There's one here :- https://www.mythtv.org/wiki/Example_Scr ... fodir_mode But I recall something simpler than that.
Anyhow, even though I do use my own scripts to remove commercials and transcode most of my recordings one should understand that this is quite optional and you will often get advice that it is really not worth it if all you are aiming to do is reduce the size of your recordings.
Creating a cutlist and keeping the recordings as they are will preserve the quality and storage space is cheap.
-
- Newcomer
- Posts: 11
- Joined: Sun May 31, 2015 4:26 pm
Re: Cutting commercials without transcoding
Thanks for the reply. I've been recording TV shows and converting them to mp4s with standard definition. I simply modified my bash script to this:
Then made a link to my Videos folder.
Code: Select all
#!/bin/sh
#export DISPLAY=:0
FILE=$1
TITLE=$2
OUTDIR="/var/lib/mythtv/Transcoded_Videos/"
mythtranscode -i $FILE --honorcutlist -m -o /var/lib/mythtv/Transcoded_Videos/tmp.mpg
nice -n 10 /usr/bin/ffmpeg -i /var/lib/mythtv/Transcoded_Videos/tmp.mpg -c:v libx264 -vf scale=854:480 -c:a libmp3lame -ac 2 "$OUTDIR$TITLE$SUBTITLE.mp4"
rm /var/lib/mythtv/Transcoded_Videos/tmp.mpg
rm /var/lib/mythtv/Transcoded_Videos/tmp.mpg.map
Re: Cutting commercials without transcoding
Sounds like writing a custom script to use ffmpeg with the "copy" flag to cut out the commercials based on the detection time stamps might be possible.
This would not transcode, but rather just copy the data you want to save to a new file omitting the ads.
I'm pretty bad at scripting though, and absolutely horrible at databases needed to look up the timestamps, so I couldn't do it on my own.
I wonder if this is really something you want to do though. Is disk space really that tight?
The reason I ask is that the commercial detection isn't always perfect. Sometimes it misses ads, but more importantly sometimes it cuts out what it thinks are ads, but are actually important content.
If you keep the ads, and just have the player skip them, you can always manually override in case of a botched detection. If you physically remove them, the content is gone, and you may be missing sections in your recordings you actually want to keep...
This would not transcode, but rather just copy the data you want to save to a new file omitting the ads.
I'm pretty bad at scripting though, and absolutely horrible at databases needed to look up the timestamps, so I couldn't do it on my own.
I wonder if this is really something you want to do though. Is disk space really that tight?
The reason I ask is that the commercial detection isn't always perfect. Sometimes it misses ads, but more importantly sometimes it cuts out what it thinks are ads, but are actually important content.
If you keep the ads, and just have the player skip them, you can always manually override in case of a botched detection. If you physically remove them, the content is gone, and you may be missing sections in your recordings you actually want to keep...
v33 backend in 22.04 LTS w. LXDE, in LXC on server w. 16C/32T Xeon E5-2650v2, 256GB RAM. 6C & 8GB assigned to container.
Re: Cutting commercials without transcoding
Have not upgraded to 29 yet, but i use this script to remove commercials.
Code: Select all
#!/usr/bin/env python2
# -*- coding: UTF-8 -*-
# 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 __future__ import print_function
import argparse
import logging
import logging.handlers
import os
import shutil
import subprocess
import sys
import tempfile
import time
from MythTV import Job, Recorded, MythDB, findfile, datetime
from datetime import timedelta
from glob import glob
# set this to True to use commercial flagging results as cut-list
# this will automatically flag commercials if no cut-list is available
use_commflag = True
# save copy of original file as file.old if not using output file
save_old = False
# user needs write access to this directory to save logfile
logdir = '/tmp/'
LOG_FILENAME = '{}cut.log'.format(logdir)
# logging setup for file and console
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
lf = logging.Formatter('%(asctime)s:%(levelname)s:%(message)s')
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
ch.setFormatter(lf)
logger.addHandler(ch)
try:
if os.access(logdir, os.W_OK):
fh = logging.handlers.TimedRotatingFileHandler(filename=LOG_FILENAME, when='W0',
interval=1, backupCount=10
)
fh.setLevel(logging.DEBUG)
fh.setFormatter(lf)
logger.addHandler(fh)
if not os.access(logdir, os.W_OK):
logging.error('log directory not accessible:{}'.format(logdir))
except Exception as e:
logging.error('log directory not accessible:{}'.format(logdir))
logging.error(e)
sys.exit(1)
try:
db = MythDB()
except Exception as e:
logging.error(e)
sys.exit(1)
def program_check():
"""checks for FFmpeg/FFprobe if not found MythFFmpeg/MythFFprobe
is used"""
from distutils import spawn
if spawn.find_executable('ffmpeg') is not None:
ffmpeg = spawn.find_executable('ffmpeg')
elif spawn.find_executable('ffmpeg') is None and spawn.find_executable('mythffmpeg'):
ffmpeg = spawn.find_executable('mythffmpeg')
else:
ffmpeg = None
if spawn.find_executable('ffprobe') is not None:
ffprobe = spawn.find_executable('ffprobe')
elif spawn.find_executable('ffprobe') is None and spawn.find_executable('mythffprobe'):
ffprobe = spawn.find_executable('mythffprobe')
else:
ffprobe = None
if ffmpeg is not None and ffprobe is not None:
return ffmpeg, ffprobe
else:
raise LookupError('Unable to find FFmpeg or MythFFmpeg')
# check for ffmpeg or mythffmpeg
ffmpeg = program_check()[0]
logging.debug('ffmpeg={}'.format(ffmpeg))
ffprobe = program_check()[1]
logging.debug('ffprobe={}'.format(ffprobe))
def run_cut(jobid=None, chanid=None, starttime=None, outfile=None):
logging.info('Started')
# Configure chanid and starttime from userjob input
job = None
if jobid:
job = Job(jobid, db=db)
chanid = job.chanid
starttime = job.starttime
logging.debug('chanid={} starttime={}'.format(chanid, starttime))
if not jobid:
chanid = chanid
starttime = starttime
logging.debug('chanid={} starttime={}'.format(chanid, starttime))
# Get database recording entry
rec = find_rec(chanid, starttime)
logging.debug('DB recording entry={}'.format(rec))
# Find and format full input file path
sg = findfile('/{}'.format(rec.basename), rec.storagegroup, db=db)
infile = os.path.join(sg.dirname, rec.basename)
# Assign and create temporary directory in recording directory
tmpdir = '{}{}{}/'.format(sg.dirname, 'crtmp.',
rec.basename.split('.')[0]
)
logging.debug('tmpdir is: {}'.format(tmpdir))
logging.info('Creating temporary directory')
tmp_chk(tmpdir)
# Get info from input file
avinfo = AVInfo(ffprobe, infile)
# Get video frame-rate
rfr = list(int(x) for x in avinfo.video.r_frame_rate.split('/'))
frame_rate = rfr[0] / float(rfr[1])
logging.debug('Video frame-rate is: {}'.format(frame_rate))
if save_old is True and outfile is None:
if jobid:
job.update({'status': job.RUNNING,
'comment': 'Backing up original file'
}
)
logging.info('Backing up original file: {}'.format(infile))
shutil.copyfile(infile, '{}.old'.format(infile))
logging.info('Finished backing up original file: {}'.format(infile))
# Configure output file
if outfile is not None:
out_file = outfile
elif outfile is None:
out_file = infile
if rec.cutlist == 1 or use_commflag is True:
# Flag commercials
if use_commflag is True and rec.commflagged != 1 and rec.cutlist != 1:
# need check for running commflag job?
try:
logging.info('Flagging commercials')
if jobid:
job.update({'status': job.RUNNING,
'comment': 'Flagging commercials'
}
)
startts = datetime.mythformat(rec.starttime)
rst = subprocess.Popen(['mythcommflag', '--chanid',
str(chanid), '--starttime',
str(startts)],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
output2 = rst.communicate()[0]
if rst.wait() == 0:
logging.info('Commercials flagged for:{}_{}'.format(chanid, starttime))
if rst.wait() != 0:
logging.error('MYTHcommflag ERROR: Flagging '
'commercials for:{}_{}'
.format(chanid, starttime)
)
logging.error('MythCommflag always exits with '
'decoding error?!'
)
# uncomment the following line to log mythcommflag errors
# logging.error('{}'.format(output2))
except Exception as e:
logging.error('Mythcommflag ERROR: Flagging commercials {}'.format(e))
# Reinitialize recording object
rec = find_rec(chanid, starttime)
if jobid:
job.update({'status': job.RUNNING,
'comment': 'Getting Cut-list'
}
)
logging.info('getting cut-list')
# Get cut/skip list from database
if rec.cutlist == 1:
myth_cut_list = rec.markup.getcutlist()
if rec.cutlist != 1 and rec.commflagged == 1 and use_commflag:
myth_cut_list = rec.markup.getskiplist()
elif rec.cutlist == 0 and rec.commflagged != 1 and not use_commflag:
logging.debug('No cut/skip list found')
sys.exit(1)
logging.debug('Myth cut_list: {}'.format(myth_cut_list))
# Format cut-list, removing starting 0 and ending 9999999
cut_list = [mark for cuts in myth_cut_list for mark in cuts]
if cut_list[0] == 0:
cut_list.pop(0)
if cut_list[-1] == 9999999:
cut_list.pop(-1)
logging.debug('Cut list: {}'.format(cut_list))
# Get the name of the video codec
video_codec = avinfo.video.codec_name
logging.info('video codec: {}'.format(video_codec))
# Set cut-list string
if video_codec == 'h264':
sys.exit(1)
elif video_codec == 'mpeg2video':
cut_string = ','.join(str(i) for i in cut_list)
logging.debug('cut_string: {}'.format(cut_string))
# Create segment files
logging.info('Cutting')
if jobid:
job.update({'status': job.RUNNING,
'comment': 'Cutting started'
}
)
cut(ffmpeg, infile, cut_string, tmpdir)
# Create list of files to join
file_list = []
# Get list of segment files
for root, dirs, files in os.walk(tmpdir):
for File in files:
if (File.endswith('.ts') and
File.startswith('cut')):
if os.path.isfile(os.path.join(root, File)):
file_list.append(os.path.join(root, File))
# Set list of files to be joined
if myth_cut_list[0][0] == 0:
join_list = file_list[1::2]
if myth_cut_list[0][0] != 0:
join_list = file_list[0::2]
logging.debug('Join file list: {}'.format(join_list))
# Join segment files
logging.info('Joining segments')
if jobid:
job.update({'status': job.RUNNING,
'comment': 'Joining segments'
}
)
join(ffmpeg, join_list, out_file)
# Remove temporary directory
logging.info('removing temporary directory')
rem_tmp(tmpdir)
if outfile is None:
logging.info('clearing database markup')
if jobid:
job.update({'status': job.RUNNING,
'comment': 'Clearing database markup'
}
)
clear_markup(rec, infile, outfile)
if jobid:
job.update({'status': job.FINISHED, 'comment': 'Commercial removal finished'})
logging.info('Finished')
class DictToNamespace(object):
""" convert a dictionary and any nested dictionaries to namespace"""
def __setitem__(self, key, item):
self.__dict__[key] = item
def __getitem__(self, key):
return self.__dict__[key]
def update(self, *args, **kwargs):
return self.__dict__.update(*args, **kwargs)
def keys(self):
return self.__dict__.keys()
def values(self):
return self.__dict__.values()
def items(self):
return self.__dict__.items()
def __contains__(self, item):
return item in self.__dict__
def __iter__(self):
return iter(self.__dict__)
def __init__(self, data):
for item, num in data.items():
if isinstance(num, str):
try:
num = float(num)
if float(num).is_integer():
num = int(num)
data.update({item: num})
except ValueError:
pass
self.__dict__.update(data)
for k, v in data.items():
if isinstance(v, dict):
self.__dict__[k] = DictToNamespace(v)
class AVInfo:
"""identify A/V configuration of input file and returns
self.video dict a list of self.audio.stream dicts,
and self.duration as float"""
def __init__(self=None, ffprobe=None, infile=None):
class Dict(dict):
def __getattr__(self, name):
return self[name]
def __setattr__(self, name, value):
self[name] = value
command = [ffprobe, '-v', '-8', '-show_entries',
'stream=codec_type,index,codec_name,channels,width,'
'height,r_frame_rate:stream_tags=language:'
'format=duration', '-of', 'csv=nk=0:p=0', infile
]
x = subprocess.check_output(command).decode('utf-8').split('\n')
vcd = {}
adict = {}
for vc in x:
if 'codec_type=video' in vc:
for item in vc.split(','):
k, v = item.split('=')
vcd.update({k: v})
for ac in x:
if 'codec_type=audio' in ac:
items = [item.split('=') for item in ac.split(',')]
streamdict = {k.strip(): v.strip() for (k, v) in items}
if 'tag:language' in streamdict.keys():
streamdict['language'] = (streamdict.pop
('tag:language')
)
adict.update({'stream{}'.format(streamdict['index'])
: streamdict})
for d in x:
if d.startswith('duration='):
dur = d.split('=')[1]
self.video = Dict(vcd)
self.audio = DictToNamespace(adict)
self.duration = dur
def encode(command, avinfo):
""" Run ffmpeg command with status output"""
# Length of progress bar
statlen = 10
# Character used for progress bar
# Use chr() in python 3
statchar = unichr(9619).encode('UTF-8')
# Character used to pad progress bar
pad = ' '
rfr = list(int(x) for x in avinfo.video.r_frame_rate.split('/'))
frame_rate = float(rfr[0]) / float(rfr[1])
duration = float(avinfo.duration)
total_frames = duration * frame_rate
with tempfile.TemporaryFile() as output:
process = subprocess.Popen(command, stdout=output,
stderr=output,
universal_newlines=True
)
while True:
if process.poll() is not None:
if process.poll() != 0:
output.seek(0)
print(output.read().decode('UTF-8'))
sys.exit(1)
if process.poll() == 0:
print('\rFinished{}'.format(pad * (statlen + 3)))
break
where = output.tell()
lines = output.read().decode('UTF-8')
if not lines:
time.sleep(0.1)
output.seek(where)
elif lines.startswith('frame='):
ln = ' '.join(lines.split()).replace('= ', '=').split(' ')
for item in ln:
if item.startswith('frame='):
framenum = int(item.replace('frame=', ''))
if item.startswith('fps='):
fps = float(item.replace('fps=', ''))
if int(framenum) == 0:
pcomp = 0
else:
# python 2 div
pcomp = 100 * (float(framenum) / float(total_frames))
# python 3 div
# pcomp = 100 * (framenum / total_frames)
# python 2 div
stat = int((float(pcomp) / float(100)) * statlen)
# python 3 div
# stat = int((int(pcomp) / 100) * statlen)
padlen = statlen - stat
status = "|{:6.2f}%|".format(pcomp)
statusbar = '|{}{}|'.format(statchar * stat, pad * padlen)
status = '\r{}{}'.format(status, statusbar)
print(status, end="")
# Replace with flush=True in print function for python 3
sys.stdout.flush()
def cut(ffmpeg, infile, cutlist, tmpdir):
"""use ffmpeg segment muxer to cut infile into multiple files
using a csv formated cut-list placing them into tmpdir"""
cmd = [ffmpeg, '-ignore_unknown', '-i', infile, '-y', '-copyts',
'-start_at_zero', '-c', 'copy', '-map', '0'
]
av_check = AVInfo(ffprobe, infile)
for streams, stream in av_check.audio.items():
if stream.channels == '0':
cmd.extend(('-map',
('-0:{}'
.format(stream.index))))
if av_check.video.codec_name == 'mpeg2video':
cmd.extend(('-f', 'segment',
'-segment_frames', cutlist,
'{}cut%03d.ts'.format(tmpdir)
)
)
if av_check.video.codec_name == 'h264':
cmd.extend(('-f', 'segment',
'-segment_frames', cutlist,
'{}cut%03d.ts'.format(tmpdir)
)
)
encode(cmd, av_check)
def join(ffmpeg, file_list, out_file):
# Create FFmpeg concat string
concat_string = ','.join(file_list).replace(',', '|')
duration_list = []
rfr_list = []
video_codec_list = []
for files in file_list:
file_avinfo = AVInfo(ffprobe, files)
# get file duration
duration_list.append(float(file_avinfo.duration.strip('\r')))
# Get file reference frame rate
rfr_list.append(file_avinfo.video.r_frame_rate.strip('\r'))
# Get file video codec
video_codec_list.append(file_avinfo.video.codec_name.strip('\r'))
# Duration of joined files
duration = sum(duration_list)
# Check that all files reference frame-rate are equal
rfr_match = all(rfr_list[0] == item for item in rfr_list)
if len(rfr_list) > 0 and rfr_match:
# Convert reference frame rate to float
rfr = rfr_list[0].split('/')
frame_rate = int(rfr[0]) * float(rfr[1])
elif len(rfr_list) == 0 or not rfr_match:
raise ValueError('Incorrect or missing reference frame rate')
# Estimate total number of frames
# total_frames = duration * frame_rate
# check if all files video codec match
codec_match = all(video_codec_list[0] == item
for item in video_codec_list
)
if codec_match:
pass
# video_codec = video_codec_list[0]
if not codec_match:
raise ValueError('Not all video codecs match')
command = [ffmpeg, '-y', '-i', 'concat:{}'.format(concat_string),
'-map', '0', '-c', 'copy', '-f', 'mpegts', out_file
]
class AVJoin:
""" status update Replacement object for AVInfo provides
self.duration and self.video.r_frame_rate for encode()"""
def __init__(self, duration, rfr):
class Dict(dict):
def __getattr__(self, name):
return self[name]
def __setattr__(self, name, value):
self[name] = value
self.duration = duration
self.video = Dict({'r_frame_rate': rfr})
join_info = AVJoin(duration, rfr_list[0])
encode(command, join_info)
def clear_markup(rec, infile, outfile):
logging.info('Started Clearing markup')
logging.debug('rec={} infile={} outfile={}'.format(rec, infile, outfile))
chanid = rec.chanid
utcstarttime = rec.starttime
starttime = str(utcstarttime.utcisoformat().replace(u':', '').replace(u' ', '').replace(u'T', '').replace('-', ''))
logging.debug('chanid={} starttime={}'.format(chanid, starttime))
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('Cut-list removed for:{}_{}'.format(chanid, starttime))
if rcl.wait() != 0:
logging.error('MYTHUTIL ERROR: clearing cut-list for:{}_{}'.format(chanid, starttime))
logging.error('{}'.format(output1))
except Exception as e:
logging.error('Mythutil exception clearing cut-list: {}'.format(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('Skip-list removed for:{}_{}'.format(chanid, starttime))
if rsl.wait() != 0:
logging.error('MYTHUTIL ERROR: clearing skip-list for:{}_{}'.format(chanid, starttime))
logging.error('{}'.format(output2))
except Exception as e:
logging.error('Mythutil exception clearing skip-list:{}'.format(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.bookmarkupdate = datetime.now()
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('{}*.png'.format(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('{}*.jpg'.format(infile)):
os.remove(jpg)
except Exception as e:
logging.error('Error removing jpg files', e)
try:
logging.info('Rebuilding seek-table')
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('Seek-table Rebuilt for:{}_{}'.format(chanid, starttime))
if rst.wait() != 0:
logging.error('MYTHcommflag ERROR: Rebuilding Seek-table for:{}_{}'.format(chanid, starttime))
logging.error('{}'.format(output2))
except Exception as e:
logging.error('Mythcommflag ERROR clearing skip-list:{}'.format(e))
def tmp_chk(tmpdir):
try:
if os.path.isdir(tmpdir):
logging.debug('chk:temp Folder found: {}'.format(tmpdir))
if os.listdir(tmpdir) != 0:
logging.warning('chk:Temp folder not empty!:Removing Files: {}'.format(tmpdir))
shutil.rmtree(tmpdir)
os.makedirs(tmpdir)
if not os.path.isdir(tmpdir):
logging.debug('chk:no temp folder found: {}'.format(tmpdir))
os.makedirs(tmpdir)
logging.debug('chk:Temp folder created: {}'.format(tmpdir))
except Exception as e:
logging.error('{}'.format(e))
def rem_tmp(tmpdir):
try:
if os.path.isdir(tmpdir):
logging.debug('rem:temp Folder found {}'.format(tmpdir))
shutil.rmtree(tmpdir)
if not os.path.isdir(tmpdir):
logging.debug('rem:temp Folder Removed {}'.format(tmpdir))
except Exception as e:
logging.error('{}'.format(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 recorded_from_basename(chanid, starttime):
bnts = '{}_{}.ts'.format(chanid, starttime)
bnmpg = '{}_{}.mpg'.format(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 {} StartTime {}'
.format(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 = recorded_from_basename(chanid, starttime)
return rec
def main():
parser = argparse.ArgumentParser(
description='MythTV Commercial removal tool.')
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 required')
main()
Re: Cutting commercials without transcoding
You can use this script and set it to do lossless cuts viewtopic.php?f=2&t=1261&p=12278#p12278