[mythtv-users] Help needed for scripted recordings

David Engel david at istwok.net
Sat Mar 28 01:41:06 UTC 2020


On Fri, Mar 27, 2020 at 06:58:16PM -0500, Bill Meek wrote:
> On 3/27/20 12:13 PM, Klaas de Waal wrote:
> > Hi all,
> > 
> > I am attempting to fix bug #13571 and to test this I need to start and then
> > delete recordings lots and lots of times. It would be great if I can start
> > a script that starts a recording on a specified channel, wait 10 seconds,
> > delete the recording, wait 10 seconds, start a recording etc etc.
> > And I then want to run another instance of the script on another channel,
> > etc.
> > Has anyone a script available that does something like this, or a basic
> > example on how to build something?
> 
> If the 10 second length, is a requirement, no need to read further. Otherwise,
> I might be able change the following to delete recordings automatically...

Klaas can correct me if I'm wrong but I don't think 10 seconds is a
hard and fast requirement.  The problem at hand seems to occur when
deleting an in-progress recording but only occasionally.  I think the
10 seconds is solely to recreate the in-progress, deletion condition
as frequently as possible in hopes of reproducing the problem more
quickly.

David

> ===============================================================================
> I've got an old python tool that *may* be a start (because it only schedules
> lots of recordings.) It could be modified to remove them. I haven't been able
> to schedule recordings for less than 1 minute. There is a --remove switch, but
> it removes all rules just created.
> 
> $ vcr.py --chanid=1021 --length=1 --number=5 --wrmi --host=mc0 --dst
> 
> Creating 5 Single Record rules for channel 1021 (CBS2-HD.)
> Each rule lasts 1 minute, total minutes: 5.
> 
> StartTime: 2020-03-27T23:36:00  EndTime: 2020-03-27T23:37:00  Result: RecordId=3596.
> StartTime: 2020-03-27T23:37:00  EndTime: 2020-03-27T23:38:00  Result: RecordId=3597.
> StartTime: 2020-03-27T23:38:00  EndTime: 2020-03-27T23:39:00  Result: RecordId=3598.
> StartTime: 2020-03-27T23:39:00  EndTime: 2020-03-27T23:40:00  Result: RecordId=3599.
> StartTime: 2020-03-27T23:40:00  EndTime: 2020-03-27T23:41:00  Result: RecordId=3600.
> 
> $ vcr.py -h
> usage: vcr.py [-h] --chanid <chanid> [--autoexpire] [--debug] [--dst]
>               [--digest <user:pass>] [--host <host>] [--length <1->60>]
>               [--number <1->24>] [--priority <-99->99>] [--port <#>]
>               [--remove] [--starttime <yyyy-mm-ddThh:mm>] [--wrmi] [--version]
> 
> Create Bulk Recording Rules
> 
> optional arguments:
>   -h, --help            show this help message and exit
>   --autoexpire          turn on the Auto Expire option (False)
>   --debug               turn on debug messages (False)
>   --dst                 turn on for Daylight Savings Time(False)
>   --digest <user:pass>  digest username:password
>   --host <host>         backend hostname or IP address (localhost)
>   --length <1->60>      length (in minutes) of each rule (30)
>   --number <1->24>      number of rules to create (1)
>   --priority <-99->99>  rule priority (-5)
>   --port <#>            backend Services API port (6544)
>   --remove              remove all API rules for a chanid (False)
>   --starttime <yyyy-mm-ddThh:mm>
>                         1st rule's local start time (2020-03-27T18:30)
>   --wrmi                actually send data to the backend (False)
>   --version             show program's version number and exit
> 
> required argument:
>   --chanid <chanid>     MythTV chanid, e.g. 1091
> 
> Defaults are in ()s
> 
> -- 
> Bill

> #!/usr/bin/python3
> # -*- coding: utf-8 -*-
> 
> '''
> Create one or more manual recording rules, like a VCR
> 
> Type: vcr.py --help
> 
> Can create up to 24 manual rules that will record programs on one channel.
> Doesn't need to be a maximum of 24, just picked a number.
> 
> This author uses Schedules Direct, and doesn't know if that's important
> for manual recordings.
> 
> For every recording rule added, the Serivces API will return:
> 
>    {'uint': 'someRecordid'}
> 
> A failure will return an XML error message, or just 4294967295
> (-1) in place of the recordid in the above JSON (if a recording
> rule already exists for example.) Run mythbackend with -v upnp
> (or use mythbackend --setverbose upnp) and look at the logs for
> additional information. May need to use: upnp:debug,http:debug.
> 
> Example: To see the StartTime/EndTimes for eight 5 minute recordings
>          on channel 1021 with a backend running on localhost:
> 
>          vcr.py --chanid=1021 --length=5 --number=8
> 
>          To actually send the data to the backend, add --wrmi
>          to the above.
> 
>          Remove all rules (created by this program) for one channel:
> 
>          vcr.py --chanid 1234 --remove --wrmi
> 
> Bill Meek 2014/2020
> '''
> 
> from __future__ import print_function
> from datetime import datetime, timedelta
> import argparse
> import logging
> from os import path as p
> import sys
> import time
> 
> try:
>     from MythTV.services_api import send as api
> except ImportError:
>     sys.exit("\nAbort, it appears that MythTV's Python bindings are missing.")
> 
> TITLE = 'Services API (Single Record)'
> 
> 
> def check_range(int_value, minimum, maximum, message):
>     ''' Make sure the integer is in the expected range, abort if not. '''
> 
>     if not isinstance(int_value, int):
>         sys.exit("\nAbort: {} must be an integer.".format(int_value))
> 
>     if int_value < minimum or int_value > maximum:
>         sys.exit("\nAbort: {} must be {}->{} not: {}"
>                  .format(message, minimum, maximum, int_value))
> 
> 
> def process_command_line():
>     ''' Do just that. '''
> 
>     now_string = datetime.strftime(datetime.now(), '%Y-%m-%dT%H:%M')
> 
>     parser = argparse.ArgumentParser(description='Create Bulk Recording Rules',
>                                      epilog='Defaults are in ()s')
> 
>     mandatory = parser.add_argument_group('requrired argument')
> 
>     mandatory.add_argument('--chanid', type=int, required=True,
>                            metavar='<chanid>',
>                            help='MythTV chanid, e.g. 1091')
> 
>     parser.add_argument('--autoexpire', action='store_true',
>                         help='turn on the Auto Expire option (%(default)s)')
> 
>     parser.add_argument('--debug', action='store_true',
>                         help='turn on debug messages (%(default)s)')
> 
>     parser.add_argument('--dst', action='store_true',
>                         help='turn on for Daylight Savings Time(%(default)s)')
> 
>     parser.add_argument('--digest', type=str, metavar='<user:pass>',
>                         help='digest username:password')
> 
>     parser.add_argument('--host', type=str, default='localhost',
>                         metavar='<host>',
>                         help='backend hostname or IP address (%(default)s)')
> 
>     parser.add_argument('--length', type=int, default=30, metavar='<1->60>',
>                         help='length (in minutes) of each rule (%(default)s)')
> 
>     parser.add_argument('--number', type=int, default=1, metavar='<1->24>',
>                         help='number of rules to create (%(default)s)')
> 
>     parser.add_argument('--priority', type=int, default=-5,
>                         metavar='<-99->99>',
>                         help='rule priority (%(default)s)')
> 
>     parser.add_argument('--port', type=int, default=6544, metavar='<#>',
>                         help='backend Services API port (%(default)s)')
> 
>     parser.add_argument('--remove', action='store_true',
>                         help='remove all API rules for a chanid (%(default)s)')
> 
>     parser.add_argument('--starttime', type=str, default=now_string,
>                         metavar='<yyyy-mm-ddThh:mm>',
>                         help='1st rule\'s local start time (%(default)s)')
> 
>     parser.add_argument('--wrmi', action='store_true',
>                         help='actually send data to the backend (%(default)s)')
> 
>     parser.add_argument('--version', action='version', version='%(prog)s 0.11')
> 
>     return parser.parse_args()
> 
> 
> def remove_api_created_rules(backend, args, opts):
>     ''' Search all rules and remove any created by this program for one
>         chanid. Not much error checking here. '''
> 
>     if not args.wrmi:
>         sys.exit('\nThe --wrmi switch is required to remove all API rules.')
> 
>     get_endpoint = 'Dvr/GetRecordScheduleList'
>     put_endpoint = 'Dvr/RemoveRecordSchedule'
> 
>     try:
>         resp_dict = backend.send(endpoint=get_endpoint,
>                                  rest='Sort=NextRecording', opts=opts)
>     except RuntimeError as error:
>         sys.exit('\n\nRuntme Error: {}'.format(error))
> 
>     rule_count = int(resp_dict['RecRuleList']['Count'])
> 
>     rule_dict = resp_dict['RecRuleList']['RecRules']
> 
>     for rule in range(rule_count):
>         this_rule = rule_dict[rule]
>         if this_rule['Title'] == TITLE and \
>                 int(this_rule['ChanId']) == args.chanid:
>             recordid = this_rule['Id']
>             postdata = {'RecordId': recordid}
>             backend.send(endpoint=put_endpoint, postdata=postdata, opts=opts)
>             print('Removed RecordId: {}'.format(recordid))
> 
>     sys.exit()
> 
> 
> def create_rules(backend, callsign, starttime, args, opts):
>     ''' Then create the requested rules and send to the backend if
>         args.wrmi was set. In any case, tell the user what would be
>         done. '''
> 
>     endtime = starttime + timedelta(minutes=(args.length * args.number))
> 
>     endpoint = 'Dvr/AddRecordSchedule'
> 
>     while starttime < endtime:
>         end_time = starttime + timedelta(minutes=args.length)
>         find_time = starttime - timedelta(seconds=time.timezone)
> 
>         postdata = {
>             'Title': TITLE,
>             'Description': 'Created by: {}'.format(p.basename(sys.argv[0])),
>             'StartTime': starttime.strftime('%Y-%m-%dT%H:%M:%S'),
>             'EndTime': end_time.strftime('%Y-%m-%dT%H:%M:%S'),
>             'ChanId': args.chanid,
>             'Station': callsign,
>             'FindDay': 0,
>             'FindTime': find_time.strftime('%H:%M:%S'),
>             'ParentId': 0,
>             'Type': 'Single Record',
>             'SearchType': 'Manual Search',
>             'AutoExpire': 'false' if not args.autoexpire else 'true',
>             'RecPriority': args.priority
>         }
> 
>         starttime += timedelta(minutes=args.length)
> 
>         print('StartTime: {}  EndTime: {}  '
>               .format(postdata['StartTime'], postdata['EndTime']),
>               end='Result: ' if args.wrmi else '\n')
> 
>         if not args.wrmi:
>             continue
> 
>         try:
>             resp_dict = backend.send(endpoint=endpoint, postdata=postdata,
>                                      opts=opts)
>         except RuntimeError as error:
>             sys.exit('\n\nRuntme Error: {}'.format(error))
> 
>         try:
>             resp_dict['uint']
>         except KeyError:
>             sys.exit('\n\nAborting, expected uint, got {}'.format(resp_dict))
> 
>         if resp_dict['uint'] == '4294967295':
>             print('Duplicate.')
>         else:
>             print('RecordId={}.'.format(resp_dict['uint']))
> 
> 
> def main():
>     ''' Do range checks, check/adjust --starttime, make sure the chanid
>         is valid, develop starttime and create the recording rule.
>         Optionally remove all rules for a specific channel. '''
> 
>     args = process_command_line()
> 
>     opts = {'debug': args.debug, 'noetag': False, 'nogzip': False,
>             'usexml': False, 'wrmi': args.wrmi, 'wsdl': False}
> 
>     logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO)
>     logging.getLogger('requests.packages.urllib3').setLevel(logging.WARNING)
>     logging.getLogger('urllib3.connectionpool').setLevel(logging.WARNING)
> 
>     backend = api.Send(host=args.host, port=args.port)
> 
>     if args.remove:
>         remove_api_created_rules(backend, args, opts)
> 
>     check_range(args.length, 1, 60, "--length")
>     check_range(args.number, 1, 24, "--number")
>     check_range(args.priority, -99, 99, "--priority")
> 
>     # Convert the local time from the command line/default to UTC
> 
>     try:
>         #TODO: this doesn't handle DST, remove args.dst ...
>         starttime = datetime.strptime(args.starttime, '%Y-%m-%dT%H:%M') \
>                   + timedelta(seconds=time.timezone - 3600 if args.dst else 0)
>     except ValueError as error:
>         sys.exit('Error={}. Try --help.'.format(error))
> 
>     utc_now = datetime.utcnow()
>     utc_now = utc_now.replace(second=0, microsecond=0)
>     starttime = starttime.replace(second=0, microsecond=0)
> 
>     if starttime < utc_now:
>         starttime = utc_now
>         print('\nThe --starttime is earlier than now, using: {}.'
>               .format(datetime.strftime(starttime -
>                                         timedelta(seconds=time.timezone),
>                                         '%Y-%m-%dT%H:%M')))
> 
>     try:
>         resp_dict = backend.send(endpoint='Channel/GetChannelInfo',
>                                  rest='ChanId={}'.format(args.chanid))
>     except RuntimeError:
>         sys.exit('\nUnable to connect to the backend, aborting.\n')
> 
>     if args.chanid != int(resp_dict['ChannelInfo']['ChanId']):
>         sys.exit('\nInvalid --chanid {}, aborting.\n'.format(args.chanid))
> 
>     callsign = resp_dict['ChannelInfo']['CallSign'].strip()
> 
>     print('\n{} {} Single Record rule{} for channel {} ({}.)'
>           .format('Creating' if args.wrmi else 'Would create',
>                   args.number, 's'[args.number == 1:], args.chanid, callsign))
> 
>     print('Each rule lasts {} minute{}, total minutes: {}.{}\n'
>           .format(args.length, 's'[args.length == 1:],
>                   args.length * args.number,
>                   '' if args.wrmi else ' Add --wrmi to send.'))
> 
>     create_rules(backend, callsign, starttime, args, opts)
> 
> 
> if __name__ == '__main__':
>     main()
> 
> # vim: set expandtab tabstop=4 shiftwidth=4 smartindent noai colorcolumn=80:

> _______________________________________________
> mythtv-users mailing list
> mythtv-users at mythtv.org
> http://lists.mythtv.org/mailman/listinfo/mythtv-users
> http://wiki.mythtv.org/Mailing_List_etiquette
> MythTV Forums: https://forum.mythtv.org


-- 
David Engel
david at istwok.net


More information about the mythtv-users mailing list