#!/usr/local/bin/python
# -*- coding: UTF-8 -*-
#---------------------------
# Name: mythvidexport.py
# Python Script
# Author: Raymond Wagner
# Purpose
#   This python script is intended to function as a user job, run through
#   mythjobqueue, capable of exporting recordings into MythVideo.
#---------------------------
__title__  = "MythVidExport"
__author__ = "Raymond Wagner"
__version__= "v0.6.0"

usage_txt = """
This script can be run from the command line, or called through the mythtv
jobqueue.  The input format will be:
  mythvidexport.py [options] <--chanid <channel id>> <--starttime <start time>>
                 --- or ---
  mythvidexport.py [options] %JOBID%

Options are:
        --mformat <format string>
        --tformat <format string>
        --gformat <format string>
            overrides the stored format string for a single run
        --listingonly
            use EPG data rather than grabbers for metadata
            will still try to grab episode and season information from ttvdb.py

Additional functions are available beyond exporting video
  mythvidexport.py <options>
        -h, --help             show this help message
        -p, --printformat      print existing format strings
        -f, --helpformat       lengthy description for formatting strings
        --mformat <string>     replace existing Movie format
        --tformat <string>     replace existing TV format
        --gformat <string>     replace existing Generic format
"""

from MythTV import MythDB, Job, Video, VideoGrabber
from socket import gethostname
from urllib import urlopen
from optparse import OptionParser
import sys, re, os, time


#log = MythLog(NOTICE, 'MythVidExport.py')

