Exemple #1
0
def get_series_id(title):
    norm_series_name = normalize_series_name(title)
    series_name = urllib.quote_plus(norm_series_name)
    url = search_show + api_key + series_name
    series = None
    try:
        response = requests.get(url)
    except requests.RequestException:
        log.warning('Request failed %s' % url)
        return
    try:
        data = response.json()
    except ValueError:
        raise LookupError('Error Parsing Traktv Json for %s' % title)
    if 'error' in data:
        raise LookupError('Error from trakt: %s' % data['error'])
    for item in data:
        if normalize_series_name(item['title']) == norm_series_name:
            series = item['tvdb_id']
            break
    else:
        for item in data:
            title_match = SequenceMatcher(lambda x: x in '\t',
                                          normalize_series_name(item['title']), norm_series_name).ratio()
            if title_match > .9:
                log.debug('Warning: Using lazy matching because title was not found exactly for %s' % title)
                series = item['tvdb_id']
                break
    if not series:
        raise LookupError('Could not find tvdb_id for `%s` on trakt' % title)
    return series
Exemple #2
0
def get_series_id(title):
    norm_series_name = normalize_series_name(title)
    series_name = urllib.quote_plus(norm_series_name)
    url = search_show + api_key + series_name
    series = None
    try:
        response = requests.get(url)
    except requests.RequestException:
        log.warning('Request failed %s' % url)
        return
    try:
        data = response.json()
    except ValueError:
        log.debug('Error Parsing Traktv Json for %s' % title)
        return
    if 'status' in data:
        log.debug('Returned Status %s' % data['status'])
    else:
        for item in data:
            if normalize_series_name(item['title']) == norm_series_name:
                series = item['tvdb_id']
        if not series:
            for item in data:
                title_match = SequenceMatcher(
                    lambda x: x in '\t', normalize_series_name(item['title']),
                    norm_series_name).ratio()
                if not series and title_match > .9:
                    log.debug(
                        'Warning: Using lazy matching because title was not found exactly for %s'
                        % title)
                    series = item['tvdb_id']
    if not series:
        log.debug('Trakt.tv Returns only EXACT Name Matching: %s' % title)
    return series
Exemple #3
0
def get_series_id(title):
    norm_series_name = normalize_series_name(title)
    series_name = urllib.quote_plus(norm_series_name)
    url = search_show + api_key + series_name
    series = None
    try:
        data = requests.get(url)
    except requests.RequestException:
        log.warning('Request failed %s' % url)
        return
    if data:
        try:
            data = data.json()
        except ValueError:
            log.debug('Error Parsing Traktv Json for %s' % title)
    if data:
        if 'status' in data:
            log.debug('Returned Status %s' % data['status'])
        else:
            for item in data:
                if normalize_series_name(item['title']) == norm_series_name:
                    series = item['tvdb_id']
            if not series:
                for item in data:
                    title_match = SequenceMatcher(lambda x: x in '\t',
                                                  normalize_series_name(item['title']), norm_series_name).ratio()
                    if not series and title_match > .9:
                        log.debug('Warning: Using lazy matching because title was not found exactly for %s' % title)
                        series = item['tvdb_id']
    if series:
        return series
    else:
        log.debug('Trakt.tv Returns only EXACT Name Matching: %s' % title)
        return series
Exemple #4
0
def get_series_id(title):
    norm_series_name = normalize_series_name(title)
    series_name = urllib.quote_plus(norm_series_name)
    url = search_show + api_key + series_name
    series = None
    try:
        response = requests.get(url)
    except requests.RequestException:
        log.warning("Request failed %s" % url)
        return
    try:
        data = response.json()
    except ValueError:
        raise LookupError("Error Parsing Traktv Json for %s" % title)
    if "error" in data:
        raise LookupError("Error from trakt: %s" % data["error"])
    for item in data:
        if normalize_series_name(item["title"]) == norm_series_name:
            series = item["tvdb_id"]
            break
    else:
        for item in data:
            title_match = SequenceMatcher(
                lambda x: x in "\t", normalize_series_name(item["title"]), norm_series_name
            ).ratio()
            if title_match > 0.9:
                log.debug("Warning: Using lazy matching because title was not found exactly for %s" % title)
                series = item["tvdb_id"]
                break
    if not series:
        raise LookupError("Could not find tvdb_id for `%s` on trakt" % title)
    return series
Exemple #5
0
 def on_task_metainfo(self, task, config):
     if not config:
         # Don't run when we are disabled
         return
     # Generate the group settings for series plugin
     group_settings = {}
     if isinstance(config, dict):
         group_settings = config
     # Generate a list of unique series that metainfo_series can parse for this task
     metainfo_series = get_plugin_by_name('metainfo_series')
     guess_entry = metainfo_series.instance.guess_entry
     guessed_series = {}
     for entry in task.entries:
         if guess_entry(entry):
             guessed_series.setdefault(
                 normalize_series_name(entry['series_name']),
                 entry['series_name'])
     # Combine settings and series into series plugin config format
     allseries = {
         'settings': {
             'all_series': group_settings
         },
         'all_series': guessed_series.values()
     }
     # Merge our config in to the main series config
     self.merge_config(task, allseries)
Exemple #6
0
    def post(self, session):
        """ Create a new show and set its first accepted episode and/or alternate names """
        data = request.json
        series_name = data.get('name')

        normalized_name = series.normalize_series_name(series_name)
        matches = series.shows_by_exact_name(normalized_name, session=session)
        if matches:
            raise Conflict('Show `%s` already exist in DB' % series_name)
        show = series.Series()
        show.name = series_name
        session.add(show)

        ep_id = data.get('begin_episode')
        alt_names = data.get('alternate_names')
        if ep_id:
            series.set_series_begin(show, ep_id)
        if alt_names:
            try:
                series.set_alt_names(alt_names, show, session)
            except PluginError as e:
                # Alternate name already exist for a different show
                raise Conflict(e.value)
        session.commit()
        rsp = jsonify(series_details(show, begin=ep_id is not None))
        rsp.status_code = 201
        return rsp
Exemple #7
0
 def on_task_metainfo(self, task, config):
     if not config:
         # Don't run when we are disabled
         return
     if task.is_rerun:
         # Since we are running after task start phase, make sure not to merge into the config again on reruns
         return
     # Generate the group settings for series plugin
     group_settings = {}
     if isinstance(config, dict):
         group_settings = config
     group_settings['identified_by'] = 'ep'
     # Generate a list of unique series that metainfo_series can parse for this task
     metainfo_series = plugin.get_plugin_by_name('metainfo_series')
     guess_entry = metainfo_series.instance.guess_entry
     guessed_series = {}
     for entry in task.entries:
         if guess_entry(entry, config=group_settings):
             guessed_series.setdefault(
                 normalize_series_name(entry['series_name']),
                 entry['series_name'])
     # Combine settings and series into series plugin config format
     allseries = {
         'settings': {
             'all_series': group_settings
         },
         'all_series': list(guessed_series.values())
     }
     # Merge our config in to the main series config
     self.merge_config(task, allseries)
