| 1 | #!/usr/local/bin/python |
|---|
| 2 | from MythTV import MythDB, MythLog, MythTV, MythVideo, Program, Job |
|---|
| 3 | from socket import gethostname |
|---|
| 4 | import sys, getopt, re, os, time, logging |
|---|
| 5 | |
|---|
| 6 | #Usage: mythvidexport.py <--chanid <channel id>> <--starttime <start time>> [options] |
|---|
| 7 | # --format <see directory/file name options> |
|---|
| 8 | # moves a single file over to MythVideo with optional directory format, format is discarded after use |
|---|
| 9 | |
|---|
| 10 | #Usage: mythvidexport.py <job id> |
|---|
| 11 | # WARNING: for internal use by job queue only! |
|---|
| 12 | # moves a single file, grabbing necessary information from job queue table |
|---|
| 13 | |
|---|
| 14 | #Usage: mythvidexport.py <options> |
|---|
| 15 | # --setformat <see directory/file name options> |
|---|
| 16 | # stored directory format in database for future use |
|---|
| 17 | # --help |
|---|
| 18 | # --helpformat |
|---|
| 19 | # lengthy discription of directory formatting |
|---|
| 20 | |
|---|
| 21 | |
|---|
| 22 | class VIDEO: |
|---|
| 23 | mythdb = None |
|---|
| 24 | dbconn = None |
|---|
| 25 | job = None |
|---|
| 26 | jobhost = None |
|---|
| 27 | chanid = None |
|---|
| 28 | rtime = None |
|---|
| 29 | source = None |
|---|
| 30 | srchost = None |
|---|
| 31 | srcdb = None |
|---|
| 32 | dest = None |
|---|
| 33 | desthost = None |
|---|
| 34 | destdb = None |
|---|
| 35 | prog = None |
|---|
| 36 | season= None |
|---|
| 37 | episode = None |
|---|
| 38 | fmt = None |
|---|
| 39 | log = None |
|---|
| 40 | |
|---|
| 41 | |
|---|
| 42 | def __init__(self, *inp): |
|---|
| 43 | if len(inp) == 1: |
|---|
| 44 | self.job = Job(inp[0]); |
|---|
| 45 | self.chanid = self.job.chanid |
|---|
| 46 | self.rtime = "%04d%02d%02d%02d%02d%02d" % self.job.starttime.timetuple()[0:6] |
|---|
| 47 | self.jobhost = self.job.host |
|---|
| 48 | self.job.setStatus(3) |
|---|
| 49 | elif len(inp) == 2: |
|---|
| 50 | self.chanid = inp[0] |
|---|
| 51 | self.rtime = inp[1] |
|---|
| 52 | else: |
|---|
| 53 | sys.exit(2) |
|---|
| 54 | self.get_source() |
|---|
| 55 | self.log = MythLog(logging.CRITICAL, '#%(levelname)s - %(message)s', 'MythTV') |
|---|
| 56 | |
|---|
| 57 | def dbconnect(self): |
|---|
| 58 | self.mythdb = MythDB() |
|---|
| 59 | self.dbconn = self.mythdb.db |
|---|
| 60 | |
|---|
| 61 | def get_source(self): |
|---|
| 62 | if not self.dbconn: |
|---|
| 63 | self.dbconnect() |
|---|
| 64 | if not self.jobhost: |
|---|
| 65 | self.jobhost = gethostname() |
|---|
| 66 | |
|---|
| 67 | mythinst = MythTV() |
|---|
| 68 | self.prog = mythinst.getRecording(self.chanid,int(self.rtime)) |
|---|
| 69 | |
|---|
| 70 | self.srcdb = self.prog.filename |
|---|
| 71 | self.srchost = self.prog.hostname |
|---|
| 72 | |
|---|
| 73 | self.source = mythinst.getCheckfile(self.prog) |
|---|
| 74 | |
|---|
| 75 | def get_dest(self): |
|---|
| 76 | #process self.fmt and generate self.dest |
|---|
| 77 | #not programmed yet... just use my own naming scheme |
|---|
| 78 | path = self.mythdb.getSetting("VideoStartupDir",hostname=self.jobhost) |
|---|
| 79 | self.desthost = self.jobhost |
|---|
| 80 | self.get_ttvdb() |
|---|
| 81 | |
|---|
| 82 | ext = self.source[self.source.rfind('.'):] |
|---|
| 83 | subpath = "TV/%s/Season %d/%dx%02d - %s%s" % (self.prog.title,self.season,self.season,self.episode,self.prog.subtitle,ext) |
|---|
| 84 | self.dest = path+'/'+subpath |
|---|
| 85 | |
|---|
| 86 | tmppath = self.dest[0:self.dest.rfind('/')] |
|---|
| 87 | if not os.access(tmppath,os.F_OK): |
|---|
| 88 | os.makedirs(tmppath) |
|---|
| 89 | |
|---|
| 90 | cursor = self.dbconn.cursor() |
|---|
| 91 | reslen = cursor.execute("SELECT dirname FROM storagegroup WHERE hostname='%s' and groupname='Videos'" % self.desthost) |
|---|
| 92 | if reslen: |
|---|
| 93 | self.destdb = subpath |
|---|
| 94 | else: |
|---|
| 95 | self.destdb = self.dest |
|---|
| 96 | |
|---|
| 97 | def get_ttvdb(self): |
|---|
| 98 | regex = re.compile('S(?P<season>[0-9]*)E(?P<episode>[0-9]*)') |
|---|
| 99 | if os.access('/usr/local/share/mythtv/mythvideo/scripts/ttvdb.py',os.F_OK): |
|---|
| 100 | path = '/usr/local/share/mythtv/mythvideo/scripts/ttvdb.py' |
|---|
| 101 | elif os.access('/usr/share/mythtv/mythvideo/scripts/ttvdb.py',os.F_OK): |
|---|
| 102 | path = '/usr/local/share/mythtv/mythvideo/scripts/ttvdb.py' |
|---|
| 103 | |
|---|
| 104 | fp = os.popen("%s -N \"%s\" \"%s\"" % (path,self.prog.title,self.prog.subtitle),"r") |
|---|
| 105 | match = regex.search(fp.read()) |
|---|
| 106 | fp.close() |
|---|
| 107 | if match is None: |
|---|
| 108 | if self.job: |
|---|
| 109 | self.job.setComment("TTVDB.py failed to get season/episode numbers") |
|---|
| 110 | self.job.setStatus(304) |
|---|
| 111 | self.log.Msg(logging.CRITICAL, "TTVDB.py failed to get season/episode numbers") |
|---|
| 112 | else: |
|---|
| 113 | print("TTVDB.py failed to get season/episode numbers") |
|---|
| 114 | sys.exit(2) |
|---|
| 115 | self.season = int(match.group('season')) |
|---|
| 116 | self.episode = int(match.group('episode')) |
|---|
| 117 | |
|---|
| 118 | def set_fmt(self, fmt): |
|---|
| 119 | self.fmt = fmt |
|---|
| 120 | |
|---|
| 121 | def get_fmt(self): |
|---|
| 122 | self.fmt = self.mythdb.getSetting('mythvideo.exportfmt') |
|---|
| 123 | |
|---|
| 124 | def copy(self): |
|---|
| 125 | cursor = None |
|---|
| 126 | fp = None |
|---|
| 127 | srcsize = None |
|---|
| 128 | dtime = None |
|---|
| 129 | if self.fmt is None: |
|---|
| 130 | self.get_fmt() |
|---|
| 131 | self.get_dest() |
|---|
| 132 | if self.desthost != self.jobhost: |
|---|
| 133 | if self.job: |
|---|
| 134 | self.job.setComment("Job must be run on destination host") |
|---|
| 135 | self.log.Msg(logging.CRITICAL, 'MythVidExport failed - must be run on destination host') |
|---|
| 136 | self.job.setStatus(304) |
|---|
| 137 | else: |
|---|
| 138 | print("ERROR: job must be run on destionation host") |
|---|
| 139 | sys.exit(2) |
|---|
| 140 | |
|---|
| 141 | stime = time.time() |
|---|
| 142 | fp = os.popen("cp \"%s\" \"%s\"" % (self.source,self.dest)) |
|---|
| 143 | # if self.srchost == self.jobhost: |
|---|
| 144 | # fp = os.popen("cp '%s' '%s'" % (self.source,self.dest)) |
|---|
| 145 | # else: |
|---|
| 146 | # fp = os.popen("scp '%s:%s' '%s'" % (self.srchost, self.source, self.dest)) |
|---|
| 147 | srcsize = self.prog.filesize |
|---|
| 148 | destsize = [0,0,0,0,0,0,0,0,0,0] |
|---|
| 149 | if self.job: |
|---|
| 150 | self.job.setStatus(4) |
|---|
| 151 | while srcsize > destsize[9]: |
|---|
| 152 | time.sleep(4) |
|---|
| 153 | destsize.append(os.stat(self.dest)[6]) |
|---|
| 154 | rem = srcsize-destsize[10] |
|---|
| 155 | if destsize[0]: |
|---|
| 156 | dtime = 40 |
|---|
| 157 | else: |
|---|
| 158 | dtime = int(time.time()-stime) |
|---|
| 159 | rate = (destsize[10]-destsize.pop(0))/dtime |
|---|
| 160 | remt = rem/rate |
|---|
| 161 | if self.job: |
|---|
| 162 | self.job.setComment("%02d%% complete - %s seconds remaining" % (destsize[9]*100/srcsize, remt)) |
|---|
| 163 | fp.close() |
|---|
| 164 | if self.job: |
|---|
| 165 | self.job.setComment("Complete - %d seconds elapsed" % (int(time.time()-stime))) |
|---|
| 166 | self.job.setStatus(256) |
|---|
| 167 | |
|---|
| 168 | |
|---|
| 169 | def write_meta(self): |
|---|
| 170 | mythvid = MythVideo() |
|---|
| 171 | mythtv = MythTV() |
|---|
| 172 | director = self.mythdb.getCast(self.chanid, int(self.rtime), roles='director') |
|---|
| 173 | if len(director) == 0: |
|---|
| 174 | director = 'NULL' |
|---|
| 175 | else: |
|---|
| 176 | director = director[0] |
|---|
| 177 | viddata = {'title': '%s - %dx%02d - %s' % (self.prog.title, self.season, self.episode, self.prog.subtitle), |
|---|
| 178 | 'director': director, |
|---|
| 179 | 'plot': self.prog.description, |
|---|
| 180 | 'year': self.prog.airdate.split('-')[0], |
|---|
| 181 | 'length': str(int((self.prog.recendts-self.prog.recstartts).seconds/60)), |
|---|
| 182 | 'filename': self.destdb, |
|---|
| 183 | 'host': self.desthost, |
|---|
| 184 | 'showlevel': 1, |
|---|
| 185 | 'rating': 'NR', |
|---|
| 186 | 'inetref': '00000000', |
|---|
| 187 | 'coverfile': 'No Cover'} |
|---|
| 188 | intid = mythvid.getMetadataId(self.destdb) |
|---|
| 189 | if intid: |
|---|
| 190 | mythvid.setMetadata(viddata,intid) |
|---|
| 191 | else: |
|---|
| 192 | intid = mythvid.setMetadata(viddata) |
|---|
| 193 | |
|---|
| 194 | cast = self.mythdb.getCast(self.chanid, int(self.rtime), roles=('actor','guest_star','host','commentator','guest')) |
|---|
| 195 | for name in cast: |
|---|
| 196 | mythvid.setCast(name, intid) |
|---|
| 197 | if self.job: |
|---|
| 198 | self.job.setStatus(272) |
|---|
| 199 | |
|---|
| 200 | def usage(): |
|---|
| 201 | print("bleg") |
|---|
| 202 | |
|---|
| 203 | def usage_format(): |
|---|
| 204 | print("more bleg") |
|---|
| 205 | |
|---|
| 206 | def main(): |
|---|
| 207 | format = None |
|---|
| 208 | jobid = None |
|---|
| 209 | chanid = None |
|---|
| 210 | rtime = None |
|---|
| 211 | skip = False |
|---|
| 212 | try: |
|---|
| 213 | opts, args = getopt.getopt(sys.argv[1:], "hf", ["help","helpformat","format=","chanid=","starttime=","skipcopy"]) |
|---|
| 214 | except getopt.GetoptError, err: |
|---|
| 215 | print(str(err)) |
|---|
| 216 | usage() |
|---|
| 217 | sys.exit(2) |
|---|
| 218 | for opt, arg in opts: |
|---|
| 219 | if opt in ("-h", "--help"): |
|---|
| 220 | usage() |
|---|
| 221 | sys.exit() |
|---|
| 222 | elif opt in ('-f', '--helpformat'): |
|---|
| 223 | usage_format() |
|---|
| 224 | sys.exit() |
|---|
| 225 | elif opt == '--format': |
|---|
| 226 | format = arg |
|---|
| 227 | elif opt == '--chanid': |
|---|
| 228 | chanid = int(arg) |
|---|
| 229 | elif opt == '--starttime': |
|---|
| 230 | rtime = int(arg) |
|---|
| 231 | elif opt == '--skipcopy': |
|---|
| 232 | skip = True |
|---|
| 233 | |
|---|
| 234 | if len(args): |
|---|
| 235 | jobid = int(args[0]) |
|---|
| 236 | |
|---|
| 237 | if chanid and rtime: |
|---|
| 238 | export = VIDEO(chanid,rtime) |
|---|
| 239 | elif jobid: |
|---|
| 240 | export = VIDEO(jobid) |
|---|
| 241 | elif format: |
|---|
| 242 | # store format to database |
|---|
| 243 | sys.exit() |
|---|
| 244 | else: |
|---|
| 245 | usage() |
|---|
| 246 | sys.exit(2) |
|---|
| 247 | |
|---|
| 248 | if format: |
|---|
| 249 | export.set_fmt(format) |
|---|
| 250 | if skip: |
|---|
| 251 | export.get_fmt() |
|---|
| 252 | export.get_dest() |
|---|
| 253 | else: |
|---|
| 254 | export.copy() |
|---|
| 255 | export.write_meta() |
|---|
| 256 | |
|---|
| 257 | if __name__ == "__main__": |
|---|
| 258 | main() |
|---|