class VIDEO:
    def __init__(self, opts, jobid=None):
        if jobid:
            self.job = Job(jobid)
            self.chanid = self.job.chanid
            self.starttime = int("%04d%02d%02d%02d%02d%02d" \
                        % self.job.starttime.timetuple()[0:6])
            self.job.update(status=3)
        else:
            self.job = None
            self.chanid = opts.chanid
            self.rtime = opts.starttime

        self.opts = opts
        self.db = MythDB()

        # load setting strings
        self.get_grabbers()
        self.get_format()

        # process file
        self.cast = ()
        self.genre = ()
        self.country = ()
        self.rec = self.db.getRecorded(chanid=self.chanid,\
                                    starttime=self.starttime)
        self.vid = Video()
        self.vid.host = gethostname()

        self.get_meta()
        self.get_dest()

        # save file
        self.copy()
        self.write_images()
        self.vid.create()
        self.write_cref()

    def get_grabbers(self):
        # TV Grabber
        self.TVgrab = VideoGrabber('TV')
        # if ttvdb.py, optionally add config file
        if self.TVgrab.path.split('/')[-1] == 'ttvdb.py':
            path = os.path.expanduser('~/.mythtv/ttvdb.conf')
            if os.access(path, os.F_OK):
                self.TVgrab.append(' -c '+path)

        # Movie Grabber
        self.Mgrab = VideoGrabber('Movie')

    def get_format(self):
        host = gethostname()
        # TV Format
        if self.opts.tformat:
            self.tfmt = self.opts.tformat
        elif self.db.settings[host]['mythvideo.TVexportfmt']:
            self.tfmt = self.db.settings[host]['mythvideo.TVexportfmt']
        else:
            self.tfmt = 'Television/%TITLE%/Season %SEASON%/'+\
                            '%TITLE% - S%SEASON%E%EPISODEPAD% - %SUBTITLE%'

        # Movie Format
        if self.opts.mformat:
            self.mfmt = self.opts.mformat
        elif self.db.settings[host]['mythvideo.MOVIEexportfmt']:
            self.mfmt = self.db.settings[host]['mythvideo.MOVIEexportfmt']
        else:
            self.mfmt = 'Movies/%TITLE%'

        # Generic Format
        if self.opts.gformat:
            self.gfmt = self.opts.gformat
        elif self.db.settings[host]['mythvideo.GENERICexportfmt']:
            self.gfmt = self.db.settings[host]['mythvideo.GENERICexportfmt']
        else:
            self.gfmt = 'Videos/%TITLE%'

    def get_meta(self):
        self.vid.hostname = gethostname()
        if self.rec.subtitle:  # subtitle exists, assume tv show
            self.get_tv()
        else:                   # assume movie
            self.get_movie()

    def get_tv(self):
        # grab season and episode number, run generic export if failed
        #inetref = self.TVgrab.searchTitle(self.rec.title)
        season, episode = self.TVgrab.searchEpisode(self.rec.title, \
                                                    self.rec.subtitle)
        if (season is None):# or (len(inetref) > 1):
            self.get_generic()
            return
        #self.vid.inetref = inetref[0][0]
        self.vid.season, self.vid.episode = (season, episode)

        if self.opts.listingonly:
            self.get_generic()
        else:
            dat, self.cast, self.genre, self.country = \
                    self.TVgrab.getData(self.rec.title,\
                                        self.vid.season,\
                                        self.vid.episode)
            self.vid.data.update(dat)
        self.type = 'TV'

    def get_movie(self):
        inetref = self.Mgrab.searchTitle(self.rec.title,\
                            self.rec.originalairdate.year)
        if len(inetref) == 1:
            inetref = inetref[0][0]
        else:
            self.get_generic()
            return

        if self.opts.listingonly:
            self.get_generic()
        else:
            dat, self.cast, self.genre, self.country = \
                    self.Mgrab.getData(inetref)
            self.vid.data.update(dat)
        self.type = 'Movie'

    def get_generic(self):
        self.vid.title = self.rec.title
        if self.rec.subtitle:
            self.vid.subtitle = self.rec.subtitle
        if self.rec.description:
            self.vid.plot = self.rec.description
        if self.rec.originalairdate:
            self.vid.year = self.rec.originalairdate.year
            self.vid.releasedate = self.rec.originalairdate
        lsec = (self.rec.endtime-self.rec.starttime).seconds
        self.vid.length = str(lsec/60)
        for member in self.rec.cast:
            if member.role == 'director':
                self.vid.director = member.name
            elif member.role == 'actor':
                self.cast.append(member.name)
        self.type = 'GENERIC'

    def get_dest(self):
        if self.type == 'TV':
            self.vid.filename = self.process_fmt(self.tfmt)
        elif self.type == 'MOVIE':
            self.vid.filename = self.process_fmt(self.mfmt)
        elif self.type == 'GENERIC':
            self.vid.filename = self.process_fmt(self.gfmt)

    def process_fmt(self, fmt):
        # replace fields from viddata
        #print self.vid.data
        ext = '.'+self.rec.basename.rsplit('.',1)[1]
        rep = ( ('%TITLE%','title','%s'),   ('%SUBTITLE%','subtitle','%s'),
            ('%SEASON%','season','%d'),     ('%SEASONPAD%','season','%02d'),
            ('%EPISODE%','episode','%d'),   ('%EPISODEPAD%','episode','%02d'),
            ('%YEAR%','year','%s'),         ('%DIRECTOR%','director','%s'))
        for (tag, data, format) in rep:
            if self.vid[data]:
                fmt = fmt.replace(tag,format % self.vid[data])
            else:
                fmt = fmt.replace(tag,'')

        # replace fields from program data
        rep = ( ('%HOSTNAME','hostname','%s'),('%STORAGEGROUP%','storagegroup','%s'))
        for (tag, data, format) in rep:
            data = eval('self.rec.%s' % data)
            fmt = fmt.replace(tag,format % data)

#       fmt = fmt.replace('%CARDID%',self.rec.cardid)
#       fmt = fmt.replace('%CARDNAME%',self.rec.cardid)
#       fmt = fmt.replace('%SOURCEID%',self.rec.cardid)
#       fmt = fmt.replace('%SOURCENAME%',self.rec.cardid)
#       fmt = fmt.replace('%CHANNUM%',self.rec.channum)
#       fmt = fmt.replace('%CHANNAME%',self.rec.cardid)

        if len(self.genre):
            fmt = fmt.replace('%GENRE%',self.genre[0])
        else:
            fmt = fmt.replace('%GENRE%','')