Exemple #8
0
 def on_task_output(self, task, config):
     if not config:
         return
     with Session() as session:
         for entry in task.accepted:
             if 'series_name' in entry and 'series_id' in entry:
                 series_name = entry['series_name'].replace(r'\!', '!')
                 normalized_name = normalize_series_name(series_name)
                 series = shows_by_exact_name(normalized_name, session)
                 if not series:
                     log.info('Series not yet in database, adding `%s`' %
                              series_name)
                     series = Series()
                     series.name = series_name
                     session.add(series)
                 else:
                     series = series[0]
                 try:
                     add_series_entity(session,
                                       series,
                                       entry['series_id'],
                                       quality=entry['quality'])
                 except ValueError as e:
                     log.warn(e.args[0])
                 else:
                     log.info('Added entity `%s` to series `%s`.' %
                              (entry['series_id'], series.name.title()))
Exemple #9
0
    def post(self, session):
        """ Create a new show and set its first accepted episode and/or alternate names """
        data = request.json
        series_name = data.get('name')

        normalized_name = series.normalize_series_name(series_name)
        matches = series.shows_by_exact_name(normalized_name, session=session)
        if matches:
            raise Conflict('Show `%s` already exist in DB' % series_name)
        show = series.Series()
        show.name = series_name
        session.add(show)

        ep_id = data.get('begin_episode')
        alt_names = data.get('alternate_names')
        if ep_id:
            series.set_series_begin(show, ep_id)
        if alt_names:
            try:
                series.set_alt_names(alt_names, show, session)
            except PluginError as e:
                # Alternate name already exist for a different show
                raise Conflict(e.value)
        session.commit()
        rsp = jsonify(series_details(show, begin=ep_id is not None))
        rsp.status_code = 201
        return rsp
Exemple #10
0
 def on_task_input(self, task, config):
     """asd"""
     slist = task.session.query(Series)
     if isinstance(config, basestring):
         name = normalize_series_name(config)
         slist = slist.filter(Series._name_normalized.contains(name))
     slist = (
         slist.outerjoin(Series.episodes).outerjoin(
             Episode.releases).outerjoin(Series.in_tasks).
         # group_by(Series.id).having(func.count(SeriesTask.id) < 1).order_by(Series.name).all())
         # group_by(Series.id).having(func.count(SeriesTask.id) >= 1).order_by(Series.name).all())
         order_by(Series.name).all())
     entries = []
     for series in slist:
         elist = (task.session.query(Episode).filter(
             Episode.series_id == series.id).order_by(
                 Episode.season, Episode.number).all())
         for episode in elist:
             self.log.debug('Found episode %s for series "%s"' %
                            (episode.identifier, series.name))
             entry = Entry()
             entry['title'] = '%s %s' % (series.name, episode.identifier)
             entry['url'] = 'http://localhost/mock/%s' % hash(
                 entry['title'])
             if entry.isvalid():
                 entries.append(entry)
             else:
                 self.log.debug('Invalid entry created? %s' % entry)
     return entries
Exemple #11
0
    def post(self, session):
        """ Create a new show and set its first accepted episode and/or alternate names """
        data = request.json
        series_name = data.get('series_name')

        normalized_name = series.normalize_series_name(series_name)
        matches = series.shows_by_exact_name(normalized_name, session=session)
        if matches:
            return {'status': 'error',
                    'message': 'Show `%s` already exist in DB' % series_name
                    }, 500
        show = series.Series()
        show.name = series_name
        session.add(show)

        ep_id = data.get('episode_identifier')
        alt_names = data.get('alternate_names')
        if ep_id:
            try:
                series.set_series_begin(show, ep_id)
            except ValueError as e:
                return {'status': 'error',
                        'message': e.args[0]
                        }, 501
        if alt_names:
            try:
                series.set_alt_names(alt_names, show, session)
            except PluginError as e:
                return {'status': 'error',
                        'message': e.value
                        }, 502

        return jsonify(get_series_details(show))
Exemple #12
0
def add(manager, options):
    series_name = options.series_name
    entity_ids = options.entity_id
    quality = options.quality or os.environ.get(ENV_ADD_QUALITY, None)
    series_name = series_name.replace(r'\!', '!')
    normalized_name = normalize_series_name(series_name)
    with Session() as session:
        series = shows_by_exact_name(normalized_name, session)
        if not series:
            console('Series not yet in database, adding `%s`' % series_name)
            series = Series()
            series.name = series_name
            session.add(series)
        else:
            series = series[0]
        for ent_id in entity_ids:
            try:
                add_series_entity(session, series, ent_id, quality=quality)
            except ValueError as e:
                console(e.args[0])
            else:
                console('Added entity `%s` to series `%s`.' %
                        (ent_id, series.name.title()))
        session.commit()
    manager.config_changed()
Exemple #13
0
def begin(manager, options):
    series_name = options.series_name
    series_name = series_name.replace(r'\!', '!')
    normalized_name = normalize_series_name(series_name)
    with Session() as session:
        series = shows_by_exact_name(normalized_name, session)
        if options.forget:
            if not series:
                console('Series `%s` was not found in the database.' % series_name)
            else:
                series = series[0]
                series.begin = None
                console('The begin episode for `%s` has been forgotten.' % series.name)
                session.commit()
                manager.config_changed()
        elif options.episode_id:
            ep_id = options.episode_id
            if not series:
                console('Series not yet in database. Adding `%s`.' % series_name)
                series = Series()
                series.name = series_name
                session.add(series)
            else:
                series = series[0]
            try:
                _, entity_type = set_series_begin(series, ep_id)
            except ValueError as e:
                console(e)
            else:
                if entity_type == 'season':
                    console('`%s` was identified as a season.' % ep_id)
                    ep_id += 'E01'
                console('Releases for `%s` will be accepted starting with `%s`.' % (series.name, ep_id))
                session.commit()
            manager.config_changed()
