2
from flexget.event import event
3
from flexget.manager import Base
4
from flexget.entry import Entry
5
from flexget.plugin import priority, register_parser_option, register_plugin, PluginError
6
from flexget.utils.tools import console
7
from sqlalchemy import Column, Integer, DateTime, Unicode, Index, or_
8
from datetime import datetime
9
from flexget.manager import Session
10
from optparse import SUPPRESS_HELP
12
log = logging.getLogger('archive')
15
class ArchiveEntry(Base):
17
__tablename__ = 'archive_entry'
19
id = Column(Integer, primary_key=True)
20
title = Column(Unicode, index=True)
21
url = Column(Unicode, index=True)
22
description = Column(Unicode)
23
feed = Column(Unicode)
24
added = Column(DateTime, index=True)
27
self.added = datetime.now()
30
return '<ArchiveEntry(title=%s,url=%s,feed=%s)>' % (self.title, self.url, self.feed)
33
columns = Base.metadata.tables['archive_entry'].c
34
Index('archive_feed_title', columns.feed, columns.title)
37
def search(session, text):
38
"""Search by :text: from archive"""
39
keyword = unicode(text).replace(' ', '%')
40
return session.query(ArchiveEntry).filter(or_(ArchiveEntry.title.like('%' + keyword + '%'),
41
ArchiveEntry.feed == keyword)).all()
44
class ArchiveSearch(object):
46
def on_process_start(self, feed):
47
if not feed.manager.options.archive_search:
50
from flexget.utils.tools import strip_html
52
feed.manager.disable_feeds()
55
diff = datetime.now() - ae.added
57
console('ID: %-6s | Feed: %-10s | Title: %s\nAdded: %s (%d days ago)\nURL: %s' % \
58
(ae.id, ae.feed, ae.title, ae.added, diff.days, ae.url))
60
console('Description: %s' % strip_html(ae.description))
65
console('Searching ...')
66
for ae in search(session, feed.manager.options.archive_search):
72
@event('manager.execute.started')
73
def reset_archive_inject(manager):
74
log.debug('reseting injection')
75
ArchiveInject.inject_entries = []
78
class ArchiveInject(object):
83
def on_process_start(self, feed):
84
inject_ids = feed.manager.options.archive_inject_id
88
# get the entries to be injected
89
if not self.inject_entries:
92
for id_str in inject_ids.split(','):
94
id = int(id_str.strip())
96
raise PluginError('Given ID `%s` is not a valid number' % id_str.strip())
97
archive_entry = session.query(ArchiveEntry).filter(ArchiveEntry.id == id).first()
100
if not archive_entry:
101
log.critical('There\'s no archive with ID `%s`' % id)
104
# feed no longer exists
105
if archive_entry.feed not in feed.manager.feeds:
106
log.critical('Feed `%s` does not seem to exists anymore, cannot inject from archive' % archive_entry.feed)
109
self.inject_entries.append(archive_entry)
113
# if this feed is not going to be injected into, abort it
114
injecting_into = [x.feed for x in self.inject_entries]
115
log.debug('injecting_into=%s' % injecting_into)
116
if feed.name not in injecting_into:
117
log.debug('not injecting to %s, aborting, disabling' % feed.name)
119
feed.abort(silent=True)
121
log.debug('injecting to %s, keeping it' % feed.name)
124
def on_feed_input(self, feed):
125
if not feed.manager.options.archive_inject_id:
128
# disable other inputs
129
log.info('Disabling all other inputs in the feed.')
130
feed.disable_phase('input')
132
for inject_entry in self.inject_entries:
133
if inject_entry.feed != feed.name:
134
# wrong feed, continue to next item
136
log.info('Injecting from archive `%s`' % inject_entry.title)
137
entry = Entry(inject_entry.title, inject_entry.url)
138
if inject_entry.description:
139
entry['description'] = inject_entry.description
140
if feed.manager.options.archive_inject_immortal:
141
log.debug('Injecting as immortal')
142
entry['immortal'] = True
143
feed.entries.append(entry)
144
feed.accept(entry, '--archive-inject')
147
class Archive(object):
149
Archives all seen items into database where they can be later searched and injected.
153
from flexget import validator
154
return validator.factory('boolean')
157
def on_feed_input(self, feed):
158
"""Add new entries into archive"""
159
for entry in feed.entries:
160
if feed.session.query(ArchiveEntry).\
161
filter(ArchiveEntry.title == entry['title']).\
162
filter(ArchiveEntry.feed == feed.name).first():
163
log.debug('Entry `%s` already archived' % entry['title'])
166
ae.title = entry['title']
167
ae.url = entry['url']
168
if 'description' in entry:
169
ae.description = entry['description']
171
log.debug('Adding `%s` to archive' % ae)
175
class UrlrewriteArchive(object):
177
entry_map = {'title': 'title',
179
'description': 'description'}
181
def search(self, query, comparator, config=None):
182
"""Search plugin API method"""
183
# TODO: Implement comparator matching
185
log.debug('looking for %s' % query)
186
results = search(session, query)
188
# TODO: some logic to return best match? what about quality?
189
log.debug('found %s' % len(results))
191
for result in results:
193
entry.update_using_map(self.entry_map, result)
195
entries.append(entry)
199
def archive_inject(option, opt, value, parser):
200
"""Option parser function"""
203
console('Usage: --archive-inject ID [IMMORTAL]')
207
parser.values.archive_inject_id = parser.rargs[0]
209
if len(parser.rargs) >= 2:
210
from flexget.utils.tools import str_to_boolean
211
parser.values.archive_inject_immortal = str_to_boolean(parser.rargs[1])
214
register_plugin(Archive, 'archive')
215
register_plugin(UrlrewriteArchive, 'flexget_archive', groups=['search'])
216
register_plugin(ArchiveSearch, '--archive-search', builtin=True)
217
register_plugin(ArchiveInject, '--archive-inject', builtin=True)
219
register_parser_option('--archive-search', action='store', dest='archive_search', default=False,
220
metavar='TXT', help='Search from the archive.')
221
register_parser_option('--archive-inject', action='callback', callback=archive_inject,
223
help='Inject entries from the archive into a feed where it was archived from. Usage: ID[,ID] [FORCE]')
225
# kludge to make these option keys available always, even when not using archive_inject callback
226
register_parser_option('--archive-inject-id', action='store', dest='archive_inject_id', default=False,
228
register_parser_option('--archive-inject-immortal', action='store', dest='archive_inject_immortal', default=False,