Ejemplo n.º 1
0
def set_episode_to_wanted(show, season, episode):
    """Set an episode to wanted, only if it is currently skipped."""
    # Episode must be loaded from DB to get current status and not default blank episode status
    ep_obj = show.get_episode(season, episode)
    if ep_obj:

        with ep_obj.lock:
            if ep_obj.status != SKIPPED or ep_obj.airdate == datetime.date.fromordinal(1):
                log.info("Not setting episode '{show}' {ep} to WANTED because current status is not SKIPPED "
                         "or it doesn't have a valid airdate", {'show': show.name, 'ep': episode_num(season, episode)})
                return

            log.info("Setting episode '{show}' {ep} to wanted", {
                'show': show.name,
                'ep': episode_num(season, episode)
            })
            # figure out what segment the episode is in and remember it so we can backlog it

            ep_obj.status = WANTED
            # As we created the episode and updated the status, need to save to DB
            ep_obj.save_to_db()

        cur_backlog_queue_item = BacklogQueueItem(show, [ep_obj])
        app.search_queue_scheduler.action.add_item(cur_backlog_queue_item)

        log.info("Starting backlog search for '{show}' {ep} because some episodes were set to wanted", {
            'show': show.name,
            'ep': episode_num(season, episode)
        })
Ejemplo n.º 2
0
    def search_backlog(self, which_shows=None):
        """Run the backlog search for given shows."""
        if self.amActive:
            log.debug(u'Backlog is still running, not starting it again')
            return

        if app.forced_search_queue_scheduler.action.is_forced_search_in_progress():
            log.warning(u'Manual search is running. Unable to start Backlog Search')
            return

        self.amActive = True
        self.amPaused = False

        if which_shows:
            show_list = which_shows
        else:
            show_list = app.showList

        self._get_last_backlog()

        cur_date = datetime.date.today().toordinal()
        from_date = datetime.date.fromordinal(1)

        if not which_shows and self.forced:
            log.info(u'Running limited backlog search on missed episodes from last {0} days',
                     app.BACKLOG_DAYS)
            from_date = datetime.date.today() - datetime.timedelta(days=app.BACKLOG_DAYS)
        else:
            log.info(u'Running full backlog search on missed episodes for selected shows')

        # go through non air-by-date shows and see if they need any episodes
        for series_obj in show_list:

            if series_obj.paused:
                continue

            segments = self._get_segments(series_obj, from_date)

            for season, segment in iteritems(segments):
                self.currentSearchInfo = {'title': '{series_name} Season {season}'.format(series_name=series_obj.name,
                                                                                          season=season)}

                backlog_queue_item = BacklogQueueItem(series_obj, segment)
                app.search_queue_scheduler.action.add_item(backlog_queue_item)  # @UndefinedVariable

            if not segments:
                log.debug(u'Nothing needs to be downloaded for {0!r}, skipping', series_obj.name)

        # don't consider this an actual backlog search if we only did recent eps
        # or if we only did certain shows
        if from_date == datetime.date.fromordinal(1) and not which_shows:
            self._set_last_backlog(cur_date)

        self.amActive = False
        self._reset_pi()