Exemple #14
0
 def on_task_metainfo(self, task, config):
     if not config:
         # Don't run when we are disabled
         return
     # Generate the group settings for series plugin
     group_settings = {}
     allow_seasonless = False
     if isinstance(config, dict):
         allow_seasonless = config.pop('allow_seasonless', False)
         group_settings = config
     # Generate a list of unique series that have premieres
     metainfo_series = get_plugin_by_name('metainfo_series')
     guess_entry = metainfo_series.instance.guess_entry
     # Make a set of unique series according to series name normalization rules
     guessed_series = {}
     for entry in task.entries:
         if guess_entry(entry, allow_seasonless=allow_seasonless):
             if entry['series_season'] == 1 and entry['series_episode'] in (0, 1):
                 guessed_series.setdefault(normalize_series_name(entry['series_name']), entry['series_name'])
     # Reject any further episodes in those series
     for entry in task.entries:
         for series in guessed_series.itervalues():
             if entry.get('series_name') == series and not (
                     entry.get('series_season') == 1
                     and entry.get('series_episode') in (0, 1) ):
                 task.reject(entry, 'Non premiere episode in a premiere series')
     # Combine settings and series into series plugin config format
     allseries = {'settings': {'series_premiere': group_settings}, 'series_premiere': guessed_series.values()}
     # Merge the our config in to the main series config
     self.merge_config(task, allseries)
Exemple #15
0
    def post(self, session):
        """ Create a new show and set its first accepted episode and/or alternate names """
        data = request.json
        series_name = data.get('series_name')

        normalized_name = series.normalize_series_name(series_name)
        matches = series.shows_by_exact_name(normalized_name, session=session)
        if matches:
            return {'status': 'error',
                    'message': 'Show `%s` already exist in DB' % series_name
                    }, 500
        show = series.Series()
        show.name = series_name
        session.add(show)

        ep_id = data.get('episode_identifier')
        alt_names = data.get('alternate_names')
        if ep_id:
            try:
                series.set_series_begin(show, ep_id)
            except ValueError as e:
                return {'status': 'error',
                        'message': e.args[0]
                        }, 501
        if alt_names:
            try:
                series.set_alt_names(alt_names, show, session)
            except PluginError as e:
                return {'status': 'error',
                        'message': e.value
                        }, 502

        return jsonify(get_series_details(show))
Exemple #16
0
 def on_task_metainfo(self, task, config):
     if not config:
         # Don't run when we are disabled
         return
     if task.is_rerun:
         return
     # Generate the group settings for series plugin
     group_settings = {}
     allow_seasonless = False
     desired_eps = [0, 1]
     if isinstance(config, dict):
         allow_seasonless = config.pop('allow_seasonless', False)
         if not config.pop('allow_teasers', True):
             desired_eps = [1]
         group_settings = config
     group_settings['identified_by'] = 'ep'
     # Generate a list of unique series that have premieres
     metainfo_series = plugin.get_plugin_by_name('metainfo_series')
     guess_entry = metainfo_series.instance.guess_entry
     # Make a set of unique series according to series name normalization rules
     guessed_series = {}
     for entry in task.entries:
         if guess_entry(entry,
                        allow_seasonless=allow_seasonless,
                        config=group_settings):
             if not entry['season_pack'] and entry[
                     'series_season'] == 1 and entry[
                         'series_episode'] in desired_eps:
                 normalized_name = normalize_series_name(
                     entry['series_name'])
                 db_series = task.session.query(Series).filter(
                     Series.name == normalized_name).first()
                 if db_series and db_series.in_tasks:
                     continue
                 guessed_series.setdefault(normalized_name,
                                           entry['series_name'])
     # Reject any further episodes in those series
     for entry in task.entries:
         for series in guessed_series.values():
             if entry.get('series_name') == series and \
                     (entry.get('season_pack') or not (
                     entry.get('series_season') == 1 and entry.get('series_episode') in desired_eps)):
                 entry.reject(
                     'Non premiere episode or season pack in a premiere series'
                 )
     # Since we are running after task start phase, make sure not to merge into the config multiple times on reruns
     if not task.is_rerun:
         # Combine settings and series into series plugin config format
         allseries = {
             'settings': {
                 'series_premiere': group_settings
             },
             'series_premiere': list(guessed_series.values())
         }
         # Merge the our config in to the main series config
         self.merge_config(task, allseries)
Exemple #17
0
def display_details(name):
    """Display detailed series information, ie. series show NAME"""
    with Session() as session:
        name = normalize_series_name(name)
        # Sort by length of name, so that partial matches always show shortest matching title
        matches = shows_by_name(name, session=session)
        if not matches:
            console('ERROR: Unknown series `%s`' % name)
            return
        # Pick the best matching series
        series = matches[0]
        console('Showing results for `%s`.' % series.name)
        if len(matches) > 1:
            console('WARNING: Multiple series match to `%s`.' % name)
            console('Be more specific to see the results of other matches:')
            for s in matches[1:]:
                console(' - %s' % s.name)

        console(' %-63s%-15s' % ('Identifier, Title', 'Quality'))
        console('-' * 79)

        episodes = show_episodes(series, session=session)
        for episode in episodes:

            if episode.identifier is None:
                console(' None <--- Broken!')
            else:
                console(' %s (%s) - %s' % (episode.identifier, episode.identified_by or 'N/A', episode.age))

            for release in episode.releases:
                status = release.quality.name
                title = release.title
                if len(title) > 55:
                    title = title[:55] + '...'
                if release.proper_count > 0:
                    status += '-proper'
                    if release.proper_count > 1:
                        status += str(release.proper_count)
                if release.downloaded:
                    console('  * %-60s%-15s' % (title, status))
                else:
                    console('    %-60s%-15s' % (title, status))

        console('-' * 79)
        console(' * = downloaded')
        if not series.identified_by:
            console('')
            console(' Series plugin is still learning which episode numbering mode is ')
            console(' correct for this series (identified_by: auto).')
            console(' Few duplicate downloads can happen with different numbering schemes')
            console(' during this time.')
        else:
            console(' Series uses `%s` mode to identify episode numbering (identified_by).' % series.identified_by)
        console(' See option `identified_by` for more information.')
        if series.begin:
            console(' Begin episode for this series set to `%s`.' % series.begin.identifier)
Exemple #18
0
def display_details(name):
    """Display detailed series information, ie. series show NAME"""
    with Session() as session:
        name = normalize_series_name(name)
        # Sort by length of name, so that partial matches always show shortest matching title
        matches = shows_by_name(name, session=session)
        if not matches:
            console('ERROR: Unknown series `%s`' % name)
            return
        # Pick the best matching series
        series = matches[0]
        console('Showing results for `%s`.' % series.name)
        if len(matches) > 1:
            console('WARNING: Multiple series match to `%s`.' % name)
            console('Be more specific to see the results of other matches:')
            for s in matches[1:]:
                console(' - %s' % s.name)

        console(' %-63s%-15s' % ('Identifier, Title', 'Quality'))
        console('-' * 79)

        episodes = show_episodes(series, session=session)
        for episode in episodes:

            if episode.identifier is None:
                console(' None <--- Broken!')
            else:
                console(' %s (%s) - %s' % (episode.identifier, episode.identified_by or 'N/A', episode.age))

            for release in episode.releases:
                status = release.quality.name
                title = release.title
                if len(title) > 55:
                    title = title[:55] + '...'
                if release.proper_count > 0:
                    status += '-proper'
                    if release.proper_count > 1:
                        status += str(release.proper_count)
                if release.downloaded:
                    console('  * %-60s%-15s' % (title, status))
                else:
                    console('    %-60s%-15s' % (title, status))

        console('-' * 79)
        console(' * = downloaded')
        if not series.identified_by:
            console('')
            console(' Series plugin is still learning which episode numbering mode is ')
            console(' correct for this series (identified_by: auto).')
            console(' Few duplicate downloads can happen with different numbering schemes')
            console(' during this time.')
        else:
            console(' Series uses `%s` mode to identify episode numbering (identified_by).' % series.identified_by)
        console(' See option `identified_by` for more information.')
        if series.begin:
            console(' Begin episode for this series set to `%s`.' % series.begin.identifier)
