RerecordLater.py
Author | Dave Fletcher |
Description | Add a delay to "delete and rerecord" so that programs repeating often can be ignored for a specified period of time and re-recorded later. |
Supports |
RerecordLater.py adds a delay to "delete and rerecord" feature so that programs being played too often can be ignored for a specified period of time and re-recorded later.
By Dave Fletcher (http://fletchtronics.net)
Rationale
Normally, the "delete and re-record" button in MythTV means something like "I liked this show, I would watch this again". However, the networks are obnoxious about repeating new shows. The way around this in Myth by default is to just say "delete" and then go back and adjust the setting later to allow re-recording. This means work!
So this script automates the process. When you pick "delete and re-record" with this script installed and configured, it will mark the entry as "deleted" as far as MythTV core is concerned. After the timeout period it will automatically switch the setting back to "allow re-record".
Setup
First of all, save the script from this wiki page into a text file. I recommend installing the file to /usr/local/bin/RerecordLater.py but you can put it wherever you want. If you choose a different location, adjust the commands below to suit.
Give the script executable permission:
# sudo chmod +x /usr/local/bin/RerecordLater.py
The script is connected to MythTV through the event system. On either your frontend or backend system (doesn't matter which as long as the script file is available and executable) navigate to the System Event page. You will see a list of system events and a box where you can put a command when the event is triggered.
In the box for "Recording deleted", put:
/usr/local/bin/RerecordLater.py "%CHANID%" "%STARTTIME%"
In the box for "mythfilldatabase ran", put:
/usr/local/bin/RerecordLater.py --reschedule
Alternatively, you can put the --reschedule command on cron instead of the mythfilldatabase event if desired.
And that's it! Programs where you specify "delete and rerecord" will not show up again for at least 60 days.
Advanced Setup
If you want to specify some number of days other than 60, adjust the "Recording deleted" command and add a --timeout argument. For example, to make the delay 30 days, use the following.
/usr/local/bin/RerecordLater.py --timeout=30 "%CHANID%" "%STARTTIME%"
When you first install RerecordLater.py you might have programs in your database where you have already specified "delete and record later". If you wish to bulk-apply RerecordLater to all recordings that were previously marked this way and RerecordLater doesn't know about them, use the following command from your command line (not from a system event!)
# /usr/local/bin/RerecordLater.py --legacy
Note: the database connection arguments below are only used when the script is not running on a MythTV box. If you don't specify host/database/user/password, these details will be read from ~/.mythtv/config.xml
Detailed Usage
Usage: RerecordLater.py [options] [(chanid starttime) (chanid starttime) ...] Options: -h, --help show this help message and exit -a, --legacy Add programs that are marked re-recordable and are not already handled by this script. Use this to seed the database at installation time. -r, --reschedule Mark programs where the timeout has expired as OK to re-record. Run daily. Recommended: put --reschedule on the "mythfilldatabase ran" system event. -i, --install Initialize the database. If it already exists, does nothing. Happens automatically if the program is run without an existing database. -g, --database-upgrade Upgrade the database to the current schema version (NOTE: currently not implemented). -t TIMEOUT, --timeout=TIMEOUT Number of days before marked programs will time out. -o HOST, --host=HOST Database host. -d NAME, --database=NAME Database name. -u USER, --user=USER Database user. -p PASSWORD, --password=PASSWORD Database password. -l LEVEL, --log-level=LEVEL Set the log level.
#!/usr/bin/env python # RerecordLater.py # By Dave Fletcher # http://fletchtronics.net # Normally, the "delete and re-record" button in MythTV means something like "I # liked this show, I would watch this again". However, the networks are # obnoxious about repeating new shows. The way around this in Myth by default # is to just say "delete" and then go back and adjust the setting later to # allow re-recording. This means work! # So this script automates the process. When you pick "delete and re-record" # with this script installed and configured, it will mark the entry as "deleted" # as far as MythTV core is concerned. After the timeout period it will # automatically switch the setting back to "allow re-record". # NOTE: this script requires MythTV 0.23+fixes r24420 or newer. # For older versions, you can try applying this patch to the python bindings: # http://svn.mythtv.org/trac/changeset/24420 # schema version that this code expects schema_version = 1000 # default number of timeout days timeout = 60 # if any of these are not None, the given value(s) will be used to create a # database connection dbhost = None dbname = None dbuser = None dbpass = None # includes from sys import exit from optparse import OptionParser from MythTV import OldRecorded from MythTV import MythDB, MythLog # utility funciton: connect and return a MythDB object def __db_connect(schemacheck=True): # connect args = [] if dbhost: args.append( ('DBHostName', dbhost) ) if dbname: args.append( ('DBName', dbname) ) if dbuser: args.append( ('DBUserName', dbuser) ) if dbpass: args.append( ('DBPassword', dbpass) ) db = MythDB(args=args) log = MythLog(module='RerecordLater', lstr='important') # schema version check if schemacheck: if db.settings.NULL.RerecordLaterDBSchemaVer == None: # first time run, auto install log( MythLog.IMPORTANT, 'RerecordLater database does not exist, creating it.' ) action_install(db) else: dbversion = int(db.settings.NULL.RerecordLaterDBSchemaVer) # db schema too new ? if dbversion > schema_version: log( MythLog.IMPORTANT, 'The schema version in the database (%d) is newer than the RerecordLater program (%d). Please update to the appropriate version of RerecordLater.' % ( dbversion, schema_version ) ) exit(3) # db schema too old? elif dbversion < schema_version: log( MythLog.IMPORTANT, 'The schema version in the database (%d) is older than the RerecordLater program (%d). Updating it.' % ( dbversion, schema_version ) ) action_database_upgrade(db) return db # --rerecord handler def action_rerecord(chanid, starttime, db=None): if not db: db = __db_connect() log = MythLog(module='RerecordLater', lstr='general') # search for old recordings with chanid/starttime q = db.searchOldRecorded(chanid=chanid, starttime=starttime) if q: for oldrec in q: if not oldrec.duplicate: c = db.cursor() # See if we have done this before. c.execute(""" SELECT chanid FROM rerecordlater WHERE chanid = %d AND starttime = "%s" """ % ( chanid, starttime ) ) # This recording already exists in rerecordlater table. Ignore. if c.rowcount: continue # This recording was set to re-record. This is where we take action. # It is told *not* to rerecord now and will be flipped back on after # the timeout period. c.execute(""" INSERT INTO rerecordlater SET chanid = %d, starttime = "%s", timeout = NOW() + INTERVAL %d DAY """ % ( chanid, starttime, timeout ) ) oldrec.setDuplicate(True) log( MythLog.GENERAL, 'RerecordLater added rule chanid: %d, starttime: %s' % ( chanid, starttime ) ) # --reschedule handler def action_reschedule(): # connect db = __db_connect() c = db.cursor() log = MythLog(module='RerecordLater', lstr='general') # get rerecordlater records where the timeout has expired c.execute(""" SELECT chanid, starttime FROM rerecordlater WHERE timeout <= NOW() """) for rec in c.fetchall(): # find the oldrecorded record and re-allow dupes. q = db.searchOldRecorded(chanid=rec[0], starttime=rec[1]) for oldrec in q: oldrec.setDuplicate(False) c.execute(""" DELETE FROM rerecordlater WHERE chanid = %d AND starttime = "%s" """ % ( rec[0], rec[1] ) ) log( MythLog.GENERAL, 'Reset recording tule to allow re-recording for chanid: %d, starttime: %s' % ( rec[0], rec[1] ) ) # --legacy handler def action_legacy(): db = __db_connect(schemacheck=False) # Find recordings that were set to duplicate q = db.searchOldRecorded(duplicate=0) for oldrec in q: action_rerecord(oldrec.chanid, oldrec.starttime, db) # --install handler def action_install(db=None): if not db: db = __db_connect(schemacheck=False) db.cursor().execute(""" CREATE TABLE IF NOT EXISTS rerecordlater( chanid INT(10) UNSIGNED, starttime datetime, timeout datetime ) """) db.settings.NULL.RerecordLaterDBSchemaVer = schema_version # --database-upgrade handler def action_database_upgrade(db=None): if not db: db = __db_connect() # TODO: update system when needed. The idea is to iterate # from db_schema_version+1 through the current version and # execute a function called database_upgrade_[n] where [n] # is a particular schema version. Each of these functions # should have the ability to update the database from the # previous version, so executing them in a row will upgrade # from whatever the current db version is to current schema. # main if __name__ == '__main__': # optparse setup parser = OptionParser() parser.add_option( '-a', '--legacy', dest='legacy', action='store_true', default=False, help='Add programs that are marked re-recordable and are not already handled by this script. Use this to seed the database at installation time.' ) parser.add_option( '-r', '--reschedule', dest='reschedule', action='store_true', default=False, help='Mark programs where the timeout has expired as OK to re-record. Run daily. Recommended: put --reschedule on the "mythfilldatabase ran" system event.' ) parser.add_option( '-i', '--install', dest='install', action='store_true', default=False, help='Initialize the database. If it already exists, does nothing. Happens automatically if the program is run without an existing database.' ) parser.add_option( '-g', '--database-upgrade', dest='upgrade', action='store_true', default=False, help='Upgrade the database to the current schema version (NOTE: currently not implemented).' ) parser.add_option( '-t', '--timeout', dest='timeout', type='int', metavar='TIMEOUT', help='Number of days before marked programs will time out.' ) parser.add_option( '-o', '--host', dest='host', metavar='HOST', help='Database host.' ) parser.add_option( '-d', '--database', dest='database', metavar='NAME', help='Database name.' ) parser.add_option( '-u', '--user', dest='user', metavar='USER', help='Database user.' ) parser.add_option( '-p', '--password', dest='password', metavar='PASSWORD', help='Database password.' ) parser.add_option( '-l', '--log-level', dest='loglevel', metavar='LEVEL', help='Set the log level.' ) # parse (options, args) = parser.parse_args() # check positional arguments if len(args) % 2 != 0: print( 'There must be an even number of positional arguments. Specify one or more sets of {chanid starttime} after other options are specified.' ) exit(1) # run configuration if options.timeout: timeout = options.timeout if options.host: dbhost = options.host if options.database: dbname = options.database if options.user: dbuser = options.user if options.password: dbpass = options.password if options.loglevel: MythLog._setlevel(options.loglevel) # actions if options.install: action_install() if options.upgrade: action_database_upgrade() if options.legacy: action_legacy() if options.reschedule: action_reschedule() # default action: rerecord for i in range(0, len(args), 2): chanid = int(args[i]) starttime = args[i+1] action_rerecord(chanid, starttime)