#       if len(self.country):
#           fmt = fmt.replace('%COUNTRY%',self.country[0])
#       else:
#           fmt = fmt.replace('%COUNTRY%','')
        return fmt+ext

    def copy(self):
        if self.opts.skip:
            self.vid.hash = self.vid.getHash()
            return
        if self.opts.sim:
            return

        #print self.vid.filename

        stime = time.time()
        srcsize = self.rec.filesize
        htime = [stime,stime,stime,stime]

        srcfp = self.rec.open('r')
        dstfp = self.vid.open('w')

        if self.job:
            self.job.setStatus(4)
        tsize = 2**24
        while tsize == 2**24:
            if (srcsize - dstfp.tell()) < tsize:
                tsize = srcsize - dstfp.tell()
            dstfp.write(srcfp.read(tsize))
            htime.append(time.time())
            rate = float(tsize*4)/(time.time()-htime.pop())
            remt = (srcsize-dstfp.tell())/rate
            if self.job:
                self.job.setComment("%02d%% complete - %s seconds remaining" %\
                            (dstfp.tell()*100/srcsize, remt))
        srcfp.close()
        dstfp.close()

        self.vid.hash = self.vid.getHash()

#       log.notice('Transfer complete','%d seconds elapsed' % int(time.time()-stime))
        if self.job:
            self.job.setComment("Complete - %d seconds elapsed" % \
                            (int(time.time()-stime)))
            self.job.setStatus(256)

    def write_images(self):
        for type in ('coverfile', 'screenshot', 'banner', 'fanart'):
            if self.vid[type] in ('No Cover','',None):
                continue
            if type == 'coverfile': name = 'coverart'
            else: name = type
            url = self.vid[type]
            if len(url.split(',')) > 1:
                url = url.split(',')[0]

            if self.type == 'TV':
                if type == 'screenshot':
                    self.vid[type] = '%s Season %dx%d_%s.%s' % \
                                (self.vid.title, self.vid.season, 
                                self.vid.episode, name, url.rsplit('.',1)[1])
                else:
                    self.vid[type] = '%s Season %d_%s.%s' % \
                                (self.vid.title, self.vid.season,
                                 name, url.rsplit('.',1)[1])
            else:
                self.vid[type] = '%s_%s.%s' % \
                            (self.vid.title, name, url.rsplit('.',1)[1])

            try:
                dstfp = self.vid._open(type, 'w', True)
                srcfp = urlopen(url)
                dstfp.write(srcfp.read())
                srcfp.close()
                dstfp.close()
            except:
                #print 'existing images: ' + self.vid[type]
                pass

    def write_cref(self):
        for member in self.cast:
            self.vid.cast.add(member)
        for member in self.genre:
            self.vid.genre.add(member)
        for member in self.country:
            self.vid.country.add(member)

def usage():
    print("mythvidexport.py [options] [--chanid=<chanid> --starttime=<starttime> | <jobid>]")
    print("        This script can be run by specifing the channel and start time directly")
    print("            or by specifing the ID of a job in jobqueue")
    print("")
    print("        Run from the command line through the former:")
    print("            mythvidexport.py --chanid=1002 --starttime=200907010000")
    print("        Or from a user script through the latter:")
    print("            mythvidexport.py %JOBID%")
    print("")
    print("    Options:")
    print("        -h/--help and -f/--helpformat:")
    print("             return this help, or a listing of available formatting strings")
    print("        --fformat='<string>' and --dformat='<string>':")
    print("             override the stored formatting string in the database")
    print("             if no recording is specified, store format string to the database")

def usage_format():
    print("The default strings are:")
    print("    Television: 'Television/%TITLE%/Season %SEASON%/%TITLE% - S%SEASON%E%EPISODEPAD% - %SUBTITLE%'")
    print("    Movie:      'Movies/%TITLE%'")
    print("    Generic:    'Videos/%TITLE%'")
    print("")
    print("Available strings:")
    print("    %TITLE%:         series title")
    print("    %SUBTITLE%:      episode title")
    print("    %SEASON%:        season number")
    print("    %SEASONPAD%:     season number, padded to 2 digits")
    print("    %EPISODE%:       episode number")
    print("    %EPISODEPAD%:    episode number, padded to 2 digits")
    print("    %YEAR%:          year")
    print("    %DIRECTOR%:      director")
