Cutting commercials without transcoding

For discussion related to MythTV which doesn't belong in another forum.

Moderator: Forum Moderators

Post Reply
flabbergast
Newcomer
Posts: 11
Joined: Sun May 31, 2015 4:26 pm
United States of America

Cutting commercials without transcoding

Post by flabbergast »

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?
User avatar
bill6502
Developer
Posts: 2325
Joined: Fri Feb 07, 2014 5:28 pm
United States of America

Re: Cutting commercials without transcoding

Post by bill6502 »

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.
flabbergast
Newcomer
Posts: 11
Joined: Sun May 31, 2015 4:26 pm
United States of America

Re: Cutting commercials without transcoding

Post by flabbergast »

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.
halucigenia
Senior
Posts: 122
Joined: Tue Nov 11, 2014 11:03 am
Great Britain

Re: Cutting commercials without transcoding

Post by halucigenia »

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.
flabbergast
Newcomer
Posts: 11
Joined: Sun May 31, 2015 4:26 pm
United States of America

Re: Cutting commercials without transcoding

Post by flabbergast »

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:

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
Then made a link to my Videos folder.
mattlach
Senior
Posts: 125
Joined: Wed Jun 11, 2014 2:52 am
United States of America

Re: Cutting commercials without transcoding

Post by mattlach »

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...
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.
daraden
Senior
Posts: 175
Joined: Tue Feb 23, 2016 7:33 am
United States of America

Re: Cutting commercials without transcoding

Post by daraden »

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()
Skinah2
Newcomer
Posts: 7
Joined: Fri Jan 20, 2017 3:57 am
Switzerland

Re: Cutting commercials without transcoding

Post by Skinah2 »

You can use this script and set it to do lossless cuts viewtopic.php?f=2&t=1261&p=12278#p12278
Post Reply