Source code for flexget.plugins.services.myepisodes

import re
from datetime import datetime

from loguru import logger
from sqlalchemy import Column, DateTime, Integer, String

from flexget import plugin
from flexget.db_schema import versioned_base
from flexget.event import event
from flexget.utils import requests

logger = logger.bind(name='myepisodes')
Base = versioned_base('myepisodes', 0)


[docs] class MyEpisodesInfo(Base): __tablename__ = 'myepisodes' id = Column(Integer, primary_key=True) series_name = Column(String, unique=True) myepisodes_id = Column(Integer, unique=True) updated = Column(DateTime) def __init__(self, series_name, myepisodes_id): self.series_name = series_name self.myepisodes_id = myepisodes_id self.updated = datetime.now() def __repr__(self): return ( f'<MyEpisodesInfo(series_name={self.series_name}, myepisodes_id={self.myepisodes_id})>' )
[docs] class MyEpisodes: """Marks a series episode as acquired in your myepisodes.com account. Simple Example: Most shows are recognized automatically from their TVDBname. And of course the plugin needs to know your MyEpisodes.com account details. tasks: tvshows: myepisodes: username: <username> password: <password> series: - human target - chuck Advanced Example: In some cases, the TVDB name is either not unique or won't even be discovered. In that case you need to specify the MyEpisodes id manually using the set plugin. .. code:: yaml tasks: tvshows: myepisodes: username: <username> password: <password> series: - human target: set: myepisodes_id: 5111 - chuck How to find the MyEpisodes id: http://matrixagents.org/screencasts/myep_example-20110507-131555.png """ schema = { 'type': 'object', 'properties': {'username': {'type': 'string'}, 'password': {'type': 'string'}}, 'required': ['username', 'password'], 'additionalProperties': False, } def __init__(self): self.plugin_config = None self.db_session = None self.test_mode = None self.http_session = None
[docs] @plugin.priority(plugin.PRIORITY_LAST) def on_task_output(self, task, config): """Mark all accepted episodes as acquired on MyEpisodes.""" if not task.accepted: # Nothing accepted, don't do anything return try: self.plugin_config = config self.db_session = task.session self.test_mode = task.options.test # attempt authentication self.http_session = self._login(config) except plugin.PluginWarning as w: logger.warning(w) return except plugin.PluginError as e: logger.error(e) return for entry in task.accepted: # mark the accepted entries as acquired try: self._validate_entry(entry) entry['myepisodes_id'] = self._lookup_myepisodes_id(entry) self._mark_episode_acquired(entry) except plugin.PluginWarning as w: logger.warning(w)
[docs] def _validate_entry(self, entry): """Check an entry for all of the fields needed to communicate with myepidoes. Return: boolean """ if ( 'series_season' not in entry or 'series_episode' not in entry or 'series_name' not in entry ): raise plugin.PluginWarning( "Can't mark entry `{}` in myepisodes without series_season, series_episode and series_name " 'fields'.format(entry['title']), logger, )
[docs] def _lookup_myepisodes_id(self, entry): """Attempt to find the myepisodes id for the series. Return: myepisode id or None """ # Do we already have the id? myepisodes_id = entry.get('myepisodes_id') if myepisodes_id: return myepisodes_id # have we previously recorded the id for this series? myepisodes_id = self._retrieve_id_from_database(entry) if myepisodes_id: return myepisodes_id # We don't know the id for this series, so it's time to search myepisodes.com for it myepisodes_id = self._retrieve_id_from_website(entry) if myepisodes_id: return myepisodes_id raise plugin.PluginWarning( 'Unable to determine the myepisodes id for: `{}`'.format(entry['title']), logger )
[docs] def _retrieve_id_from_database(self, entry): """Attempt to find the myepisodes id in the database. Return: myepisode id or None """ lc_series_name = entry['series_name'].lower() info = ( self.db_session .query(MyEpisodesInfo) .filter(MyEpisodesInfo.series_name == lc_series_name) .first() ) if info: return info.myepisodes_id return None
[docs] def _retrieve_id_from_website(self, entry): """Attempt to find the myepisodes id for the series for the website itself. Return: myepisode id or None """ myepisodes_id = None baseurl = 'http://www.myepisodes.com/search/' search_value = self._generate_search_value(entry) payload = {'tvshow': search_value, 'action': 'Search'} try: response = self.http_session.post(baseurl, data=payload) regex = r'"/epsbyshow\/([0-9]*)\/.*">' + search_value + '</a>' match_obj = re.search(regex, response.text, re.MULTILINE | re.IGNORECASE) if match_obj: myepisodes_id = match_obj.group(1) self._save_id(search_value, myepisodes_id) except requests.RequestException as e: raise plugin.PluginError(f'Error searching for myepisodes id: {e}') return myepisodes_id
[docs] def _generate_search_value(self, entry): """Find the TVDB name for searching myepisodes with. myepisodes.com is backed by tvrage, so this will not be perfect. Return: myepisode id or None """ search_value = entry['series_name'] # Get the series name from thetvdb to increase match chance on myepisodes if entry.get('tvdb_series_name'): search_value = entry['tvdb_series_name'] else: try: series = plugin.get('api_tvdb', self).lookup_series( name=entry['series_name'], tvdb_id=entry.get('tvdb_id') ) search_value = series.name except LookupError: logger.warning( 'Unable to lookup series `{}` from tvdb, using raw name.', entry['series_name'] ) return search_value
[docs] def _save_id(self, series_name, myepisodes_id): """Save the myepisodes id in the database. This will help prevent unecceary communication with the website """ # if we already have the a record for that id, update the name so that we find it next time db_item = ( self.db_session .query(MyEpisodesInfo) .filter(MyEpisodesInfo.myepisodes_id == myepisodes_id) .first() ) if db_item: logger.info( 'Changing name to `{}` for series with myepisodes_id {}', series_name.lower(), myepisodes_id, ) db_item.series_name = series_name.lower() else: self.db_session.add(MyEpisodesInfo(series_name.lower(), myepisodes_id))
[docs] def _mark_episode_acquired(self, entry): """Mark episode as acquired. Required entry fields: - series_name - series_season - series_episode Raises: PluginWarning if operation fails """ url = 'http://www.myepisodes.com/ajax/service.php?mode=eps_update' myepisodes_id = entry['myepisodes_id'] season = entry['series_season'] episode = entry['series_episode'] super_secret_code = f'A{myepisodes_id!s}-{season!s}-{episode!s}' payload = {super_secret_code: 'true'} if self.test_mode: logger.info( 'Would mark {} of `{}` as acquired.', entry['series_id'], entry['series_name'] ) return try: self.http_session.post(url, data=payload) except requests.RequestException: raise plugin.PluginError( 'Failed to mark {} of `{}` as acquired.'.format( entry['series_id'], entry['series_name'] ) ) logger.info('Marked {} of `{}` as acquired.', entry['series_id'], entry['series_name'])
[docs] def _login(self, config): """Authenticate with the myepisodes service and return a requests session. Return: requests session Raises: PluginWarning if login fails PluginError if http communication fails """ url = 'https://www.myepisodes.com/login.php' session = requests.Session() payload = { 'username': config['username'], 'password': config['password'], 'action': 'Login', } try: response = session.post(url, data=payload) if 'login' in response.url: raise plugin.PluginWarning( ( 'Login to myepisodes.com failed, please see if the site is down and verify ' 'your credentials.' ), logger, ) except requests.RequestException as e: raise plugin.PluginError(f'Error logging in to myepisodes: {e}') return session
[docs] @event('plugin.register') def register_plugin(): plugin.register(MyEpisodes, 'myepisodes', api_ver=2)