2
from datetime import datetime
3
from sqlalchemy import Column, Integer, String, Unicode, DateTime
4
from sqlalchemy.schema import Index, MetaData
5
from flexget import schema
6
from flexget.plugin import register_plugin, register_parser_option, priority
7
from flexget.manager import Session
8
from flexget.utils.tools import console
9
from flexget.utils.sqlalchemy_utils import table_add_column
13
log = logging.getLogger('failed')
14
Base = schema.versioned_base('failed', SCHEMA_VER)
17
@schema.upgrade('failed')
18
def upgrade(ver, session):
21
table_add_column('failed', 'count', Integer, session, default=1)
25
log.info('Adding database index ...')
26
meta = MetaData(bind=session.connection(), reflect=True)
27
failed = meta.tables['failed']
28
Index('failed_title_url', failed.c.title, failed.c.url, failed.c.count).create()
33
class FailedEntry(Base):
34
__tablename__ = 'failed'
36
id = Column(Integer, primary_key=True)
37
title = Column(Unicode)
39
tof = Column(DateTime)
40
count = Column(Integer, default=1)
42
def __init__(self, title, url):
45
self.tof = datetime.now()
48
return '<Failed(title=%s)>' % self.title
50
# create indexes, used when creating tables
51
columns = Base.metadata.tables['failed'].c
52
Index('failed_title_url', columns.title, columns.url, columns.count)
55
class PluginFailed(object):
56
"""Provides tracking for failures and related commandline utilities."""
58
def on_process_start(self, feed, config):
59
if feed.manager.options.failed:
60
feed.manager.disable_feeds()
63
if feed.manager.options.clear_failed:
64
feed.manager.disable_feeds()
68
def print_failed(self):
69
"""Parameter --failed"""
73
results = failed.query(FailedEntry).all()
75
console('No failed entries recorded')
77
console('%16s - %s - %s times' % (entry.tof.strftime('%Y-%m-%d %H:%M'), entry.title, entry.count))
81
# TODO: add reason support
82
def add_failed(self, entry):
83
"""Adds entry to internal failed list, displayed with --failed"""
86
# query item's existence
87
item = failed.query(FailedEntry).filter(FailedEntry.title == entry['title']).\
88
filter(FailedEntry.url == entry['url']).first()
90
item = FailedEntry(entry['title'], entry['url'])
93
item.tof = datetime.now()
95
log.debug('Marking %s in failed list. Has failed %s times.' % (item.title, item.count))
97
# limit item number to 25
98
for row in failed.query(FailedEntry).order_by(FailedEntry.tof.desc())[25:]:
104
def clear_failed(self):
105
"""Clears list of failed entries"""
108
results = session.query(FailedEntry).all()
111
console('Cleared %i items.' % len(results))
116
def on_entry_fail(self, feed, entry, **kwargs):
117
self.add_failed(entry)
120
class FilterRejectFailed(object):
121
"""Rejects entries that have failed X or more times in the past."""
124
from flexget import validator
125
root = validator.factory()
126
root.accept('integer')
127
root.accept('boolean')
131
def on_feed_filter(self, feed, config):
134
max_count = 3 if config in [None, True] else config
135
for entry in feed.entries:
136
item = feed.session.query(FailedEntry).filter(FailedEntry.title == entry['title']).\
137
filter(FailedEntry.url == entry['url']).\
138
filter(FailedEntry.count >= max_count).first()
140
feed.reject(entry, 'Has already failed %s times in the past' % item.count)
142
register_plugin(PluginFailed, '--failed', builtin=True, api_ver=2)
143
register_plugin(FilterRejectFailed, 'reject_failed', builtin=True, api_ver=2)
144
register_parser_option('--failed', action='store_true', dest='failed', default=0,
145
help='List recently failed entries.')
146
register_parser_option('--clear', action='store_true', dest='clear_failed', default=0,
147
help='Clear recently failed list.')