#   print("    %CARDID%:        ID of tuner card used to record show")
#   print("    %CARDNAME%:      name of tuner card used to record show")
#   print("    %SOURCEID%:      ID of video source used to record show")
#   print("    %SOURCENAME%:    name of video source used to record show")
    print("    %HOSTNAME%:      backend used to record show")
    print("    %STORAGEGROUP%:  storage group containing recorded show")
#   print("    %CHANNUM%:       ID of channel used to record show")
#   print("    %CHANNAME%:      name of channel used to record show")
    print("    %GENRE%:         first genre listed for recording")
#   print("    %COUNTRY%:       first country listed for recording")

def print_format():
    db = MythDB()
    tfmt = db.getSetting('mythvideo.TVexportfmt')
    if not tfmt:
        tfmt = 'Television/%TITLE%/%TITLE% - S%SEASON%E%EPISODEPAD% - %SUBTITLE%'
    mfmt = db.getSetting('mythvideo.MOVIEexportfmt')
    if not mfmt:
        mfmt = 'Movies/%TITLE%'
    gfmt = db.getSetting('mythvideo.GENERICexportfmt')
    if not gfmt:
        gfmt = 'Videos/%TITLE%'
    print "Current output formats:"
    print "    TV:      "+tfmt
    print "    Movies:  "+mfmt
    print "    Generic: "+gfmt

def main():
    parser = OptionParser(usage="usage: %prog [options] [jobid]")

    parser.add_option("-f", "--helpformat", action="store_true", default=False, dest="fmthelp",
            help="Print explination of file format string.")
    parser.add_option("-p", "--printformat", action="store_true", default=False, dest="fmtprint",
            help="Print current file format string.")
    parser.add_option("--tformat", action="store", type="string", dest="tformat",
            help="Use TV format for current task. If no task, store in database.")
    parser.add_option("--mformat", action="store", type="string", dest="mformat",
            help="Use Movie format for current task. If no task, store in database.")
    parser.add_option("--gformat", action="store", type="string", dest="gformat",
            help="Use Generic format for current task. If no task, store in database.")
    parser.add_option("--chanid", action="store", type="int", dest="chanid",
            help="Use chanid for manual operation")
    parser.add_option("--starttime", action="store", type="int", dest="starttime",
            help="Use starttime for manual operation")
    parser.add_option("--listingonly", action="store_true", default=False, dest="listingonly",
            help="Use data from listing provider, rather than grabber")
    parser.add_option("-s", "--simulation", action="store_true", default=False, dest="sim",
            help="Simulation (dry run), no files are copied or new entries made")
    parser.add_option("--skip", action="store_true", default=False, dest="skip") # debugging use only

    opts, args = parser.parse_args()

    if opts.fmthelp:
        usage_format()
        sys.exit(0)

    if opts.fmtprint:
        usage_current()
        sys.exit(0)

    if opts.chanid and opts.starttime:
        export = VIDEO(opts)
    elif len(args) == 1:
        export = VIDEO(opts,int(args[0]))
    else:
        if opts.tformat or opts.mformat or opts.gformat:
            db = MythDBConn()
            if opts.tformat:
                print "Changing TV format to: "+opts.tformat
                db.setting['NULL']['mythvideo.TVexportfmt'] = opts.tformat
            if opts.mformat:
                print "Changing Movie format to: "+opts.mformat
                db.setting['NULL']['mythvideo.MOVIEexportfmt'] = opts.mformat
            if opts.gformat:
                print "Changing Generic format to: "+opts.gformat
                db.setting['NULL']['mythvideo.GENERICexportfmt'] = opts.gformat
            sys.exit(0)
        else:
            parser.print_help()
            sys.exit(2)

if __name__ == "__main__":
    main()