Exemple #19
0
    def get(self, name, session):
        """ List of shows matching lookup name """
        name = series.normalize_series_name(name)
        matches = series.shows_by_name(name, session=session)

        shows = []
        for match in matches:
            shows.append(get_series_details(match))

        return jsonify({'shows': shows, 'number_of_shows': len(shows)})
Exemple #20
0
    def get(self, name, session):
        """ List of shows matching lookup name """
        name = series.normalize_series_name(name)
        matches = series.shows_by_name(name, session=session)

        shows = []
        for match in matches:
            shows.append(get_series_details(match))

        return jsonify({
            'shows': shows,
            'number_of_shows': len(shows)
        })
Exemple #21
0
    def get(self, name, session):
        """ List of shows matching lookup name """
        name = series.normalize_series_name(name)
        matches = series.shows_by_name(name, session=session)

        args = series_list_parser.parse_args()
        begin = args.get('begin')
        latest = args.get('latest')

        shows = []
        for match in matches:
            shows.append(series_details(match, begin, latest))

        return jsonify(shows)
Exemple #22
0
    def get(self, name, session):
        """ List of shows matching lookup name """
        name = series.normalize_series_name(name)
        matches = series.shows_by_name(name, session=session)

        args = series_list_parser.parse_args()
        begin = args.get('begin')
        latest = args.get('latest')

        shows = []
        for match in matches:
            shows.append(series_details(match, begin, latest))

        return jsonify(shows)
Exemple #23
0
    def lookup_episode(title=None, seasonnum=None, episodenum=None, tvdb_id=None, session=None, only_cached=False):
        series = ApiTrakt.lookup_series(title=title, tvdb_id=tvdb_id, only_cached=only_cached, session=session)
        if not series:
            raise LookupError('Could not identify series')
        if series.tvdb_id:
            ep_description = '%s.S%sE%s' % (series.title, seasonnum, episodenum)
            episode = session.query(TraktEpisode).filter(TraktEpisode.series_id == series.tvdb_id).\
                filter(TraktEpisode.season == seasonnum).filter(TraktEpisode.number == episodenum).first()
            url = episode_summary + api_key + '%s/%s/%s' % (series.tvdb_id, seasonnum, episodenum)
        elif title:
            title = normalize_series_name(title)
            title = re.sub(' ', '-', title)
            ep_description = '%s.S%sE%s' % (series.title, seasonnum, episodenum)
            episode = session.query(TraktEpisode).filter(title == series.title).\
                filter(TraktEpisode.season == seasonnum).\
                filter(TraktEpisode.number == episodenum).first()
            url = episode_summary + api_key + '%s/%s/%s' % (title, seasonnum, episodenum)
        if not episode:
            if only_cached:
                raise LookupError('Episode %s not found in cache' % ep_description)

            log.debug('Episode %s not found in cache, looking up from trakt.' % ep_description)
            try:
                data = requests.get(url)
            except requests.RequestException:
                log.debug('Error Retrieving Trakt url: %s' % url)
            try:
                data = data.json()
            except ValueError:
                log.debug('Error parsing Trakt episode json for %s' % title)
            if data:
                if 'status' in data:
                    raise LookupError('Error looking up episode')
                ep_data = data['episode']
                if ep_data:
                    episode = session.query(TraktEpisode).filter(TraktEpisode.tvdb_id == ep_data['tvdb_id']).first()
                    if not episode:
                        ep_data['episode_name'] = ep_data.pop('title')
                        for i in ep_data['images']:
                            ep_data[i] = ep_data['images'][i]
                            del ep_data['images']
                        episode = TraktEpisode(ep_data)
                    series.episodes.append(episode)
                    session.merge(episode)
        if episode:
            return episode
        else:
            raise LookupError('No results found for (%s)' % episode)
Exemple #24
0
 def post(self, name, session):
     """ Create a new show and set its first accepted episode """
     normalized_name = series.normalize_series_name(name)
     matches = series.shows_by_exact_name(normalized_name, session=session)
     if matches:
         return {
             'status': 'error',
             'message': 'Show `%s` already exist in DB' % name
         }, 500
     show = series.Series()
     show.name = name
     session.add(show)
     data = request.json
     ep_id = data.get('episode_identifier')
     try:
         series.set_series_begin(show, ep_id)
     except ValueError as e:
         return {'status': 'error', 'message': e.args[0]}, 400
     return jsonify(get_series_details(show))
Exemple #25
0
 def on_task_metainfo(self, task, config):
     if not config:
         # Don't run when we are disabled
         return
     # Generate the group settings for series plugin
     group_settings = {}
     if isinstance(config, dict):
         group_settings = config
     # Generate a list of unique series that metainfo_series can parse for this task
     metainfo_series = get_plugin_by_name('metainfo_series')
     guess_entry = metainfo_series.instance.guess_entry
     guessed_series = {}
     for entry in task.entries:
         if guess_entry(entry):
             guessed_series.setdefault(normalize_series_name(entry['series_name']), entry['series_name'])
     # Combine settings and series into series plugin config format
     allseries = {'settings': {'all_series': group_settings}, 'all_series': guessed_series.values()}
     # Merge our config in to the main series config
     self.merge_config(task, allseries)
