2
from flexget.feed import Entry
3
from flexget.plugin import register_plugin, register_parser_option, get_plugin_by_name, DependencyError
4
from flexget.utils.cached_input import cached
8
log = logging.getLogger('tail')
11
class ResetTail(object):
12
"""Adds --tail-reset"""
14
def on_process_start(self, feed):
15
if not feed.manager.options.tail_reset:
18
feed.manager.disable_feeds()
20
from flexget.utils.simple_persistence import SimpleKeyValue
21
from flexget.manager import Session
25
poses = session.query(SimpleKeyValue).filter(SimpleKeyValue.key == feed.manager.options.tail_reset).all()
27
print 'No position stored for file %s' % feed.manager.options.tail_reset
28
print 'Note that file must give in same format as in config, ie. ~/logs/log can not be given as /home/user/logs/log'
31
print 'Feed %s tail position is already zero' % pos.feed
33
print 'Feed %s tail position (%s) reseted to zero' % (pos.feed, pos.value)
40
class InputTail(object):
43
Parse any text for entries using regular expression.
47
<field>: <regexp to match value>
49
<field>: <python string formatting>
51
Note: each entry must have atleast two fields, title and url
56
file: ~/irclogs/some/log
58
title: 'TITLE: (.*) URL:'
63
from flexget import validator
64
root = validator.factory('dict')
65
root.accept('file', key='file', required=True)
66
entry = root.accept('dict', key='entry', required=True)
67
entry.accept('regexp', key='url', required=True)
68
entry.accept('regexp', key='title', required=True)
69
entry.accept_any_key('regexp')
70
format = root.accept('dict', key='format')
71
format.accept_any_key('text')
74
def format_entry(self, entry, d):
75
for k, v in d.iteritems():
78
@cached('tail', 'file')
79
def on_feed_input(self, feed):
82
# details plugin will complain if no entries are created, with this we disable that
83
details = get_plugin_by_name('details').instance
84
if feed.name not in details.no_entries_ok:
85
log.debug('appending %s to details plugin no_entries_ok' % feed.name)
86
details.no_entries_ok.append(feed.name)
87
except DependencyError:
88
log.debug('unable to get details plugin')
90
filename = os.path.expanduser(feed.config['tail']['file'])
91
file = open(filename, 'r')
93
last_pos = feed.simple_persistence.setdefault(filename, 0)
94
if os.path.getsize(filename) < last_pos:
95
log.info('File size is smaller than in previous execution, reseting to beginning of the file')
100
log.debug('continuing from last position %s' % last_pos)
102
entry_config = feed.config['tail'].get('entry')
103
format_config = feed.config['tail'].get('format', {})
105
# keep track what fields have been found
111
line = file.readline()
113
feed.simple_persistence.set(filename, file.tell())
116
for field, regexp in entry_config.iteritems():
117
#log.debug('search field: %s regexp: %s' % (field, regexp))
118
match = re.search(regexp, line)
120
# check if used field detected, in such case start with new entry
121
if used.has_key(field):
123
log.info('Found field %s again before entry was completed. \
124
Adding current incomplete, but valid entry and moving to next.' % field)
125
self.format_entry(entry, format_config)
126
feed.entries.append(entry)
128
log.info('Invalid data, entry field %s is already found once. Ignoring entry.' % field)
134
entry[field] = match.group(1)
136
log.debug('found field: %s value: %s' % (field, entry[field]))
138
# if all fields have been found
139
if len(used) == len(entry_config):
140
# check that entry has at least title and url
141
if not entry.isvalid():
142
log.info('Invalid data, constructed entry is missing mandatory fields (title or url)')
144
self.format_entry(entry, format_config)
145
feed.entries.append(entry)
146
log.debug('Added entry %s' % entry)
151
register_plugin(InputTail, 'tail')
152
register_plugin(ResetTail, '--tail-reset', builtin=True)
153
register_parser_option('--tail-reset', action='store', dest='tail_reset', default=False, metavar='FILE',
154
help='Reset tail position for a file.')