def fromPowerRule(cls, title='unnamed (Power Search)', where='', args=None, join='', db=None, type=RECTYPE.kAllRecord, searchtype=RECSEARCHTYPE.kPowerSearch, wait=False): if type not in (RECTYPE.kAllRecord, RECTYPE.kFindDailyRecord, RECTYPE.kFindWeeklyRecord, RECTYPE.kFindOneRecord): raise MythDBError("Invalid 'type' set for power recording rule.") rec = cls(None, db=db) if args is not None: where = rec._db.literal(where, args) now = datetime.now() rec.starttime = now.time() rec.endtime = now.time() rec.startdate = now.date() rec.enddate = now.date() rec.title = title rec.description = where rec.subtitle = join rec.type = type rec.search = searchtype return rec.create(wait=wait)
def fromGuide(cls, guide, type=RECTYPE.kAllRecord, wait=False): if datetime.now() > guide.endtime: raise MythError('Cannot create recording rule for past recording.') rec = cls(None, db=guide._db) for key in ('chanid','title','subtitle','description', 'category', 'seriesid','programid'): rec[key] = guide[key] rec.startdate = guide.starttime.date() rec.starttime = guide.starttime-datetime.combine(rec.startdate, time()) rec.enddate = guide.endtime.date() rec.endtime = guide.endtime-datetime.combine(rec.enddate, time()) rec.station = Channel(guide.chanid, db=guide._db).callsign rec.type = type return rec.create(wait=wait)
def fromProgram(cls, program, type=RECTYPE.kAllRecord, wait=False): if datetime.now() > program.endtime: raise MythError('Cannot create recording rule for past recording.') rec = cls(None, db=program._db) for key in ('chanid','title','subtitle','description','category', 'seriesid','programid'): rec[key] = program[key] rec.station = program.callsign rec.startdate = program.starttime.date() rec.starttime = program.starttime-datetime.combine(rec.startdate, time()) rec.enddate = program.endtime.date() rec.endtime = program.endtime-datetime.combine(rec.enddate, time()) if program.recordid: rec.parentid = program.recordid if program.recstatus == RECTYPE.kNotRecording: rec.type = RECTYPE.kOverrideRecord else: rec.type = RECTYPE.kDontRecord else: rec.type = type return rec.create(wait=wait)
class Video( CMPVideo, VideoSchema, DBDataWrite ): """Video(id=None, db=None, raw=None) -> Video object""" _table = 'videometadata' _defaults = {'subtitle':u'', 'director':u'Unknown', 'rating':u'NR', 'inetref':u'00000000', 'year':1895, 'userrating':0.0, 'length':0, 'showlevel':1, 'coverfile':u'No Cover', 'host':u'', 'homepage':u'', 'insertdate': datetime.now(), 'watched':False, 'category':0, 'browse':True, 'hash':u'', 'season':0, 'episode':0, 'releasedate':date(1,1,1), 'childid':-1} _cm_toid, _cm_toname = DictInvertCI.createPair({0:'none'}) @classmethod def _setClassDefs(cls, db=None): db = DBCache(db) super(Video, cls)._setClassDefs(db) cls._fill_cm(db) @classmethod def _fill_cm(cls, db=None): db = DBCache(db) with db.cursor() as cursor: cursor.execute("""SELECT * FROM videocategory""") for row in cursor: cls._cm_toname[row[0]] = row[1] def _cat_toname(self): if self.category is not None: try: self.category = self._cm_toname[int(self.category)] except ValueError: # already a named category pass except KeyError: self._fill_cm(self._db) if int(self.category) in self._cm_toname: self.category = self._cm_toname[int(self.category)] else: raise MythDBError('Video defined with unknown category id') else: self.category = 'none' def _cat_toid(self): if self.category is not None: try: if self.category.lower() not in self._cm_toid: self._fill_cm(self._db) if self.category.lower() not in self._cm_toid: with self._db.cursor(self._log) as cursor: cursor.execute("""INSERT INTO videocategory SET category=%s""", self.category) self._cm_toid[self.category] = cursor.lastrowid self.category = self._cm_toid[self.category] except AttributeError: # already an integer category pass else: self.category = 0 def _pull(self): DBDataWrite._pull(self) self._fill_cm() self._cat_toname() def _push(self): self._cat_toid() DBDataWrite._push(self) self._cat_toname() self.cast.commit() self.genre.commit() self.country.commit() def __repr__(self): if self._wheredat is None: return u"<Uninitialized Video at %s>" % hex(id(self)) res = self.title if self.season and self.episode: res += u' - %dx%02d' % (self.season, self.episode) if self.subtitle: res += u' - '+self.subtitle return u"<Video '%s' at %s>" % (res, hex(id(self))) def _postinit(self): self._fill_cm() self._cat_toname() self.cast = self._Cast(self._wheredat, self._db) self.genre = self._Genre(self._wheredat, self._db) self.country = self._Country(self._wheredat, self._db) self.markup = self._Markup((self.filename,), self._db) def create(self, data=None): """Video.create(data=None) -> Video object""" if (self.host is not None) and (self.host != ''): # check for pre-existing entry if self.hash == '': self.hash = self.getHash() with self._db as cursor: if cursor.execute("""SELECT intid FROM videometadata WHERE hash=%s""", self.hash) > 0: id = cursor.fetchone()[0] self._evalwheredat([id]) self._pull() self._postinit() return self # create new entry self._import(data) self._cat_toid() return DBDataWrite._create_autoincrement(self) class _Cast( DBDataCRef ): _table = ['videometadatacast','videocast'] _ref = ['idvideo'] _cref = ['idcast','intid'] class _Genre( DBDataCRef ): _table = ['videometadatagenre','videogenre'] _ref = ['idvideo'] _cref = ['idgenre','intid'] class _Country( DBDataCRef ): _table = ['videometadatacountry','videocountry'] _ref = ['idvideo'] _cref = ['idcountry','intid'] class _Markup( DBDataRef, MARKUP ): _table = 'filemarkup' _ref = ['filename',] def delete(self): """Video.delete() -> None""" if (self._where is None) or \ (self._wheredat is None): return self.cast.clean() self.genre.clean() self.country.clean() DBDataWrite.delete(self) banner = Artwork('banner') coverfile = coverart = Artwork('coverfile') fanart = Artwork('fanart') screenshot = Artwork('screenshot') trailer = Artwork('trailer') def open(self, mode='r', nooverwrite=False): return ftopen((self.host, 'Videos', self.filename), mode, False, nooverwrite, self._db) def getHash(self): """Video.getHash() -> file hash""" if self.host is None: return None be = FileOps(db=self._db) hash = be.getHash(self.filename, 'Videos', self.host) return hash def parseFilename(self): filename = self.filename filename = filename[:filename.rindex('.')] for old in ('%20','_','.'): filename = filename.replace(old, ' ') sep = '(?:\s?(?:-|/)?\s?)?' regex1 = re.compile( sep.join(['^(.*[^s0-9])', '(?:s|(?:Season))?', '(\d{1,4})', '(?:[ex/]|Episode)', '(\d{1,3})', '(.*)$']), re.I) regex2 = re.compile('(%s(?:Season%s\d*%s)*%s)$' \ % (sep, sep, sep, sep), re.I) match1 = regex1.search(filename) if match1: title = match1.group(1) season = int(match1.group(2)) episode = int(match1.group(3)) subtitle = match1.group(4) match2 = regex2.search(title) if match2: title = title[:match2.start()] title = title.rsplit('/',1)[-1] else: season = None episode = None subtitle = None title = filename.rsplit('/',1)[-1] for left,right in (('(',')'), ('[',']'), ('{','}')): while left in title: lin = title.index(left) rin = title.index(right,lin) title = title[:lin]+title[rin+1:] title = title return (title, season, episode, subtitle) def importMetadata(self, metadata, overwrite=False): """Imports data from a VideoMetadata object.""" def _allow_change(self, tag, overwrite): if overwrite: return True if self[tag] is None: return True if self[tag] == '': return True if tag in self._defaults: if self[tag] == self._defaults[tag]: return True return False # only operate on existing entries if self._wheredat is None: return # pull direct tags for tag in ('title', 'subtitle', 'tagline', 'season', 'episode', 'inetref', 'homepage', 'trailer', 'userrating', 'year', 'releasedate'): if metadata[tag] and _allow_change(self, tag, overwrite): self[tag] = metadata[tag] # pull tags needing renaming for tagf,tagt in (('description','plot'), ('runtime','length')): if metadata[tagf] and _allow_change(self, tagt, overwrite): self[tagt] = metadata[tagf] # pull director try: if _allow_change(self, 'director', overwrite): self.director = [person.name for person in metadata.people \ if person.job=='Director'].pop(0) except IndexError: pass # pull actors for actor in [person for person in metadata.people \ if person.job=='Actor']: self.cast.add(unicode(actor.name)) # pull genres for category in metadata.categories: self.genre.add(unicode(category)) # pull images (SG content only) if bool(self.host): for image in metadata.images: if not hasattr(self, image.type): continue current = getattr(self, image.type) if current and (current != 'No Cover') and not overwrite: continue setattr(self, image.type, image.filename) getattr(self, image.type).downloadFrom(image.url) self.processed = True self.update() def exportMetadata(self): """Exports data to a VideoMetadata object.""" # only work on entries from the database if self._wheredat is None: return metadata = VideoMetadata() # pull direct tags for tag in ('title', 'subtitle', 'tagline', 'season', 'episode', 'inetref', 'homepage', 'trailer', 'userrating', 'year', 'releasedate'): if self[tag]: metadata[tag] = self[tag] # pull translated tags for tagf, tagt in (('plot', 'description'), ('length', 'runtime')): if self[tagf]: metadata[tagt] = self[tagf] # pull director if self.director: metadata.people.append(OrdDict((('name',self.director), ('job','Director')))) # pull actors for actor in self.cast: metadata.people.append(OrdDict((('name',actor.cast), ('job','Actor')))) # pull genres for genre in self.genre: metadata.categories.append(genre.genre) # pull countries for country in self.country: metadata.countries.append(country.country) # pull images # for arttype in ['coverart', 'fanart', 'banner', 'screenshot']: # art = getattr(self, arttype) # if art: # metadata.images.append(OrdDict((('type',arttype), ('filename',art)))) return metadata def __getstate__(self): data = DBDataWrite.__getstate__(self) data['cast'] = self.cast._picklelist() data['genre'] = self.genre._picklelist() data['markup'] = self.markup._picklelist() data['country'] = self.country._picklelist() return data def __setstate__(self, state): DBDataWrite.__setstate__(self, state) if self._wheredat is not None: self.cast._populate(data=state['cast']) self.genre._populate(data=state['genre']) self.markup._populate(data=state['markup']) self.country._populate(data=state['country']) @classmethod def fromFilename(cls, filename, db=None): vid = cls(db=db) vid.filename = filename vid.title, vid.season, vid.episode, vid.subtitle = \ vid.parseFilename() return vid def _playOnFe(self, fe): return fe.send('play','file myth://Videos@%s/%s' % (self.host, self.filename)) #### LEGACY #### # of course this will likely all get scrapped for 0.26... def openBanner(self, mode='r', nooverwrite=False): return self.banner.open(mode) def openCoverart(self, mode='r', nooverwrite=False): return self.coverfile.open(mode) def openFanart(self, mode='r', nooverwrite=False): return self.fanart.open(mode) def openScreenshot(self, mode='r', nooverwrite=False): return self.screenshot.open(mode) def openTrailer(self, mode='r', nooverwrite=False): return self.trailer.open(mode)
class Job( DBDataWrite, JOBTYPE, JOBCMD, JOBFLAG, JOBSTATUS ): """ Job(id=None, db=None) -> Job object """ _table = 'jobqueue' _logmodule = 'Python Jobqueue' _defaults = {'id':None, 'inserttime':datetime.now(), 'hostname':'', 'status':JOBSTATUS.QUEUED, 'comment':'', 'schedruntime':datetime.now()} def __str__(self): if self._wheredat is None: return u"<Uninitialized Job at %s>" % hex(id(self)) return u"<Job '%s' at %s>" % (self.id, hex(id(self))) def __repr__(self): return str(self).encode('utf-8') def setComment(self,comment): """Job.setComment(comment) -> None, updates comment""" self.comment = comment self.update() def setStatus(self,status): """Job.setStatus(Status) -> None, updates status""" self.status = status self.update() @classmethod def fromRecorded(cls, rec, type, status=None, schedruntime=None, hostname=None, args=None, flags=None): job = cls(db=rec._db) job.type = type job.chanid = rec.chanid job.starttime = rec.starttime if status: job.status = status if schedruntime: job.schedruntime = schedruntime if hostname: job.hostname = hostname if args: job.args = args if flags: job.flags = flags job.create() @classmethod def fromProgram(cls, prog, type, status=None, schedruntime=None, hostname=None, args=None, flags=None): if prog.rectype != prog.rsRecorded: raise MythError('Invalid recording type for Job.') job = cls(db=prog._db) job.type = type job.chanid = prog.chanid job.starttime = prog.recstartts if status: job.status = status if schedruntime: job.schedruntime = schedruntime if hostname: job.hostname = hostname if args: job.args = args if flags: job.flags = flags job.create()