Exemple #26
0
 def on_task_metainfo(self, task, config):
     if not config:
         # Don't run when we are disabled
         return
     if task.is_rerun:
         return
     # Generate the group settings for series plugin
     group_settings = {}
     allow_seasonless = False
     desired_eps = [0, 1]
     if isinstance(config, dict):
         allow_seasonless = config.pop('allow_seasonless', False)
         if not config.pop('allow_teasers', True):
             desired_eps = [1]
         group_settings = config
     group_settings['identified_by'] = 'ep'
     # Generate a list of unique series that have premieres
     metainfo_series = plugin.get_plugin_by_name('metainfo_series')
     guess_entry = metainfo_series.instance.guess_entry
     # Make a set of unique series according to series name normalization rules
     guessed_series = {}
     for entry in task.entries:
         if guess_entry(entry, allow_seasonless=allow_seasonless, config=group_settings):
             if entry['series_season'] == 1 and entry['series_episode'] in desired_eps:
                 normalized_name = normalize_series_name(entry['series_name'])
                 db_series = task.session.query(Series).filter(Series.name == normalized_name).first()
                 if db_series and db_series.in_tasks:
                     continue
                 guessed_series.setdefault(normalized_name, entry['series_name'])
     # Reject any further episodes in those series
     for entry in task.entries:
         for series in guessed_series.values():
             if entry.get('series_name') == series and not (
                     entry.get('series_season') == 1 and
                     entry.get('series_episode') in desired_eps):
                 entry.reject('Non premiere episode in a premiere series')
     # Since we are running after task start phase, make sure not to merge into the config multiple times on reruns
     if not task.is_rerun:
         # Combine settings and series into series plugin config format
         allseries = {'settings': {'series_premiere': group_settings},
                      'series_premiere': list(guessed_series.values())}
         # Merge the our config in to the main series config
         self.merge_config(task, allseries)
Exemple #27
0
 def post(self, name, session):
     """ Create a new show and set its first accepted episode """
     normalized_name = series.normalize_series_name(name)
     matches = series.shows_by_exact_name(normalized_name, session=session)
     if matches:
         return {'status': 'error',
                 'message': 'Show `%s` already exist in DB' % name
                 }, 500
     show = series.Series()
     show.name = name
     session.add(show)
     data = request.json
     ep_id = data.get('episode_identifier')
     try:
         series.set_series_begin(show, ep_id)
     except ValueError as e:
         return {'status': 'error',
                 'message': e.args[0]
                 }, 400
     return jsonify(get_series_details(show))
Exemple #28
0
def begin(manager, options):
    series_name = options.series_name
    ep_id = options.episode_id
    normalized_name = normalize_series_name(series_name)
    with Session() as session:
        series = shows_by_exact_name(normalized_name, session)
        if not series:
            console('Series not yet in database, adding `%s`' % series_name)
            series = Series()
            series.name = series_name
            session.add(series)
        else:
            series = series[0]
        try:
            set_series_begin(series, ep_id)
        except ValueError as e:
            console(e)
        else:
            console('Episodes for `%s` will be accepted starting with `%s`' % (series.name, ep_id))
            session.commit()
        manager.config_changed()
Exemple #29
0
def begin(manager, options):
    series_name = options.series_name
    ep_id = options.episode_id
    normalized_name = normalize_series_name(series_name)
    with Session() as session:
        series = shows_by_exact_name(normalized_name, session)
        if not series:
            console('Series not yet in database, adding `%s`' % series_name)
            series = Series()
            series.name = series_name
            session.add(series)
        else:
            series = series[0]
        try:
            set_series_begin(series, ep_id)
        except ValueError as e:
            console(e)
        else:
            console('Episodes for `%s` will be accepted starting with `%s`' % (series.name, ep_id))
            session.commit()
        manager.config_changed()
Exemple #30
0
def begin(manager, options):
    series_name = options.series_name
    series_name = series_name.replace(r'\!', '!')
    normalized_name = normalize_series_name(series_name)
    with Session() as session:
        series = shows_by_exact_name(normalized_name, session)
        if options.forget:
            if not series:
                console('Series `%s` was not found in the database.' %
                        series_name)
            else:
                series = series[0]
                series.begin = None
                console('The begin episode for `%s` has been forgotten.' %
                        series.name)
                session.commit()
                manager.config_changed()
        elif options.episode_id:
            ep_id = options.episode_id
            if not series:
                console('Series not yet in database. Adding `%s`.' %
                        series_name)
                series = Series()
                series.name = series_name
                session.add(series)
            else:
                series = series[0]
            try:
                _, entity_type = set_series_begin(series, ep_id)
            except ValueError as e:
                console(e)
            else:
                if entity_type == 'season':
                    console('`%s` was identified as a season.' % ep_id)
                    ep_id += 'E01'
                console(
                    'Releases for `%s` will be accepted starting with `%s`.' %
                    (series.name, ep_id))
                session.commit()
            manager.config_changed()
Exemple #31
0
 def on_task_metainfo(self, task, config):
     if not config:
         # Don't run when we are disabled
         return
     # Generate the group settings for series plugin
     group_settings = {}
     allow_seasonless = False
     if isinstance(config, dict):
         allow_seasonless = config.pop('allow_seasonless', False)
         group_settings = config
     # Generate a list of unique series that have premieres
     metainfo_series = get_plugin_by_name('metainfo_series')
     guess_entry = metainfo_series.instance.guess_entry
     # Make a set of unique series according to series name normalization rules
     guessed_series = {}
     for entry in task.entries:
         if guess_entry(entry, allow_seasonless=allow_seasonless):
             if entry['series_season'] == 1 and entry['series_episode'] in (
                     0, 1):
                 guessed_series.setdefault(
                     normalize_series_name(entry['series_name']),
                     entry['series_name'])
     # Reject any further episodes in those series
     for entry in task.entries:
         for series in guessed_series.itervalues():
             if entry.get('series_name') == series and not (
                     entry.get('series_season') == 1
                     and entry.get('series_episode') in (0, 1)):
                 task.reject(entry,
                             'Non premiere episode in a premiere series')
     # Combine settings and series into series plugin config format
     allseries = {
         'settings': {
             'series_premiere': group_settings
         },
         'series_premiere': guessed_series.values()
     }
     # Merge the our config in to the main series config
     self.merge_config(task, allseries)
Exemple #32
0
 def on_task_metainfo(self, task, config):
     if not config:
         # Don't run when we are disabled
         return
     if task.is_rerun:
         # Since we are running after task start phase, make sure not to merge into the config again on reruns
         return
     # Generate the group settings for series plugin
     group_settings = {}
     if isinstance(config, dict):
         group_settings = config
     group_settings['identified_by'] = 'ep'
     # Generate a list of unique series that metainfo_series can parse for this task
     metainfo_series = plugin.get_plugin_by_name('metainfo_series')
     guess_entry = metainfo_series.instance.guess_entry
     guessed_series = {}
     for entry in task.entries:
         if guess_entry(entry):
             guessed_series.setdefault(normalize_series_name(entry['series_name']), entry['series_name'])
     # Combine settings and series into series plugin config format
     allseries = {'settings': {'all_series': group_settings}, 'all_series': guessed_series.values()}
     # Merge our config in to the main series config
     self.merge_config(task, allseries)
