from loguru import logger
from flexget import plugin
from flexget.event import event
from flexget.utils.log import log_once
logger = logger.bind(name='thetvdb')
[docs]
class FilterTvdb:
"""Allow filtering based on thetvdb properties.
Allowed properties are series rating,
episode rating, status, genres, runtime, content-rating,
languages, directors, writers, network, guest stars, episode
rating, and actors
Configuration:
Note: All parameters are optional. Some are mutually exclusive. ::
min_series_rating: <num>
min_episode_rating: <num>
min_episode_air_year: <num>
max_episode_air_year: <num>
min_episode_runtime: <num>
max_episode_runtime: <num>
# reject if genre contains any of these
reject_content_rating:
- TV-MA
# accept only this content rating
accept_content_rating:
- TV-PG
# accept only these networks
accept_network:
- NBC
# reject if this network
reject_network:
- ABC
# reject if genre contains any of these
reject_genres:
- drama
- romance
# reject if status contains any of these
reject_status:
- Ended
# reject if language contain any of these
reject_languages:
- fr
# accept only this language
accept_languages:
- en
# Actors below take into account series actors, and guest stars
# accept episode with any of these actors
accept_actors:
- Heidi Klum
- Bruce Willis
# reject episode if it has any of these actors
reject_actors:
- Cher
- Tamala Jones
# accept all episodes by these writers
accept_writers:
- Andrew W. Marlowe
# reject episodes by these writers
reject_writers:
- Barry Schindel
# accept all episodes by these directors
accept_directors:
- Rob Bowman
# reject movies by these directors
reject_directors:
- John Terlesky
"""
schema = {
'type': 'object',
'properties': {
'min_series_rating': {'type': 'number'},
'min_episode_rating': {'type': 'number'},
'min_episode_air_year': {'type': 'integer'},
'max_episode_air_year': {'type': 'integer'},
'min_episode_runtime': {'type': 'number'},
'max_episode_runtime': {'type': 'number'},
'reject_content_rating': {'type': 'array', 'items': {'type': 'string'}},
'accept_content_rating': {'type': 'array', 'items': {'type': 'string'}},
'accept_network': {'type': 'array', 'items': {'type': 'string'}},
'reject_network': {'type': 'array', 'items': {'type': 'string'}},
'reject_genres': {'type': 'array', 'items': {'type': 'string'}},
'reject_status': {'type': 'array', 'items': {'type': 'string'}},
'accept_actors': {'type': 'array', 'items': {'type': 'string'}},
'reject_actors': {'type': 'array', 'items': {'type': 'string'}},
'accept_directors': {'type': 'array', 'items': {'type': 'string'}},
'reject_directors': {'type': 'array', 'items': {'type': 'string'}},
},
'additionalProperties': False,
}
[docs]
def is_in_set(self, config, configkey, entryitem):
"""Return True if the object matches, (be that a subset of the list, or if the entry item is contained within the config object list) or false if it does not."""
# will want to port this over to filter_imdb as well, for code
# clarity in that module.
if configkey in config:
configlist = config[configkey]
if isinstance(entryitem, list):
for item in entryitem:
if item in configlist:
return True
elif entryitem in configlist:
return True
return False
[docs]
@plugin.priority(126)
def on_task_filter(self, task, config):
lookup = plugin.get('thetvdb_lookup', self).lookup
for entry in task.entries:
force_accept = False
try:
lookup(task, entry)
except plugin.PluginError as e:
logger.error('Skipping {} because of an error: {}', entry['title'], e.value)
continue
# Check defined conditions
reasons = []
if (
'min_series_rating' in config
and entry['tvdb_rating'] < config['min_series_rating']
):
reasons.append(
'series_rating ({} < {})'.format(
entry['tvdb_rating'], config['min_series_rating']
)
)
if (
'min_episode_rating' in config
and entry['tvdb_ep_rating'] < config['min_episode_rating']
):
reasons.append(
'tvdb_ep_rating ({} < {})'.format(
entry['tvdb_ep_rating'], config['min_episode_rating']
)
)
if (
'min_episode_air_year' in config
and entry['tvdb_ep_air_date'].strftime('%Y') < config['min_episode_air_year']
):
reasons.append(
'tvdb_ep_air_date ({} < {})'.format(
entry['tvdb_ep_air_date'].strftime('%Y'),
config['min_episode_air_year'],
)
)
if (
'max_episode_air_year' in config
and entry['tvdb_ep_air_date'].strftime('%Y') > config['max_episode_air_year']
):
reasons.append(
'tvdb_ep_air_date ({} < {})'.format(
entry['tvdb_ep_air_date'].strftime('%Y'),
config['max_episode_air_year'],
)
)
if self.is_in_set(config, 'reject_content_rating', entry['tvdb_content_rating']):
reasons.append('reject_content_rating')
if not self.is_in_set(config, 'accept_content_rating', entry['tvdb_content_rating']):
reasons.append('accept_content_rating')
if self.is_in_set(config, 'reject_network', entry['tvdb_network']):
reasons.append('reject_network')
if not self.is_in_set(config, 'accept_network', entry['tvdb_network']):
reasons.append('accept_network')
if self.is_in_set(config, 'reject_genres', entry['tvdb_genres']):
reasons.append('reject_genres')
if self.is_in_set(config, 'reject_status', entry['tvdb_status']):
reasons.append('reject_status')
# Accept if actors contains an accepted actor, but don't reject otherwise
if self.is_in_set(
config, 'accept_actors', entry['tvdb_actors'] + entry['tvdb_ep_guest_stars']
):
force_accept = True
if self.is_in_set(
config, 'reject_actors', entry['tvdb_actors'] + entry['tvdb_ep_guest_stars']
):
reasons.append('reject_genres')
# Accept if director is an accepted director, but don't reject otherwise
if self.is_in_set(config, 'accept_directors', entry['tvdb_ep_director']):
force_accept = True
if self.is_in_set(config, 'reject_directors', entry['tvdb_ep_director']):
reasons.append('reject_directors')
if reasons and not force_accept:
msg = 'Skipping {} because of rule(s) {}'.format(
entry.get('series_name_thetvdb', None) or entry['title'],
', '.join(reasons),
)
if task.options.debug:
logger.debug(msg)
else:
log_once(msg, logger)
else:
logger.debug('Accepting {}', entry)
entry.accept()
[docs]
@event('plugin.register')
def register_plugin():
plugin.register(FilterTvdb, 'thetvdb', api_ver=2)