Example #1
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)
Example #2
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')
Example #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
Example #4
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
Example #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)
Example #6
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)
Example #7
0
    def resource_get_subtitle_missed(self):
        """Return a list of episodes which are missing a specific subtitle language."""
        language = self.get_argument('language' '').strip()

        main_db_con = db.DBConnection()
        results = main_db_con.select(
            'SELECT show_name, tv_shows.show_id, tv_shows.indexer, '
            'tv_shows.indexer_id AS indexer_id, tv_episodes.subtitles subtitles, '
            'tv_episodes.episode AS episode, tv_episodes.season AS season, '
            'tv_episodes.name AS name '
            'FROM tv_episodes, tv_shows '
            'WHERE tv_shows.subtitles = 1 '
            'AND tv_episodes.status = ? '
            'AND tv_episodes.season != 0 '
            "AND tv_episodes.location != '' "
            'AND tv_episodes.showid = tv_shows.indexer_id '
            'AND tv_episodes.indexer = tv_shows.indexer '
            'ORDER BY show_name', [DOWNLOADED])

        subtitle_status = {}
        for cur_status_result in results:
            cur_indexer = int(cur_status_result['indexer'])
            cur_series_id = int(cur_status_result['indexer_id'])
            show_slug = SeriesIdentifier.from_id(cur_indexer,
                                                 cur_series_id).slug
            subtitles = cur_status_result['subtitles'].split(',')

            if language == 'all':
                if not frozenset(wanted_languages()).difference(subtitles):
                    continue
            elif language in subtitles:
                continue

            if show_slug not in subtitle_status:
                subtitle_status[show_slug] = {
                    'selected': True,
                    'slug': show_slug,
                    'name': cur_status_result['show_name'],
                    'episodes': [],
                    'showEpisodes': False
                }

            subtitle_status[show_slug]['episodes'].append({
                'episode':
                cur_status_result['episode'],
                'season':
                cur_status_result['season'],
                'selected':
                True,
                'slug':
                str(
                    RelativeNumber(cur_status_result['season'],
                                   cur_status_result['episode'])),
                'name':
                cur_status_result['name'],
                'subtitles':
                subtitles
            })

        return self._ok(data=subtitle_status)
Example #8
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'])
Example #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')
Example #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()
Example #11
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)
Example #12
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)
Example #13
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')
Example #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')
Example #15
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'])
Example #16
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']))
Example #17
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)
Example #18
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/')
Example #19
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)
Example #20
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)
Example #21
0
    def resource_get_episode_status(self):
        """Return a list of episodes with a specific status."""
        status = self.get_argument('status' '').strip()

        status_list = [int(status)]

        if status_list:
            if status_list[0] == SNATCHED:
                status_list = [SNATCHED, SNATCHED_PROPER, SNATCHED_BEST]
        else:
            status_list = []

        main_db_con = db.DBConnection()
        status_results = main_db_con.select(
            'SELECT show_name, tv_shows.indexer, tv_shows.show_id, tv_shows.indexer_id AS indexer_id, '
            'tv_episodes.season AS season, tv_episodes.episode AS episode, tv_episodes.name as name '
            'FROM tv_episodes, tv_shows '
            'WHERE season != 0 '
            'AND tv_episodes.showid = tv_shows.indexer_id '
            'AND tv_episodes.indexer = tv_shows.indexer '
            'AND tv_episodes.status IN ({statuses}) '.format(
                statuses=','.join(['?'] * len(status_list))), status_list)

        episode_status = {}
        for cur_status_result in status_results:
            cur_indexer = int(cur_status_result['indexer'])
            cur_series_id = int(cur_status_result['indexer_id'])
            show_slug = SeriesIdentifier.from_id(cur_indexer,
                                                 cur_series_id).slug

            if show_slug not in episode_status:
                episode_status[show_slug] = {
                    'selected': True,
                    'slug': show_slug,
                    'name': cur_status_result['show_name'],
                    'episodes': [],
                    'showEpisodes': False
                }

            episode_status[show_slug]['episodes'].append({
                'episode':
                cur_status_result['episode'],
                'season':
                cur_status_result['season'],
                'selected':
                True,
                'slug':
                str(
                    RelativeNumber(cur_status_result['season'],
                                   cur_status_result['episode'])),
                'name':
                cur_status_result['name']
            })

        return self._ok(data={'episodeStatus': episode_status})
Example #22
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()
Example #23
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']))
Example #24
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))
Example #25
0
        def data_generator():
            """Read and paginate history records."""
            start = arg_limit * (arg_page - 1)

            for item in results[start:start + arg_limit]:
                provider = {}
                release_group = None
                release_name = None
                file_name = None
                subtitle_language = None

                if item['action'] in (SNATCHED, FAILED):
                    provider.update({
                        'id': GenericProvider.make_id(item['provider']),
                        'name': item['provider']
                    })
                    release_name = item['resource']

                if item['action'] == DOWNLOADED:
                    release_group = item['provider']
                    file_name = item['resource']

                if item['action'] == SUBTITLED:
                    subtitle_language = item['resource']

                if item['action'] == SUBTITLED:
                    subtitle_language = item['resource']

                yield {
                    'id': item['rowid'],
                    'series': SeriesIdentifier.from_id(item['indexer_id'], item['showid']).slug,
                    'status': item['action'],
                    'statusName': statusStrings.get(item['action']),
                    'actionDate': item['date'],
                    'quality': item['quality'],
                    'resource': basename(item['resource']),
                    'size': item['size'],
                    'properTags': item['proper_tags'],
                    'season': item['season'],
                    'episode': item['episode'],
                    'manuallySearched': bool(item['manually_searched']),
                    'infoHash': item['info_hash'],
                    'provider': provider,
                    'release_name': release_name,
                    'releaseGroup': release_group,
                    'fileName': file_name,
                    'subtitleLanguage': subtitle_language
                }