Exemple #33
0
 def on_task_input(self, task, config):
     """asd"""
     slist = task.session.query(Series)
     if isinstance(config, basestring):
         name = normalize_series_name(config)
         slist = slist.filter(Series._name_normalized.contains(name))
     slist = (slist.outerjoin(Series.episodes).outerjoin(Episode.releases).outerjoin(Series.in_tasks).
              # group_by(Series.id).having(func.count(SeriesTask.id) < 1).order_by(Series.name).all())
              # group_by(Series.id).having(func.count(SeriesTask.id) >= 1).order_by(Series.name).all())
              order_by(Series.name).all())
     entries = []
     for series in slist:
         elist = (task.session.query(Episode).filter(Episode.series_id == series.id).
                  order_by(Episode.season, Episode.number).all())
         for episode in elist:
             self.log.debug('Found episode %s for series "%s"' % (episode.identifier, series.name))
             entry = Entry()
             entry['title'] = '%s %s' % (series.name, episode.identifier)
             entry['url'] = 'http://localhost/mock/%s' % hash(entry['title'])
             if entry.isvalid():
                 entries.append(entry)
             else:
                 self.log.debug('Invalid entry created? %s' % entry)
     return entries
Exemple #34
0
def add(manager, options):
    series_name = options.series_name
    entity_ids = options.entity_id
    quality = options.quality or os.environ.get(ENV_ADD_QUALITY, None)
    series_name = series_name.replace(r'\!', '!')
    normalized_name = normalize_series_name(series_name)
    with Session() as session:
        series = shows_by_exact_name(normalized_name, session)
        if not series:
            console('Series not yet in database, adding `%s`' % series_name)
            series = Series()
            series.name = series_name
            session.add(series)
        else:
            series = series[0]
        for ent_id in entity_ids:
            try:
                add_series_entity(session, series, ent_id, quality=quality)
            except ValueError as e:
                console(e.args[0])
            else:
                console('Added entity `%s` to series `%s`.' % (ent_id, series.name.title()))
        session.commit()
    manager.config_changed()
Exemple #35
0
    def lookup_episode(title=None,
                       seasonnum=None,
                       episodenum=None,
                       tvdb_id=None,
                       session=None,
                       only_cached=False):
        series = ApiTrakt.lookup_series(title=title,
                                        tvdb_id=tvdb_id,
                                        only_cached=only_cached,
                                        session=session)
        if not series:
            raise LookupError('Could not identify series')
        if series.tvdb_id:
            ep_description = '%s.S%sE%s' % (series.title, seasonnum,
                                            episodenum)
            episode = session.query(TraktEpisode).filter(TraktEpisode.series_id == series.tvdb_id).\
                filter(TraktEpisode.season == seasonnum).filter(TraktEpisode.number == episodenum).first()
            url = episode_summary + api_key + '%s/%s/%s' % (
                series.tvdb_id, seasonnum, episodenum)
        elif title:
            title = normalize_series_name(title)
            title = re.sub(' ', '-', title)
            ep_description = '%s.S%sE%s' % (series.title, seasonnum,
                                            episodenum)
            episode = session.query(TraktEpisode).filter(title == series.title).\
                filter(TraktEpisode.season == seasonnum).\
                filter(TraktEpisode.number == episodenum).first()
            url = episode_summary + api_key + '%s/%s/%s' % (title, seasonnum,
                                                            episodenum)
        if not episode:
            if only_cached:
                raise LookupError('Episode %s not found in cache' %
                                  ep_description)

            log.debug('Episode %s not found in cache, looking up from trakt.' %
                      ep_description)
            try:
                data = requests.get(url)
            except requests.RequestException:
                log.debug('Error Retrieving Trakt url: %s' % url)
            try:
                data = data.json()
            except ValueError:
                log.debug('Error parsing Trakt episode json for %s' % title)
            if data:
                if 'status' in data:
                    raise LookupError('Error looking up episode')
                ep_data = data['episode']
                if ep_data:
                    episode = session.query(TraktEpisode).filter(
                        TraktEpisode.tvdb_id == ep_data['tvdb_id']).first()
                    if not episode:
                        ep_data['episode_name'] = ep_data.pop('title')
                        for i in ep_data['images']:
                            ep_data[i] = ep_data['images'][i]
                            del ep_data['images']
                        episode = TraktEpisode(ep_data)
                    series.episodes.append(episode)
                    session.merge(episode)
        if episode:
            return episode
        else:
            raise LookupError('No results found for (%s)' % episode)
Exemple #36
0
    def get(self, session=None):
        """ List existing shows """
        args = series_list_parser.parse_args()

        # Filter params
        configured = args['in_config']
        premieres = args['premieres']

        # Pagination and sorting params
        page = args['page']
        per_page = args['per_page']
        sort_by = args['sort_by']
        sort_order = args['order']
        name = series.normalize_series_name(args['query']) if args['query'] else None

        # Handle max size limit
        if per_page > 100:
            per_page = 100

        descending = sort_order == 'desc'

        # Data params
        lookup = args.get('lookup')
        begin = args.get('begin')
        latest = args.get('latest')

        start = per_page * (page - 1)
        stop = start + per_page

        kwargs = {
            'configured': configured,
            'premieres': premieres,
            'start': start,
            'stop': stop,
            'sort_by': sort_by,
            'descending': descending,
            'session': session,
            'name': name
        }

        total_items = series.get_series_summary(count=True, **kwargs)

        if not total_items:
            return jsonify([])

        series_list = []
        for s in series.get_series_summary(**kwargs):
            series_object = series_details(s, begin, latest)
            series_list.append(series_object)

        # Total number of pages
        total_pages = int(ceil(total_items / float(per_page)))

        if total_pages < page and total_pages != 0:
            raise NotFoundError('page %s does not exist' % page)

        # Actual results in page
        actual_size = min(per_page, len(series_list))

        # Do relevant lookups
        if lookup:
            api_client = APIClient()
            for endpoint in lookup:
                base_url = '/%s/series/' % endpoint
                for show in series_list:
                    pos = series_list.index(show)
                    series_list[pos].setdefault('lookup', {})
                    url = base_url + show['name'] + '/'
                    result = api_client.get_endpoint(url)
                    series_list[pos]['lookup'].update({endpoint: result})

        # Get pagination headers
        pagination = pagination_headers(total_pages, total_items, actual_size, request)

        # Created response
        rsp = jsonify(series_list)

        # Add link header to response
        rsp.headers.extend(pagination)
        return rsp
