#!/usr/bin/env python
# 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 = 30
# 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 argv, exit
from MythTV import OldRecorded
from MythTV import MythDB, MythLog
# print optional message, usage info, and then exit with the given code or not
def __usage(exitmsg='', exitcode=0, shouldexit=True):
if len(exitmsg): print exitmsg
print 'Usage: %s [options]' % argv[0]
if shouldexit: exit(exitcode)
# utility funciton: sotr list of commands by priority
def __commandprioritycmp(a, b):
return cmp(a[1], b[1])
# 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)
# correct db schema version?
else:
pass
return db
# --help handler
def action_help():
__usage('', 0, False)
# TODO: iterate ARGT and print a nice list
exit(0)
# --rerecord handler
def action_rerecord(chanid, starttime):
db = __db_connect()
# normally comes from argv as a string, make it an int
chanid = int(chanid)
# search for old recordings with chanid/starttime
q = db.searchOldRecorded(chanid=chanid, starttime=starttime)
if q:
for oldrec in q:
if not oldrec.duplicate:
# 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.
db.cursor().execute("""
INSERT INTO rerecordlater
SET
chanid = %d,
starttime = "%s",
timeout = NOW() + INTERVAL %d DAY
""" % ( chanid, starttime, timeout ) )
oldrec.setDuplicate(True)
# --reschedule handler
def action_reschedule():
# connect
db = __db_connect()
c = db.cursor()
# 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] ) )
# --legacy handler
def action_legacy():
# Find recordings that were set to duplicate
q = db.searchOldRecorded(duplicate=0)
for oldrec in q: action_rerecord(oldrec.chanid, oldrec.starttime)
# --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.
# argument handler functions
def arg_timeout(x):
global timeout
timeout = int(x)
def arg_dbhost(x):
global dbhost
dbhost = x
def arg_dbname(x):
global dbname
dbname = x
def arg_dbuser(x):
global dbuser
dbuser = x
def arg_dbpass(x):
global dbpass
dbpass = x
# argt entries are (action, priority, param count, param names, description)
ARGT = {
'legacy': (
action_legacy, 10, 0, '',
'Add programs that are marked re-recordable and are not already handled by this script. Use this to seed the database at installation time.'
),
'rerecord': (
action_rerecord, 20, 2, '(CHANID) (STARTTIME)',
'Specify a program that should be re-recorded.'
),
'reschedule': (
action_reschedule, 30, 0, '',
'Mark programs where the timeout has expired as OK to re-record. Run daily. Recommended: put --update on the "mythfilldatabase ran" system event.'
),
'install': (
action_install, -20, 0, '',
'Initialize the database. If it already exists, does nothing. Happens automatically if the program is run without an existing database.'
),
'database-upgrade': (
action_database_upgrade, -10, 0, '',
'Initialize the database. If it already exists, does nothing. Happens automatically if the program is run without an existing database.'
),
'timeout': (
arg_timeout, 0, 1, '(DAYS)',
'Number of days before marked programs will time out. Not used during --reschedule.'
),
'host': (
arg_dbhost, 0, 1, '(HOST)',
'Database host. NOTE: if no database information is given, UPnP'
),
'database': (
arg_dbname, 0, 1, '(NAME)',
'Database name.'
),
'user': (
arg_dbuser, 0, 1, '(USER)',
'Database user name.'
),
'password': (
arg_dbpass, 0, 1, '(PASS)',
'Database password.'
),
'help': (
action_help, -5, 0, '',
'Display help.'
),
}
if __name__ == '__main__':
#MythLog._setlevel('database')
argc = len(argv)
if argc < 2: __usage()
# process arguments
# first, put them into groups with --optionname and user values
i = 1
command = []
commands = []
while i < argc:
# if argv[i] starts with '--', append and clear
if argv[i][0:2] == '--':
if len(command): commands.append(command)
command = []
# if this is the first item in the command, resolve an ARGT entry
if not len(command):
# look up the ARGT by name
argname = argv[i][2:]
try: argt = ARGT[argname]
except KeyError:
__usage(
'Unknown argument "%s". Try %s --help for detailed __usage instructions.' %
(argname, argv[0]), 1
)
# append function, paramc, priority to the command
command.append(argt[0])
command.append(argt[1])
command.append(argt[2])
else:
# this isn't the first item in the command, it's a user param, append
command.append(argv[i])
i += 1
if len(command): commands.append(command)
# sort by priority
commands.sort(__commandprioritycmp)
# now handle each argument
for command in commands:
# print repr(command) # use to check the order and view command arrays
# check the parameter count for this argument type against received argc
paramc = len(command)-3
if paramc != command[2]:
__usage(
'The "%s" argument takes %d parameter values, %d given.' %
(argname, command[2], paramc), 2
)
# evaluate
args = command[3:]
command[0](*args)