flexget.plugins.input_backlog
Covered: 96 lines
Missed: 1 lines
Skipped 33 lines
Percent: 98 %
  1
import logging
  2
from datetime import datetime, timedelta
  3
from sqlalchemy import Column, Integer, String, DateTime, PickleType
  4
from flexget.manager import Base, Session
  5
from flexget.feed import Entry
  6
from flexget.plugin import register_plugin, priority, PluginWarning
  8
log = logging.getLogger('backlog')
 11
class BacklogEntry(Base):
 13
    __tablename__ = 'backlog'
 15
    id = Column(Integer, primary_key=True)
 16
    feed = Column(String)
 17
    title = Column(String)
 18
    expire = Column(DateTime)
 19
    entry = Column(PickleType(mutable=False))
 21
    def __repr__(self):
 22
        return '<BacklogEntry(title=%s)>' % (self.title)
 25
class InputBacklog(object):
 26
    """
 27
    Keeps feed history for given amount of time.
 29
    Example:
 31
    backlog: 4 days
 33
    Rarely useful for end users, mainly used by other plugins.
 34
    """
 36
    def validator(self):
 37
        from flexget import validator
 38
        root = validator.factory('regexp_match', message='Must be in format <number> <hours|minutes|days|weeks>')
 39
        root.accept('\d+ (minute|hour|day|week)s?')
 40
        return root
 42
    def get_amount(self, value):
 43
        if not value:
 45
            return timedelta()
 46
        amount, unit = value.split(' ')
 48
        if not unit.endswith('s'):
 49
            unit = unit + 's'
 50
        log.debug('amount: %s unit: %s' % (repr(amount), repr(unit)))
 51
        params = {unit: int(amount)}
 52
        try:
 53
            return timedelta(**params)
 54
        except TypeError:
 55
            raise PluginWarning('Invalid time format \'%s\'' % value, log)
 57
    @priority(-255)
 58
    def on_feed_input(self, feed, config):
 60
        injections = self.get_injections(feed)
 62
        for entry in feed.entries:
 63
            entry.take_snapshot('after_input')
 64
        if config:
 66
            self.learn_backlog(feed, config)
 68
        return injections
 70
    def on_feed_abort(self, feed, config):
 71
        """Remember all entries until next execution when feed gets aborted."""
 72
        log.debug('Remembering all entries to backlog because of feed abort.')
 73
        self.learn_backlog(feed)
 75
    def add_backlog(self, feed, entry, amount=''):
 76
        """Add single entry to feed backlog
 78
        If :amount: is not specified, entry will only be injected on next execution."""
 79
        snapshot = entry.snapshots.get('after_input')
 80
        if not snapshot:
 81
            log.warning('No input snapshot available for `%s`, using current state' % entry['title'])
 82
            snapshot = dict(entry)
 83
        session = Session()
 84
        expire_time = datetime.now() + self.get_amount(amount)
 85
        backlog_entry = session.query(BacklogEntry).filter(BacklogEntry.title == entry['title']).\
 86
                                                filter(BacklogEntry.feed == feed.name).first()
 87
        if backlog_entry:
 89
            if backlog_entry.expire < expire_time:
 90
                log.debug('Updating expiry time for %s' % entry['title'])
 91
                backlog_entry.expire = expire_time
 92
        else:
 93
            log.debug('Saving %s' % entry['title'])
 94
            backlog_entry = BacklogEntry()
 95
            backlog_entry.title = entry['title']
 96
            backlog_entry.entry = snapshot
 97
            backlog_entry.feed = feed.name
 98
            backlog_entry.expire = expire_time
 99
            session.add(backlog_entry)
100
        session.commit()
102
    def learn_backlog(self, feed, amount=''):
103
        """Learn current entries into backlog. All feed inputs must have been executed."""
104
        for entry in feed.entries:
105
            self.add_backlog(feed, entry, amount)
107
    def get_injections(self, feed):
108
        """Insert missing entries from backlog."""
109
        entries = []
110
        feed_backlog = feed.session.query(BacklogEntry).filter(BacklogEntry.feed == feed.name)
111
        for backlog_entry in feed_backlog.all():
112
            entry = Entry(backlog_entry.entry)
115
            if feed.find_entry(title=entry['title'], url=entry['url']):
116
                continue
117
            log.debug('Restoring %s' % entry['title'])
118
            entries.append(entry)
119
        if entries:
120
            feed.verbose_progress('Added %s entries from backlog' % len(entries), log)
123
        for backlog_entry in feed_backlog.filter(datetime.now() > BacklogEntry.expire).all():
124
            log.debug('Purging %s' % backlog_entry.title)
125
            feed.session.delete(backlog_entry)
127
        return entries
129
register_plugin(InputBacklog, 'backlog', builtin=True, api_ver=2)