Exemple #37
0
def display_details(options):
    """Display detailed series information, ie. series show NAME"""
    name = options.series_name
    sort_by = options.sort_by or os.environ.get(ENV_SHOW_SORTBY_FIELD, 'age')
    if options.order is not None:
        reverse = True if options.order == 'desc' else False
    else:
        reverse = True if os.environ.get(ENV_SHOW_SORTBY_ORDER) == 'desc' else False
    with Session() as session:
        name = normalize_series_name(name)
        # Sort by length of name, so that partial matches always show shortest matching title
        matches = shows_by_name(name, session=session)
        if not matches:
            console(colorize(ERROR_COLOR, 'ERROR: Unknown series `%s`' % name))
            return
        # Pick the best matching series
        series = matches[0]
        table_title = colorize('white', series.name)
        if len(matches) > 1:
            warning = (colorize('red', ' WARNING: ') +
                       'Multiple series match to `{}`.\n '
                       'Be more specific to see the results of other matches:\n\n'
                       ' {}'.format(name, ', '.join(s.name for s in matches[1:])))
            if not options.table_type == 'porcelain':
                console(warning)
        header = ['Identifier', 'Last seen', 'Release titles', 'Release Quality', 'Proper']
        table_data = [header]
        entities = get_all_entities(series, session=session, sort_by=sort_by, reverse=reverse)
        for entity in entities:
            if not entity.releases:
                continue
            if entity.identifier is None:
                identifier = colorize(ERROR_COLOR, 'MISSING')
                age = ''
            else:
                identifier = entity.identifier
                age = entity.age
            entity_data = [identifier, age]
            release_titles = []
            release_qualities = []
            release_propers = []
            for release in entity.releases:
                title = release.title
                quality = release.quality.name
                if not release.downloaded:
                    title = colorize(UNDOWNLOADED_RELEASE_COLOR, title)
                    quality = quality
                else:
                    title += ' *'
                    title = colorize(DOWNLOADED_RELEASE_COLOR, title)
                    quality = quality
                release_titles.append(title)
                release_qualities.append(quality)
                release_propers.append('Yes' if release.proper_count > 0 else '')
            entity_data.append('\n'.join(release_titles))
            entity_data.append('\n'.join(release_qualities))
            entity_data.append('\n'.join(release_propers))
            table_data.append(entity_data)
        footer = ' %s \n' % (colorize(DOWNLOADED_RELEASE_COLOR, '* Downloaded'))
        if not series.identified_by:
            footer += ('\n Series plugin is still learning which episode numbering mode is \n'
                       ' correct for this series (identified_by: auto).\n'
                       ' Few duplicate downloads can happen with different numbering schemes\n'
                       ' during this time.')
        else:
            footer += '\n `%s` uses `%s` mode to identify episode numbering.' % (series.name, series.identified_by)
        begin_text = 'option'
        if series.begin:
            footer += ' \n Begin for `%s` is set to `%s`.' % (series.name, series.begin.identifier)
            begin_text = 'and `begin` options'
        footer += ' \n See `identified_by` %s for more information.' % begin_text
    try:
        table = TerminalTable(options.table_type, table_data, table_title, drop_columns=[4, 3, 1])
        console(table.output)
    except TerminalTableError as e:
        console('ERROR: %s' % str(e))
        return
    if not options.table_type == 'porcelain':
        console(footer)
Exemple #38
0
def display_details(name):
    """Display detailed series information, ie. series show NAME"""

    from flexget.manager import Session
    with contextlib.closing(Session()) as session:

        name = normalize_series_name(name)
        # Sort by length of name, so that partial matches always show shortest matching title
        matches = (session.query(Series).filter(Series._name_normalized.contains(name)).
                   order_by(func.char_length(Series.name)).all())
        if not matches:
            console('ERROR: Unknown series `%s`' % name)
            return
        # Pick the best matching series
        series = matches[0]
        console('Showing results for `%s`.' % series.name)
        if len(matches) > 1:
            console('WARNING: Multiple series match to `%s`.' % name)
            console('Be more specific to see the results of other matches:')
            for s in matches[1:]:
                console(' - %s' % s.name)

        console(' %-63s%-15s' % ('Identifier, Title', 'Quality'))
        console('-' * 79)

        # Query episodes in sane order instead of iterating from series.episodes
        episodes = session.query(Episode).filter(Episode.series_id == series.id)
        if series.identified_by == 'sequence':
            episodes = episodes.order_by(Episode.number).all()
        elif series.identified_by == 'ep':
            episodes = episodes.order_by(Episode.season, Episode.number).all()
        else:
            episodes = episodes.order_by(Episode.identifier).all()

        for episode in episodes:

            if episode.identifier is None:
                console(' None <--- Broken!')
            else:
                console(' %s (%s) - %s' % (episode.identifier, episode.identified_by or 'N/A', episode.age))

            for release in episode.releases:
                status = release.quality.name
                title = release.title
                if len(title) > 55:
                    title = title[:55] + '...'
                if release.proper_count > 0:
                    status += '-proper'
                    if release.proper_count > 1:
                        status += str(release.proper_count)
                if release.downloaded:
                    console('  * %-60s%-15s' % (title, status))
                else:
                    console('    %-60s%-15s' % (title, status))

        console('-' * 79)
        console(' * = downloaded')
        if not series.identified_by:
            console('')
            console(' Series plugin is still learning which episode numbering mode is ')
            console(' correct for this series (identified_by: auto).')
            console(' Few duplicate downloads can happen with different numbering schemes')
            console(' during this time.')
        else:
            console(' Series uses `%s` mode to identify episode numbering (identified_by).' % series.identified_by)
        console(' See option `identified_by` for more information.')
        if series.begin:
            console(' Begin episode for this series set to `%s`.' % series.begin.identifier)
Exemple #39
0
def display_details(options):
    """Display detailed series information, ie. series show NAME"""
    name = options.series_name
    with Session() as session:
        name = normalize_series_name(name)
        # Sort by length of name, so that partial matches always show shortest matching title
        matches = shows_by_name(name, session=session)
        if not matches:
            console(colorize(ERROR_COLOR, 'ERROR: Unknown series `%s`' % name))
            return
        # Pick the best matching series
        series = matches[0]
        table_title = colorize('white', series.name)
        if len(matches) > 1:
            warning = (colorize('red', ' WARNING: ') +
                       'Multiple series match to `{}`.\n '
                       'Be more specific to see the results of other matches:\n\n'
                       ' {}'.format(name, ', '.join(s.name for s in matches[1:])))
            if not options.table_type == 'porcelain':
                console(warning)
        header = ['Episode ID', 'Latest age', 'Release titles', 'Release Quality', 'Proper']
        table_data = [header]
        episodes = show_episodes(series, session=session)
        for episode in episodes:
            if episode.identifier is None:
                identifier = colorize(ERROR_COLOR, 'MISSING')
                age = ''
            else:
                identifier = episode.identifier
                age = episode.age
            ep_data = [identifier, age]
            release_titles = []
            release_qualities = []
            release_propers = []
            for release in episode.releases:
                title = release.title
                quality = release.quality.name
                if not release.downloaded:
                    title = colorize(UNDOWNLOADED_RELEASE_COLOR, title)
                    quality = quality
                else:
                    title = colorize(DOWNLOADED_RELEASE_COLOR, title)
                    quality = quality
                release_titles.append(title)
                release_qualities.append(quality)
                release_propers.append('Yes' if release.proper_count > 0 else '')
            ep_data.append('\n'.join(release_titles))
            ep_data.append('\n'.join(release_qualities))
            ep_data.append('\n'.join(release_propers))
            table_data.append(ep_data)
        footer = (' %s %s\n' % (colorize(DOWNLOADED_RELEASE_COLOR, 'Downloaded'),
                                colorize(UNDOWNLOADED_RELEASE_COLOR, 'Un-downloaded')))
        if not series.identified_by:
            footer += ('\n'
                       ' Series plugin is still learning which episode numbering mode is \n'
                       ' correct for this series (identified_by: auto).\n'
                       ' Few duplicate downloads can happen with different numbering schemes\n'
                       ' during this time.')
        else:
            footer += ' \n Series uses `%s` mode to identify episode numbering (identified_by).' % series.identified_by
        footer += ' \n See option `identified_by` for more information.\n'
        if series.begin:
            footer += ' Begin episode for this series set to `%s`.' % series.begin.identifier
    table = TerminalTable(options.table_type, table_data, table_title, drop_columns=[4, 3, 1])
    try:
        console(table.output)
    except TerminalTableError as e:
        console('ERROR: %s' % str(e))
        return
    if not options.table_type == 'porcelain':
        console(footer)
