[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