Ejemplo n.º 3
0
    def _search_backlog(self, data=None):
        """Queue a backlog search for results for the provided episodes or season.

        :param data:
        :return:
        :example:
            Start a backlog search for show slug tvdb1234 with episodes s01e01, s01e02, s03e03.
            route: `apiv2/search/backlog`
            { showSlug: "tvdb1234",
              episodes: [
                "s01e01",
                "s01e02",
                "s03e03",
              ]
            }
        """
        if not data:
            # Trigger a full backlog search
            if app.backlog_search_scheduler.forceRun():
                return self._accepted('Full backlog search started')

            return self._bad_request('Triggering a full backlog search failed')

        if not data.get('showSlug'):
            return self._bad_request('You need to provide a show slug')

        if not data.get('episodes') and not data.get('season'):
            return self._bad_request(
                'For a backlog search you need to provide a list of episodes or seasons'
            )

        identifier = SeriesIdentifier.from_slug(data['showSlug'])
        if not identifier:
            return self._bad_request('Invalid series slug')

        series = Series.find_by_identifier(identifier)
        if not series:
            return self._not_found('Series not found')

        episode_segments = self._get_episode_segments(series, data)

        # If a season is passed, we transform it to a list of episode objects. And merge it with the episode_segment.
        # This because the backlog search has its own logic for searching per episode or season packs. And falling back
        # between them, if configured.
        if data.get('season'):
            for season_slug in data['season']:
                episode_season = int(season_slug[1:])
                episodes = series.get_all_episodes(episode_season)
                for episode in episodes:
                    if episode not in episode_segments[episode_season]:
                        episode_segments[episode_season].append(episode)

        if not episode_segments:
            return self._not_found(
                'Could not find any episode for show {show}. Did you provide the correct format?'
                .format(show=series.name))

        for segment in itervalues(episode_segments):
            cur_backlog_queue_item = BacklogQueueItem(series, segment)
            app.forced_search_queue_scheduler.action.add_item(
                cur_backlog_queue_item)

        return self._accepted('Backlog search for {0} started'.format(
            data['showSlug']))