Example #26
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
        })
Example #27
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)
Example #28
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)
Example #29
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)}
Example #30
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()
Example #31
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
Example #32
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
Example #33
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()
Example #34
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)
Example #35
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))
Example #36
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))
Example #37
0
        def data_generator():
            """Read history data and normalize key/value pairs."""
            for item in results:
                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
Example #38
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)
Example #39
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)
Example #40
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)
Example #41
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))
            }
        })
Example #42
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')
Example #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)
Example #44
0
    def get_coming_episodes(categories, sort, group, paused=app.COMING_EPS_DISPLAY_PAUSED):
        """
        :param categories: The categories of coming episodes. See ``ComingEpisodes.categories``
        :param sort: The sort to apply to the coming episodes. See ``ComingEpisodes.sorts``
        :param group: ``True`` to group the coming episodes by category, ``False`` otherwise
        :param paused: ``True`` to include paused shows, ``False`` otherwise
        :return: The list of coming episodes
        """
        categories = ComingEpisodes._get_categories(categories)
        sort = ComingEpisodes._get_sort(sort)

        today = date.today().toordinal()
        next_week = (date.today() + timedelta(days=7)).toordinal()
        recently = (date.today() - timedelta(days=app.COMING_EPS_MISSED_RANGE)).toordinal()
        status_list = [DOWNLOADED, SNATCHED, SNATCHED_BEST, SNATCHED_PROPER,
                       ARCHIVED, IGNORED]

        db = DBConnection()
        fields_to_select = ', '.join(
            ['airdate', 'airs', 'e.description as description', 'episode', 'imdb_id', 'e.indexer',
             'indexer_id', 'name', 'network', 'paused', 's.quality', 'runtime', 'season', 'show_name',
             'showid', 's.status']
        )
        results = db.select(
            'SELECT %s ' % fields_to_select +
            'FROM tv_episodes e, tv_shows s '
            'WHERE season != 0 '
            'AND airdate >= ? '
            'AND airdate < ? '
            'AND s.indexer = e.indexer '
            'AND s.indexer_id = e.showid '
            'AND e.status NOT IN (' + ','.join(['?'] * len(status_list)) + ')',
            [today, next_week] + status_list
        )

        done_shows_list = [int(result['showid']) for result in results]
        placeholder = ','.join(['?'] * len(done_shows_list))
        placeholder2 = ','.join(['?'] * len([DOWNLOADED, SNATCHED, SNATCHED_BEST, SNATCHED_PROPER]))

        # FIXME: This inner join is not multi indexer friendly.
        results += db.select(
            'SELECT %s ' % fields_to_select +
            'FROM tv_episodes e, tv_shows s '
            'WHERE season != 0 '
            'AND showid NOT IN (' + placeholder + ') '
            'AND s.indexer_id = e.showid '
            'AND airdate = (SELECT airdate '
            'FROM tv_episodes inner_e '
            'WHERE inner_e.season != 0 '
            'AND inner_e.showid = e.showid '
            'AND inner_e.indexer = e.indexer '
            'AND inner_e.airdate >= ? '
            'ORDER BY inner_e.airdate ASC LIMIT 1) '
            'AND e.status NOT IN (' + placeholder2 + ')',
            done_shows_list + [next_week] + [DOWNLOADED, SNATCHED, SNATCHED_BEST, SNATCHED_PROPER]
        )

        results += db.select(
            'SELECT %s ' % fields_to_select +
            'FROM tv_episodes e, tv_shows s '
            'WHERE season != 0 '
            'AND s.indexer_id = e.showid '
            'AND airdate < ? '
            'AND airdate >= ? '
            'AND e.status IN (?,?) '
            'AND e.status NOT IN (' + ','.join(['?'] * len(status_list)) + ')',
            [today, recently, WANTED, UNAIRED] + status_list
        )

        for index, item in enumerate(results):
            item['series_slug'] = str(SeriesIdentifier.from_id(int(item['indexer']), item['indexer_id']))
            results[index]['localtime'] = sbdatetime.convert_to_setting(
                parse_date_time(item['airdate'], item['airs'], item['network']))

        results.sort(key=ComingEpisodes.sorts[sort])

        if not group:
            return results

        grouped_results = ComingEpisodes._get_categories_map(categories)

        for result in results:
            if result['paused'] and not paused:
                continue

            result['airs'] = str(result['airs']).replace('am', ' AM').replace('pm', ' PM').replace('  ', ' ')
            result['airdate'] = result['localtime'].toordinal()

            if result['airdate'] < today:
                category = 'missed'
            elif result['airdate'] >= next_week:
                category = 'later'
            elif result['airdate'] == today:
                category = 'today'
            else:
                category = 'soon'

            if len(categories) > 0 and category not in categories:
                continue

            if not result['network']:
                result['network'] = ''

            result['quality'] = get_quality_string(result['quality'])
            result['airs'] = sbdatetime.sbftime(result['localtime'], t_preset=timeFormat).lstrip('0').replace(' 0', ' ')
            result['weekday'] = 1 + date.fromordinal(result['airdate']).weekday()
            result['tvdbid'] = result['indexer_id']
            result['airdate'] = sbdatetime.sbfdate(result['localtime'], d_preset=dateFormat)
            result['localtime'] = result['localtime'].toordinal()

            grouped_results[category].append(result)

        return grouped_results
Example #45
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)
Example #46
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/')
Example #47
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/')
Example #48
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)