Ejemplo n.º 1
0
    def delete(self, series_slug, episode_slug, **kwargs):
        """Delete the episode."""
        if not series_slug:
            return self._method_not_allowed(
                'Deleting multiple series are not allowed')

        identifier = SeriesIdentifier.from_slug(series_slug)
        if not identifier:
            return self._bad_request('Invalid series identifier')

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

        episode_number = EpisodeNumber.from_slug(episode_slug)
        if not episode_number:
            return self._bad_request('Invalid episode number')

        episode = Episode.find_by_series_and_episode(series, episode_number)
        if not episode:
            return self._not_found('Episode not found')

        try:
            episode.delete_episode()
        except EpisodeDeletedException:
            return self._no_content()
        else:
            return self._conflict('Unable to delete episode')
Ejemplo n.º 2
0
    def http_post(self, identifier, **kwargs):
        """Add an alias."""
        if identifier is not None:
            return self._bad_request('Alias id should not be specified')

        data = json_decode(self.request.body)

        if not data or not all([data.get('series'), data.get('name'),
                                data.get('type')]) or 'id' in data or data['type'] != 'local':
            return self._bad_request('Invalid request body')

        series_identifier = SeriesIdentifier.from_slug(data.get('series'))
        if not series_identifier:
            return self._bad_request('Invalid series')

        cache_db_con = db.DBConnection('cache.db')
        last_changes = cache_db_con.connection.total_changes
        cursor = cache_db_con.action('INSERT INTO scene_exceptions'
                                     ' (indexer, indexer_id, show_name, season, custom) '
                                     ' values (?,?,?,?,1)',
                                     [series_identifier.indexer.id,
                                      series_identifier.id,
                                      data['name'],
                                      data.get('season')])

        if cache_db_con.connection.total_changes - last_changes <= 0:
            return self._conflict('Unable to create alias')

        data['id'] = cursor.lastrowid
        return self._created(data=data, identifier=data['id'])
Ejemplo n.º 3
0
        def get_show_from_slug(slug):
            identifier = SeriesIdentifier.from_slug(slug)
            if not identifier:
                raise ChangeIndexerException(f'Could not create identifier with slug {slug}')

            show = Series.find_by_identifier(identifier)
            return show
Ejemplo n.º 4
0
    def patch(self, series_slug, episode_slug=None, path_param=None):
        """Patch episode."""
        series_identifier = SeriesIdentifier.from_slug(series_slug)
        if not series_identifier:
            return self._bad_request('Invalid series slug')

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

        data = json_decode(self.request.body)

        # Multi-patch request
        if not episode_slug:
            return self._patch_multi(series, data)

        episode_number = EpisodeNumber.from_slug(episode_slug)
        if not episode_number:
            return self._bad_request('Invalid episode number')

        episode = Episode.find_by_series_and_episode(series, episode_number)
        if not episode:
            return self._not_found('Episode not found')

        accepted = self._patch_episode(episode, data)

        return self._ok(data=accepted)
Ejemplo n.º 5
0
    def http_post(self, series_slug=None, path_param=None):
        """Add a new series."""
        if series_slug is not None:
            return self._bad_request('Series slug should not be specified')

        data = json_decode(self.request.body)
        if not data or 'id' not in data:
            return self._bad_request('Invalid series data')

        ids = {k: v for k, v in viewitems(data['id']) if k != 'imdb'}
        if len(ids) != 1:
            return self._bad_request('Only 1 indexer identifier should be specified')

        identifier = SeriesIdentifier.from_slug('{slug}{id}'.format(slug=list(ids)[0], id=list(itervalues(ids))[0]))
        if not identifier:
            return self._bad_request('Invalid series identifier')

        series = Series.find_by_identifier(identifier)
        if series:
            return self._conflict('Series already exist added')

        series = Series.from_identifier(identifier)
        if not Series.save_series(series):
            return self._not_found('Series not found in the specified indexer')

        return self._created(series.to_json(), identifier=identifier.slug)
Ejemplo n.º 6
0
    def get_results(self, show_slug=None, season=None, episode=None):
        """Get cached results for this provider."""
        cache_db_con = self._get_db()

        param = []
        where = []

        if show_slug:
            show = SeriesIdentifier.from_slug(show_slug)
            where += ['indexer', 'indexerid']
            param += [show.indexer.id, show.id]

        if season:
            where += ['season']
            param += [season]

        if episode:
            where += ['episodes']
            param += ['|{0}|'.format(episode)]

        base_sql = 'SELECT * FROM [{name}]'.format(name=self.provider_id)
        base_params = []

        if where and param:
            base_sql += ' WHERE '
            base_sql += ' AND '.join([item + ' = ? ' for item in where])
            base_params += param

        results = cache_db_con.select(
            base_sql,
            base_params
        )

        return results
Ejemplo n.º 7
0
    def post(self, series_slug=None, path_param=None):
        """Add a new series."""
        if series_slug is not None:
            return self._bad_request('Series slug should not be specified')

        data = json_decode(self.request.body)
        if not data or 'id' not in data:
            return self._bad_request('Invalid series data')

        ids = {k: v for k, v in viewitems(data['id']) if k != 'imdb'}
        if len(ids) != 1:
            return self._bad_request(
                'Only 1 indexer identifier should be specified')

        identifier = SeriesIdentifier.from_slug('{slug}{id}'.format(
            slug=list(ids)[0], id=list(itervalues(ids))[0]))
        if not identifier:
            return self._bad_request('Invalid series identifier')

        series = Series.find_by_identifier(identifier)
        if series:
            return self._conflict('Series already exist added')

        series = Series.from_identifier(identifier)
        if not Series.save_series(series):
            return self._not_found('Series not found in the specified indexer')

        return self._created(series.to_json(), identifier=identifier.slug)
Ejemplo n.º 8
0
    def post(self, identifier, **kwargs):
        """Add an alias."""
        if identifier is not None:
            return self._bad_request('Alias id should not be specified')

        data = json_decode(self.request.body)

        if not data or not all(
            [data.get('series'),
             data.get('name'),
             data.get('type')]) or 'id' in data or data['type'] != 'local':
            return self._bad_request('Invalid request body')

        series_identifier = SeriesIdentifier.from_slug(data.get('series'))
        if not series_identifier:
            return self._bad_request('Invalid series')

        cache_db_con = db.DBConnection('cache.db')
        last_changes = cache_db_con.connection.total_changes
        cursor = cache_db_con.action(
            b'INSERT INTO scene_exceptions'
            b' (indexer, indexer_id, show_name, season, custom) '
            b' values (?,?,?,?,1)', [
                series_identifier.indexer.id, series_identifier.id,
                data['name'],
                data.get('season')
            ])

        if cache_db_con.connection.total_changes - last_changes <= 0:
            return self._conflict('Unable to create alias')

        data['id'] = cursor.lastrowid
        return self._created(data=data, identifier=data['id'])