Ejemplo n.º 4
0
    def run(self):

        ShowQueueItem.run(self)

        log.info('Starting to add show by {0}',
                 ('show_dir: {0}'.format(self.show_dir) if self.show_dir else
                  'Indexer Id: {0}'.format(self.indexer_id)))

        # make sure the Indexer IDs are valid
        try:
            l_indexer_api_params = indexerApi(self.indexer).api_params.copy()
            if self.lang:
                l_indexer_api_params['language'] = self.lang

            log.info(
                '{indexer_name}: {indexer_params!r}', {
                    'indexer_name': indexerApi(self.indexer).name,
                    'indexer_params': l_indexer_api_params
                })

            indexer_api = indexerApi(
                self.indexer).indexer(**l_indexer_api_params)
            s = indexer_api[self.indexer_id]

            # Let's try to create the show Dir if it's not provided. This way we force the show dir
            # to build build using the Indexers provided series name
            if not self.show_dir and self.root_dir:
                show_name = get_showname_from_indexer(self.indexer,
                                                      self.indexer_id,
                                                      self.lang)
                if show_name:
                    self.show_dir = os.path.join(self.root_dir,
                                                 sanitize_filename(show_name))
                    dir_exists = make_dir(self.show_dir)
                    if not dir_exists:
                        log.info(
                            "Unable to create the folder {0}, can't add the show",
                            self.show_dir)
                        return

                    chmod_as_parent(self.show_dir)
                else:
                    log.info("Unable to get a show {0}, can't add the show",
                             self.show_dir)
                    return

            # this usually only happens if they have an NFO in their show dir which gave us a Indexer ID that
            # has no proper english version of the show
            if getattr(s, 'seriesname', None) is None:
                log.error(
                    'Show in {path} has no name on {indexer}, probably searched with the wrong language.',
                    {
                        'path': self.show_dir,
                        'indexer': indexerApi(self.indexer).name
                    })

                ui.notifications.error(
                    'Unable to add show',
                    'Show in {path} has no name on {indexer}, probably the wrong language.'
                    ' Delete .nfo and manually add the correct language.'.
                    format(path=self.show_dir,
                           indexer=indexerApi(self.indexer).name))
                self._finishEarly()
                return

            # Check if we can already find this show in our current showList.
            try:
                check_existing_shows(s, self.indexer)
            except IndexerShowAlreadyInLibrary as error:
                log.warning(
                    'Could not add the show {series}, as it already is in your library.'
                    ' Error: {error}', {
                        'series': s['seriesname'],
                        'error': error
                    })
                ui.notifications.error('Unable to add show',
                                       'reason: {0}'.format(error))
                self._finishEarly()

                # Clean up leftover if the newly created directory is empty.
                delete_empty_folders(self.show_dir)
                return

        # TODO: Add more specific indexer exceptions, that should provide the user with some accurate feedback.
        except IndexerShowNotFound as error:
            log.warning(
                '{id}: Unable to look up the show in {path} using id {id} on {indexer}.'
                ' Delete metadata files from the folder and try adding it again.\n'
                'With error: {error}', {
                    'id': self.indexer_id,
                    'path': self.show_dir,
                    'indexer': indexerApi(self.indexer).name,
                    'error': error
                })
            ui.notifications.error(
                'Unable to add show',
                'Unable to look up the show in {path} using id {id} on {indexer}.'
                ' Delete metadata files from the folder and try adding it again.'
                .format(path=self.show_dir,
                        id=self.indexer_id,
                        indexer=indexerApi(self.indexer).name))
            self._finishEarly()
            return
        except IndexerShowNotFoundInLanguage as error:
            log.warning(
                '{id}: Data retrieved from {indexer} was incomplete. The indexer does not provide'
                ' show information in the searched language {language}. Aborting: {error_msg}',
                {
                    'id': self.indexer_id,
                    'indexer': indexerApi(self.indexer).name,
                    'language': error.language,
                    'error_msg': error
                })
            ui.notifications.error(
                'Error adding show!',
                'Unable to add show {indexer_id} on {indexer} with this language: {language}'
                .format(indexer_id=self.indexer_id,
                        indexer=indexerApi(self.indexer).name,
                        language=error.language))
            self._finishEarly()
            return
        except Exception as error:
            log.error(
                '{id}: Error while loading information from indexer {indexer}. Error: {error!r}',
                {
                    'id': self.indexer_id,
                    'indexer': indexerApi(self.indexer).name,
                    'error': error
                })
            ui.notifications.error(
                'Unable to add show',
                'Unable to look up the show in {path} on {indexer} using ID {id}.'
                .format(path=self.show_dir,
                        indexer=indexerApi(self.indexer).name,
                        id=self.indexer_id))
            self._finishEarly()
            return

        try:
            newShow = Series(self.indexer, self.indexer_id, self.lang)
            newShow.load_from_indexer(indexer_api)

            self.show = newShow

            # set up initial values
            self.show.location = self.show_dir
            self.show.subtitles = self.subtitles if self.subtitles is not None else app.SUBTITLES_DEFAULT
            self.show.quality = self.quality if self.quality else app.QUALITY_DEFAULT
            self.show.season_folders = self.season_folders if self.season_folders is not None \
                else app.SEASON_FOLDERS_DEFAULT
            self.show.anime = self.anime if self.anime is not None else app.ANIME_DEFAULT
            self.show.scene = self.scene if self.scene is not None else app.SCENE_DEFAULT
            self.show.paused = self.paused if self.paused is not None else False

            # set up default new/missing episode status
            log.info(
                'Setting all previously aired episodes to the specified status: {status}',
                {'status': statusStrings[self.default_status]})
            self.show.default_ep_status = self.default_status

            if self.show.anime:
                self.show.release_groups = BlackAndWhiteList(self.show)
                if self.blacklist:
                    self.show.release_groups.set_black_keywords(self.blacklist)
                if self.whitelist:
                    self.show.release_groups.set_white_keywords(self.whitelist)

        except IndexerException as error:
            log.error(
                'Unable to add show due to an error with {indexer}: {error}', {
                    'indexer': indexerApi(self.indexer).name,
                    'error': error
                })
            ui.notifications.error(
                'Unable to add {series_name} due to an error with {indexer_name}'
                .format(series_name=self.show.name if self.show else 'show',
                        indexer_name=indexerApi(self.indexer).name))
            self._finishEarly()
            return

        except MultipleShowObjectsException:
            log.warning(
                'The show in {show_dir} is already in your show list, skipping',
                {'show_dir': self.show_dir})
            ui.notifications.error(
                'Show skipped',
                'The show in {show_dir} is already in your show list'.format(
                    show_dir=self.show_dir))
            self._finishEarly()
            return

        except Exception as error:
            log.error('Error trying to add show: {0}', error)
            log.debug(traceback.format_exc())
            self._finishEarly()
            raise

        log.debug('Retrieving show info from IMDb')
        try:
            self.show.load_imdb_info()
        except ImdbAPIError as error:
            log.info('Something wrong on IMDb api: {0}', error)
        except RequestException as error:
            log.warning('Error loading IMDb info: {0}', error)

        try:
            log.debug('{id}: Saving new show to database',
                      {'id': self.show.series_id})
            self.show.save_to_db()
        except Exception as error:
            log.error('Error saving the show to the database: {0}', error)
            log.debug(traceback.format_exc())
            self._finishEarly()
            raise

        # add it to the show list
        app.showList.append(self.show)

        try:
            self.show.load_episodes_from_indexer(tvapi=indexer_api)
        except Exception as error:
            log.error(
                'Error with {indexer}, not creating episode list: {error}', {
                    'indexer': indexerApi(self.show.indexer).name,
                    'error': error
                })
            log.debug(traceback.format_exc())

        # update internal name cache
        name_cache.build_name_cache(self.show)

        try:
            self.show.load_episodes_from_dir()
        except Exception as error:
            log.error('Error searching dir for episodes: {0}', error)
            log.debug(traceback.format_exc())

        # if they set default ep status to WANTED then run the backlog to search for episodes
        if self.show.default_ep_status == WANTED:
            log.info(
                'Launching backlog for this show since its episodes are WANTED'
            )
            wanted_segments = self.show.get_wanted_segments()
            for season, segment in viewitems(wanted_segments):
                cur_backlog_queue_item = BacklogQueueItem(self.show, segment)
                app.forced_search_queue_scheduler.action.add_item(
                    cur_backlog_queue_item)

                log.info('Sending forced backlog for {show} season {season}'
                         ' because some episodes were set to wanted'.format(
                             show=self.show.name, season=season))

        self.show.write_metadata()
        self.show.update_metadata()
        self.show.populate_cache()

        self.show.flush_episodes()

        if app.USE_TRAKT:
            # if there are specific episodes that need to be added by trakt
            app.trakt_checker_scheduler.action.manage_new_show(self.show)

            # add show to trakt.tv library
            if app.TRAKT_SYNC:
                app.trakt_checker_scheduler.action.add_show_trakt_library(
                    self.show)

            if app.TRAKT_SYNC_WATCHLIST:
                log.info('update watchlist')
                notifiers.trakt_notifier.update_watchlist(show_obj=self.show)

        # Load XEM data to DB for show
        scene_numbering.xem_refresh(self.show, force=True)

        # check if show has XEM mapping so we can determine if searches
        # should go by scene numbering or indexer numbering. Warn the user.
        if not self.scene and scene_numbering.get_xem_numbering_for_show(
                self.show):
            log.warning(
                '{id}: while adding the show {title} we noticed thexem.de has an episode mapping available'
                '\nyou might want to consider enabling the scene option for this show.',
                {
                    'id': self.show.series_id,
                    'title': self.show.name
                })
            ui.notifications.message(
                'consider enabling scene for this show',
                'for show {title} you might want to consider enabling the scene option'
                .format(title=self.show.name))

        # After initial add, set to default_status_after.
        self.show.default_ep_status = self.default_status_after

        try:
            log.debug('{id}: Saving new show info to database',
                      {'id': self.show.series_id})
            self.show.save_to_db()
        except Exception as error:
            log.warning(
                '{id}: Error saving new show info to database: {error_msg}', {
                    'id': self.show.series_id,
                    'error_msg': error
                })
            log.error(traceback.format_exc())

        # Send ws update to client
        ws.Message('showAdded', self.show.to_json(detailed=False)).push()

        self.finish()