Exemple #40
0
def display_details(options):
    """Display detailed series information, ie. series show NAME"""
    name = options.series_name
    with Session() as session:
        name = normalize_series_name(name)
        # Sort by length of name, so that partial matches always show shortest matching title
        matches = shows_by_name(name, session=session)
        if not matches:
            console(colorize(ERROR_COLOR, 'ERROR: Unknown series `%s`' % name))
            return
        # Pick the best matching series
        series = matches[0]
        table_title = colorize('white', series.name)
        if len(matches) > 1:
            warning = (
                colorize('red', ' WARNING: ') +
                'Multiple series match to `{}`.\n '
                'Be more specific to see the results of other matches:\n\n'
                ' {}'.format(name, ', '.join(s.name for s in matches[1:])))
            if not options.table_type == 'porcelain':
                console(warning)
        header = [
            'Entity ID', 'Latest age', 'Release titles', 'Release Quality',
            'Proper'
        ]
        table_data = [header]
        entities = get_all_entities(series, session=session)
        for entity in entities:
            if entity.identifier is None:
                identifier = colorize(ERROR_COLOR, 'MISSING')
                age = ''
            else:
                identifier = entity.identifier
                age = entity.age
            entity_data = [identifier, age]
            release_titles = []
            release_qualities = []
            release_propers = []
            for release in entity.releases:
                title = release.title
                quality = release.quality.name
                if not release.downloaded:
                    title = colorize(UNDOWNLOADED_RELEASE_COLOR, title)
                    quality = quality
                else:
                    title += ' *'
                    title = colorize(DOWNLOADED_RELEASE_COLOR, title)
                    quality = quality
                release_titles.append(title)
                release_qualities.append(quality)
                release_propers.append(
                    'Yes' if release.proper_count > 0 else '')
            entity_data.append('\n'.join(release_titles))
            entity_data.append('\n'.join(release_qualities))
            entity_data.append('\n'.join(release_propers))
            table_data.append(entity_data)
        footer = ' %s \n' % (colorize(DOWNLOADED_RELEASE_COLOR,
                                      '* Downloaded'))
        if not series.identified_by:
            footer += (
                '\n Series plugin is still learning which episode numbering mode is \n'
                ' correct for this series (identified_by: auto).\n'
                ' Few duplicate downloads can happen with different numbering schemes\n'
                ' during this time.')
        else:
            footer += '\n Series uses `%s` mode to identify episode numbering (identified_by).' % series.identified_by
        footer += ' \n See option `identified_by` for more information.\n'
        if series.begin:
            footer += ' Begin episode for this series set to `%s`.' % series.begin.identifier
    try:
        table = TerminalTable(options.table_type,
                              table_data,
                              table_title,
                              drop_columns=[4, 3, 1])
        console(table.output)
    except TerminalTableError as e:
        console('ERROR: %s' % str(e))
        return
    if not options.table_type == 'porcelain':
        console(footer)
Exemple #41
0
def display_details(name):
    """Display detailed series information, ie. series show NAME"""

    from flexget.manager import Session
    session = Session()

    name = normalize_series_name(name)
    # Sort by length of name, so that partial matches always show shortest matching title
    matches = (session.query(Series).filter(
        Series._name_normalized.contains(name)).order_by(
            func.char_length(Series.name)).all())
    if not matches:
        console('ERROR: Unknown series `%s`' % name)
        return
    # Pick the best matching series
    series = matches[0]
    console('Showing results for `%s`.' % series.name)
    if len(matches) > 1:
        console('WARNING: Multiple series match to `%s`.' % name)
        console('Be more specific to see the results of other matches:')
        for s in matches[1:]:
            console(' - %s' % s.name)

    console(' %-63s%-15s' % ('Identifier, Title', 'Quality'))
    console('-' * 79)

    # Query episodes in sane order instead of iterating from series.episodes
    episodes = session.query(Episode).filter(Episode.series_id == series.id)
    if series.identified_by == 'sequence':
        episodes = episodes.order_by(Episode.number).all()
    elif series.identified_by == 'ep':
        episodes = episodes.order_by(Episode.season, Episode.number).all()
    else:
        episodes = episodes.order_by(Episode.identifier).all()

    for episode in episodes:

        if episode.identifier is None:
            console(' None <--- Broken!')
        else:
            console(' %s (%s) - %s' %
                    (episode.identifier, episode.identified_by
                     or 'N/A', episode.age))

        for release in episode.releases:
            status = release.quality.name
            title = release.title
            if len(title) > 55:
                title = title[:55] + '...'
            if release.proper_count > 0:
                status += '-proper'
                if release.proper_count > 1:
                    status += str(release.proper_count)
            if release.downloaded:
                console('  * %-60s%-15s' % (title, status))
            else:
                console('    %-60s%-15s' % (title, status))

    console('-' * 79)
    console(' * = downloaded')
    if not series.identified_by:
        console('')
        console(
            ' Series plugin is still learning which episode numbering mode is '
        )
        console(' correct for this series (identified_by: auto).')
        console(
            ' Few duplicate downloads can happen with different numbering schemes'
        )
        console(' during this time.')
    else:
        console(
            ' Series uses `%s` mode to identify episode numbering (identified_by).'
            % series.identified_by)
    console(' See option `identified_by` for more information.')
    if series.begin:
        console(' Begin episode for this series set to `%s`.' %
                series.begin.identifier)
    session.close()