Ejemplo n.º 9
0
    def get(self, series_slug, identifier):
        """Query series information.

        :param series_slug: series slug. E.g.: tvdb1234
        :param identifier:
        """
        series_identifier = SeriesIdentifier.from_slug(series_slug)
        if not series_identifier:
            return self._bad_request('Invalid series slug')

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

        if identifier == 'backlogged':
            # TODO: revisit
            allowed_qualities = self._parse(
                self.get_argument('allowed', default=None), str)
            allowed_qualities = list(map(
                int,
                allowed_qualities.split(','))) if allowed_qualities else []
            preferred_qualities = self._parse(
                self.get_argument('preferred', default=None), str)
            preferred_qualities = list(map(int, preferred_qualities.split(
                ','))) if preferred_qualities else []
            new, existing = series.get_backlogged_episodes(
                allowed_qualities=allowed_qualities,
                preferred_qualities=preferred_qualities)
            data = {'new': new, 'existing': existing}
            return self._ok(data=data)

        return self._bad_request('Invalid request')
Ejemplo n.º 10
0
    def http_put(self, identifier, **kwargs):
        """Update alias information."""
        identifier = self._parse(identifier)
        if not identifier:
            return self._not_found('Invalid alias id')

        data = json_decode(self.request.body)

        if not data or not all([data.get('id'), data.get('series'), data.get('name'),
                                data.get('type')]) or data['id'] != identifier:
            return self._bad_request('Invalid request body')

        series_identifier = SeriesIdentifier.from_slug(data.get('series'))
        if not series_identifier:
            return self._bad_request('Invalid series')

        cache_db_con = db.DBConnection('cache.db')
        last_changes = cache_db_con.connection.total_changes
        cache_db_con.action('UPDATE scene_exceptions'
                            ' set indexer = ?'
                            ', indexer_id = ?'
                            ', show_name = ?'
                            ', season = ?'
                            ', custom = 1'
                            ' WHERE exception_id = ?',
                            [series_identifier.indexer.id,
                             series_identifier.id,
                             data['name'],
                             data.get('season'),
                             identifier])

        if cache_db_con.connection.total_changes - last_changes != 1:
            return self._not_found('Alias not found')

        return self._no_content()
Ejemplo n.º 11
0
    def http_delete(self, series_slug, episode_slug, **kwargs):
        """Delete the episode."""
        if not series_slug:
            return self._method_not_allowed('Deleting multiple series are not allowed')

        identifier = SeriesIdentifier.from_slug(series_slug)
        if not identifier:
            return self._bad_request('Invalid series identifier')

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

        episode_number = EpisodeNumber.from_slug(episode_slug)
        if not episode_number:
            return self._bad_request('Invalid episode number')

        episode = Episode.find_by_series_and_episode(series, episode_number)
        if not episode:
            return self._not_found('Episode not found')

        try:
            episode.delete_episode()
        except EpisodeDeletedException:
            return self._no_content()
        else:
            return self._conflict('Unable to delete episode')
Ejemplo n.º 12
0
    def http_patch(self, series_slug, episode_slug=None, path_param=None):
        """Patch episode."""
        series_identifier = SeriesIdentifier.from_slug(series_slug)
        if not series_identifier:
            return self._bad_request('Invalid series slug')

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

        data = json_decode(self.request.body)

        # Multi-patch request
        if not episode_slug:
            return self._patch_multi(series, data)

        episode_number = EpisodeNumber.from_slug(episode_slug)
        if not episode_number:
            return self._bad_request('Invalid episode number')

        episode = Episode.find_by_series_and_episode(series, episode_number)
        if not episode:
            return self._not_found('Episode not found')

        accepted = self._patch_episode(episode, data)

        return self._ok(data=accepted)
Ejemplo n.º 13
0
    def get(self, series_slug, path_param=None):
        """Query series information.

        :param series_slug: series slug. E.g.: tvdb1234
        :param path_param:
        """
        arg_paused = self._parse_boolean(self.get_argument('paused', default=None))

        def filter_series(current):
            return arg_paused is None or current.paused == arg_paused

        if not series_slug:
            detailed = self._parse_boolean(self.get_argument('detailed', default=False))
            data = [s.to_json(detailed=detailed) for s in Series.find_series(predicate=filter_series)]
            return self._paginate(data, sort='title')

        identifier = SeriesIdentifier.from_slug(series_slug)
        if not identifier:
            return self._bad_request('Invalid series slug')

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

        detailed = self._parse_boolean(self.get_argument('detailed', default=True))
        data = series.to_json(detailed=detailed)
        if path_param:
            if path_param not in data:
                return self._bad_request("Invalid path parameter'{0}'".format(path_param))
            data = data[path_param]

        return self._ok(data)
Ejemplo n.º 14
0
    def http_get(self, series_slug, identifier):
        """Query series information.

        :param series_slug: series slug. E.g.: tvdb1234
        :param identifier:
        """
        series_identifier = SeriesIdentifier.from_slug(series_slug)
        if not series_identifier:
            return self._bad_request('Invalid series slug')

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

        if identifier == 'backlogged':
            # TODO: revisit
            allowed_qualities = self._parse(self.get_argument('allowed', default=None), str)
            allowed_qualities = list(map(int, allowed_qualities.split(','))) if allowed_qualities else []
            preferred_qualities = self._parse(self.get_argument('preferred', default=None), str)
            preferred_qualities = list(map(int, preferred_qualities.split(','))) if preferred_qualities else []
            new, existing = series.get_backlogged_episodes(allowed_qualities=allowed_qualities,
                                                           preferred_qualities=preferred_qualities)
            data = {'new': new, 'existing': existing}
            return self._ok(data=data)

        return self._bad_request('Invalid request')
