List Interface¶
List interface is a unique type of plugin that enables manipulation of its content using flexget normal task operation. Different phases and interaction with the plugin enable using it as an input, filter or removing entries from it. It’s especially useful for watchlist type list, but not limited to those. Any list that can return entries can be used as a list interface plugin.
The class of the plugin is based on python’s MutableSet with overrides of its methods where needed.
Usage¶
As various plugins have different meaning, the specific of the implementation can change. However a few specific override methods will always be needed, in addition to a few custom ones required by flexget.
Init¶
class ListInterfaceClass(MutableSet):
def __init__(self, config):
self.config = config
The init method should pass the config to a class variable, that will be used by other class methods. Also, any other
global data that is need for the class operation to work should be retrieved. For example, from trakt_list
:
def __init__(self, config):
self.config = config
if self.config.get('account') and not self.config.get('username'):
self.config['username'] = 'me'
self.session = get_session(self.config.get('account'))
# Lists may not have modified results if modified then accessed in quick succession.
self.session.add_domain_limiter(TimedLimiter('trakt.tv', '2 seconds'))
self._items = None
Note the usage of self._items
. In case of an online list, the data should be fetch as little as possible, so a local
cache that can be invalidated should be created. Then a property method that call on that data should be used throughout
the class:
@property
def items(self):
if self._items is None:
do_stuff()
self._items = entries
return self._items
The cache could be invalidated when need by simply resetting the local cache:
self._items = None
Overridden methods¶
Below are code examples of overridden method taken from trakt_list
and entry_list
.
__iter__
¶
def __iter__(self):
return iter(self.items)
__len__
¶
def __len__(self):
return len(self.items)
__discard__
¶
def discard(self, entry, session=None):
db_entry = self._entry_query(session=session, entry=entry)
if db_entry:
log.debug('deleting entry %s', db_entry)
session.delete(db_entry)
__ior__
¶
def __ior__(self, other):
# Optimization to only open one session when adding multiple items
# Make sure lazy lookups are done before opening our session to prevent db locks
for value in other:
value.values()
with Session() as session:
for value in other:
self.add(value, session=session)
return self
__contains__
¶
@with_session
def __contains__(self, entry, session=None):
return self._entry_query(session, entry) is not None
__add__
¶
def add(self, entry):
self.submit([entry])
___from_iterable__
¶
def _from_iterable(self, it):
return set(it)
Custom methods¶
These are custom methods that all list type plugin need to implement to work with flexget.
immutable
¶
Used to specify if some elements of the list plugins are immutable.
IMMUTABLE_LISTS = ['ratings', 'checkins']
@property
def immutable(self):
if self.config['list'] in IMMUTABLE_LISTS:
return '%s list is not modifiable' % self.config['list']
online
¶
Used to determine whether this plugin is an online one and change functionality accordingly in certain situations, like test mode.
@property
def online(self):
""" Set the online status of the plugin, online plugin should be treated differently in certain situations,
like test mode"""
return True
get
¶
Used to return entry match from internal used. list_queue
plugin calls it in order to create a cached list of entries
and avoid acceptance duplication during filter phase.
@with_session
def get(self, entry, session):
match = self._find_entry(entry=entry, session=session)
return match.to_entry() if match else None
Plugin format¶
After creating the base class, the plugin class itself need to be created.
class EntryList:
schema = {'type': 'string'}
@staticmethod
def get_list(config):
return DBEntrySet(config)
def on_task_input(self, task, config):
return list(DBEntrySet(config))
@event('plugin.register')
def register_plugin():
plugin.register(EntryList, 'entry_list', api_ver=2, interfaces=['list', 'task'])
All list plugins must declare the list interface, and implement the get_list(config)
method. Declaring the task
interface and the on_task_input
method will allow the plugin to be used as an input plugin.