RerecordLater.py

From MythTV Official Wiki
Jump to: navigation, search

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.


Script.png RerecordLater.py

#!/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)