Ejemplo n.º 15
0
    def _search_manual(self, data):
        """Queue a manual search for results for the provided episodes or season.

        :param data:
        :return:
        """
        if not data.get('showSlug'):
            return self._bad_request('For a manual search you need to provide a show slug')

        if not data.get('episodes') and not data.get('season'):
            return self._bad_request('For a manual 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)
        season_segments = self._get_season_segments(series, data)

        for segments in ({'segment': episode_segments, 'manual_search_type': 'episode'},
                         {'segment': season_segments, 'manual_search_type': 'season'}):
            for segment in itervalues(segments['segment']):
                cur_manual_search_queue_item = ManualSearchQueueItem(series, segment, manual_search_type=segments['manual_search_type'])
                app.forced_search_queue_scheduler.action.add_item(cur_manual_search_queue_item)

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

        return self._accepted('Manual search for {0} started'.format(data['showSlug']))
Ejemplo n.º 16
0
    def http_patch(self, series_slug, path_param=None):
        """Patch series."""
        if not series_slug:
            return self._method_not_allowed('Patching multiple series is not allowed')

        identifier = SeriesIdentifier.from_slug(series_slug)
        if not identifier:
            return self._bad_request('Invalid series identifier')

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

        data = json_decode(self.request.body)
        indexer_id = data.get('id', {}).get(identifier.indexer.slug)
        if indexer_id is not None and indexer_id != identifier.id:
            return self._bad_request('Conflicting series identifier')

        accepted = {}
        ignored = {}
        patches = {
            'config.aliases': ListField(series, 'aliases'),
            'config.defaultEpisodeStatus': StringField(series, 'default_ep_status_name'),
            'config.dvdOrder': BooleanField(series, 'dvd_order'),
            'config.seasonFolders': BooleanField(series, 'season_folders'),
            'config.anime': BooleanField(series, 'anime'),
            'config.scene': BooleanField(series, 'scene'),
            'config.sports': BooleanField(series, 'sports'),
            'config.paused': BooleanField(series, 'paused'),
            'config.location': StringField(series, 'location'),
            'config.airByDate': BooleanField(series, 'air_by_date'),
            'config.subtitlesEnabled': BooleanField(series, 'subtitles'),
            'config.release.requiredWords': ListField(series, 'release_required_words'),
            'config.release.ignoredWords': ListField(series, 'release_ignore_words'),
            'config.release.blacklist': ListField(series, 'blacklist'),
            'config.release.whitelist': ListField(series, 'whitelist'),
            'config.release.requiredWordsExclude': BooleanField(series, 'rls_require_exclude'),
            'config.release.ignoredWordsExclude': BooleanField(series, 'rls_ignore_exclude'),
            'language': StringField(series, 'lang'),
            'config.qualities.allowed': ListField(series, 'qualities_allowed'),
            'config.qualities.preferred': ListField(series, 'qualities_preferred'),
            'config.qualities.combined': IntegerField(series, 'quality'),
            'config.airdateOffset': IntegerField(series, 'airdate_offset'),
        }

        for key, value in iter_nested_items(data):
            patch_field = patches.get(key)
            if patch_field and patch_field.patch(series, value):
                set_nested_value(accepted, key, value)
            else:
                set_nested_value(ignored, key, value)

        # Save patched attributes in db.
        series.save_to_db()

        if ignored:
            log.warning('Series patch ignored {items!r}', {'items': ignored})

        return self._ok(data=accepted)
Ejemplo n.º 17
0
    def backlogShow(self, showslug):
        identifier = SeriesIdentifier.from_slug(showslug)
        series_obj = Series.find_by_identifier(identifier)

        if series_obj:
            app.backlog_search_scheduler.action.search_backlog([series_obj])

        return self.redirect('/manage/backlogOverview/')
Ejemplo n.º 18
0
    def patch(self, series_slug, path_param=None):
        """Patch series."""
        if not series_slug:
            return self._method_not_allowed('Patching multiple series is not allowed')

        identifier = SeriesIdentifier.from_slug(series_slug)
        if not identifier:
            return self._bad_request('Invalid series identifier')

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

        data = json_decode(self.request.body)
        indexer_id = data.get('id', {}).get(identifier.indexer.slug)
        if indexer_id is not None and indexer_id != identifier.id:
            return self._bad_request('Conflicting series identifier')

        accepted = {}
        ignored = {}
        patches = {
            'config.aliases': ListField(series, 'aliases'),
            'config.defaultEpisodeStatus': StringField(series, 'default_ep_status_name'),
            'config.dvdOrder': BooleanField(series, 'dvd_order'),
            'config.seasonFolders': BooleanField(series, 'season_folders'),
            'config.anime': BooleanField(series, 'anime'),
            'config.scene': BooleanField(series, 'scene'),
            'config.sports': BooleanField(series, 'sports'),
            'config.paused': BooleanField(series, 'paused'),
            'config.location': StringField(series, '_location'),
            'config.airByDate': BooleanField(series, 'air_by_date'),
            'config.subtitlesEnabled': BooleanField(series, 'subtitles'),
            'config.release.requiredWords': ListField(series, 'release_required_words'),
            'config.release.ignoredWords': ListField(series, 'release_ignore_words'),
            'config.release.blacklist': ListField(series, 'blacklist'),
            'config.release.whitelist': ListField(series, 'whitelist'),
            'language': StringField(series, 'lang'),
            'config.qualities.allowed': ListField(series, 'qualities_allowed'),
            'config.qualities.preferred': ListField(series, 'qualities_preferred'),
            'config.qualities.combined': IntegerField(series, 'quality'),
        }

        for key, value in iter_nested_items(data):
            patch_field = patches.get(key)
            if patch_field and patch_field.patch(series, value):
                set_nested_value(accepted, key, value)
            else:
                set_nested_value(ignored, key, value)

        # Save patched attributes in db.
        series.save_to_db()

        if ignored:
            log.warning('Series patch ignored {items!r}', {'items': ignored})

        self._ok(data=accepted)
Ejemplo n.º 19
0
    def get(self, series_slug, path_param):
        """
        Get history records.

        History records can be specified using a show slug.
        """
        sql_base = """
            SELECT rowid, date, action, quality,
                   provider, version, proper_tags, manually_searched,
                   resource, size, indexer_id, showid, season, episode
            FROM history
        """
        params = []

        arg_page = self._get_page()
        arg_limit = self._get_limit(default=50)

        if series_slug is not None:
            series_identifier = SeriesIdentifier.from_slug(series_slug)
            if not series_identifier:
                return self._bad_request('Invalid series')

            sql_base += ' WHERE indexer_id = ? AND showid = ?'
            params += [series_identifier.indexer.id, series_identifier.id]

        sql_base += ' ORDER BY date DESC'
        results = db.DBConnection().select(sql_base, params)

        def data_generator():
            """Read and paginate history records."""
            start = arg_limit * (arg_page - 1)

            for item in results[start:start + arg_limit]:
                d = {}
                d['id'] = item['rowid']
                d['series'] = SeriesIdentifier.from_id(item['indexer_id'],
                                                       item['showid']).slug
                d['status'] = item['action']
                d['actionDate'] = item['date']

                d['resource'] = basename(item['resource'])
                d['size'] = item['size']
                d['properTags'] = item['proper_tags']
                d['statusName'] = statusStrings.get(item['action'])
                d['season'] = item['season']
                d['episode'] = item['episode']
                d['manuallySearched'] = bool(item['manually_searched'])
                d['provider'] = item['provider']

                yield d

        if not results:
            return self._not_found('History data not found')

        return self._paginate(data_generator=data_generator)
Ejemplo n.º 20
0
    def resource_search_missing_subtitles(self):
        """
        Search missing subtitles for multiple episodes at once.

        example: Pass the following structure:
            language: 'all', // Or a three letter language code.
            shows: [
                {
                    'slug': 'tvdb1234',
                    'episodes': ['s01e01', 's02e03', 's10e10']
                },
            ]
        """
        data = json_decode(self.request.body)
        language = data.get('language', 'all')
        shows = data.get('shows', [])

        if language != 'all' and language not in subtitle_code_filter():
            return self._bad_request(
                'You need to provide a valid subtitle code')

        for show in shows:
            # Loop through the shows. Each show should have an array of episode slugs
            series_identifier = SeriesIdentifier.from_slug(show.get('slug'))
            if not series_identifier:
                log.warning(
                    'Could not create a show identifier with slug {slug}',
                    {'slug': show.get('slug')})
                continue

            series = Series.find_by_identifier(series_identifier)
            if not series:
                log.warning(
                    'Could not match to a show in the library with slug {slug}',
                    {'slug': show.get('slug')})
                continue

            for episode_slug in show.get('episodes', []):
                episode_number = EpisodeNumber.from_slug(episode_slug)
                if not episode_number:
                    log.warning('Bad episode number from slug {slug}',
                                {'slug': episode_slug})
                    continue

                episode = Episode.find_by_series_and_episode(
                    series, episode_number)
                if not episode:
                    log.warning('Episode not found with slug {slug}',
                                {'slug': episode_slug})

                episode.download_subtitles(
                    lang=language if language != 'all' else None)

        return self._ok()
Ejemplo n.º 21
0
    def _search_failed(self, data):
        """Queue a failed search.

        :param data:
        :return:
        """
        statuses = {}

        if not data.get('showSlug'):
            return self._bad_request(
                'For a failed search you need to provide a show slug')

        if not data.get('episodes'):
            return self._bad_request(
                'For a failed search you need to provide a list of episodes')

        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')

        season_segments = defaultdict(list)
        for episode_slug in data['episodes']:
            episode_number = EpisodeNumber.from_slug(episode_slug)
            if not episode_number:
                statuses[episode_slug] = {'status': 400}
                continue

            episode = Episode.find_by_series_and_episode(
                series, episode_number)
            if not episode:
                statuses[episode_slug] = {'status': 404}
                continue

            season_segments[episode.season].append(episode)

        if not season_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(season_segments):
            cur_failed_queue_item = FailedQueueItem(series, segment)
            app.forced_search_queue_scheduler.action.add_item(
                cur_failed_queue_item)

        return self._accepted('Failed search for {0} started'.format(
            data['showSlug']))
Ejemplo n.º 22
0
    def togglePause(self, showslug=None):
        # @TODO: Replace with PUT to update the state var /api/v2/show/{id}
        identifier = SeriesIdentifier.from_slug(showslug)
        error, series_obj = Show.pause(identifier.indexer.slug, identifier.id)

        if error is not None:
            return self._genericMessage('Error', error)

        ui.notifications.message('{show} has been {state}'.format(
            show=series_obj.name,
            state='paused' if series_obj.paused else 'resumed'))

        return self.redirect(
            '/home/displayShow?showslug={series_obj.slug}'.format(
                series_obj=series_obj))
Ejemplo n.º 23
0
    def searchEpisodeSubtitles(self,
                               showslug=None,
                               season=None,
                               episode=None,
                               lang=None):
        # retrieve the episode object and fail if we can't get one
        series_obj = Series.find_by_identifier(
            SeriesIdentifier.from_slug(showslug))
        ep_obj = series_obj.get_episode(season, episode)
        if not ep_obj:
            return json.dumps({
                'result': 'failure',
            })

        try:
            if lang:
                logger.log(
                    'Manual re-downloading subtitles for {show} with language {lang}'
                    .format(show=ep_obj.series.name, lang=lang))
            new_subtitles = ep_obj.download_subtitles(lang=lang)
        except Exception as error:
            return json.dumps({
                'result':
                'failure',
                'description':
                'Error while downloading subtitles: {error}'.format(
                    error=error)
            })

        if new_subtitles:
            new_languages = [
                subtitles.name_from_code(code) for code in new_subtitles
            ]
            description = 'New subtitles downloaded: {languages}'.format(
                languages=', '.join(new_languages))
            result = 'success'
        else:
            new_languages = []
            description = 'No subtitles downloaded'
            result = 'failure'

        ui.notifications.message(ep_obj.series.name, description)
        return json.dumps({
            'result': result,
            'subtitles': ep_obj.subtitles,
            'languages': new_languages,
            'description': description
        })
Ejemplo n.º 24
0
    def get(self, series_slug, identifier, *args, **kwargs):
        """Get an asset."""
        series_identifier = SeriesIdentifier.from_slug(series_slug)
        if not series_identifier:
            return self._bad_request('Invalid series slug')

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

        asset_type = identifier or 'banner'
        asset = series.get_asset(asset_type)
        if not asset:
            return self._not_found('Asset not found')

        self._ok(stream=asset.media, content_type=asset.media_type)
Ejemplo n.º 25
0
    def get(self, series_slug, episode_slug, path_param):
        """Query episode information.

        :param series_slug: series slug. E.g.: tvdb1234
        :param episode_number:
        :param path_param:
        """
        series_identifier = SeriesIdentifier.from_slug(series_slug)
        if not series_identifier:
            return self._bad_request('Invalid series slug')

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

        if not episode_slug:
            detailed = self._parse_boolean(
                self.get_argument('detailed', default=False))
            season = self._parse(self.get_argument('season', None), int)
            data = [
                e.to_json(detailed=detailed)
                for e in series.get_all_episodes(season=season)
            ]
            return self._paginate(data, sort='airDate')

        episode_number = EpisodeNumber.from_slug(episode_slug)
        if not episode_number:
            return self._bad_request('Invalid episode number')

        episode = Episode.find_by_series_and_episode(series, episode_number)
        if not episode:
            return self._not_found('Episode not found')

        detailed = self._parse_boolean(
            self.get_argument('detailed', default=True))
        data = episode.to_json(detailed=detailed)
        if path_param:
            if path_param == 'metadata':
                data = episode.metadata() if episode.is_location_valid(
                ) else {}
            elif path_param in data:
                data = data[path_param]
            else:
                return self._bad_request(
                    "Invalid path parameter '{0}'".format(path_param))

        return self._ok(data=data)
Ejemplo n.º 26
0
    def delete(self, series_slug, path_param=None):
        """Delete the series."""
        if not series_slug:
            return self._method_not_allowed('Deleting multiple series are not allowed')

        identifier = SeriesIdentifier.from_slug(series_slug)
        if not identifier:
            return self._bad_request('Invalid series identifier')

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

        remove_files = self._parse_boolean(self.get_argument('remove-files', default=None))
        if not series.delete(remove_files):
            return self._conflict('Unable to delete series')

        return self._no_content()
Ejemplo n.º 27
0
    def get(self, identifier):
        """Collect ran, running and queued searches for a specific show.

        :param identifier:
        """
        if not identifier:
            return self._bad_request(
                'You need to add the show slug to the route')

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

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

        return {'results': collect_episodes_from_search_thread(series_obj)}
Ejemplo n.º 28
0
    def http_delete(self, series_slug, path_param=None):
        """Delete the series."""
        if not series_slug:
            return self._method_not_allowed('Deleting multiple series are not allowed')

        identifier = SeriesIdentifier.from_slug(series_slug)
        if not identifier:
            return self._bad_request('Invalid series identifier')

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

        remove_files = self._parse_boolean(self.get_argument('remove-files', default=None))
        if not series.delete(remove_files):
            return self._conflict('Unable to delete series')

        return self._no_content()
Ejemplo n.º 29
0
def test_series_identifier(p):
    # Given
    slug = p['slug']
    expected = p['expected']

    # When
    actual = SeriesIdentifier.from_slug(slug)

    # Then
    if expected is None:
        assert actual is None
    else:
        assert actual
        assert expected == actual
        assert expected.id == actual.id
        assert expected.indexer == actual.indexer
        assert expected.id != actual
        assert expected.indexer != actual
Ejemplo n.º 30
0
def test_series_identifier(p):
    # Given
    slug = p['slug']
    expected = p['expected']

    # When
    actual = SeriesIdentifier.from_slug(slug)

    # Then
    if expected is None:
        assert actual is None
    else:
        assert actual
        assert expected == actual
        assert expected.id == actual.id
        assert expected.indexer == actual.indexer
        assert expected.id != actual
        assert expected.indexer != actual
Ejemplo n.º 31
0
    def patch(self, series_slug, path_param=None):
        """Patch series."""
        if not series_slug:
            return self._method_not_allowed(
                'Patching multiple series is not allowed')

        identifier = SeriesIdentifier.from_slug(series_slug)
        if not identifier:
            return self._bad_request('Invalid series identifier')

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

        data = json_decode(self.request.body)
        indexer_id = data.get('id', {}).get(identifier.indexer.slug)
        if indexer_id is not None and indexer_id != identifier.id:
            return self._bad_request('Conflicting series identifier')

        accepted = {}
        ignored = {}
        patches = {
            'config.dvdOrder': BooleanField(series, 'dvd_order'),
            'config.flattenFolders': BooleanField(series, 'flatten_folders'),
            'config.scene': BooleanField(series, 'scene'),
            'config.paused': BooleanField(series, 'paused'),
            'config.location': StringField(series, '_location'),
            'config.airByDate': BooleanField(series, 'air_by_date'),
            'config.subtitlesEnabled': BooleanField(series, 'subtitles')
        }
        for key, value in iter_nested_items(data):
            patch_field = patches.get(key)
            if patch_field and patch_field.patch(series, value):
                set_nested_value(accepted, key, value)
            else:
                set_nested_value(ignored, key, value)

        # Save patched attributes in db.
        series.save_to_db()

        if ignored:
            log.warning('Series patch ignored %r', ignored)

        self._ok(data=accepted)
Ejemplo n.º 32
0
    def refreshShow(self, showslug=None):
        # @TODO: Replace with status=refresh from PATCH /api/v2/show/{id}
        identifier = SeriesIdentifier.from_slug(showslug)
        error, series_obj = Show.refresh(identifier.indexer.slug,
                                         identifier.id)

        # This is a show validation error
        if error is not None and series_obj is None:
            return self._genericMessage('Error', error)

        # This is a refresh error
        if error is not None:
            ui.notifications.error('Unable to refresh this show.', error)

        time.sleep(cpu_presets[app.CPU_PRESET])

        return self.redirect(
            '/home/displayShow?showslug={series_obj.slug}'.format(
                series_obj=series_obj))
Ejemplo n.º 33
0
    def subtitleShow(self, showslug=None):
        if showslug is None:
            return self._genericMessage('Error', 'Invalid show ID')

        identifier = SeriesIdentifier.from_slug(showslug)
        series_obj = Series.find_by_identifier(identifier)

        if series_obj is None:
            return self._genericMessage('Error',
                                        'Unable to find the specified show')

        # search and download subtitles
        app.show_queue_scheduler.action.download_subtitles(series_obj)

        time.sleep(cpu_presets[app.CPU_PRESET])

        return self.redirect(
            '/home/displayShow?showslug={series_obj.slug}'.format(
                series_obj=series_obj))
Ejemplo n.º 34
0
    def http_get(self, series_slug, identifier, *args, **kwargs):
        """Get an asset."""
        series_identifier = SeriesIdentifier.from_slug(series_slug)
        if not series_identifier:
            return self._bad_request('Invalid series slug')

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

        asset_type = identifier or 'banner'
        asset = series.get_asset(asset_type, fallback=False)
        if not asset:
            return self._not_found('Asset not found')

        media = asset.media
        if not media:
            return self._not_found('{kind} not found'.format(kind=asset_type.capitalize()))

        return self._ok(stream=media, content_type=asset.media_type)
Ejemplo n.º 35
0
    def http_get(self, series_slug, identifier, *args, **kwargs):
        """Get an asset."""
        series_identifier = SeriesIdentifier.from_slug(series_slug)
        if not series_identifier:
            return self._bad_request('Invalid series slug')

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

        asset_type = identifier or 'banner'
        asset = series.get_asset(asset_type, fallback=False)
        if not asset:
            return self._not_found('Asset not found')

        media = asset.media
        if not media:
            return self._not_found(
                '{kind} not found'.format(kind=asset_type.capitalize()))

        return self._ok(stream=media, content_type=asset.media_type)
Ejemplo n.º 36
0
    def http_get(self, series_slug, episode_slug, path_param):
        """Query episode information.

        :param series_slug: series slug. E.g.: tvdb1234
        :param episode_number:
        :param path_param:
        """
        series_identifier = SeriesIdentifier.from_slug(series_slug)
        if not series_identifier:
            return self._bad_request('Invalid series slug')

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

        if not episode_slug:
            detailed = self._parse_boolean(self.get_argument('detailed', default=False))
            season = self._parse(self.get_argument('season', None), int)
            data = [e.to_json(detailed=detailed) for e in series.get_all_episodes(season=season)]
            return self._paginate(data, sort='airDate')

        episode_number = EpisodeNumber.from_slug(episode_slug)
        if not episode_number:
            return self._bad_request('Invalid episode number')

        episode = Episode.find_by_series_and_episode(series, episode_number)
        if not episode:
            return self._not_found('Episode not found')

        detailed = self._parse_boolean(self.get_argument('detailed', default=True))
        data = episode.to_json(detailed=detailed)
        if path_param:
            if path_param == 'metadata':
                data = episode.metadata() if episode.is_location_valid() else {}
            elif path_param in data:
                data = data[path_param]
            else:
                return self._bad_request("Invalid path parameter '{0}'".format(path_param))

        return self._ok(data=data)
Ejemplo n.º 37
0
    def put(self, identifier, **kwargs):
        """Update alias information."""
        identifier = self._parse(identifier)
        if not identifier:
            return self._not_found('Invalid alias id')

        data = json_decode(self.request.body)

        if not data or not all([
                data.get('id'),
                data.get('series'),
                data.get('name'),
                data.get('type')
        ]) or data['id'] != identifier:
            return self._bad_request('Invalid request body')

        series_identifier = SeriesIdentifier.from_slug(data.get('series'))
        if not series_identifier:
            return self._bad_request('Invalid series')

        cache_db_con = db.DBConnection('cache.db')
        last_changes = cache_db_con.connection.total_changes
        cache_db_con.action(
            b'UPDATE scene_exceptions'
            b' set indexer = ?'
            b', indexer_id = ?'
            b', show_name = ?'
            b', season = ?'
            b', custom = 1'
            b' WHERE exception_id = ?', [
                series_identifier.indexer.id, series_identifier.id,
                data['name'],
                data.get('season'), identifier
            ])

        if cache_db_con.connection.total_changes - last_changes != 1:
            return self._not_found('Alias not found')

        return self._no_content()
Ejemplo n.º 38
0
    def emby_update(self):
        """Update emby's show library."""
        show_slug = self.get_argument('showslug', '')
        show = None

        if show_slug:
            show_identifier = SeriesIdentifier.from_slug(show_slug)
            if not show_identifier:
                return self._bad_request('Invalid show slug')

            show = Series.find_by_identifier(show_identifier)
            if not show:
                return self._not_found('Series not found')

        if notifiers.emby_notifier.update_library(show):
            ui.notifications.message(
                f'Library update command sent to Emby host: {app.EMBY_HOST}')
        else:
            ui.notifications.error(
                f'Unable to contact Emby host: {app.EMBY_HOST}')

        return self._created()
Ejemplo n.º 39
0
    def getSeasonSceneExceptions(self, showslug=None):
        """Get show name scene exceptions per season

        :param indexer: The shows indexer
        :param indexer_id: The shows indexer_id
        :return: A json with the scene exceptions per season.
        """
        identifier = SeriesIdentifier.from_slug(showslug)
        series_obj = Series.find_by_identifier(identifier)

        return json.dumps({
            'seasonExceptions': {
                season: list(exception_name)
                for season, exception_name in iteritems(
                    get_all_scene_exceptions(series_obj))
            },
            'xemNumbering': {
                tvdb_season_ep[0]: anidb_season_ep[0]
                for (tvdb_season_ep, anidb_season_ep) in iteritems(
                    get_xem_numbering_for_show(series_obj, refresh_data=False))
            }
        })
Ejemplo n.º 40
0
    def post(self, series_slug):
        """Query series information.

        :param series_slug: series slug. E.g.: tvdb1234
        """
        series_identifier = SeriesIdentifier.from_slug(series_slug)
        if not series_identifier:
            return self._bad_request('Invalid series slug')

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

        data = json_decode(self.request.body)
        if not data or not all([data.get('type')]) or len(data) != 1:
            return self._bad_request('Invalid request body')

        if data['type'] == 'ARCHIVE_EPISODES':
            if series.set_all_episodes_archived(final_status_only=True):
                return self._created()
            return self._no_content()

        return self._bad_request('Invalid operation')
Ejemplo n.º 41
0
    def http_post(self, series_slug):
        """Query series information.

        :param series_slug: series slug. E.g.: tvdb1234
        """
        series_identifier = SeriesIdentifier.from_slug(series_slug)
        if not series_identifier:
            return self._bad_request('Invalid series slug')

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

        data = json_decode(self.request.body)
        if not data or not all([data.get('type')]) or len(data) != 1:
            return self._bad_request('Invalid request body')

        if data['type'] == 'ARCHIVE_EPISODES':
            if series.set_all_episodes_archived(final_status_only=True):
                return self._created()
            return self._no_content()

        return self._bad_request('Invalid operation')
Ejemplo n.º 42
0
    def saveShowNotifyList(show=None, emails=None, prowlAPIs=None):
        series_identifier = SeriesIdentifier.from_slug(show)
        series_obj = Series.find_by_identifier(series_identifier)

        # Create a new dict, to force the "dirty" flag on the Series object.
        entries = {'emails': '', 'prowlAPIs': ''}

        if not series_obj:
            return 'show missing'

        if series_obj.notify_list:
            entries.update(series_obj.notify_list)

        if emails is not None:
            entries['emails'] = emails

        if prowlAPIs is not None:
            entries['prowlAPIs'] = prowlAPIs

        series_obj.notify_list = entries
        series_obj.save_to_db()

        return 'OK'
Ejemplo n.º 43
0
    def http_get(self, series_slug, path_param=None):
        """Query series information.

        :param series_slug: series slug. E.g.: tvdb1234
        :param path_param:
        """
        arg_paused = self._parse_boolean(self.get_argument('paused', default=None))

        def filter_series(current):
            return arg_paused is None or current.paused == arg_paused

        if not series_slug:
            detailed = self._parse_boolean(self.get_argument('detailed', default=False))
            fetch = self._parse_boolean(self.get_argument('fetch', default=False))
            data = [
                s.to_json(detailed=detailed, fetch=fetch)
                for s in Series.find_series(predicate=filter_series)
            ]
            return self._paginate(data, sort='title')

        identifier = SeriesIdentifier.from_slug(series_slug)
        if not identifier:
            return self._bad_request('Invalid series slug')

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

        detailed = self._parse_boolean(self.get_argument('detailed', default=True))
        fetch = self._parse_boolean(self.get_argument('fetch', default=False))
        data = series.to_json(detailed=detailed, fetch=fetch)
        if path_param:
            if path_param not in data:
                return self._bad_request("Invalid path parameter '{0}'".format(path_param))
            data = data[path_param]

        return self._ok(data)
Ejemplo n.º 44
0
    def massEdit(self, toEdit=None):
        t = PageTemplate(rh=self, filename='manage_massEdit.mako')

        if not toEdit:
            return self.redirect('/manage/')

        series_slugs = toEdit.split('|')
        show_list = []
        show_names = []
        for slug in series_slugs:
            identifier = SeriesIdentifier.from_slug(slug)
            series_obj = Series.find_by_identifier(identifier)

            if series_obj:
                show_list.append(series_obj)
                show_names.append(series_obj.name)

        season_folders_all_same = True
        last_season_folders = None

        paused_all_same = True
        last_paused = None

        default_ep_status_all_same = True
        last_default_ep_status = None

        anime_all_same = True
        last_anime = None

        sports_all_same = True
        last_sports = None

        quality_all_same = True
        last_quality = None

        subtitles_all_same = True
        last_subtitles = None

        scene_all_same = True
        last_scene = None

        air_by_date_all_same = True
        last_air_by_date = None

        dvd_order_all_same = True
        last_dvd_order = None

        root_dir_list = []

        for cur_show in show_list:

            cur_root_dir = os.path.dirname(cur_show._location)  # pylint: disable=protected-access
            if cur_root_dir not in root_dir_list:
                root_dir_list.append(cur_root_dir)

            # if we know they're not all the same then no point even bothering
            if paused_all_same:
                # if we had a value already and this value is different then they're not all the same
                if last_paused not in (None, cur_show.paused):
                    paused_all_same = False
                else:
                    last_paused = cur_show.paused

            if default_ep_status_all_same:
                if last_default_ep_status not in (None, cur_show.default_ep_status):
                    default_ep_status_all_same = False
                else:
                    last_default_ep_status = cur_show.default_ep_status

            if anime_all_same:
                # if we had a value already and this value is different then they're not all the same
                if last_anime not in (None, cur_show.is_anime):
                    anime_all_same = False
                else:
                    last_anime = cur_show.anime

            if season_folders_all_same:
                if last_season_folders not in (None, cur_show.season_folders):
                    season_folders_all_same = False
                else:
                    last_season_folders = cur_show.season_folders

            if quality_all_same:
                if last_quality not in (None, cur_show.quality):
                    quality_all_same = False
                else:
                    last_quality = cur_show.quality

            if subtitles_all_same:
                if last_subtitles not in (None, cur_show.subtitles):
                    subtitles_all_same = False
                else:
                    last_subtitles = cur_show.subtitles

            if scene_all_same:
                if last_scene not in (None, cur_show.scene):
                    scene_all_same = False
                else:
                    last_scene = cur_show.scene

            if sports_all_same:
                if last_sports not in (None, cur_show.sports):
                    sports_all_same = False
                else:
                    last_sports = cur_show.sports

            if air_by_date_all_same:
                if last_air_by_date not in (None, cur_show.air_by_date):
                    air_by_date_all_same = False
                else:
                    last_air_by_date = cur_show.air_by_date

            if dvd_order_all_same:
                if last_dvd_order not in (None, cur_show.dvd_order):
                    dvd_order_all_same = False
                else:
                    last_dvd_order = cur_show.dvd_order

        default_ep_status_value = last_default_ep_status if default_ep_status_all_same else None
        paused_value = last_paused if paused_all_same else None
        anime_value = last_anime if anime_all_same else None
        season_folders_value = last_season_folders if season_folders_all_same else None
        quality_value = last_quality if quality_all_same else None
        subtitles_value = last_subtitles if subtitles_all_same else None
        scene_value = last_scene if scene_all_same else None
        sports_value = last_sports if sports_all_same else None
        air_by_date_value = last_air_by_date if air_by_date_all_same else None
        dvd_order_value = last_dvd_order if dvd_order_all_same else None
        root_dir_list = root_dir_list

        return t.render(showList=toEdit, showNames=show_names, default_ep_status_value=default_ep_status_value, dvd_order_value=dvd_order_value,
                        paused_value=paused_value, anime_value=anime_value, season_folders_value=season_folders_value,
                        quality_value=quality_value, subtitles_value=subtitles_value, scene_value=scene_value, sports_value=sports_value,
                        air_by_date_value=air_by_date_value, root_dir_list=root_dir_list)
Ejemplo n.º 45
0
    def massEditSubmit(self, paused=None, default_ep_status=None, dvd_order=None,
                       anime=None, sports=None, scene=None, season_folders=None, quality_preset=None,
                       subtitles=None, air_by_date=None, allowed_qualities=None, preferred_qualities=None, toEdit=None, *args,
                       **kwargs):
        allowed_qualities = allowed_qualities or []
        preferred_qualities = preferred_qualities or []

        dir_map = {}
        for cur_arg in kwargs:
            if not cur_arg.startswith('orig_root_dir_'):
                continue
            which_index = cur_arg.replace('orig_root_dir_', '')
            end_dir = kwargs['new_root_dir_{index}'.format(index=which_index)]
            dir_map[kwargs[cur_arg]] = end_dir

        series_slugs = toEdit.split('|') if toEdit else []
        errors = 0
        for series_slug in series_slugs:
            identifier = SeriesIdentifier.from_slug(series_slug)
            series_obj = Series.find_by_identifier(identifier)

            if not series_obj:
                continue

            cur_root_dir = os.path.dirname(series_obj._location)
            cur_show_dir = os.path.basename(series_obj._location)
            if cur_root_dir in dir_map and cur_root_dir != dir_map[cur_root_dir]:
                new_show_dir = os.path.join(dir_map[cur_root_dir], cur_show_dir)
                logger.log(u'For show {show.name} changing dir from {show._location} to {location}'.format
                           (show=series_obj, location=new_show_dir))
            else:
                new_show_dir = series_obj._location

            if paused == 'keep':
                new_paused = series_obj.paused
            else:
                new_paused = True if paused == 'enable' else False
            new_paused = 'on' if new_paused else 'off'

            if default_ep_status == 'keep':
                new_default_ep_status = series_obj.default_ep_status
            else:
                new_default_ep_status = default_ep_status

            if anime == 'keep':
                new_anime = series_obj.anime
            else:
                new_anime = True if anime == 'enable' else False
            new_anime = 'on' if new_anime else 'off'

            if sports == 'keep':
                new_sports = series_obj.sports
            else:
                new_sports = True if sports == 'enable' else False
            new_sports = 'on' if new_sports else 'off'

            if scene == 'keep':
                new_scene = series_obj.is_scene
            else:
                new_scene = True if scene == 'enable' else False
            new_scene = 'on' if new_scene else 'off'

            if air_by_date == 'keep':
                new_air_by_date = series_obj.air_by_date
            else:
                new_air_by_date = True if air_by_date == 'enable' else False
            new_air_by_date = 'on' if new_air_by_date else 'off'

            if dvd_order == 'keep':
                new_dvd_order = series_obj.dvd_order
            else:
                new_dvd_order = True if dvd_order == 'enable' else False
            new_dvd_order = 'on' if new_dvd_order else 'off'

            if season_folders == 'keep':
                new_season_folders = series_obj.season_folders
            else:
                new_season_folders = True if season_folders == 'enable' else False
            new_season_folders = 'on' if new_season_folders else 'off'

            if subtitles == 'keep':
                new_subtitles = series_obj.subtitles
            else:
                new_subtitles = True if subtitles == 'enable' else False

            new_subtitles = 'on' if new_subtitles else 'off'

            if quality_preset == 'keep':
                allowed_qualities, preferred_qualities = series_obj.current_qualities
            elif try_int(quality_preset, None):
                preferred_qualities = []

            exceptions_list = []

            errors += self.editShow(identifier.indexer.slug, identifier.id, new_show_dir, allowed_qualities,
                                    preferred_qualities, exceptions_list,
                                    defaultEpStatus=new_default_ep_status,
                                    season_folders=new_season_folders,
                                    paused=new_paused, sports=new_sports, dvd_order=new_dvd_order,
                                    subtitles=new_subtitles, anime=new_anime,
                                    scene=new_scene, air_by_date=new_air_by_date,
                                    directCall=True)

        if errors:
            ui.notifications.error('Errors', '{num} error{s} while saving changes. Please check logs'.format
                                   (num=errors, s='s' if errors > 1 else ''))

        return self.redirect('/manage/')
Ejemplo n.º 46
0
    def massUpdate(self, toUpdate=None, toRefresh=None, toRename=None, toDelete=None, toRemove=None, toMetadata=None,
                   toSubtitle=None, toImageUpdate=None):
        to_update = toUpdate.split('|') if toUpdate else []
        to_refresh = toRefresh.split('|') if toRefresh else []
        to_rename = toRename.split('|') if toRename else []
        to_subtitle = toSubtitle.split('|') if toSubtitle else []
        to_delete = toDelete.split('|') if toDelete else []
        to_remove = toRemove.split('|') if toRemove else []
        to_metadata = toMetadata.split('|') if toMetadata else []
        to_image_update = toImageUpdate.split('|') if toImageUpdate else []

        errors = []
        refreshes = []
        updates = []
        renames = []
        subtitles = []
        image_update = []

        for slug in set(to_update + to_refresh + to_rename + to_subtitle + to_delete + to_remove + to_metadata + to_image_update):
            identifier = SeriesIdentifier.from_slug(slug)
            series_obj = Series.find_by_identifier(identifier)

            if not series_obj:
                continue

            if slug in to_delete + to_remove:
                app.show_queue_scheduler.action.removeShow(series_obj, slug in to_delete)
                continue  # don't do anything else if it's being deleted or removed

            if slug in to_update:
                try:
                    app.show_queue_scheduler.action.updateShow(series_obj)
                    updates.append(series_obj.name)
                except CantUpdateShowException as msg:
                    errors.append('Unable to update show: {error}'.format(error=msg))

            elif slug in to_refresh:  # don't bother refreshing shows that were updated
                try:
                    app.show_queue_scheduler.action.refreshShow(series_obj)
                    refreshes.append(series_obj.name)
                except CantRefreshShowException as msg:
                    errors.append('Unable to refresh show {show.name}: {error}'.format
                                  (show=series_obj, error=msg))

            if slug in to_rename:
                app.show_queue_scheduler.action.renameShowEpisodes(series_obj)
                renames.append(series_obj.name)

            if slug in to_subtitle:
                app.show_queue_scheduler.action.download_subtitles(series_obj)
                subtitles.append(series_obj.name)

            if slug in to_image_update:
                image_cache.replace_images(series_obj)

        if errors:
            ui.notifications.error('Errors encountered',
                                   '<br />\n'.join(errors))

        message = ''
        if updates:
            message += '\nUpdates: {0}'.format(len(updates))
        if refreshes:
            message += '\nRefreshes: {0}'.format(len(refreshes))
        if renames:
            message += '\nRenames: {0}'.format(len(renames))
        if subtitles:
            message += '\nSubtitles: {0}'.format(len(subtitles))
        if image_update:
            message += '\nImage updates: {0}'.format(len(image_update))

        if message:
            ui.notifications.message('Queued actions:', message)

        return self.redirect('/manage/')
Ejemplo n.º 47
0
    def http_get(self, identifier, path_param):
        """Query scene_exception information."""
        cache_db_con = db.DBConnection('cache.db')
        sql_base = ('SELECT '
                    '  exception_id, '
                    '  indexer, '
                    '  indexer_id, '
                    '  show_name, '
                    '  season, '
                    '  custom '
                    'FROM scene_exceptions ')
        sql_where = []
        params = []

        if identifier is not None:
            sql_where.append('exception_id')
            params += [identifier]
        else:
            series_slug = self.get_query_argument('series', None)
            series_identifier = SeriesIdentifier.from_slug(series_slug)

            if series_slug and not series_identifier:
                return self._bad_request('Invalid series')

            season = self._parse(self.get_query_argument('season', None))
            exception_type = self.get_query_argument('type', None)
            if exception_type and exception_type not in ('local', ):
                return self._bad_request('Invalid type')

            if series_identifier:
                sql_where.append('indexer')
                sql_where.append('indexer_id')
                params += [series_identifier.indexer.id, series_identifier.id]

            if season is not None:
                sql_where.append('season')
                params += [season]

            if exception_type == 'local':
                sql_where.append('custom')
                params += [1]

        if sql_where:
            sql_base += ' WHERE ' + ' AND '.join([where + ' = ? ' for where in sql_where])

        sql_results = cache_db_con.select(sql_base, params)

        data = []
        for item in sql_results:
            d = {}
            d['id'] = item['exception_id']
            d['series'] = SeriesIdentifier.from_id(item['indexer'], item['indexer_id']).slug
            d['name'] = item['show_name']
            d['season'] = item['season'] if item['season'] >= 0 else None
            d['type'] = 'local' if item['custom'] else None
            data.append(d)

        if not identifier:
            return self._paginate(data, sort='id')

        if not data:
            return self._not_found('Alias not found')

        data = data[0]
        if path_param:
            if path_param not in data:
                return self._bad_request('Invalid path parameter')
            data = data[path_param]

        return self._ok(data=data)