diff --git a/couchpotato/core/helpers/variable.py b/couchpotato/core/helpers/variable.py old mode 100644 new mode 100755 index fc844aa..db68da2 --- a/couchpotato/core/helpers/variable.py +++ b/couchpotato/core/helpers/variable.py @@ -380,3 +380,11 @@ def getFreeSpace(directories): free_space[folder] = size return free_space + + +def find(func, iterable): + for item in iterable: + if func(item): + return item + + return None diff --git a/couchpotato/core/media/_base/library/main.py b/couchpotato/core/media/_base/library/main.py index 2f5629d..da526ec 100755 --- a/couchpotato/core/media/_base/library/main.py +++ b/couchpotato/core/media/_base/library/main.py @@ -78,13 +78,18 @@ class Library(LibraryBase): return cur - def tree(self, media): - result = media - + def tree(self, media = None, media_id = None): db = get_db() + if media: + result = media + elif media_id: + result = db.get('id', media_id, with_doc = True) + else: + return None + # Find children - items = db.get_many('media_children', media['_id'], with_doc = True) + items = db.get_many('media_children', result['_id'], with_doc = True) keys = [] # Build children arrays @@ -94,6 +99,8 @@ class Library(LibraryBase): if key not in result: result[key] = {} + elif type(result[key]) is not dict: + result[key] = {} if key not in keys: keys.append(key) @@ -105,6 +112,6 @@ class Library(LibraryBase): result[key] = result[key].values() # Include releases - result['releases'] = fireEvent('release.for_media', media['_id'], single = True) + result['releases'] = fireEvent('release.for_media', result['_id'], single = True) return result diff --git a/couchpotato/core/media/_base/media/main.py b/couchpotato/core/media/_base/media/main.py old mode 100644 new mode 100755 index ee9e6cc..bcb8402 --- a/couchpotato/core/media/_base/media/main.py +++ b/couchpotato/core/media/_base/media/main.py @@ -109,7 +109,7 @@ class MediaPlugin(MediaBase): try: media = get_db().get('id', media_id) - event = '%s.update_info' % media.get('type') + event = '%s.update' % media.get('type') def handler(): fireEvent(event, media_id = media_id, on_complete = self.createOnComplete(media_id)) @@ -160,10 +160,13 @@ class MediaPlugin(MediaBase): 'media': media, } - def withStatus(self, status, with_doc = True): + def withStatus(self, status, types = None, with_doc = True): db = get_db() + if types and not isinstance(types, (list, tuple)): + types = [types] + status = list(status if isinstance(status, (list, tuple)) else [status]) for s in status: @@ -171,6 +174,10 @@ class MediaPlugin(MediaBase): if with_doc: try: doc = db.get('id', ms['_id']) + + if types and doc.get('type') not in types: + continue + yield doc except RecordNotFound: log.debug('Record not found, skipping: %s', ms['_id']) diff --git a/couchpotato/core/media/movie/_base/main.py b/couchpotato/core/media/movie/_base/main.py index 3afe1e8..8a04d0b 100755 --- a/couchpotato/core/media/movie/_base/main.py +++ b/couchpotato/core/media/movie/_base/main.py @@ -46,7 +46,7 @@ class MovieBase(MovieTypeBase): }) addEvent('movie.add', self.add) - addEvent('movie.update_info', self.updateInfo) + addEvent('movie.update', self.update) addEvent('movie.update_release_dates', self.updateReleaseDate) def add(self, params = None, force_readd = True, search_after = True, update_after = True, notify_after = True, status = None): @@ -172,7 +172,7 @@ class MovieBase(MovieTypeBase): # Trigger update info if added and update_after: # Do full update to get images etc - fireEventAsync('movie.update_info', m['_id'], default_title = params.get('title'), on_complete = onComplete) + fireEventAsync('movie.update', m['_id'], default_title = params.get('title'), on_complete = onComplete) # Remove releases for rel in fireEvent('release.for_media', m['_id'], single = True): @@ -256,7 +256,7 @@ class MovieBase(MovieTypeBase): 'success': False, } - def updateInfo(self, media_id = None, identifier = None, default_title = None, extended = False): + def update(self, media_id = None, identifier = None, default_title = None, extended = False): """ Update movie information inside media['doc']['info'] @@ -337,7 +337,7 @@ class MovieBase(MovieTypeBase): media = db.get('id', media_id) if not media.get('info'): - media = self.updateInfo(media_id) + media = self.update(media_id) dates = media.get('info', {}).get('release_date') else: dates = media.get('info').get('release_date') diff --git a/couchpotato/core/media/movie/providers/metadata/base.py b/couchpotato/core/media/movie/providers/metadata/base.py old mode 100644 new mode 100755 index 7968000..cc914af --- a/couchpotato/core/media/movie/providers/metadata/base.py +++ b/couchpotato/core/media/movie/providers/metadata/base.py @@ -28,7 +28,7 @@ class MovieMetaData(MetaDataBase): # Update library to get latest info try: - group['media'] = fireEvent('movie.update_info', group['media'].get('_id'), identifier = getIdentifier(group['media']), extended = True, single = True) + group['media'] = fireEvent('movie.update', group['media'].get('_id'), identifier = getIdentifier(group['media']), extended = True, single = True) except: log.error('Failed to update movie, before creating metadata: %s', traceback.format_exc()) diff --git a/couchpotato/core/media/movie/searcher.py b/couchpotato/core/media/movie/searcher.py old mode 100644 new mode 100755 index 4bd8c8d..e943c21 --- a/couchpotato/core/media/movie/searcher.py +++ b/couchpotato/core/media/movie/searcher.py @@ -94,7 +94,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase): self.single(media, search_protocols, manual = manual) except IndexError: log.error('Forcing library update for %s, if you see this often, please report: %s', (getIdentifier(media), traceback.format_exc())) - fireEvent('movie.update_info', media_id) + fireEvent('movie.update', media_id) except: log.error('Search failed for %s: %s', (getIdentifier(media), traceback.format_exc())) diff --git a/couchpotato/core/media/movie/suggestion/main.py b/couchpotato/core/media/movie/suggestion/main.py old mode 100644 new mode 100755 index 146a6a0..3df67ab --- a/couchpotato/core/media/movie/suggestion/main.py +++ b/couchpotato/core/media/movie/suggestion/main.py @@ -27,7 +27,7 @@ class Suggestion(Plugin): else: if not movies or len(movies) == 0: - active_movies = fireEvent('media.with_status', ['active', 'done'], single = True) + active_movies = fireEvent('media.with_status', ['active', 'done'], 'movie', single = True) movies = [getIdentifier(x) for x in active_movies] if not ignored or len(ignored) == 0: diff --git a/couchpotato/core/media/show/_base/main.py b/couchpotato/core/media/show/_base/main.py index da70c3d..29af63a 100755 --- a/couchpotato/core/media/show/_base/main.py +++ b/couchpotato/core/media/show/_base/main.py @@ -5,7 +5,7 @@ from couchpotato import get_db from couchpotato.api import addApiView from couchpotato.core.event import fireEvent, fireEventAsync, addEvent from couchpotato.core.helpers.encoding import simplifyString -from couchpotato.core.helpers.variable import getTitle +from couchpotato.core.helpers.variable import getTitle, find from couchpotato.core.logger import CPLog from couchpotato.core.media import MediaBase from qcond import QueryCondenser @@ -32,7 +32,8 @@ class ShowBase(MediaBase): }) addEvent('show.add', self.add) - addEvent('show.update_info', self.updateInfo) + addEvent('show.update', self.update) + addEvent('show.update_extras', self.updateExtras) def addView(self, **kwargs): add_dict = self.add(params = kwargs) @@ -45,8 +46,6 @@ class ShowBase(MediaBase): def add(self, params = None, force_readd = True, search_after = True, update_after = True, notify_after = True, status = None): if not params: params = {} - db = get_db() - # Identifiers if not params.get('identifiers'): msg = 'Can\'t add show without at least 1 identifier.' @@ -58,6 +57,31 @@ class ShowBase(MediaBase): if not info or (info and len(info.get('titles', [])) == 0): info = fireEvent('show.info', merge = True, identifiers = params.get('identifiers')) + # Add Show + try: + m, added = self.create(info, params, force_readd, search_after, update_after) + + result = fireEvent('media.get', m['_id'], single = True) + + if added and notify_after: + if params.get('title'): + message = 'Successfully added "%s" to your wanted list.' % params.get('title', '') + else: + title = getTitle(m) + if title: + message = 'Successfully added "%s" to your wanted list.' % title + else: + message = 'Successfully added to your wanted list.' + + fireEvent('notify.frontend', type = 'show.added', data = result, message = message) + + return result + except: + log.error('Failed adding media: %s', traceback.format_exc()) + + def create(self, info, params = None, force_readd = True, search_after = True, update_after = True, notify_after = True, status = None): + db = get_db() + # Set default title def_title = self.getDefaultTitle(info) @@ -65,136 +89,101 @@ class ShowBase(MediaBase): default_profile = {} if not params.get('profile_id'): default_profile = fireEvent('profile.default', single = True) + cat_id = params.get('category_id') - # Add Show + media = { + '_t': 'media', + 'type': 'show', + 'title': def_title, + 'identifiers': info.get('identifiers'), + 'status': status if status else 'active', + 'profile_id': params.get('profile_id', default_profile.get('_id')), + 'category_id': cat_id if cat_id is not None and len(cat_id) > 0 and cat_id != '-1' else None + } + + identifiers = info.pop('identifiers', {}) + seasons = info.pop('seasons', {}) + + # Update media with info + self.updateInfo(media, info) + + new = False try: - media = { - '_t': 'media', - 'type': 'show', - 'title': def_title, - 'identifiers': info.get('identifiers'), - 'status': status if status else 'active', - 'profile_id': params.get('profile_id', default_profile.get('_id')), - 'category_id': cat_id if cat_id is not None and len(cat_id) > 0 and cat_id != '-1' else None - } + m = fireEvent('media.with_identifiers', params.get('identifiers'), with_doc = True, single = True)['doc'] + except: + new = True + m = db.insert(media) - # TODO: stuff below is mostly a copy of what is done in movie - # Can we make a base function to do this stuff? - - # Remove season info for later use (save separately) - seasons_info = info.get('seasons', {}) - identifiers = info.get('identifiers', {}) - - # Make sure we don't nest in_wanted data - del info['identifiers'] - try: del info['in_wanted'] - except: pass - try: del info['in_library'] - except: pass - try: del info['identifiers'] - except: pass - try: del info['seasons'] - except: pass - - media['info'] = info - - new = False - try: - m = fireEvent('media.with_identifiers', params.get('identifiers'), with_doc = True, single = True)['doc'] - except: - new = True - m = db.insert(media) - - # Update dict to be usable - m.update(media) - - - added = True - do_search = False - search_after = search_after and self.conf('search_on_add', section = 'showsearcher') - onComplete = None - - if new: - if search_after: - onComplete = self.createOnComplete(m['_id']) - search_after = False - elif force_readd: - - # Clean snatched history - for release in fireEvent('release.for_media', m['_id'], single = True): - if release.get('status') in ['downloaded', 'snatched', 'done']: - if params.get('ignore_previous', False): - release['status'] = 'ignored' - db.update(release) - else: - fireEvent('release.delete', release['_id'], single = True) - - m['profile_id'] = params.get('profile_id', default_profile.get('id')) - m['category_id'] = media.get('category_id') - m['last_edit'] = int(time.time()) - - do_search = True - db.update(m) - else: - try: del params['info'] - except: pass - log.debug('Show already exists, not updating: %s', params) - added = False - - # Trigger update info - if added and update_after: - # Do full update to get images etc - fireEventAsync('show.update_info', m['_id'], info = info, on_complete = onComplete) - - # Remove releases - for rel in fireEvent('release.for_media', m['_id'], single = True): - if rel['status'] is 'available': - db.delete(rel) - - movie_dict = fireEvent('media.get', m['_id'], single = True) - - if do_search and search_after: + # Update dict to be usable + m.update(media) + + added = True + do_search = False + search_after = search_after and self.conf('search_on_add', section = 'showsearcher') + onComplete = None + + if new: + if search_after: onComplete = self.createOnComplete(m['_id']) - onComplete() - # Add Seasons - for season_nr in seasons_info: + search_after = False + elif force_readd: + # Clean snatched history + for release in fireEvent('release.for_media', m['_id'], single = True): + if release.get('status') in ['downloaded', 'snatched', 'done']: + if params.get('ignore_previous', False): + release['status'] = 'ignored' + db.update(release) + else: + fireEvent('release.delete', release['_id'], single = True) - season_info = seasons_info[season_nr] - episodes = season_info.get('episodes', {}) + m['profile_id'] = params.get('profile_id', default_profile.get('id')) + m['category_id'] = media.get('category_id') + m['last_edit'] = int(time.time()) - season = fireEvent('show.season.add', m.get('_id'), season_info, single = True) + do_search = True + db.update(m) + else: + params.pop('info', None) + log.debug('Show already exists, not updating: %s', params) + added = False - # Add Episodes - for episode_nr in episodes: + # Create episodes + self.createEpisodes(m, seasons) - episode_info = episodes[episode_nr] - episode_info['season_number'] = season_nr - fireEvent('show.episode.add', season.get('_id'), episode_info, single = True) + # Trigger update info + if added and update_after: + # Do full update to get images etc + fireEventAsync('show.update_extras', m, info, store = True, on_complete = onComplete) + # Remove releases + for rel in fireEvent('release.for_media', m['_id'], single = True): + if rel['status'] is 'available': + db.delete(rel) - if added and notify_after: + if do_search and search_after: + onComplete = self.createOnComplete(m['_id']) + onComplete() - if params.get('title'): - message = 'Successfully added "%s" to your wanted list.' % params.get('title', '') - else: - title = getTitle(m) - if title: - message = 'Successfully added "%s" to your wanted list.' % title - else: - message = 'Successfully added to your wanted list.' - fireEvent('notify.frontend', type = 'show.added', data = movie_dict, message = message) + return m, added + def createEpisodes(self, m, seasons_info): + # Add Seasons + for season_nr in seasons_info: + season_info = seasons_info[season_nr] + episodes = season_info.get('episodes', {}) - return movie_dict - except: - log.error('Failed adding media: %s', traceback.format_exc()) + season = fireEvent('show.season.add', m.get('_id'), season_info, update_after = False, single = True) - def updateInfo(self, media_id = None, identifiers = None, info = None): - if not info: info = {} - if not identifiers: identifiers = {} + # Add Episodes + for episode_nr in episodes: + episode_info = episodes[episode_nr] + episode_info['season_number'] = season_nr + + fireEvent('show.episode.add', season.get('_id'), episode_info, update_after = False, single = True) + def update(self, media_id = None, media = None, identifiers = None, info = None): """ Update movie information inside media['doc']['info'] @@ -209,41 +198,30 @@ class ShowBase(MediaBase): @return: dict, with media """ - if self.shuttingDown(): - return - - try: - db = get_db() - - if media_id: - media = db.get('id', media_id) - else: - media = db.get('media', identifiers, with_doc = True)['doc'] + if not info: info = {} + if not identifiers: identifiers = {} - if not info: - info = fireEvent('show.info', identifiers = media.get('identifiers'), merge = True) + db = get_db() - # Don't need those here - try: del info['seasons'] - except: pass - try: del info['identifiers'] - except: pass - try: del info['in_wanted'] - except: pass - try: del info['in_library'] - except: pass + if self.shuttingDown(): + return - if not info or len(info) == 0: - log.error('Could not update, no show info to work with: %s', media.get('identifier')) - return False + if media is None and media_id: + media = db.get('id', media_id) + else: + log.error('missing "media" and "media_id" parameters, unable to update') + return - # Update basic info - media['info'] = info + if not info: + info = fireEvent('show.info', identifiers = media.get('identifiers'), merge = True) - # Update image file - image_urls = info.get('images', []) + try: + identifiers = info.pop('identifiers', {}) + seasons = info.pop('seasons', {}) - self.getPoster(media, image_urls) + self.updateInfo(media, info) + self.updateEpisodes(media, seasons) + self.updateExtras(media, info) db.update(media) return media @@ -251,3 +229,61 @@ class ShowBase(MediaBase): log.error('Failed update media: %s', traceback.format_exc()) return {} + + def updateInfo(self, media, info): + db = get_db() + + # Remove season info for later use (save separately) + info.pop('in_wanted', None) + info.pop('in_library', None) + + if not info or len(info) == 0: + log.error('Could not update, no show info to work with: %s', media.get('identifier')) + return False + + # Update basic info + media['info'] = info + + def updateEpisodes(self, media, seasons): + # Fetch current season/episode tree + show_tree = fireEvent('library.tree', media_id = media['_id'], single = True) + + # Update seasons + for season_num in seasons: + season_info = seasons[season_num] + episodes = season_info.get('episodes', {}) + + # Find season that matches number + season = find(lambda s: s.get('info', {}).get('number', 0) == season_num, show_tree.get('seasons', [])) + + if not season: + log.warning('Unable to find season "%s"', season_num) + continue + + # Update season + fireEvent('show.season.update', season['_id'], info = season_info, single = True) + + # Update episodes + for episode_num in episodes: + episode_info = episodes[episode_num] + episode_info['season_number'] = season_num + + # Find episode that matches number + episode = find(lambda s: s.get('info', {}).get('number', 0) == episode_num, season.get('episodes', [])) + + if not episode: + log.debug('Creating new episode %s in season %s', (episode_num, season_num)) + fireEvent('show.episode.add', season.get('_id'), episode_info, update_after = False, single = True) + continue + + fireEvent('show.episode.update', episode['_id'], info = episode_info, single = True) + + def updateExtras(self, media, info, store=False): + db = get_db() + + # Update image file + image_urls = info.get('images', []) + self.getPoster(media, image_urls) + + if store: + db.update(media) diff --git a/couchpotato/core/media/show/episode.py b/couchpotato/core/media/show/episode.py index 754a48f..0557bb4 100755 --- a/couchpotato/core/media/show/episode.py +++ b/couchpotato/core/media/show/episode.py @@ -14,14 +14,17 @@ class Episode(MediaBase): def __init__(self): addEvent('show.episode.add', self.add) - addEvent('show.episode.update_info', self.updateInfo) + addEvent('show.episode.update', self.update) + addEvent('show.episode.update_extras', self.updateExtras) def add(self, parent_id, info = None, update_after = True, status = None): if not info: info = {} - identifiers = info.get('identifiers') - try: del info['identifiers'] - except: pass + identifiers = info.pop('identifiers', None) + + if not identifiers: + log.warning('Unable to add episode, missing identifiers (info provider mismatch?)') + return # Add Season episode_info = { @@ -41,6 +44,7 @@ class Episode(MediaBase): if existing_episode: s = existing_episode['doc'] s.update(episode_info) + episode = db.update(s) else: episode = db.insert(episode_info) @@ -48,11 +52,11 @@ class Episode(MediaBase): # 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'), info = info, single = True) + handle('show.episode.update_extras', episode, info, store = True, single = True) return episode - def updateInfo(self, media_id = None, info = None, force = False): + def update(self, media_id = None, identifiers = None, info = None): if not info: info = {} if self.shuttingDown(): @@ -69,26 +73,35 @@ class Episode(MediaBase): info = fireEvent( 'episode.info', show.get('identifiers'), { - 'season_identifier': season.get('info', {}).get('number'), - 'episode_identifier': episode.get('identifiers') + 'season_identifiers': season.get('identifiers'), + 'season_number': season.get('info', {}).get('number'), + + 'episode_identifiers': episode.get('identifiers'), + 'episode_number': episode.get('info', {}).get('number'), + + 'absolute_number': episode.get('info', {}).get('absolute_number') }, merge = True ) + identifiers = info.pop('identifiers', None) or identifiers + # Update/create media - if force: + episode['identifiers'].update(identifiers) + episode.update({'info': info}) - episode['identifiers'].update(info['identifiers']) - if 'identifiers' in info: - del info['identifiers'] + self.updateExtras(episode, info) - episode.update({'info': info}) - e = db.update(episode) - episode.update(e) + db.update(episode) + return episode + + def updateExtras(self, episode, info, store=False): + db = get_db() # Get images image_urls = info.get('images', []) existing_files = episode.get('files', {}) self.getPoster(image_urls, existing_files) - return episode + if store: + db.update(episode) diff --git a/couchpotato/core/media/show/providers/info/thetvdb.py b/couchpotato/core/media/show/providers/info/thetvdb.py index e57057c..4aa989c 100755 --- a/couchpotato/core/media/show/providers/info/thetvdb.py +++ b/couchpotato/core/media/show/providers/info/thetvdb.py @@ -60,8 +60,9 @@ class TheTVDb(ShowProvider): self.tvdb_api_parms['language'] = language self._setup() - search_string = simplifyString(q) - cache_key = 'thetvdb.cache.search.%s.%s' % (search_string, limit) + query = q + #query = simplifyString(query) + cache_key = 'thetvdb.cache.search.%s.%s' % (query, limit) results = self.getCache(cache_key) if not results: @@ -69,9 +70,9 @@ class TheTVDb(ShowProvider): raw = None try: - raw = self.tvdb.search(search_string) + raw = self.tvdb.search(query) except (tvdb_exceptions.tvdb_error, IOError), e: - log.error('Failed searching TheTVDB for "%s": %s', (search_string, traceback.format_exc())) + log.error('Failed searching TheTVDB for "%s": %s', (query, traceback.format_exc())) return False results = [] @@ -129,22 +130,17 @@ class TheTVDb(ShowProvider): return result or {} - def getSeasonInfo(self, identifier = None, params = {}): + def getSeasonInfo(self, identifiers = None, params = {}): """Either return a list of all seasons or a single season by number. identifier is the show 'id' """ - if not identifier: - return False - - season_identifier = params.get('season_identifier', None) + if not identifiers or not identifiers.get('thetvdb'): + return None - # season_identifier must contain the 'show id : season number' since there is no tvdb id - # for season and we need a reference to both the show id and season number - if season_identifier: - try: season_identifier = int(season_identifier.split(':')[1]) - except: return False + season_number = params.get('season_number', None) + identifier = tryInt(identifiers.get('thetvdb')) - cache_key = 'thetvdb.cache.%s.%s' % (identifier, season_identifier) + cache_key = 'thetvdb.cache.%s.%s' % (identifier, season_number) log.debug('Getting SeasonInfo: %s', cache_key) result = self.getCache(cache_key) or {} if result: @@ -158,12 +154,12 @@ class TheTVDb(ShowProvider): result = [] for number, season in show.items(): - if season_identifier is not None and number == season_identifier: - result = self._parseSeason(show, (number, season)) + if season_number is not None and number == season_number: + result = self._parseSeason(show, number, season) self.setCache(cache_key, result) return result else: - result.append(self._parseSeason(show, (number, season))) + result.append(self._parseSeason(show, number, season)) self.setCache(cache_key, result) return result @@ -172,22 +168,22 @@ class TheTVDb(ShowProvider): """Either return a list of all episodes or a single episode. If episode_identifer contains an episode number to search for """ - season_identifier = self.getIdentifier(params.get('season_identifier', None)) - episode_identifier = self.getIdentifier(params.get('episode_identifier', None)) + season_number = self.getIdentifier(params.get('season_number', None)) + episode_identifier = self.getIdentifier(params.get('episode_identifiers', None)) identifier = self.getIdentifier(identifier) - if not identifier and season_identifier is None: + if not identifier and season_number is None: return False # season_identifier must contain the 'show id : season number' since there is no tvdb id # for season and we need a reference to both the show id and season number - if not identifier and season_identifier: + if not identifier and season_number: try: - identifier, season_identifier = season_identifier.split(':') - season_identifier = int(season_identifier) + identifier, season_number = season_number.split(':') + season_number = int(season_number) except: return None - cache_key = 'thetvdb.cache.%s.%s.%s' % (identifier, episode_identifier, season_identifier) + cache_key = 'thetvdb.cache.%s.%s.%s' % (identifier, episode_identifier, season_number) log.debug('Getting EpisodeInfo: %s', cache_key) result = self.getCache(cache_key) or {} if result: @@ -201,7 +197,7 @@ class TheTVDb(ShowProvider): result = [] for number, season in show.items(): - if season_identifier is not None and number != season_identifier: + if season_number is not None and number != season_number: continue for episode in season.values(): diff --git a/couchpotato/core/media/show/providers/info/xem.py b/couchpotato/core/media/show/providers/info/xem.py old mode 100644 new mode 100755 index ec7d343..f8c003c --- a/couchpotato/core/media/show/providers/info/xem.py +++ b/couchpotato/core/media/show/providers/info/xem.py @@ -5,6 +5,8 @@ from couchpotato.core.media.show.providers.base import ShowProvider log = CPLog(__name__) +autoload = 'Xem' + class Xem(ShowProvider): ''' @@ -75,76 +77,69 @@ class Xem(ShowProvider): self.config['url_names'] = u"%(base_url)s/map/names?" % self.config self.config['url_all_names'] = u"%(base_url)s/map/allNames?" % self.config - # TODO: Also get show aliases (store as titles) - def getShowInfo(self, identifier = None): + def getShowInfo(self, identifiers = None): if self.isDisabled(): return {} + identifier = identifiers.get('thetvdb') + + if not identifier: + return {} + cache_key = 'xem.cache.%s' % identifier log.debug('Getting showInfo: %s', cache_key) result = self.getCache(cache_key) or {} if result: return result + result['seasons'] = {} + # Create season/episode and absolute mappings - url = self.config['url_all'] + "id=%s&origin=tvdb" % tryUrlencode(identifier) + url = self.config['url_all'] + "id=%s&origin=tvdb" % tryUrlencode(identifier) response = self.getJsonData(url) - if response: - if response.get('result') == 'success': - data = response.get('data', None) - result = self._parse(data) + + if response and response.get('result') == 'success': + data = response.get('data', None) + self.parseMaps(result, data) # Create name alias mappings - url = self.config['url_names'] + "id=%s&origin=tvdb" % tryUrlencode(identifier) + url = self.config['url_names'] + "id=%s&origin=tvdb" % tryUrlencode(identifier) response = self.getJsonData(url) - if response: - if response.get('result') == 'success': - data = response.get('data', None) - result.update({'map_names': data}) + + if response and response.get('result') == 'success': + data = response.get('data', None) + self.parseNames(result, data) self.setCache(cache_key, result) return result - def getEpisodeInfo(self, identifier = None, params = {}): - episode = params.get('episode', None) - if episode is None: + def getEpisodeInfo(self, identifiers = None, params = {}): + episode_num = params.get('episode_number', None) + if episode_num is None: return False - season_identifier = params.get('season_identifier', None) - if season_identifier is None: + season_num = params.get('season_number', None) + if season_num is None: return False - episode_identifier = params.get('episode_identifier', None) - absolute = params.get('absolute', None) - - # season_identifier must contain the 'show id : season number' since there is no tvdb id - # for season and we need a reference to both the show id and season number - if season_identifier: - try: - identifier, season_identifier = season_identifier.split(':') - season = int(season_identifier) - except: return False + result = self.getShowInfo(identifiers) - result = self.getShowInfo(identifier) - map = {} - if result: - map_episode = result.get('map_episode', {}).get(season, {}).get(episode, {}) - if map_episode: - map.update({'map_episode': map_episode}) + if not result: + return False - if absolute: - map_absolute = result.get('map_absolute', {}).get(absolute, {}) - if map_absolute: - map.update({'map_absolute': map_absolute}) + # Find season + if season_num not in result['seasons']: + return False - map_names = result.get('map_names', {}).get(toUnicode(season), {}) - if map_names: - map.update({'map_names': map_names}) + season = result['seasons'][season_num] - return map + # Find episode + if episode_num not in season['episodes']: + return False + return season['episodes'][episode_num] - def _parse(self, data, master = 'tvdb'): + def parseMaps(self, result, data, master = 'tvdb'): '''parses xem map and returns a custom formatted dict map To retreive map for scene: @@ -152,17 +147,44 @@ class Xem(ShowProvider): print map['map_episode'][1][1]['scene']['season'] ''' if not isinstance(data, list): - return {} + return - map = {'map_episode': {}, 'map_absolute': {}} - for maps in data: - origin = maps.pop(master, None) + for episode_map in data: + origin = episode_map.pop(master, None) if origin is None: - continue # No master origin to map to - map.get('map_episode').setdefault(origin['season'], {}).setdefault(origin['episode'], maps.copy()) - map.get('map_absolute').setdefault(origin['absolute'], maps.copy()) + continue # No master origin to map to + + o_season = origin['season'] + o_episode = origin['episode'] + + # Create season info + if o_season not in result['seasons']: + result['seasons'][o_season] = {} + + season = result['seasons'][o_season] + + if 'episodes' not in season: + season['episodes'] = {} + + # Create episode info + if o_episode not in season['episodes']: + season['episodes'][o_episode] = {} + + episode = season['episodes'][o_episode] + episode['episode_map'] = episode_map + + def parseNames(self, result, data): + result['title_map'] = data.pop('all', None) + + for season, title_map in data.items(): + season = int(season) + + # Create season info + if season not in result['seasons']: + result['seasons'][season] = {} - return map + season = result['seasons'][season] + season['title_map'] = title_map def isDisabled(self): if __name__ == '__main__': diff --git a/couchpotato/core/media/show/season.py b/couchpotato/core/media/show/season.py index 6c9ff09..e41e460 100755 --- a/couchpotato/core/media/show/season.py +++ b/couchpotato/core/media/show/season.py @@ -14,16 +14,14 @@ class Season(MediaBase): def __init__(self): addEvent('show.season.add', self.add) - addEvent('show.season.update_info', self.updateInfo) + addEvent('show.season.update', self.update) + addEvent('show.season.update_extras', self.updateExtras) def add(self, parent_id, info = None, update_after = True, status = None): if not info: info = {} - identifiers = info.get('identifiers') - try: del info['identifiers'] - except: pass - try: del info['episodes'] - except: pass + identifiers = info.pop('identifiers', None) + info.pop('episodes', None) # Add Season season_info = { @@ -43,6 +41,7 @@ class Season(MediaBase): if existing_season: s = existing_season['doc'] s.update(season_info) + season = db.update(s) else: season = db.insert(season_info) @@ -50,11 +49,11 @@ class Season(MediaBase): # Update library info if update_after is not False: handle = fireEventAsync if update_after is 'async' else fireEvent - handle('show.season.update_info', season.get('_id'), info = info, single = True) + handle('show.season.update_extras', season, info, store = True, single = True) return season - def updateInfo(self, media_id = None, info = None, force = False): + def update(self, media_id = None, identifiers = None, info = None): if not info: info = {} if self.shuttingDown(): @@ -63,25 +62,33 @@ class Season(MediaBase): db = get_db() season = db.get('id', media_id) + show = db.get('id', season['parent_id']) # Get new info if not info: - info = fireEvent('season.info', season.get('identifiers'), merge = True) + info = fireEvent('season.info', show.get('identifiers'), { + 'season_number': season.get('info', {}).get('number', 0) + }, merge = True) + + identifiers = info.pop('identifiers', None) or identifiers + info.pop('episodes', None) # Update/create media - if force: + season['identifiers'].update(identifiers) + season.update({'info': info}) + + self.updateExtras(season, info) - season['identifiers'].update(info['identifiers']) - if 'identifiers' in info: - del info['identifiers'] + db.update(season) + return season - season.update({'info': info}) - s = db.update(season) - season.update(s) + def updateExtras(self, season, info, store=False): + db = get_db() # Get images image_urls = info.get('images', []) existing_files = season.get('files', {}) self.getPoster(image_urls, existing_files) - return season + if store: + db.update(season) diff --git a/couchpotato/core/plugins/dashboard.py b/couchpotato/core/plugins/dashboard.py index 1f5fdcd..0df828d 100755 --- a/couchpotato/core/plugins/dashboard.py +++ b/couchpotato/core/plugins/dashboard.py @@ -62,7 +62,7 @@ class Dashboard(Plugin): for media_id in active_ids: media = db.get('id', media_id) - pp = profile_pre.get(media['profile_id']) + pp = profile_pre.get(media.get('profile_id')) if not pp: continue eta = media['info'].get('release_date', {}) or {} diff --git a/couchpotato/core/plugins/manage.py b/couchpotato/core/plugins/manage.py old mode 100644 new mode 100755 index c8d53ea..d389e22 --- a/couchpotato/core/plugins/manage.py +++ b/couchpotato/core/plugins/manage.py @@ -219,7 +219,7 @@ class Manage(Plugin): # Add it to release and update the info fireEvent('release.add', group = group, update_info = False) - fireEvent('movie.update_info', identifier = group['identifier'], on_complete = self.createAfterUpdate(folder, group['identifier'])) + fireEvent('movie.update', identifier = group['identifier'], on_complete = self.createAfterUpdate(folder, group['identifier'])) return addToLibrary diff --git a/couchpotato/core/plugins/renamer.py b/couchpotato/core/plugins/renamer.py old mode 100644 new mode 100755 index 9f6792a..b331ec8 --- a/couchpotato/core/plugins/renamer.py +++ b/couchpotato/core/plugins/renamer.py @@ -247,7 +247,7 @@ class Renamer(Plugin): 'profile_id': None }, search_after = False, status = 'done', single = True) else: - group['media'] = fireEvent('movie.update_info', media_id = group['media'].get('_id'), single = True) + group['media'] = fireEvent('movie.update', media_id = group['media'].get('_id'), single = True) if not group['media'] or not group['media'].get('_id'): log.error('Could not rename, no library item to work with: %s', group_identifier) diff --git a/libs/tvdb_api/tvdb_api.py b/libs/tvdb_api/tvdb_api.py old mode 100644 new mode 100755 index 4bfe78a..87e8336 --- a/libs/tvdb_api/tvdb_api.py +++ b/libs/tvdb_api/tvdb_api.py @@ -705,7 +705,7 @@ class Tvdb: for k, v in banners[btype][btype2][bid].items(): if k.endswith("path"): new_key = "_%s" % (k) - log().debug("Transforming %s to %s" % (k, new_key)) + #log().debug("Transforming %s to %s" % (k, new_key)) new_url = self.config['url_artworkPrefix'] % (v) banners[btype][btype2][bid][new_key] = new_url