From cfc9f524a70f5e2e44603e5be3007e8786b79814 Mon Sep 17 00:00:00 2001 From: Ruud Date: Mon, 17 Mar 2014 22:11:55 +0100 Subject: [PATCH] Cleanup --- couchpotato/core/media/show/_base/main.py | 268 +++++---------------- couchpotato/core/media/show/episode.py | 125 ++++++++++ couchpotato/core/media/show/library/__init__.py | 0 couchpotato/core/media/show/library/episode.py | 173 ------------- couchpotato/core/media/show/library/season.py | 158 ------------ couchpotato/core/media/show/library/show.py | 196 --------------- couchpotato/core/media/show/matcher.py | 4 +- .../core/media/show/providers/nzb/binsearch.py | 4 +- .../core/media/show/providers/nzb/newznab.py | 8 +- .../core/media/show/providers/nzb/nzbclub.py | 4 +- .../core/media/show/providers/nzb/nzbindex.py | 4 +- .../core/media/show/providers/torrent/bithdtv.py | 4 +- .../core/media/show/providers/torrent/bitsoup.py | 4 +- .../core/media/show/providers/torrent/publichd.py | 4 +- .../media/show/providers/torrent/sceneaccess.py | 4 +- .../media/show/providers/torrent/thepiratebay.py | 4 +- .../media/show/providers/torrent/torrentday.py | 4 +- .../media/show/providers/torrent/torrentleech.py | 4 +- .../media/show/providers/torrent/torrentpotato.py | 4 +- .../media/show/providers/torrent/torrentshack.py | 4 +- couchpotato/core/media/show/searcher.py | 3 +- couchpotato/core/media/show/season.py | 114 +++++++++ 22 files changed, 333 insertions(+), 764 deletions(-) create mode 100644 couchpotato/core/media/show/episode.py delete mode 100644 couchpotato/core/media/show/library/__init__.py delete mode 100644 couchpotato/core/media/show/library/episode.py delete mode 100644 couchpotato/core/media/show/library/season.py delete mode 100644 couchpotato/core/media/show/library/show.py create mode 100644 couchpotato/core/media/show/season.py diff --git a/couchpotato/core/media/show/_base/main.py b/couchpotato/core/media/show/_base/main.py index 5207929..8e26c6a 100644 --- a/couchpotato/core/media/show/_base/main.py +++ b/couchpotato/core/media/show/_base/main.py @@ -14,20 +14,47 @@ log = CPLog(__name__) class ShowBase(MediaBase): _type = 'show' + query_condenser = QueryCondenser() def __init__(self): super(ShowBase, self).__init__() addApiView('show.add', self.addView, docs = { - 'desc': 'Add new movie to the wanted list', + 'desc': 'Add new show to the wanted list', 'params': { - 'identifier': {'desc': 'IMDB id of the movie your want to add.'}, - 'profile_id': {'desc': 'ID of quality profile you want the add the movie in. If empty will use the default profile.'}, - 'title': {'desc': 'Movie title to use for searches. Has to be one of the titles returned by movie.search.'}, + 'identifier': {'desc': 'IMDB id of the show your want to add.'}, + 'profile_id': {'desc': 'ID of quality profile you want the add the show in. If empty will use the default profile.'}, + 'category_id': {'desc': 'ID of category you want the add the show in.'}, + 'title': {'desc': 'Title of the show to use for search and renaming'}, } }) addEvent('show.add', self.add) + addEvent('show.update_info', self.add) + + addEvent('media.search_query', self.query) + + def query(self, library, first = True, condense = True, **kwargs): + if library is list or library.get('type') != 'show': + return + + titles = [title['title'] for title in library['titles']] + + if condense: + # Use QueryCondenser to build a list of optimal search titles + condensed_titles = self.query_condenser.distinct(titles) + + if condensed_titles: + # Use condensed titles if we got a valid result + titles = condensed_titles + else: + # Fallback to simplifying titles + titles = [simplify(title) for title in titles] + + if first: + return titles[0] if titles else None + + return titles def addView(self, **kwargs): add_dict = self.add(params = kwargs) @@ -37,204 +64,35 @@ class ShowBase(MediaBase): 'show': add_dict, } - def add(self, params = {}, force_readd = True, search_after = True, update_library = False, status_id = None): - """ - params - {'category_id': u'-1', - 'identifier': u'tt1519931', - 'profile_id': u'12', - 'thetvdb_id': u'158661', - 'title': u'Haven'} - """ - log.debug("show.add") - - # Add show parent to db first; need to update library so maps will be in place (if any) - parent = self.addToDatabase(params = params, update_library = True, type = 'show') - - # TODO: add by airdate - - # Add by Season/Episode numbers - self.addBySeasonEpisode(parent, - params = params, - force_readd = force_readd, - search_after = search_after, - update_library = update_library, - status_id = status_id - ) - - def addBySeasonEpisode(self, parent, params = {}, force_readd = True, search_after = True, update_library = False, status_id = None): - identifier = params.get('id') - # 'tvdb' will always be the master for our purpose. All mapped data can be mapped - # to another source for downloading, but it will always be remapped back to tvdb numbering - # when renamed so media can be used in media players that use tvdb for info provider - # - # This currently means the episode must actually exist in tvdb in order to be found but - # the numbering can be different - - #master = 'tvdb' - #destination = 'scene' - #destination = 'anidb' - #destination = 'rage' - #destination = 'trakt' - # TODO: auto mode. if anime exists use it. if scene exists use it else use tvdb - - # XXX: We should abort adding show, etc if either tvdb or xem is down or we will have incorrent mappings - # I think if tvdb gets error we wont have anydata anyway, but we must make sure XEM returns!!!! - - # Only the master should return results here; all other info providers should just return False - # since we are just interested in the structure at this point. - seasons = fireEvent('season.info', merge = True, identifier = identifier) - if seasons is not None: - for season in seasons: - # Make sure we are only dealing with 'tvdb' responses at this point - if season.get('primary_provider', None) != 'thetvdb': - continue - season_id = season.get('id', None) - if season_id is None: continue - - season_params = {'season_identifier': season_id} - # Calling all info providers; merge your info now for individual season - single_season = fireEvent('season.info', merge = True, identifier = identifier, params = season_params) - single_season['category_id'] = params.get('category_id') - single_season['profile_id'] = params.get('profile_id') - single_season['title'] = single_season.get('original_title', None) - single_season['identifier'] = season_id - single_season['parent_identifier'] = identifier - log.info("Adding Season %s" % season_id) - s = self.addToDatabase(params = single_season, type = "season") - - episode_params = {'season_identifier': season_id} - episodes = fireEvent('episode.info', merge = True, identifier = identifier, params = episode_params) - if episodes is not None: - for episode in episodes: - # Make sure we are only dealing with 'tvdb' responses at this point - if episode.get('primary_provider', None) != 'thetvdb': - continue - episode_id = episode.get('id', None) - if episode_id is None: continue - try: - episode_number = int(episode.get('episodenumber', None)) - except (ValueError, TypeError): - continue - try: - absolute_number = int(episode.get('absolute_number', None)) - except (ValueError, TypeError): - absolute_number = None - - episode_params = {'season_identifier': season_id, - 'episode_identifier': episode_id, - 'episode': episode_number} - if absolute_number: - episode_params['absolute'] = absolute_number - # Calling all info providers; merge your info now for individual episode - single_episode = fireEvent('episode.info', merge = True, identifier = identifier, params = episode_params) - single_episode['category_id'] = params.get('category_id') - single_episode['profile_id'] = params.get('profile_id') - single_episode['title'] = single_episode.get('original_title', None) - single_episode['identifier'] = episode_id - single_episode['parent_identifier'] = single_season['identifier'] - log.info("Adding [%sx%s] %s - %s" % (season_id, - episode_number, - params['title'], - single_episode.get('original_title', ''))) - e = self.addToDatabase(params = single_episode, type = "episode") - - # Start searching now that all the media has been added - if search_after: - onComplete = self.createOnComplete(parent['id']) - onComplete() - - return parent - - def addToDatabase(self, params = {}, type = "show", force_readd = True, search_after = False, update_library = False, status_id = None): - log.debug("show.addToDatabase") - - if not params.get('identifier'): - msg = 'Can\'t add show without imdb identifier.' - log.error(msg) - fireEvent('notify.frontend', type = 'show.is_tvshow', message = msg) - return False - #else: - #try: - #is_show = fireEvent('movie.is_show', identifier = params.get('identifier'), single = True) - #if not is_show: - #msg = 'Can\'t add show, seems to be a TV show.' - #log.error(msg) - #fireEvent('notify.frontend', type = 'show.is_tvshow', message = msg) - #return False - #except: - #pass - - library = fireEvent('library.add.%s' % type, single = True, attrs = params, update_after = update_library) - if not library: - return False - - # Status - status_active, snatched_status, ignored_status, done_status, downloaded_status = \ - fireEvent('status.get', ['active', 'snatched', 'ignored', 'done', 'downloaded'], single = True) - - default_profile = fireEvent('profile.default', single = True) - cat_id = params.get('category_id', None) - - db = get_session() - m = db.query(Media).filter_by(library_id = library.get('id')).first() - added = True - do_search = False - if not m: - m = Media( - type = type, - library_id = library.get('id'), - profile_id = params.get('profile_id', default_profile.get('id')), - status_id = status_id if status_id else status_active.get('id'), - category_id = tryInt(cat_id) if cat_id is not None and tryInt(cat_id) > 0 else None, - ) - db.add(m) - db.commit() - - onComplete = None - if search_after: - onComplete = self.createOnComplete(m.id) - - fireEventAsync('library.update.%s' % type, params.get('identifier'), default_title = params.get('title', ''), on_complete = onComplete) - search_after = False - elif force_readd: - - # Clean snatched history - for release in m.releases: - if release.status_id in [downloaded_status.get('id'), snatched_status.get('id'), done_status.get('id')]: - if params.get('ignore_previous', False): - release.status_id = ignored_status.get('id') - else: - fireEvent('release.delete', release.id, single = True) - - m.profile_id = params.get('profile_id', default_profile.get('id')) - m.category_id = tryInt(cat_id) if cat_id is not None and tryInt(cat_id) > 0 else None - else: - log.debug('Show already exists, not updating: %s', params) - added = False - - if force_readd: - m.status_id = status_id if status_id else status_active.get('id') - m.last_edit = int(time.time()) - do_search = True - - db.commit() - - # Remove releases - available_status = fireEvent('status.get', 'available', single = True) - for rel in m.releases: - if rel.status_id is available_status.get('id'): - db.delete(rel) - db.commit() - - show_dict = m.to_dict(self.default_dict) - - if do_search and search_after: - onComplete = self.createOnComplete(m.id) - onComplete() - - if added: - fireEvent('notify.frontend', type = 'show.added', data = show_dict, message = 'Successfully added "%s" to your wanted list.' % params.get('title', '')) - - db.expire_all() - return show_dict + def add(self, params = {}, force_readd = True, search_after = True, update_library = False, status = None): + + db = get_db() + + # Add Show + show = { + 'identifiers': { + 'imdb': 'tt1234', + 'thetvdb': 123, + 'tmdb': 123, + 'rage': 123 + }, + 'status': 'active', + 'title': title, + 'description': description, + 'profile_id': profile_id, + 'category_id': category_id, + 'primary_provider': 'thetvdb', + 'absolute_nr': True, + 'info': {} + } + + show_info = fireEvent('show.info', show.get('identifiers')) + + # Add Seasons + for season_info in show_info.get('seasons', []): + + season = fireEvent('show.season.add', show.get('_id'), season_info) + + for episode_info in season_info.get('seasons', []): + + fireEvent('show.episode.add', season.get('_id'), episode_info) diff --git a/couchpotato/core/media/show/episode.py b/couchpotato/core/media/show/episode.py new file mode 100644 index 0000000..fb5fbdc --- /dev/null +++ b/couchpotato/core/media/show/episode.py @@ -0,0 +1,125 @@ +from couchpotato.core.event import addEvent, fireEvent +from couchpotato.core.helpers.encoding import toUnicode +from couchpotato.core.logger import CPLog +from couchpotato.core.helpers.variable import tryInt +from couchpotato.core.plugins.base import Plugin + + +log = CPLog(__name__) + +autload = 'Episode' + +class Episode(Plugin): + + def __init__(self): + addEvent('media.search_query', self.query) + addEvent('media.identifier', self.identifier) + + addEvent('show.episode.add', self.add) + addEvent('show.episode.update_info', self.update) + + def query(self, library, first = True, condense = True, include_identifier = True, **kwargs): + if library is list or library.get('type') != 'episode': + return + + # Get the titles of the season + if not library.get('related_libraries', {}).get('season', []): + log.warning('Invalid library, unable to determine title.') + return + + titles = fireEvent( + 'media.search_query', + library['related_libraries']['season'][0], + first=False, + include_identifier=include_identifier, + condense=condense, + + single=True + ) + + identifier = fireEvent('media.identifier', library, single = True) + + # Add episode identifier to titles + if include_identifier and identifier.get('episode'): + titles = [title + ('E%02d' % identifier['episode']) for title in titles] + + + if first: + return titles[0] if titles else None + + return titles + + + def identifier(self, library): + if library.get('type') != 'episode': + return + + identifier = { + 'season': None, + 'episode': None + } + + scene_map = library['info'].get('map_episode', {}).get('scene') + + if scene_map: + # Use scene mappings if they are available + identifier['season'] = scene_map.get('season') + identifier['episode'] = scene_map.get('episode') + else: + # Fallback to normal season/episode numbers + identifier['season'] = library.get('season_number') + identifier['episode'] = library.get('episode_number') + + + # Cast identifiers to integers + # TODO this will need changing to support identifiers with trailing 'a', 'b' characters + identifier['season'] = tryInt(identifier['season'], None) + identifier['episode'] = tryInt(identifier['episode'], None) + + return identifier + + def add(self, parent_id, update_after = True): + + # Add Season + season = { + 'nr': 1, + 'identifiers': { + 'imdb': 'tt1234', + 'thetvdb': 123, + 'tmdb': 123, + 'rage': 123 + }, + 'parent': '_id', + 'info': {}, # Returned dict by providers + } + + episode_exists = True or False + + if episode_exists: + pass #update existing + else: + pass # Add Episode + + + # Update library info + if update_after is not False: + handle = fireEventAsync if update_after is 'async' else fireEvent + handle('show.episode.update_info', season.get('_id'), default_title = toUnicode(attrs.get('title', ''))) + + return season + + def update_info(self, media_id = None, default_title = '', force = False): + + if self.shuttingDown(): + return + + # Get new info + fireEvent('episode.info', merge = True) + + # Update/create media + + + # Get images + + + return info diff --git a/couchpotato/core/media/show/library/__init__.py b/couchpotato/core/media/show/library/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/couchpotato/core/media/show/library/episode.py b/couchpotato/core/media/show/library/episode.py deleted file mode 100644 index 0461a23..0000000 --- a/couchpotato/core/media/show/library/episode.py +++ /dev/null @@ -1,173 +0,0 @@ -import time -import traceback - -from couchpotato import get_db -from couchpotato.core.event import addEvent, fireEvent -from couchpotato.core.helpers.encoding import toUnicode -from couchpotato.core.logger import CPLog -from couchpotato.core.media._base.library.base import LibraryBase -from couchpotato.core.helpers.variable import tryInt - - -log = CPLog(__name__) - -autload = 'EpisodeLibraryPlugin' - - -class EpisodeLibraryPlugin(LibraryBase): - - default_dict = {'titles': {}, 'files':{}} - - def __init__(self): - addEvent('library.query', self.query) - addEvent('library.identifier', self.identifier) - addEvent('library.update.episode', self.update) - - def query(self, library, first = True, condense = True, include_identifier = True, **kwargs): - if library is list or library.get('type') != 'episode': - return - - # Get the titles of the season - if not library.get('related_libraries', {}).get('season', []): - log.warning('Invalid library, unable to determine title.') - return - - titles = fireEvent( - 'library.query', - library['related_libraries']['season'][0], - first=False, - include_identifier=include_identifier, - condense=condense, - - single=True - ) - - identifier = fireEvent('library.identifier', library, single = True) - - # Add episode identifier to titles - if include_identifier and identifier.get('episode'): - titles = [title + ('E%02d' % identifier['episode']) for title in titles] - - - if first: - return titles[0] if titles else None - - return titles - - - def identifier(self, library): - if library.get('type') != 'episode': - return - - identifier = { - 'season': None, - 'episode': None - } - - scene_map = library['info'].get('map_episode', {}).get('scene') - - if scene_map: - # Use scene mappings if they are available - identifier['season'] = scene_map.get('season') - identifier['episode'] = scene_map.get('episode') - else: - # Fallback to normal season/episode numbers - identifier['season'] = library.get('season_number') - identifier['episode'] = library.get('episode_number') - - - # Cast identifiers to integers - # TODO this will need changing to support identifiers with trailing 'a', 'b' characters - identifier['season'] = tryInt(identifier['season'], None) - identifier['episode'] = tryInt(identifier['episode'], None) - - return identifier - - def update(self, media_id = None, identifier = None, default_title = '', force = False): - - if self.shuttingDown(): - return - - db = get_db() - - if media_id: - media = db.get('id', media_id) - else: - media = db.get('media', identifier, with_doc = True)['doc'] - - do_update = True - - if media.get('status') == 'done' and not force: - do_update = False - - episode_params = { - 'season_identifier': media.get('parent'), - 'episode_identifier': media.get('identifier'), - 'episode': media.get('episode_number'), - 'absolute': media.get('episode_number'), - } - info = fireEvent('episode.info', merge = True, params = episode_params) - - # Don't need those here - try: del info['in_wanted'] - except: pass - try: del info['in_library'] - except: pass - - if not info or len(info) == 0: - log.error('Could not update, no movie info to work with: %s', identifier) - return False - - # Main info - if do_update: - episode = { - 'plot': toUnicode(info.get('plot', '')), - 'tagline': toUnicode(info.get('tagline', '')), - 'year': info.get('year', 0), - 'status_id': 'done', - 'season_number': tryInt(info.get('seasonnumber', None)), - 'episode_number': tryInt(info.get('episodenumber', None)), - 'absolute_number': tryInt(info.get('absolute_number', None)), - 'last_updated': tryInt(info.get('lastupdated', time.time())), - } - - titles = info.get('titles', []) - log.debug('Adding titles: %s', titles) - counter = 0 - for title in titles: - if not title: - continue - title = toUnicode(title) - t = LibraryTitle( - title = title, - simple_title = self.simplifyTitle(title), - default = (len(default_title) == 0 and counter == 0) or len(titles) == 1 or title.lower() == toUnicode(default_title.lower()) or (toUnicode(default_title) == u'' and toUnicode(titles[0]) == title) - ) - library.titles.append(t) - counter += 1 - - media.update(episode) - db.update(media) - - # Files - images = info.get('images', []) - for image_type in ['poster']: - for image in images.get(image_type, []): - if not isinstance(image, (str, unicode)): - continue - - file_path = fireEvent('file.download', url = image, single = True) - if file_path: - file_obj = fireEvent('file.add', path = file_path, type_tuple = ('image', image_type), single = True) - try: - file_obj = db.query(File).filter_by(id = file_obj.get('id')).one() - library.files.append(file_obj) - db.commit() - - break - except: - log.debug('Failed to attach to library: %s', traceback.format_exc()) - - library_dict = library.to_dict(self.default_dict) - db.expire_all() - return library_dict diff --git a/couchpotato/core/media/show/library/season.py b/couchpotato/core/media/show/library/season.py deleted file mode 100644 index 0a9ff2c..0000000 --- a/couchpotato/core/media/show/library/season.py +++ /dev/null @@ -1,158 +0,0 @@ -import traceback - -from couchpotato import get_session -from couchpotato.core.event import addEvent, fireEvent -from couchpotato.core.helpers.encoding import toUnicode -from couchpotato.core.logger import CPLog -from couchpotato.core.media._base.library.base import LibraryBase -from couchpotato.core.helpers.variable import tryInt - - -log = CPLog(__name__) - -autload = 'SeasonLibraryPlugin' - - -class SeasonLibraryPlugin(LibraryBase): - - def __init__(self): - addEvent('library.query', self.query) - addEvent('library.identifier', self.identifier) - addEvent('library.update.season', self.update) - - def query(self, library, first = True, condense = True, include_identifier = True, **kwargs): - if library is list or library.get('type') != 'season': - return - - # Get the titles of the show - if not library.get('related_libraries', {}).get('show', []): - log.warning('Invalid library, unable to determine title.') - return - - titles = fireEvent( - 'library.query', - library['related_libraries']['show'][0], - first=False, - condense=condense, - - single=True - ) - - # Add season map_names if they exist - if 'map_names' in library['info']: - season_names = library['info']['map_names'].get(str(library['season_number']), {}) - - # Add titles from all locations - # TODO only add name maps from a specific location - for location, names in season_names.items(): - titles += [name for name in names if name and name not in titles] - - - identifier = fireEvent('library.identifier', library, single = True) - - # Add season identifier to titles - if include_identifier and identifier.get('season') is not None: - titles = [title + (' S%02d' % identifier['season']) for title in titles] - - - if first: - return titles[0] if titles else None - - return titles - - def identifier(self, library): - if library.get('type') != 'season': - return - - return { - 'season': tryInt(library['season_number'], None) - } - - def update(self, identifier, default_title = '', force = False): - - if self.shuttingDown(): - return - - db = get_session() - library = db.query(SeasonLibrary).filter_by(identifier = identifier).first() - done_status = fireEvent('status.get', 'done', single = True) - - if library: - library_dict = library.to_dict(self.default_dict) - - do_update = True - - parent_identifier = None - if library.parent is not None: - parent_identifier = library.parent.identifier - - if library.status_id == done_status.get('id') and not force: - do_update = False - - season_params = {'season_identifier': identifier} - info = fireEvent('season.info', merge = True, identifier = parent_identifier, params = season_params) - - # Don't need those here - try: del info['in_wanted'] - except: pass - try: del info['in_library'] - except: pass - - if not info or len(info) == 0: - log.error('Could not update, no movie info to work with: %s', identifier) - return False - - # Main info - if do_update: - library.plot = toUnicode(info.get('plot', '')) - library.tagline = toUnicode(info.get('tagline', '')) - library.year = info.get('year', 0) - library.status_id = done_status.get('id') - library.season_number = tryInt(info.get('seasonnumber', None)) - library.info.update(info) - db.commit() - - # Titles - [db.delete(title) for title in library.titles] - db.commit() - - titles = info.get('titles', []) - log.debug('Adding titles: %s', titles) - counter = 0 - for title in titles: - if not title: - continue - title = toUnicode(title) - t = LibraryTitle( - title = title, - simple_title = self.simplifyTitle(title), - # XXX: default was None; so added a quick hack since we don't really need titiles for seasons anyway - #default = (len(default_title) == 0 and counter == 0) or len(titles) == 1 or title.lower() == toUnicode(default_title.lower()) or (toUnicode(default_title) == u'' and toUnicode(titles[0]) == title) - default = True, - ) - library.titles.append(t) - counter += 1 - - db.commit() - - # Files - images = info.get('images', []) - for image_type in ['poster']: - for image in images.get(image_type, []): - if not isinstance(image, (str, unicode)): - continue - - file_path = fireEvent('file.download', url = image, single = True) - if file_path: - file_obj = fireEvent('file.add', path = file_path, type_tuple = ('image', image_type), single = True) - try: - file_obj = db.query(File).filter_by(id = file_obj.get('id')).one() - library.files.append(file_obj) - db.commit() - break - except: - log.debug('Failed to attach to library: %s', traceback.format_exc()) - - library_dict = library.to_dict(self.default_dict) - db.expire_all() - return library_dict diff --git a/couchpotato/core/media/show/library/show.py b/couchpotato/core/media/show/library/show.py deleted file mode 100644 index c43288a..0000000 --- a/couchpotato/core/media/show/library/show.py +++ /dev/null @@ -1,196 +0,0 @@ -from string import ascii_letters -import time -import traceback - -from couchpotato import get_session -from couchpotato.core.event import addEvent, fireEventAsync, fireEvent -from couchpotato.core.helpers.encoding import toUnicode, simplifyString -from couchpotato.core.logger import CPLog -from couchpotato.core.media._base.library.base import LibraryBase -from qcond.helpers import simplify -from qcond import QueryCondenser - - -log = CPLog(__name__) - -autload = 'SeasonLibraryPlugin' - - -class ShowLibraryPlugin(LibraryBase): - - default_dict = {'titles': {}, 'files':{}} - - def __init__(self): - self.query_condenser = QueryCondenser() - - addEvent('library.query', self.query) - addEvent('library.add.show', self.add) - addEvent('library.update.show', self.update) - addEvent('library.update.show_release_date', self.updateReleaseDate) - - def query(self, library, first = True, condense = True, **kwargs): - if library is list or library.get('type') != 'show': - return - - titles = [title['title'] for title in library['titles']] - - if condense: - # Use QueryCondenser to build a list of optimal search titles - condensed_titles = self.query_condenser.distinct(titles) - - if condensed_titles: - # Use condensed titles if we got a valid result - titles = condensed_titles - else: - # Fallback to simplifying titles - titles = [simplify(title) for title in titles] - - if first: - return titles[0] if titles else None - - return titles - - def add(self, attrs = {}, update_after = True): - type = attrs.get('type', 'show') - primary_provider = attrs.get('primary_provider', 'thetvdb') - - db = get_session() - - l = db.query(ShowLibrary).filter_by(type = type, identifier = attrs.get('identifier')).first() - if not l: - status = fireEvent('status.get', 'needs_update', single = True) - l = ShowLibrary( - type = type, - primary_provider = primary_provider, - year = attrs.get('year'), - identifier = attrs.get('identifier'), - plot = toUnicode(attrs.get('plot')), - tagline = toUnicode(attrs.get('tagline')), - status_id = status.get('id'), - info = {}, - parent = None, - ) - - title = LibraryTitle( - title = toUnicode(attrs.get('title')), - simple_title = self.simplifyTitle(attrs.get('title')), - ) - - l.titles.append(title) - - db.add(l) - db.commit() - - # Update library info - if update_after is not False: - handle = fireEventAsync if update_after is 'async' else fireEvent - handle('library.update.show', identifier = l.identifier, default_title = toUnicode(attrs.get('title', ''))) - - library_dict = l.to_dict(self.default_dict) - db.expire_all() - return library_dict - - def update(self, identifier, default_title = '', force = False): - - if self.shuttingDown(): - return - - db = get_session() - library = db.query(ShowLibrary).filter_by(identifier = identifier).first() - done_status = fireEvent('status.get', 'done', single = True) - - if library: - library_dict = library.to_dict(self.default_dict) - - do_update = True - - info = fireEvent('show.info', merge = True, identifier = identifier) - - # Don't need those here - try: del info['in_wanted'] - except: pass - try: del info['in_library'] - except: pass - - if not info or len(info) == 0: - log.error('Could not update, no show info to work with: %s', identifier) - return False - - # Main info - if do_update: - library.plot = toUnicode(info.get('plot', '')) - library.tagline = toUnicode(info.get('tagline', '')) - library.year = info.get('year', 0) - library.status_id = done_status.get('id') - library.show_status = toUnicode(info.get('status', '').lower()) - library.airs_time = info.get('airs_time', None) - - # Bits - days_of_week_map = { - u'Monday': 1, - u'Tuesday': 2, - u'Wednesday': 4, - u'Thursday': 8, - u'Friday': 16, - u'Saturday': 32, - u'Sunday': 64, - u'Daily': 127, - } - try: - library.airs_dayofweek = days_of_week_map.get(info.get('airs_dayofweek')) - except: - library.airs_dayofweek = 0 - - try: - library.last_updated = int(info.get('lastupdated')) - except: - library.last_updated = int(time.time()) - - library.info.update(info) - - db.commit() - - # Titles - [db.delete(title) for title in library.titles] - db.commit() - - titles = info.get('titles', []) - log.debug('Adding titles: %s', titles) - counter = 0 - for title in titles: - if not title: - continue - title = toUnicode(title) - t = LibraryTitle( - title = title, - simple_title = self.simplifyTitle(title), - default = (len(default_title) == 0 and counter == 0) or len(titles) == 1 or title.lower() == toUnicode(default_title.lower()) or (toUnicode(default_title) == u'' and toUnicode(titles[0]) == title) - ) - library.titles.append(t) - counter += 1 - - db.commit() - - # Files - images = info.get('images', []) - for image_type in ['poster']: - for image in images.get(image_type, []): - if not isinstance(image, (str, unicode)): - continue - - file_path = fireEvent('file.download', url = image, single = True) - if file_path: - file_obj = fireEvent('file.add', path = file_path, type_tuple = ('image', image_type), single = True) - try: - file_obj = db.query(File).filter_by(id = file_obj.get('id')).one() - library.files.append(file_obj) - db.commit() - - break - except: - log.debug('Failed to attach to library: %s', traceback.format_exc()) - - library_dict = library.to_dict(self.default_dict) - - db.expire_all() - return library_dict diff --git a/couchpotato/core/media/show/matcher.py b/couchpotato/core/media/show/matcher.py index 05d7873..4056d64 100644 --- a/couchpotato/core/media/show/matcher.py +++ b/couchpotato/core/media/show/matcher.py @@ -96,7 +96,7 @@ class Episode(Base): log.info2('Wrong: releases with identifier ranges are not supported yet') return False - required = fireEvent('library.identifier', media['library'], single = True) + required = fireEvent('media.identifier', media['library'], single = True) # TODO - Support air by date episodes # TODO - Support episode parts @@ -121,7 +121,7 @@ class Season(Base): log.info2('Wrong: releases with identifier ranges are not supported yet') return False - required = fireEvent('library.identifier', media['library'], single = True) + required = fireEvent('media.identifier', media['library'], single = True) if identifier != required: log.info2('Wrong: required identifier (%s) does not match release identifier (%s)', (required, identifier)) diff --git a/couchpotato/core/media/show/providers/nzb/binsearch.py b/couchpotato/core/media/show/providers/nzb/binsearch.py index 0492424..166b3bc 100644 --- a/couchpotato/core/media/show/providers/nzb/binsearch.py +++ b/couchpotato/core/media/show/providers/nzb/binsearch.py @@ -21,7 +21,7 @@ class Season(SeasonProvider, Base): def buildUrl(self, media, quality): query = tryUrlencode({ - 'q': fireEvent('library.query', media, single = True), + 'q': fireEvent('media.search_query', media, single = True), 'm': 'n', 'max': 400, 'adv_age': Env.setting('retention', 'nzb'), @@ -38,7 +38,7 @@ class Episode(EpisodeProvider, Base): def buildUrl(self, media, quality): query = tryUrlencode({ - 'q': fireEvent('library.query', media, single = True), + 'q': fireEvent('media.search_query', media, single = True), 'm': 'n', 'max': 400, 'adv_age': Env.setting('retention', 'nzb'), diff --git a/couchpotato/core/media/show/providers/nzb/newznab.py b/couchpotato/core/media/show/providers/nzb/newznab.py index 0c9d49a..9469551 100644 --- a/couchpotato/core/media/show/providers/nzb/newznab.py +++ b/couchpotato/core/media/show/providers/nzb/newznab.py @@ -19,8 +19,8 @@ class Newznab(MultiProvider): class Season(SeasonProvider, Base): def buildUrl(self, media, api_key): - search_title = fireEvent('library.query', media, include_identifier = False, single = True) - identifier = fireEvent('library.identifier', media, single = True) + search_title = fireEvent('media.search_query', media, include_identifier = False, single = True) + identifier = fireEvent('media.identifier', media, single = True) query = tryUrlencode({ 't': 'tvsearch', @@ -35,8 +35,8 @@ class Season(SeasonProvider, Base): class Episode(EpisodeProvider, Base): def buildUrl(self, media, api_key): - search_title = fireEvent('library.query', media, include_identifier = False, single = True) - identifier = fireEvent('library.identifier', media, single = True) + search_title = fireEvent('media.search_query', media, include_identifier = False, single = True) + identifier = fireEvent('media.identifier', media, single = True) query = tryUrlencode({ 't': 'tvsearch', diff --git a/couchpotato/core/media/show/providers/nzb/nzbclub.py b/couchpotato/core/media/show/providers/nzb/nzbclub.py index d3fb696..65f35ad 100644 --- a/couchpotato/core/media/show/providers/nzb/nzbclub.py +++ b/couchpotato/core/media/show/providers/nzb/nzbclub.py @@ -21,7 +21,7 @@ class Season(SeasonProvider, Base): def buildUrl(self, media): q = tryUrlencode({ - 'q': fireEvent('library.query', media, single = True), + 'q': fireEvent('media.search_query', media, single = True), }) query = tryUrlencode({ @@ -39,7 +39,7 @@ class Episode(EpisodeProvider, Base): def buildUrl(self, media): q = tryUrlencode({ - 'q': fireEvent('library.query', media, single = True), + 'q': fireEvent('media.search_query', media, single = True), }) query = tryUrlencode({ diff --git a/couchpotato/core/media/show/providers/nzb/nzbindex.py b/couchpotato/core/media/show/providers/nzb/nzbindex.py index 7900df4..63cbe90 100644 --- a/couchpotato/core/media/show/providers/nzb/nzbindex.py +++ b/couchpotato/core/media/show/providers/nzb/nzbindex.py @@ -21,7 +21,7 @@ class Season(SeasonProvider, Base): def buildUrl(self, media, quality): query = tryUrlencode({ - 'q': fireEvent('library.query', media, single = True), + 'q': fireEvent('media.search_query', media, single = True), 'age': Env.setting('retention', 'nzb'), 'sort': 'agedesc', 'minsize': quality.get('size_min'), @@ -38,7 +38,7 @@ class Episode(EpisodeProvider, Base): def buildUrl(self, media, quality): query = tryUrlencode({ - 'q': fireEvent('library.query', media, single = True), + 'q': fireEvent('media.search_query', media, single = True), 'age': Env.setting('retention', 'nzb'), 'sort': 'agedesc', 'minsize': quality.get('size_min'), diff --git a/couchpotato/core/media/show/providers/torrent/bithdtv.py b/couchpotato/core/media/show/providers/torrent/bithdtv.py index 474a653..905f636 100644 --- a/couchpotato/core/media/show/providers/torrent/bithdtv.py +++ b/couchpotato/core/media/show/providers/torrent/bithdtv.py @@ -20,7 +20,7 @@ class Season(SeasonProvider, Base): def buildUrl(self, media): query = tryUrlencode({ - 'search': fireEvent('library.query', media, single = True), + 'search': fireEvent('media.search_query', media, single = True), 'cat': 12 # Season cat }) return query @@ -30,7 +30,7 @@ class Episode(EpisodeProvider, Base): def buildUrl(self, media): query = tryUrlencode({ - 'search': fireEvent('library.query', media, single = True), + 'search': fireEvent('media.search_query', media, single = True), 'cat': 10 # Episode cat }) return query diff --git a/couchpotato/core/media/show/providers/torrent/bitsoup.py b/couchpotato/core/media/show/providers/torrent/bitsoup.py index 31774ee..ed87af4 100644 --- a/couchpotato/core/media/show/providers/torrent/bitsoup.py +++ b/couchpotato/core/media/show/providers/torrent/bitsoup.py @@ -20,7 +20,7 @@ class Season(SeasonProvider, Base): # For season bundles, bitsoup currently only has one category def buildUrl(self, media, quality): query = tryUrlencode({ - 'search': fireEvent('library.query', media, single = True), + 'search': fireEvent('media.search_query', media, single = True), 'cat': 45 # TV-Packs Category }) return query @@ -35,7 +35,7 @@ class Episode(EpisodeProvider, Base): def buildUrl(self, media, quality): query = tryUrlencode({ - 'search': fireEvent('library.query', media, single = True), + 'search': fireEvent('media.search_query', media, single = True), 'cat': self.getCatId(quality['identifier'])[0], }) return query diff --git a/couchpotato/core/media/show/providers/torrent/publichd.py b/couchpotato/core/media/show/providers/torrent/publichd.py index 5865070..058ca8c 100644 --- a/couchpotato/core/media/show/providers/torrent/publichd.py +++ b/couchpotato/core/media/show/providers/torrent/publichd.py @@ -18,10 +18,10 @@ class PublicHD(MultiProvider): class Season(SeasonProvider, Base): def buildUrl(self, media): - return fireEvent('library.query', media, single = True) + return fireEvent('media.search_query', media, single = True) class Episode(EpisodeProvider, Base): def buildUrl(self, media): - return fireEvent('library.query', media, single = True) + return fireEvent('media.search_query', media, single = True) diff --git a/couchpotato/core/media/show/providers/torrent/sceneaccess.py b/couchpotato/core/media/show/providers/torrent/sceneaccess.py index 2fa266d..7959f58 100644 --- a/couchpotato/core/media/show/providers/torrent/sceneaccess.py +++ b/couchpotato/core/media/show/providers/torrent/sceneaccess.py @@ -30,7 +30,7 @@ class Season(SeasonProvider, Base): ) arguments = tryUrlencode({ - 'search': fireEvent('library.query', media, single = True), + 'search': fireEvent('media.search_query', media, single = True), 'method': 3, }) query = "%s&%s" % (url, arguments) @@ -52,7 +52,7 @@ class Episode(EpisodeProvider, Base): ) arguments = tryUrlencode({ - 'search': fireEvent('library.query', media, single = True), + 'search': fireEvent('media.search_query', media, single = True), 'method': 3, }) query = "%s&%s" % (url, arguments) diff --git a/couchpotato/core/media/show/providers/torrent/thepiratebay.py b/couchpotato/core/media/show/providers/torrent/thepiratebay.py index b648fe0..2a7e084 100644 --- a/couchpotato/core/media/show/providers/torrent/thepiratebay.py +++ b/couchpotato/core/media/show/providers/torrent/thepiratebay.py @@ -25,7 +25,7 @@ class Season(SeasonProvider, Base): def buildUrl(self, media, page, cats): return ( - tryUrlencode('"%s"' % fireEvent('library.query', media, single = True)), + tryUrlencode('"%s"' % fireEvent('media.search_query', media, single = True)), page, ','.join(str(x) for x in cats) ) @@ -40,7 +40,7 @@ class Episode(EpisodeProvider, Base): def buildUrl(self, media, page, cats): return ( - tryUrlencode('"%s"' % fireEvent('library.query', media, single = True)), + tryUrlencode('"%s"' % fireEvent('media.search_query', media, single = True)), page, ','.join(str(x) for x in cats) ) diff --git a/couchpotato/core/media/show/providers/torrent/torrentday.py b/couchpotato/core/media/show/providers/torrent/torrentday.py index 68b4c01..e77aa17 100644 --- a/couchpotato/core/media/show/providers/torrent/torrentday.py +++ b/couchpotato/core/media/show/providers/torrent/torrentday.py @@ -21,7 +21,7 @@ class Season(SeasonProvider, Base): ([14], ['hdtv_sd', 'hdtv_720p', 'webdl_720p', 'webdl_1080p']), ] def buildUrl(self, media): - return fireEvent('library.query', media, single = True) + return fireEvent('media.search_query', media, single = True) class Episode(EpisodeProvider, Base): @@ -30,5 +30,5 @@ class Episode(EpisodeProvider, Base): ([2], [24], [26], ['hdtv_sd']) ] def buildUrl(self, media): - return fireEvent('library.query', media, single = True) + return fireEvent('media.search_query', media, single = True) diff --git a/couchpotato/core/media/show/providers/torrent/torrentleech.py b/couchpotato/core/media/show/providers/torrent/torrentleech.py index 312ec19..6c8abf8 100644 --- a/couchpotato/core/media/show/providers/torrent/torrentleech.py +++ b/couchpotato/core/media/show/providers/torrent/torrentleech.py @@ -24,7 +24,7 @@ class Season(SeasonProvider, Base): def buildUrl(self, media, quality): return ( - tryUrlencode(fireEvent('library.query', media, single = True)), + tryUrlencode(fireEvent('media.search_query', media, single = True)), self.getCatId(quality['identifier'])[0] ) @@ -37,6 +37,6 @@ class Episode(EpisodeProvider, Base): def buildUrl(self, media, quality): return ( - tryUrlencode(fireEvent('library.query', media, single = True)), + tryUrlencode(fireEvent('media.search_query', media, single = True)), self.getCatId(quality['identifier'])[0] ) diff --git a/couchpotato/core/media/show/providers/torrent/torrentpotato.py b/couchpotato/core/media/show/providers/torrent/torrentpotato.py index 35028a2..480b545 100644 --- a/couchpotato/core/media/show/providers/torrent/torrentpotato.py +++ b/couchpotato/core/media/show/providers/torrent/torrentpotato.py @@ -22,7 +22,7 @@ class Season(SeasonProvider, Base): arguments = tryUrlencode({ 'user': host['name'], 'passkey': host['pass_key'], - 'search': fireEvent('library.query', media, single = True) + 'search': fireEvent('media.search_query', media, single = True) }) return '%s?%s' % (host['host'], arguments) @@ -33,6 +33,6 @@ class Episode(EpisodeProvider, Base): arguments = tryUrlencode({ 'user': host['name'], 'passkey': host['pass_key'], - 'search': fireEvent('library.query', media, single = True) + 'search': fireEvent('media.search_query', media, single = True) }) return '%s?%s' % (host['host'], arguments) diff --git a/couchpotato/core/media/show/providers/torrent/torrentshack.py b/couchpotato/core/media/show/providers/torrent/torrentshack.py index 8d11759..d67451c 100644 --- a/couchpotato/core/media/show/providers/torrent/torrentshack.py +++ b/couchpotato/core/media/show/providers/torrent/torrentshack.py @@ -29,7 +29,7 @@ class Season(SeasonProvider, Base): cat_backup_id = 980 def buildUrl(self, media, quality): - query = (tryUrlencode(fireEvent('library.query', media, single = True)), + query = (tryUrlencode(fireEvent('media.search_query', media, single = True)), self.getCatId(quality['identifier'])[0], self.getSceneOnly()) return query @@ -46,7 +46,7 @@ class Episode(EpisodeProvider, Base): cat_backup_id = 620 def buildUrl(self, media, quality): - query = (tryUrlencode(fireEvent('library.query', media, single = True)), + query = (tryUrlencode(fireEvent('media.search_query', media, single = True)), self.getCatId(quality['identifier'])[0], self.getSceneOnly()) return query diff --git a/couchpotato/core/media/show/searcher.py b/couchpotato/core/media/show/searcher.py index 5a22cd4..87232aa 100644 --- a/couchpotato/core/media/show/searcher.py +++ b/couchpotato/core/media/show/searcher.py @@ -5,7 +5,6 @@ from couchpotato.core.logger import CPLog from couchpotato.core.media._base.searcher.base import SearcherBase from couchpotato.core.media._base.searcher.main import SearchSetupError from couchpotato.core.media.show import ShowTypeBase -from couchpotato.core.media.show._base import ShowBase from qcond import QueryCondenser log = CPLog(__name__) @@ -61,7 +60,7 @@ class ShowSearcher(SearcherBase, ShowTypeBase): found_releases = [] too_early_to_search = [] - default_title = fireEvent('library.query', media['library'], condense = False, single=True) + default_title = fireEvent('media.search_query', media['library'], condense = False, single=True) if not default_title: log.error('No proper info found for episode, removing it from library to cause it from having more issues.') #fireEvent('episode.delete', episode['id'], single = True) diff --git a/couchpotato/core/media/show/season.py b/couchpotato/core/media/show/season.py new file mode 100644 index 0000000..2d32e25 --- /dev/null +++ b/couchpotato/core/media/show/season.py @@ -0,0 +1,114 @@ +from couchpotato.core.event import addEvent, fireEvent +from couchpotato.core.logger import CPLog +from couchpotato.core.helpers.variable import tryInt +from couchpotato.core.plugins.base import Plugin + + +log = CPLog(__name__) + +autload = 'Season' + + +class Season(Plugin): + + def __init__(self): + addEvent('media.search_query', self.query) + addEvent('media.identifier', self.identifier) + + addEvent('show.season.add', self.update) + addEvent('show.season.update_info', self.update) + + def query(self, library, first = True, condense = True, include_identifier = True, **kwargs): + if library is list or library.get('type') != 'season': + return + + # Get the titles of the show + if not library.get('related_libraries', {}).get('show', []): + log.warning('Invalid library, unable to determine title.') + return + + titles = fireEvent( + 'media._search_query', + library['related_libraries']['show'][0], + first=False, + condense=condense, + + single=True + ) + + # Add season map_names if they exist + if 'map_names' in library['info']: + season_names = library['info']['map_names'].get(str(library['season_number']), {}) + + # Add titles from all locations + # TODO only add name maps from a specific location + for location, names in season_names.items(): + titles += [name for name in names if name and name not in titles] + + + identifier = fireEvent('media.identifier', library, single = True) + + # Add season identifier to titles + if include_identifier and identifier.get('season') is not None: + titles = [title + (' S%02d' % identifier['season']) for title in titles] + + + if first: + return titles[0] if titles else None + + return titles + + def identifier(self, library): + if library.get('type') != 'season': + return + + return { + 'season': tryInt(library['season_number'], None) + } + + def add(self, parent_id, update_after = True): + + # Add Season + season = { + 'nr': 1, + 'identifiers': { + 'imdb': 'tt1234', + 'thetvdb': 123, + 'tmdb': 123, + 'rage': 123 + }, + 'parent': '_id', + 'info': {}, # Returned dict by providers + } + + # Check if season already exists + season_exists = True or False + + if season_exists: + pass #update existing + else: + + db.insert(season) + + + # Update library info + if update_after is not False: + handle = fireEventAsync if update_after is 'async' else fireEvent + handle('show.season.update_info', episode.get('_id')) + + return season + + def update_info(self, media_id = None, default_title = '', force = False): + + if self.shuttingDown(): + return + + # Get new info + fireEvent('season.info', merge = True) + + # Update/create media + + # Get images + + + return info