Esempio n. 1
0
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()
Esempio n. 2
0
    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)
Esempio n. 3
0
    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)
Esempio n. 4
0
    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)
Esempio n. 5
0
    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)
Esempio n. 6
0
    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)
Esempio n. 7
0
    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)
Esempio n. 8
0
class Video(VideoSchema, DBDataWrite, CMPVideo):
    """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'})

    class _open(object):
        def __init__(self, func):
            self.__name__ = func.__name__
            self.__module__ = func.__module__
            self.__doc__ = """Video.%s(mode='r', nooverwrite=False)
                        -> file or FileTransfer object""" % self.__name__
            self.type, self.sgroup = \
                        {'':('filename','Videos'),
                         'Banner':('banner','Banners'),
                         'Coverart':('coverfile','Coverart'),
                         'Fanart':('fanart','Fanart'),
                         'Screenshot':('screenshot','Screenshots'),
                         'Trailer':('trailer','Trailers')}[self.__name__[4:]]

        def __get__(self, inst, own):
            self.inst = inst
            return self

        def __call__(self, mode='r', nooverwrite=False):
            if self.inst.host == '':
                raise MythFileError('File access only works '
                                    'with Storage Group content')
            return ftopen(
                'myth://%s@%s/%s' %
                (self.sgroup, self.inst.host, self.inst[self.type]), mode,
                False, nooverwrite, self.inst._db)

    @classmethod
    def _setClassDefs(cls, db=None):
        db = DBCache(db)
        super(Video, cls)._setClassDefs(db)
        cls._fill_cm(db)

    @classmethod
    def _getGroup(cls, host, groupname=None, db=None):
        db = DBCache(db)
        metadata = ['coverart', 'fanart', 'banner', 'screenshot']
        fields = ['coverfile', 'fanart', 'banner', 'screenshot']
        groups = ['Coverart', 'Fanart', 'Banners', 'Screenshots']

        if (groupname is None) or (groupname == 'Videos'):
            if len(list(db.getStorageGroup('Videos', host))) == 0:
                raise MythError('MythVideo not set up for this host.')
            else:
                return 'Videos'
        elif groupname in groups:
            if len(list(db.getStorageGroup(groupname, host))) == 0:
                return cls._getGroup(host, 'Videos', db)
            else:
                return groupname
        elif groupname in fields:
            return cls._getGroup(host, groups[fields.index(groupname)])
        elif groupname in metadata:
            return cls._getGroup(host, groups[metadata.index(groupname)])
        else:
            raise MythError('Invalid Video StorageGroup name.')

    @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):
        return str(self).encode('utf-8')

    def __str__(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()
                    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)

    @_open
    def open(self, mode='r', nooverwrite=False):
        pass

    @_open
    def openBanner(self, mode='r', nooverwrite=False):
        pass

    @_open
    def openCoverart(self, mode='r', nooverwrite=False):
        pass

    @_open
    def openFanart(self, mode='r', nooverwrite=False):
        pass

    @_open
    def openScreenshot(self, mode='r', nooverwrite=False):
        pass

    @_open
    def openTrailer(self, mode='r', nooverwrite=False):
        pass

    def getHash(self):
        """Video.getHash() -> file hash"""
        if self.host is None:
            return None
        be = FileOps(self.host)
        hash = be.getHash(self.filename, 'Videos')
        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'):
            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):
            # only perform image grabs if 'host' is set, denoting SG use
            t1 = ['coverart', 'fanart', 'banner', 'screenshot']
            t2 = ['coverfile', 'fanart', 'banner', 'screenshot']
            mdtype = dict(zip(t1, t2))
            exists = dict(zip(t1, [False, False, False, False]))

            be = FileOps(self.host, db=self._db)
            for image in metadata.images:
                if exists[image.type]:
                    continue
                if not _allow_change(self, mdtype[image.type], overwrite):
                    continue
                self[mdtype[image.type]] = image.filename
                group = self._getGroup(self.host, image.type, self._db)
                if not be.fileExists(image.filename, group):
                    be.downloadTo(image.url, group, image.filename)
                exists[image.type] = True

        self.processed = True
        self.update()

    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))
Esempio n. 9
0
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.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.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()