Example #1
0
def get_xem_ids():
    global xem_ids_list

    for tvid, name in iteritems(sickbeard.TVInfoAPI().xem_supported_sources):
        xem_ids = _xem_get_ids(name,
                               sickbeard.TVInfoAPI(tvid).config['xem_origin'])
        if len(xem_ids):
            xem_ids_list[tvid] = xem_ids
Example #2
0
 def get_url(self, key):
     if TVINFO_TVMAZE == key:
         return '%sshows/%s' % (sickbeard.TVInfoAPI(
             TVINFO_TVMAZE).config['base_url'], self.tvmaze_ids[key])
     return '%slookup/shows?%s=%s%s' % (
         sickbeard.TVInfoAPI(TVINFO_TVMAZE).config['base_url'],
         self.tvmaze_ids[key], ('', 'tt')[key == TVINFO_IMDB],
         (self[key], '%07d' % self[key])[key == TVINFO_IMDB])
Example #3
0
    def _get_episode_thumb_url(ep_obj):
        # type: (sickbeard.tv.TVEpisode) -> Optional[AnyStr]
        """
        Returns the URL to use for downloading an episode's thumbnail. Uses
        theTVDB.com and TVRage.com data.

        :param ep_obj: a TVEpisode object for which to grab the thumb URL
        :return: URL to thumb
        """

        ep_obj_list = [ep_obj] + ep_obj.related_ep_obj

        # validate show
        from .. import helpers
        if not helpers.validate_show(ep_obj.show_obj):
            return None

        # try all included episodes in case some have thumbs and others don't
        for cur_ep_obj in ep_obj_list:
            if TVINFO_TVDB == cur_ep_obj.show_obj.tvid:
                show_lang = cur_ep_obj.show_obj.lang

                try:
                    tvinfo_config = sickbeard.TVInfoAPI(TVINFO_TVDB).api_params.copy()
                    tvinfo_config['dvdorder'] = 0 != cur_ep_obj.show_obj.dvdorder
                    tvinfo_config['no_dummy'] = True

                    if show_lang and not 'en' == show_lang:
                        tvinfo_config['language'] = show_lang

                    t = sickbeard.TVInfoAPI(TVINFO_TVDB).setup(**tvinfo_config)

                    ep_info = t[cur_ep_obj.show_obj.prodid][cur_ep_obj.season][cur_ep_obj.episode]
                except (BaseTVinfoEpisodenotfound, BaseTVinfoSeasonnotfound, TypeError):
                    ep_info = None
            else:
                ep_info = helpers.validate_show(cur_ep_obj.show_obj, cur_ep_obj.season, cur_ep_obj.episode)

            if not ep_info:
                continue

            thumb_url = getattr(ep_info, 'filename', None) \
                or (isinstance(ep_info, dict) and ep_info.get('filename', None))
            if thumb_url not in (None, False, ''):
                return thumb_url

        return None
Example #4
0
    def _episode_strings(self,
                         ep_obj  # type: TVEpisode
                         ):  # type: (...) -> List[Dict[AnyStr, List[AnyStr]]]
        """

        :param ep_obj: episode object
        :return:
        """
        search_params = []
        base_params = {}

        if not ep_obj:
            return [base_params]

        ep_detail = None
        if ep_obj.show_obj.air_by_date or ep_obj.show_obj.is_sports:
            airdate = str(ep_obj.airdate).split('-')
            base_params['season'] = airdate[0]
            if ep_obj.show_obj.air_by_date:
                base_params['ep'] = '/'.join(airdate[1:])
                ep_detail = '+"%s.%s"' % (base_params['season'], '.'.join(airdate[1:]))
        elif ep_obj.show_obj.is_anime:
            base_params['ep'] = '%i' % (helpers.try_int(ep_obj.scene_absolute_number) or
                                        helpers.try_int(ep_obj.scene_episode))
            ep_detail = '%02d' % helpers.try_int(base_params['ep'])
        else:
            base_params['season'], base_params['ep'] = (
                (ep_obj.season, ep_obj.episode), (ep_obj.scene_season, ep_obj.scene_episode))[ep_obj.show_obj.is_scene]
            ep_detail = sickbeard.config.naming_ep_type[2] % {
                'seasonnumber': helpers.try_int(base_params['season'], 1),
                'episodenumber': helpers.try_int(base_params['ep'], 1)}

        # id search
        params = base_params.copy()
        use_id = False
        for i in sickbeard.TVInfoAPI().all_sources:
            if i in ep_obj.show_obj.ids and 0 < ep_obj.show_obj.ids[i]['id'] and i in self.caps:
                params[self.caps[i]] = ep_obj.show_obj.ids[i]['id']
                use_id = True
        use_id and search_params.append(params)

        spacer = 'nzbgeek.info' in self.url.lower() and ' ' or '.'
        # query search and exceptions
        name_exceptions = get_show_names(ep_obj, spacer)
        if sickbeard.scene_exceptions.has_abs_episodes(ep_obj):
            search_params.append({'q': '%s%s%s' % (ep_obj.show_obj.name, spacer, base_params['ep'])})
        for cur_exception in name_exceptions:
            params = base_params.copy()
            params['q'] = cur_exception
            search_params.append(params)

            if ep_detail:
                params = base_params.copy()
                params['q'] = '%s%s%s' % (cur_exception, spacer, ep_detail)
                'season' in params and params.pop('season')
                'ep' in params and params.pop('ep')
                search_params.append(params)

        return [{'Episode': search_params}]
Example #5
0
    def _season_image_dict(self, show_obj, season, image_type):
        # type: (sickbeard.tv.TVShow, int, AnyStr) -> Dict[int, Dict[int, AnyStr]]
        """
        image_type : Type of image to fetch, 'seasons' or 'seasonwides'
        image_type type : String

        Should return a dict like:

        result = {<season number>:
                    {1: '<url 1>', 2: <url 2>, ...},}
        """
        result = {}

        try:
            # There's gotta be a better way of doing this but we don't wanna
            # change the language value elsewhere
            tvinfo_config = sickbeard.TVInfoAPI(show_obj.tvid).api_params.copy()
            tvinfo_config[image_type] = True
            tvinfo_config['dvdorder'] = 0 != show_obj.dvdorder

            if 'en' != getattr(show_obj, 'lang', None):
                tvinfo_config['language'] = show_obj.lang

            t = sickbeard.TVInfoAPI(show_obj.tvid).setup(**tvinfo_config)
            tvinfo_obj_show = t[show_obj.prodid]
        except (BaseTVinfoError, IOError) as e:
            logger.log(u'Unable to look up show on ' + sickbeard.TVInfoAPI(
                show_obj.tvid).name + ', not downloading images: ' + ex(e), logger.WARNING)
            return result

        if not self._valid_show(tvinfo_obj_show, show_obj):
            return result

        season_images = getattr(tvinfo_obj_show, 'banners', {}).get(
            ('season', 'seasonwide')['seasonwides' == image_type], {}).get(season, {})
        for image_id in season_images:
            if season not in result:
                result[season] = {}
            result[season][image_id] = season_images[image_id]['bannerpath']

        return result
Example #6
0
    def create_show_metadata(self, show_obj, force=False):
        # type: (sickbeard.tv.TVShow, bool) -> bool
        result = False
        if self.show_metadata and show_obj and (not self._has_show_metadata(show_obj) or force):
            logger.log('Metadata provider %s creating show metadata for %s' % (self.name, show_obj.name), logger.DEBUG)
            try:
                result = self.write_show_file(show_obj)
            except BaseTVinfoError as e:
                logger.log('Unable to find useful show metadata for %s on %s: %s' % (
                    self.name, sickbeard.TVInfoAPI(show_obj.tvid).name, ex(e)), logger.WARNING)

        return result
Example #7
0
    def create_episode_metadata(self, ep_obj, force=False):
        # type: (sickbeard.tv.TVEpisode, bool) -> bool
        result = False
        if self.episode_metadata and ep_obj and (not self.has_episode_metadata(ep_obj) or force):
            logger.log('Metadata provider %s creating episode metadata for %s' % (self.name, ep_obj.pretty_name()),
                       logger.DEBUG)
            try:
                result = self.write_ep_file(ep_obj)
            except BaseTVinfoError as e:
                logger.log('Unable to find useful episode metadata for %s on %s: %s' % (
                    self.name, sickbeard.TVInfoAPI(ep_obj.show_obj.tvid).name, ex(e)), logger.WARNING)

        return result
Example #8
0
 def __str__(self):
     if self.show_obj:
         prodid = self.show_obj.prodid
         tvid = self.show_obj.tvid
     elif self.parsed_show_obj:
         prodid = self.parsed_show_obj.prodid
         tvid = self.parsed_show_obj.tvid
     else:
         prodid = self.prodid
         tvid = self.tvid
     return '%s %s %sx%s of %s from %s' % (
         self.date, self.name, self.season, self.episode, prodid,
         sickbeard.TVInfoAPI(tvid).name)
Example #9
0
def _xem_exceptions_fetcher():
    global xem_exception_dict

    xem_list = 'xem_us'
    for cur_show_obj in sickbeard.showList:
        if cur_show_obj.is_anime and not cur_show_obj.paused:
            xem_list = 'xem'
            break

    if should_refresh(xem_list):
        for tvid in [
                i for i in sickbeard.TVInfoAPI().sources
                if 'xem_origin' in sickbeard.TVInfoAPI(i).config
        ]:
            logger.log(u'Checking for XEM scene exception updates for %s' %
                       sickbeard.TVInfoAPI(tvid).name)

            url = 'http://thexem.de/map/allNames?origin=%s%s&seasonNumbers=1'\
                  % (sickbeard.TVInfoAPI(tvid).config['xem_origin'], ('&language=us', '')['xem' == xem_list])

            parsed_json = helpers.get_url(url, parse_json=True, timeout=90)
            if not parsed_json:
                logger.log(
                    u'Check scene exceptions update failed for %s, Unable to get URL: %s'
                    % (sickbeard.TVInfoAPI(tvid).name, url), logger.ERROR)
                continue

            if 'failure' == parsed_json['result']:
                continue

            for prodid, names in iteritems(parsed_json['data']):
                try:
                    xem_exception_dict[(tvid, int(prodid))] = names
                except (BaseException, Exception):
                    continue

        set_last_refresh(xem_list)

    return xem_exception_dict
Example #10
0
    def _season_strings(self,
                        ep_obj  # type: TVEpisode
                        ):  # type: (...) -> List[Dict[AnyStr, List[AnyStr]]]
        """

        :param ep_obj: episode object
        :return:
        """
        search_params = []
        base_params = {}

        # season
        ep_detail = None
        if ep_obj.show_obj.air_by_date or ep_obj.show_obj.is_sports:
            airdate = str(ep_obj.airdate).split('-')[0]
            base_params['season'] = airdate
            base_params['q'] = airdate
            if ep_obj.show_obj.air_by_date:
                ep_detail = '+"%s"' % airdate
        elif ep_obj.show_obj.is_anime:
            base_params['season'] = '%d' % ep_obj.scene_absolute_number
        else:
            base_params['season'] = str((ep_obj.season, ep_obj.scene_season)[bool(ep_obj.show_obj.is_scene)])
            ep_detail = 'S%02d' % helpers.try_int(base_params['season'], 1)

        # id search
        params = base_params.copy()
        use_id = False
        for i in sickbeard.TVInfoAPI().all_sources:
            if i in ep_obj.show_obj.ids and 0 < ep_obj.show_obj.ids[i]['id'] and i in self.caps:
                params[self.caps[i]] = ep_obj.show_obj.ids[i]['id']
                use_id = True
        use_id and search_params.append(params)

        spacer = 'nzbgeek.info' in self.url.lower() and ' ' or '.'
        # query search and exceptions
        name_exceptions = get_show_names(ep_obj, spacer)
        for cur_exception in name_exceptions:
            params = base_params.copy()
            if 'q' in params:
                params['q'] = '%s%s%s' % (cur_exception, spacer, params['q'])
                search_params.append(params)

            if ep_detail:
                params = base_params.copy()
                params['q'] = '%s%s%s' % (cur_exception, spacer, ep_detail)
                'season' in params and params.pop('season')
                'ep' in params and params.pop('ep')
                search_params.append(params)

        return [{'Season': search_params}]
Example #11
0
    def _valid_show(fetched_show_info, show_obj):
        # type: (Dict, sickbeard.tv.TVShow) -> bool
        """
        Test the integrity of fetched show data

        :param fetched_show_info: the object returned from the tvinfo source
        :param show_obj: Show that the fetched data relates to
        :return: True if fetched_show_obj is valid data otherwise False
        """
        if not (isinstance(fetched_show_info, dict) and
                isinstance(getattr(fetched_show_info, 'data', None), (list, dict)) and
                'seriesname' in getattr(fetched_show_info, 'data', [])):
            logger.log(u'Show %s not found on %s ' %
                       (show_obj.name, sickbeard.TVInfoAPI(show_obj.tvid).name), logger.WARNING)
            return False
        return True
Example #12
0
def get_tvmaze_by_name(showname, premiere_date):
    """

    :param showname: show name
    :type showname: AnyStr
    :param premiere_date: premiere date
    :type premiere_date: datetime.date
    :return:
    :rtype: Dict
    """
    ids = {}
    try:
        url = '%ssearch/shows?%s' % (
            sickbeard.TVInfoAPI(TVINFO_TVMAZE).config['base_url'],
            urlencode({'q': clean_show_name(showname)}))
        res = get_tvmaze_data(url=url,
                              parse_json=True,
                              raise_status_code=True,
                              timeout=120)
        if res:
            for r in res:
                if 'show' in r and 'premiered' in r[
                        'show'] and 'externals' in r['show']:
                    premiered = parse(r['show']['premiered'], fuzzy=True)
                    if abs(premiere_date -
                           premiered.date()) < datetime.timedelta(days=2):
                        ids[TVINFO_TVRAGE] = r['show']['externals'].get(
                            'tvrage', 0)
                        ids[TVINFO_TVDB] = r['show']['externals'].get(
                            'thetvdb', 0)
                        ids[TVINFO_IMDB] = try_int(
                            str(r['show']['externals'].get('imdb')).replace(
                                'tt', ''))
                        ids[TVINFO_TVMAZE] = r['show'].get('id', 0)
                        break
    except (BaseException, Exception):
        pass
    return {k: v for k, v in iteritems(ids) if v not in (None, '', 0)}
Example #13
0
def fix_xem_numbering(tvid, prodid):
    """

    :param tvid: tvid
    :type tvid: int
    :param prodid: prodid
    :type prodid: int or long
    """
    if None is prodid:
        return {}

    tvid, prodid = int(tvid), int(prodid)

    my_db = db.DBConnection()
    rows = my_db.select(
        'SELECT season, episode, absolute_number, scene_season, scene_episode, scene_absolute_number'
        ' FROM tv_episodes'
        ' WHERE indexer = ? AND showid = ?', [tvid, prodid])

    last_absolute_number = None
    last_scene_season = None
    last_scene_episode = None
    last_scene_absolute_number = None

    update_absolute_number = False
    update_scene_season = False
    update_scene_episode = False
    update_scene_absolute_number = False

    logger.log(
        u'Fixing any XEM scene mapping issues for show %s on %s' %
        (prodid, sickbeard.TVInfoAPI(tvid).name), logger.DEBUG)

    cl = []
    for row in rows:
        season = int(row['season'])
        episode = int(row['episode'])

        if not int(row['scene_season']) and last_scene_season:
            scene_season = last_scene_season + 1
            update_scene_season = True
        else:
            scene_season = int(row['scene_season'])
            if last_scene_season and scene_season < last_scene_season:
                scene_season = last_scene_season + 1
                update_scene_season = True

        if not int(row['scene_episode']) and last_scene_episode:
            scene_episode = last_scene_episode + 1
            update_scene_episode = True
        else:
            scene_episode = int(row['scene_episode'])
            if last_scene_episode and scene_episode < last_scene_episode:
                scene_episode = last_scene_episode + 1
                update_scene_episode = True

        # check for unset values and correct them
        if not int(row['absolute_number']) and last_absolute_number:
            absolute_number = last_absolute_number + 1
            update_absolute_number = True
        else:
            absolute_number = int(row['absolute_number'])
            if last_absolute_number and absolute_number < last_absolute_number:
                absolute_number = last_absolute_number + 1
                update_absolute_number = True

        if not int(
                row['scene_absolute_number']) and last_scene_absolute_number:
            scene_absolute_number = last_scene_absolute_number + 1
            update_scene_absolute_number = True
        else:
            scene_absolute_number = int(row['scene_absolute_number'])
            if last_scene_absolute_number and scene_absolute_number < last_scene_absolute_number:
                scene_absolute_number = last_scene_absolute_number + 1
                update_scene_absolute_number = True

        # store values for lookup on next iteration
        last_absolute_number = absolute_number
        last_scene_season = scene_season
        last_scene_episode = scene_episode
        last_scene_absolute_number = scene_absolute_number

        if update_absolute_number:
            cl.append([
                'UPDATE tv_episodes'
                ' SET absolute_number = ?'
                ' WHERE indexer = ? AND showid = ?'
                ' AND season = ? AND episode = ?',
                [absolute_number, tvid, prodid, season, episode]
            ])
            update_absolute_number = False

        if update_scene_season:
            cl.append([
                'UPDATE tv_episodes'
                ' SET scene_season = ?'
                ' WHERE indexer = ? AND showid = ?'
                ' AND season = ? AND episode = ?',
                [scene_season, tvid, prodid, season, episode]
            ])
            update_scene_season = False

        if update_scene_episode:
            cl.append([
                'UPDATE tv_episodes'
                ' SET scene_episode = ?'
                ' WHERE indexer = ? AND showid = ?'
                ' AND season = ? AND episode = ?',
                [scene_episode, tvid, prodid, season, episode]
            ])
            update_scene_episode = False

        if update_scene_absolute_number:
            cl.append([
                'UPDATE tv_episodes'
                ' SET scene_absolute_number = ?'
                ' WHERE indexer = ? AND showid = ?'
                ' AND season = ? AND episode = ?',
                [scene_absolute_number, tvid, prodid, season, episode]
            ])
            update_scene_absolute_number = False

    if 0 < len(cl):
        my_db = db.DBConnection()
        my_db.mass_action(cl)
Example #14
0
    def run(self):

        ShowQueueItem.run(self)

        logger.log('Starting to add show %s' % self.showDir)
        # make sure the TV info source IDs are valid
        try:

            tvinfo_config = sickbeard.TVInfoAPI(self.tvid).api_params.copy()
            if self.lang:
                tvinfo_config['language'] = self.lang

            logger.log(u'' + str(sickbeard.TVInfoAPI(self.tvid).name) + ': ' +
                       repr(tvinfo_config))

            t = sickbeard.TVInfoAPI(self.tvid).setup(**tvinfo_config)
            s = t[self.prodid, False]

            if getattr(t, 'show_not_found', False):
                logger.log(
                    'Show %s was not found on %s, maybe show was deleted' %
                    (self.show_name, sickbeard.TVInfoAPI(self.tvid).name),
                    logger.ERROR)
                self._finishEarly()
                return

            # this usually only happens if they have an NFO in their show dir
            # which gave us a TV info source ID that has no proper english version of the show
            if None is getattr(s, 'seriesname', None):
                logger.log(
                    'Show in %s has no name on %s, probably the wrong language used to search with.'
                    % (self.showDir, sickbeard.TVInfoAPI(self.tvid).name),
                    logger.ERROR)
                ui.notifications.error(
                    'Unable to add show',
                    'Show in %s has no name on %s, probably the wrong language.'
                    ' Delete .nfo and add manually in the correct language.' %
                    (self.showDir, sickbeard.TVInfoAPI(self.tvid).name))
                self._finishEarly()
                return
        except (BaseException, Exception):
            logger.log(
                'Unable to find show ID:%s on TV info: %s' %
                (self.prodid, sickbeard.TVInfoAPI(self.tvid).name),
                logger.ERROR)
            ui.notifications.error(
                'Unable to add show',
                'Unable to look up the show in %s on %s using ID %s, not using the NFO.'
                ' Delete .nfo and try adding manually again.' %
                (self.showDir, sickbeard.TVInfoAPI(
                    self.tvid).name, self.prodid))
            self._finishEarly()
            return

        try:
            new_show_obj = TVShow(self.tvid, self.prodid, self.lang)
            new_show_obj.load_from_tvinfo()

            self.show_obj = new_show_obj

            # set up initial values
            self.show_obj.location = self.showDir
            self.show_obj.subtitles = self.subtitles if None is not self.subtitles else sickbeard.SUBTITLES_DEFAULT
            self.show_obj.quality = self.quality if self.quality else sickbeard.QUALITY_DEFAULT
            self.show_obj.upgrade_once = self.upgrade_once
            self.show_obj.flatten_folders = self.flatten_folders if None is not self.flatten_folders \
                else sickbeard.FLATTEN_FOLDERS_DEFAULT
            self.show_obj.anime = self.anime if None is not self.anime else sickbeard.ANIME_DEFAULT
            self.show_obj.scene = self.scene if None is not self.scene else sickbeard.SCENE_DEFAULT
            self.show_obj.paused = self.paused if None is not self.paused else False
            self.show_obj.prune = self.prune if None is not self.prune else 0
            self.show_obj.tag = self.tag if None is not self.tag else 'Show List'

            if self.show_obj.anime:
                self.show_obj.release_groups = BlackAndWhiteList(
                    self.show_obj.tvid, self.show_obj.prodid,
                    self.show_obj.tvid_prodid)
                if self.blacklist:
                    self.show_obj.release_groups.set_black_keywords(
                        self.blacklist)
                if self.whitelist:
                    self.show_obj.release_groups.set_white_keywords(
                        self.whitelist)

            # be smartish about this
            if self.show_obj.genre and 'talk show' in self.show_obj.genre.lower(
            ):
                self.show_obj.air_by_date = 1
            if self.show_obj.genre and 'documentary' in self.show_obj.genre.lower(
            ):
                self.show_obj.air_by_date = 0
            if self.show_obj.classification and 'sports' in self.show_obj.classification.lower(
            ):
                self.show_obj.sports = 1

        except Exception as e:
            if check_exception_type(e, ExceptionTuples.tvinfo_exception):
                logger.log(
                    'Unable to add show due to an error with %s: %s' %
                    (sickbeard.TVInfoAPI(self.tvid).name, ex(e)), logger.ERROR)
                if self.show_obj:
                    ui.notifications.error(
                        'Unable to add %s due to an error with %s' %
                        (self.show_obj.name, sickbeard.TVInfoAPI(
                            self.tvid).name))
                else:
                    ui.notifications.error(
                        'Unable to add show due to an error with %s' %
                        sickbeard.TVInfoAPI(self.tvid).name)
                self._finishEarly()
                return

            elif check_exception_type(
                    e, exceptions_helper.MultipleShowObjectsException):
                logger.log(
                    'The show in %s is already in your show list, skipping' %
                    self.showDir, logger.ERROR)
                ui.notifications.error(
                    'Show skipped',
                    'The show in %s is already in your show list' %
                    self.showDir)
                self._finishEarly()
                return

            else:
                logger.log('Error trying to add show: %s' % ex(e),
                           logger.ERROR)
                logger.log(traceback.format_exc(), logger.ERROR)
                self._finishEarly()
                raise

        self.show_obj.load_imdb_info()

        try:
            self.show_obj.save_to_db()
        except (BaseException, Exception) as e:
            logger.log('Error saving the show to the database: %s' % ex(e),
                       logger.ERROR)
            logger.log(traceback.format_exc(), logger.ERROR)
            self._finishEarly()
            raise

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

        try:
            self.show_obj.load_episodes_from_tvinfo()
        except (BaseException, Exception) as e:
            logger.log(
                'Error with %s, not creating episode list: %s' %
                (sickbeard.TVInfoAPI(self.show_obj.tvid).name, ex(e)),
                logger.ERROR)
            logger.log(traceback.format_exc(), logger.ERROR)

        try:
            self.show_obj.load_episodes_from_dir()
        except (BaseException, Exception) as e:
            logger.log('Error searching directory for episodes: %s' % ex(e),
                       logger.ERROR)
            logger.log(traceback.format_exc(), logger.ERROR)

        # if they gave a custom status then change all the eps to it
        my_db = db.DBConnection()
        if self.default_status != SKIPPED:
            logger.log(
                'Setting all episodes to the specified default status: %s' %
                sickbeard.common.statusStrings[self.default_status])
            my_db.action(
                'UPDATE tv_episodes'
                ' SET status = ?'
                ' WHERE status = ?'
                ' AND indexer = ? AND showid = ?'
                ' AND season != 0', [
                    self.default_status, SKIPPED, self.show_obj.tvid,
                    self.show_obj.prodid
                ])

        items_wanted = self._get_wanted(my_db,
                                        self.default_wanted_begin,
                                        latest=False)
        items_wanted += self._get_wanted(my_db,
                                         self.default_wanted_latest,
                                         latest=True)

        self.show_obj.write_metadata()
        self.show_obj.update_metadata()
        self.show_obj.populate_cache()

        self.show_obj.flush_episodes()

        # load ids
        _ = self.show_obj.ids

        # if sickbeard.USE_TRAKT:
        #     # if there are specific episodes that need to be added by trakt
        #     sickbeard.traktCheckerScheduler.action.manageNewShow(self.show_obj)
        #
        #     # add show to trakt.tv library
        #     if sickbeard.TRAKT_SYNC:
        #         sickbeard.traktCheckerScheduler.action.addShowToTraktLibrary(self.show_obj)

        # Load XEM data to DB for show
        sickbeard.scene_numbering.xem_refresh(self.show_obj.tvid,
                                              self.show_obj.prodid,
                                              force=True)
        if self.show_obj.scene:
            # enable/disable scene flag based on if show has an explicit _scene_ mapping at XEM
            self.show_obj.scene = sickbeard.scene_numbering.has_xem_scene_mapping(
                self.show_obj.tvid, self.show_obj.prodid)
        # if "scene" numbering is disabled during add show, output availability to log
        if None is not self.scene and not self.show_obj.scene and \
                self.show_obj.prodid in sickbeard.scene_exceptions.xem_ids_list[self.show_obj.tvid]:
            logger.log(
                'No scene number mappings found at TheXEM. Therefore, episode scene numbering disabled, '
                'edit show and enable it to manually add custom numbers for search and media processing.'
            )
        try:
            self.show_obj.save_to_db()
        except (BaseException, Exception) as e:
            logger.log('Error saving the show to the database: %s' % ex(e),
                       logger.ERROR)
            logger.log(traceback.format_exc(), logger.ERROR)
            self._finishEarly()
            raise

        # update internal name cache
        name_cache.buildNameCache(self.show_obj)

        self.show_obj.load_episodes_from_db()

        if self.show_obj.tvid in (TVINFO_TVRAGE, TVINFO_TVDB):
            # noinspection SqlResolve
            oh = my_db.select(
                'SELECT resource FROM history WHERE indexer = 0 AND showid = ?',
                [self.show_obj.prodid])
            if oh:
                found = False
                for o in oh:
                    np = NameParser(file_name=True,
                                    indexer_lookup=False,
                                    try_scene_exceptions=True)
                    try:
                        pr = np.parse(o['resource'])
                    except (BaseException, Exception):
                        continue
                    if pr.show_obj.tvid == self.show_obj.tvid and pr.show_obj.prodid == self.show_obj.prodid:
                        found = True
                        break
                if found:
                    my_db.action(
                        'UPDATE history SET indexer = ? WHERE indexer = 0 AND showid = ?',
                        [self.show_obj.tvid, self.show_obj.prodid])

        msg = ' the specified show into ' + self.showDir
        # if started with WANTED eps then run the backlog
        if WANTED == self.default_status or items_wanted:
            logger.log(
                'Launching backlog for this show since episodes are WANTED')
            sickbeard.backlogSearchScheduler.action.search_backlog(
                [self.show_obj])
            ui.notifications.message(
                'Show added/search',
                'Adding and searching for episodes of' + msg)
        else:
            ui.notifications.message('Show added', 'Adding' + msg)

        self.finish()
Example #15
0
def xem_refresh(tvid, prodid, force=False):
    """
    Refresh data from xem for a tv show

    :param tvid:
    :type tvid: int
    :param prodid:
    :type prodid: int
    :param force:
    :type force: bool
    """
    if None is prodid:
        return

    tvid, prodid = int(tvid), int(prodid)
    tvinfo = sickbeard.TVInfoAPI(tvid)

    if 'xem_origin' not in tvinfo.config or prodid not in xem_ids_list.get(
            tvid, []):
        return

    xem_origin = tvinfo.config['xem_origin']

    # XEM API URL
    url = 'http://thexem.de/map/all?id=%s&origin=%s&destination=scene' % (
        prodid, xem_origin)

    max_refresh_age_secs = 86400  # 1 day

    my_db = db.DBConnection()
    rows = my_db.select(
        'SELECT last_refreshed'
        ' FROM xem_refresh'
        ' WHERE indexer = ? AND indexer_id = ?', [tvid, prodid])
    if rows:
        last_refresh = int(rows[0]['last_refreshed'])
        refresh = int(time.mktime(datetime.datetime.today().timetuple())
                      ) > last_refresh + max_refresh_age_secs
    else:
        refresh = True

    if refresh or force:
        logger.log(
            u'Looking up XEM scene mapping for show %s on %s' %
            (prodid, tvinfo.name), logger.DEBUG)

        # mark refreshed
        my_db.upsert(
            'xem_refresh',
            dict(last_refreshed=int(
                time.mktime(datetime.datetime.today().timetuple()))),
            dict(indexer=tvid, indexer_id=prodid))

        try:
            parsed_json = sickbeard.helpers.get_url(url,
                                                    parse_json=True,
                                                    timeout=90)
            if not parsed_json or '' == parsed_json:
                logger.log(
                    u'No XEM data for show %s on %s' % (prodid, tvinfo.name),
                    logger.MESSAGE)
                return

            if 'success' in parsed_json['result']:
                cl = map_list(
                    lambda entry: [
                        'UPDATE tv_episodes'
                        ' SET scene_season = ?, scene_episode = ?, scene_absolute_number = ?'
                        ' WHERE indexer = ? AND showid = ?'
                        ' AND season = ? AND episode = ?',
                        [
                            entry.get('scene%s' %
                                      ('', '_2')['scene_2' in entry]).get(v)
                            for v in ('season', 'episode', 'absolute')
                        ] + [tvid, prodid] + [
                            entry.get(xem_origin).get(v)
                            for v in ('season', 'episode')
                        ]
                    ], filter_iter(lambda x: 'scene' in x,
                                   parsed_json['data']))

                if 0 < len(cl):
                    my_db = db.DBConnection()
                    my_db.mass_action(cl)
            else:
                logger.log(
                    u'Empty lookup result - no XEM data for show %s on %s' %
                    (prodid, tvinfo.name), logger.DEBUG)
        except (BaseException, Exception) as e:
            logger.log(
                u'Exception refreshing XEM data for show ' + str(prodid) +
                ' on ' + tvinfo.name + ': ' + ex(e), logger.WARNING)
            logger.log(traceback.format_exc(), logger.ERROR)
Example #16
0
    def _show_data(self, show_obj):
        # type: (sickbeard.tv.TVShow) -> Optional[Union[bool, etree.Element]]
        """
        Creates an elementTree XML structure for a Kodi-style tvshow.nfo and
        returns the resulting data object.

        show_obj: a TVShow instance to create the NFO for
        """

        show_ID = show_obj.prodid

        show_lang = show_obj.lang
        tvinfo_config = sickbeard.TVInfoAPI(show_obj.tvid).api_params.copy()

        tvinfo_config['actors'] = True

        if show_lang and not 'en' == show_lang:
            tvinfo_config['language'] = show_lang

        if 0 != show_obj.dvdorder:
            tvinfo_config['dvdorder'] = True

        t = sickbeard.TVInfoAPI(show_obj.tvid).setup(**tvinfo_config)

        tv_node = etree.Element('tvshow')

        try:
            show_info = t[int(show_ID)]
        except BaseTVinfoShownotfound as e:
            logger.log(
                'Unable to find show with id %s on %s, skipping it' %
                (show_ID, sickbeard.TVInfoAPI(show_obj.tvid).name),
                logger.ERROR)
            raise e
        except BaseTVinfoError as e:
            logger.log(
                '%s is down, can\'t use its data to add this show' %
                sickbeard.TVInfoAPI(show_obj.tvid).name, logger.ERROR)
            raise e

        if not self._valid_show(show_info, show_obj):
            return

        # check for title and id
        if None is getattr(show_info, 'seriesname', None) or None is getattr(
                show_info, 'id', None):
            logger.log(
                'Incomplete info for show with id %s on %s, skipping it' %
                (show_ID, sickbeard.TVInfoAPI(show_obj.tvid).name),
                logger.ERROR)
            return False

        title = etree.SubElement(tv_node, 'title')
        if None is not getattr(show_info, 'seriesname', None):
            title.text = '%s' % show_info['seriesname']

        # year = etree.SubElement(tv_node, 'year')
        premiered = etree.SubElement(tv_node, 'premiered')
        premiered_text = self.get_show_year(show_obj,
                                            show_info,
                                            year_only=False)
        if premiered_text:
            premiered.text = '%s' % premiered_text

        has_id = False
        tvdb_id = None
        for tvid, slug in map_iter(
                lambda _tvid:
            (_tvid, sickbeard.TVInfoAPI(_tvid).config.get('kodi_slug')),
                list(sickbeard.TVInfoAPI().all_sources)):
            mid = slug and show_obj.ids[tvid].get('id')
            if mid:
                has_id = True
                kwargs = dict(type=slug)
                if TVINFO_TVDB == tvid:
                    kwargs.update(dict(default='true'))
                    tvdb_id = str(mid)
                uniqueid = etree.SubElement(tv_node, 'uniqueid', **kwargs)
                uniqueid.text = '%s%s' % (('', 'tt')[TVINFO_IMDB == tvid], mid)
        if not has_id:
            logger.log(
                'Incomplete info for show with id %s on %s, skipping it' %
                (show_ID, sickbeard.TVInfoAPI(show_obj.tvid).name),
                logger.ERROR)
            return False

        ratings = etree.SubElement(tv_node, 'ratings')
        if None is not getattr(show_info, 'rating', None):
            # todo: name dynamic depending on source
            rating = etree.SubElement(ratings,
                                      'rating',
                                      name='thetvdb',
                                      max='10')
            rating_value = etree.SubElement(rating, 'value')
            rating_value.text = '%s' % show_info['rating']
            if None is not getattr(show_info, 'siteratingcount', None):
                ratings_votes = etree.SubElement(rating, 'votes')
                ratings_votes.text = '%s' % show_info['siteratingcount']

        plot = etree.SubElement(tv_node, 'plot')
        if None is not getattr(show_info, 'overview', None):
            plot.text = '%s' % show_info['overview']

        episodeguide = etree.SubElement(tv_node, 'episodeguide')
        episodeguideurl = etree.SubElement(episodeguide,
                                           'url',
                                           post='yes',
                                           cache='auth.json')
        if tvdb_id:
            episodeguideurl.text = sickbeard.TVInfoAPI(
                TVINFO_TVDB).config['epg_url'].replace('{MID}', tvdb_id)

        mpaa = etree.SubElement(tv_node, 'mpaa')
        if None is not getattr(show_info, 'contentrating', None):
            mpaa.text = '%s' % show_info['contentrating']

        genre = etree.SubElement(tv_node, 'genre')
        if None is not getattr(show_info, 'genre', None):
            if isinstance(show_info['genre'], string_types):
                genre.text = ' / '.join([
                    x.strip() for x in show_info['genre'].split('|')
                    if x.strip()
                ])

        studio = etree.SubElement(tv_node, 'studio')
        if None is not getattr(show_info, 'network', None):
            studio.text = '%s' % show_info['network']

        self.add_actor_element(show_info, etree, tv_node)

        # Make it purdy
        sg_helpers.indent_xml(tv_node)

        # output valid xml
        # data = etree.ElementTree(tv_node)
        # output non valid xml that Kodi accepts
        data = decode_str(etree.tostring(tv_node))
        parts = data.split('episodeguide')
        if 3 == len(parts):
            data = 'episodeguide'.join(
                [parts[0], parts[1].replace('&amp;quot;', '&quot;'), parts[2]])

        return data
Example #17
0
    def run(self):

        ShowQueueItem.run(self)
        last_update = datetime.date.fromordinal(
            self.show_obj.last_update_indexer)

        if not sickbeard.TVInfoAPI(self.show_obj.tvid).config['active']:
            logger.log(
                'TV info source %s is marked inactive, aborting update for show %s and continue with refresh.'
                % (sickbeard.TVInfoAPI(
                    self.show_obj.tvid).config['name'], self.show_obj.name))
            sickbeard.showQueueScheduler.action.refreshShow(
                self.show_obj,
                self.force,
                self.scheduled_update,
                after_update=True)
            return

        logger.log('Beginning update of %s' % self.show_obj.name)

        logger.log(
            'Retrieving show info from %s' %
            sickbeard.TVInfoAPI(self.show_obj.tvid).name, logger.DEBUG)
        try:
            result = self.show_obj.load_from_tvinfo(cache=not self.force)
            if None is not result:
                return
        except Exception as e:
            if check_exception_type(e, ExceptionTuples.tvinfo_error):
                logger.log(
                    'Unable to contact %s, aborting: %s' %
                    (sickbeard.TVInfoAPI(self.show_obj.tvid).name, ex(e)),
                    logger.WARNING)
                return
            elif check_exception_type(
                    e, ExceptionTuples.tvinfo_attributenotfound):
                logger.log(
                    'Data retrieved from %s was incomplete, aborting: %s' %
                    (sickbeard.TVInfoAPI(self.show_obj.tvid).name, ex(e)),
                    logger.ERROR)
                return
            else:
                raise e

        if self.force_web:
            self.show_obj.load_imdb_info()

        try:
            self.show_obj.save_to_db()
        except (BaseException, Exception) as e:
            logger.log('Error saving the show to the database: %s' % ex(e),
                       logger.ERROR)
            logger.log(traceback.format_exc(), logger.ERROR)

        # get episode list from DB
        logger.log('Loading all episodes from the database', logger.DEBUG)
        db_ep_obj_list = self.show_obj.load_episodes_from_db(update=True)

        # get episode list from TVDB
        logger.log(
            'Loading all episodes from %s' %
            sickbeard.TVInfoAPI(self.show_obj.tvid).name, logger.DEBUG)
        try:
            tvinfo_ep_list = self.show_obj.load_episodes_from_tvinfo(
                cache=not self.force, update=True)
        except Exception as e:
            if check_exception_type(e, ExceptionTuples.tvinfo_exception):
                logger.log(
                    'Unable to get info from %s, the show info will not be refreshed: %s'
                    % (sickbeard.TVInfoAPI(self.show_obj.tvid).name, ex(e)),
                    logger.ERROR)
                tvinfo_ep_list = None
            else:
                raise e

        if None is tvinfo_ep_list:
            logger.log(
                'No data returned from %s, unable to update episodes for show: %s'
                % (sickbeard.TVInfoAPI(
                    self.show_obj.tvid).name, self.show_obj.name),
                logger.ERROR)
        elif not tvinfo_ep_list or 0 == len(tvinfo_ep_list):
            logger.log(
                'No episodes returned from %s for show: %s' %
                (sickbeard.TVInfoAPI(
                    self.show_obj.tvid).name, self.show_obj.name),
                logger.WARNING)
        else:
            # for each ep we found on TVDB delete it from the DB list
            for cur_season in tvinfo_ep_list:
                for cur_episode in tvinfo_ep_list[cur_season]:
                    logger.log(
                        'Removing %sx%s from the DB list' %
                        (cur_season, cur_episode), logger.DEBUG)
                    if cur_season in db_ep_obj_list and cur_episode in db_ep_obj_list[
                            cur_season]:
                        del db_ep_obj_list[cur_season][cur_episode]

            # for the remaining episodes in the DB list just delete them from the DB
            for cur_season in db_ep_obj_list:
                for cur_episode in db_ep_obj_list[cur_season]:
                    ep_obj = self.show_obj.get_episode(cur_season, cur_episode)
                    status = sickbeard.common.Quality.splitCompositeStatus(
                        ep_obj.status)[0]
                    if should_delete_episode(status):
                        logger.log(
                            'Permanently deleting episode %sx%s from the database'
                            % (cur_season, cur_episode), logger.MESSAGE)
                        try:
                            ep_obj.delete_episode()
                        except exceptions_helper.EpisodeDeletedException:
                            pass
                    else:
                        logger.log(
                            'Not deleting episode %sx%s from the database because status is: %s'
                            % (cur_season, cur_episode, statusStrings[status]),
                            logger.MESSAGE)

            # update indexer mapper once a month (using the day of the first ep as random date)
            update_over_month = (datetime.date.today() - last_update).days > 31
            if (self.scheduled_update
                    or update_over_month) and tvinfo_ep_list.get(1, {}).get(
                        1, False):
                first_ep_airdate = self.show_obj.get_episode(
                    1, 1, no_create=True).airdate
                day = (first_ep_airdate.day, 28)[28 < first_ep_airdate.day]
                if datetime.date.today().day == day or update_over_month or \
                        -8 < (datetime.date.today() - first_ep_airdate).days < 31:
                    map_indexers_to_show(self.show_obj, force=True)

        if self.priority != generic_queue.QueuePriorities.NORMAL:
            self.kwargs['priority'] = self.priority
        sickbeard.showQueueScheduler.action.refreshShow(
            self.show_obj,
            self.force,
            self.scheduled_update,
            after_update=True,
            force_image_cache=self.force_web,
            **self.kwargs)
Example #18
0
    def _parse_string(self, name):
        # type: (AnyStr) -> Optional[ParseResult]
        """

        :param name: name to parse
        :type name: AnyStr
        :return:
        :rtype: ParseResult or None
        """
        if not name:
            return

        matches = []
        initial_best_result = None
        for reg_ex in self.compiled_regexes:
            for (cur_regex_num, cur_regex_name, cur_regex) in self.compiled_regexes[reg_ex]:
                new_name = helpers.remove_non_release_groups(name, 'anime' in cur_regex_name)
                match = cur_regex.match(new_name)

                if not match:
                    continue

                if 'garbage_name' == cur_regex_name:
                    return

                result = ParseResult(new_name)
                result.which_regex = [cur_regex_name]
                result.score = 0 - cur_regex_num

                named_groups = list_keys(match.groupdict())

                if 'series_name' in named_groups:
                    result.series_name = match.group('series_name')
                    if result.series_name:
                        result.series_name = self.clean_series_name(result.series_name)
                        name_parts = re.match(r'(?i)(.*)[ -]((?:part|pt)[ -]?\w+)$', result.series_name)
                        try:
                            result.series_name = name_parts.group(1)
                            result.extra_info = name_parts.group(2)
                        except (AttributeError, IndexError):
                            pass

                        result.score += 1

                if 'anime' in cur_regex_name and not (self.show_obj and self.show_obj.is_anime):
                    p_show_obj = helpers.get_show(result.series_name, True)
                    if p_show_obj and self.show_obj and not (p_show_obj.tvid == self.show_obj.tvid and
                                                             p_show_obj.prodid == self.show_obj.prodid):
                        p_show_obj = None
                    if not p_show_obj and self.show_obj:
                        p_show_obj = self.show_obj
                    if p_show_obj and not p_show_obj.is_anime:
                        continue

                if 'series_num' in named_groups and match.group('series_num'):
                    result.score += 1

                if 'season_num' in named_groups:
                    tmp_season = int(match.group('season_num'))
                    if 'bare' == cur_regex_name and tmp_season in (19, 20):
                        continue
                    result.season_number = tmp_season
                    result.score += 1

                def _process_epnum(captures, capture_names, grp_name, extra_grp_name, ep_numbers, parse_result):
                    ep_num = self._convert_number(captures.group(grp_name))
                    extra_grp_name = 'extra_%s' % extra_grp_name
                    ep_numbers = '%sepisode_numbers' % ep_numbers
                    if extra_grp_name in capture_names and captures.group(extra_grp_name):
                        try:
                            if hasattr(self.show_obj, 'get_episode'):
                                _ep_obj = self.show_obj.get_episode(parse_result.season_number, ep_num)
                            else:
                                tmp_show_obj = helpers.get_show(parse_result.series_name, True)
                                if tmp_show_obj and hasattr(tmp_show_obj, 'get_episode'):
                                    _ep_obj = tmp_show_obj.get_episode(parse_result.season_number, ep_num)
                                else:
                                    _ep_obj = None
                        except (BaseException, Exception):
                            _ep_obj = None
                        en = _ep_obj and _ep_obj.name and re.match(r'^\W*(\d+)', _ep_obj.name) or None
                        es = en and en.group(1) or None

                        extra_ep_num = self._convert_number(captures.group(extra_grp_name))
                        parse_result.__dict__[ep_numbers] = list_range(ep_num, extra_ep_num + 1) if not (
                            _ep_obj and es and es != captures.group(extra_grp_name)) and (
                            0 < extra_ep_num - ep_num < 10) else [ep_num]
                        parse_result.score += 1
                    else:
                        parse_result.__dict__[ep_numbers] = [ep_num]
                    parse_result.score += 1
                    return parse_result

                if 'ep_num' in named_groups:
                    result = _process_epnum(match, named_groups, 'ep_num', 'ep_num', '', result)

                if 'ep_ab_num' in named_groups:
                    result = _process_epnum(match, named_groups, 'ep_ab_num', 'ab_ep_num', 'ab_', result)

                if 'air_year' in named_groups and 'air_month' in named_groups and 'air_day' in named_groups:
                    year = int(match.group('air_year'))
                    try:
                        month = int(match.group('air_month'))
                    except ValueError:
                        try:
                            month = time.strptime(match.group('air_month')[0:3], '%b').tm_mon
                        except ValueError as e:
                            raise InvalidNameException(ex(e))
                    day = int(match.group('air_day'))
                    # make an attempt to detect YYYY-DD-MM formats
                    if 12 < month:
                        tmp_month = month
                        month = day
                        day = tmp_month
                    try:
                        result.air_date = datetime.date(
                            year + ((1900, 2000)[0 < year < 28], 0)[1900 < year], month, day)
                    except ValueError as e:
                        raise InvalidNameException(ex(e))

                if 'extra_info' in named_groups:
                    tmp_extra_info = match.group('extra_info')

                    # Show.S04.Special or Show.S05.Part.2.Extras is almost certainly not every episode in the season
                    if tmp_extra_info and 'season_only' == cur_regex_name and re.search(
                            r'([. _-]|^)(special|extra)s?\w*([. _-]|$)', tmp_extra_info, re.I):
                        continue
                    if tmp_extra_info:
                        if result.extra_info:
                            tmp_extra_info = '%s %s' % (result.extra_info, tmp_extra_info)
                        result.extra_info = tmp_extra_info
                    result.score += 1

                if 'release_group' in named_groups:
                    result.release_group = match.group('release_group')
                    result.score += 1

                if 'version' in named_groups:
                    # assigns version to anime file if detected using anime regex. Non-anime regex receives -1
                    version = match.group('version')
                    if version:
                        result.version = helpers.try_int(version)
                    else:
                        result.version = 1
                else:
                    result.version = -1

                if None is result.season_number and result.episode_numbers and not result.air_date and \
                        cur_regex_name in ['no_season', 'no_season_general', 'no_season_multi_ep'] and \
                        re.search(r'(?i)\bpart.?\d{1,2}\b', result.original_name):
                    result.season_number = 1

                matches.append(result)

            if len(matches):
                # pick best match with highest score based on placement
                best_result = max(sorted(matches, reverse=True, key=lambda x: x.which_regex), key=lambda x: x.score)

                show_obj = None
                if not self.naming_pattern:
                    # try and create a show object for this result
                    show_obj = helpers.get_show(best_result.series_name, self.try_scene_exceptions)

                # confirm passed in show object tvid_prodid matches result show object tvid_prodid
                if show_obj and not self.testing:
                    if self.show_obj and show_obj.tvid_prodid != self.show_obj.tvid_prodid:
                        show_obj = None
                elif not show_obj and self.show_obj:
                    show_obj = self.show_obj
                best_result.show_obj = show_obj
                if not best_result.series_name and getattr(show_obj, 'name', None):
                    best_result.series_name = show_obj.name

                if show_obj and show_obj.is_anime and 1 < len(self.compiled_regexes[1]) and 1 != reg_ex:
                    continue

                # if this is a naming pattern test then return best result
                if not show_obj or self.naming_pattern:
                    if not show_obj and not self.naming_pattern and not self.testing:
                        # ensure anime regex test but use initial best if show still not found
                        if 0 == reg_ex:
                            initial_best_result = best_result
                            matches = []  # clear non-anime match scores
                            continue
                        return initial_best_result
                    return best_result

                # get quality
                new_name = helpers.remove_non_release_groups(name, show_obj.is_anime)
                best_result.quality = common.Quality.nameQuality(new_name, show_obj.is_anime)

                new_episode_numbers = []
                new_season_numbers = []
                new_absolute_numbers = []

                # if we have an air-by-date show then get the real season/episode numbers
                if best_result.is_air_by_date:
                    season_number, episode_numbers = None, []

                    airdate = best_result.air_date.toordinal()
                    my_db = db.DBConnection()
                    sql_result = my_db.select(
                        'SELECT season, episode, name'
                        ' FROM tv_episodes'
                        ' WHERE indexer = ? AND showid = ?'
                        ' AND airdate = ?',
                        [show_obj.tvid, show_obj.prodid, airdate])

                    if sql_result:
                        season_number = int(sql_result[0]['season'])
                        episode_numbers = [int(sql_result[0]['episode'])]
                        if 1 < len(sql_result):
                            # multi-eps broadcast on this day
                            nums = {'1': 'one', '2': 'two', '3': 'three', '4': 'four', '5': 'five',
                                    '6': 'six', '7': 'seven', '8': 'eight', '9': 'nine', '10': 'ten'}
                            patt = '(?i)(?:e(?:p(?:isode)?)?|part|pt)[. _-]?(%s)'
                            try:
                                src_num = str(re.findall(patt % r'\w+', best_result.extra_info)[0])
                                alt_num = nums.get(src_num) or list(iterkeys(nums))[
                                    list(itervalues(nums)).index(src_num)]
                                re_partnum = re.compile(patt % ('%s|%s' % (src_num, alt_num)))
                                for ep_details in sql_result:
                                    if re_partnum.search(ep_details['name']):
                                        season_number = int(ep_details['season'])
                                        episode_numbers = [int(ep_details['episode'])]
                                        break
                            except (BaseException, Exception):
                                pass

                    if self.indexer_lookup and not season_number or not len(episode_numbers):
                        try:
                            tvinfo_config = sickbeard.TVInfoAPI(show_obj.tvid).api_params.copy()

                            if show_obj.lang:
                                tvinfo_config['language'] = show_obj.lang

                            t = sickbeard.TVInfoAPI(show_obj.tvid).setup(**tvinfo_config)

                            ep_obj = t[show_obj.prodid].aired_on(best_result.air_date)[0]

                            season_number = int(ep_obj['seasonnumber'])
                            episode_numbers = [int(ep_obj['episodenumber'])]
                        except BaseTVinfoEpisodenotfound as e:
                            logger.log(u'Unable to find episode with date ' + str(best_result.air_date)
                                       + ' for show ' + show_obj.name + ', skipping', logger.WARNING)
                            episode_numbers = []
                        except BaseTVinfoError as e:
                            logger.log(u'Unable to contact ' + sickbeard.TVInfoAPI(show_obj.tvid).name
                                       + ': ' + ex(e), logger.WARNING)
                            episode_numbers = []

                    for epNo in episode_numbers:
                        s = season_number
                        e = epNo

                        if self.convert and show_obj.is_scene:
                            (s, e) = scene_numbering.get_indexer_numbering(
                                show_obj.tvid, show_obj.prodid, season_number, epNo)
                        new_episode_numbers.append(e)
                        new_season_numbers.append(s)

                elif show_obj.is_anime and len(best_result.ab_episode_numbers) and not self.testing:
                    scene_season = scene_exceptions.get_scene_exception_by_name(best_result.series_name)[2]
                    for epAbsNo in best_result.ab_episode_numbers:
                        a = epAbsNo

                        if self.convert and show_obj.is_scene:
                            a = scene_numbering.get_indexer_absolute_numbering(
                                show_obj.tvid, show_obj.prodid, epAbsNo, True, scene_season)

                        (s, e) = helpers.get_all_episodes_from_absolute_number(show_obj, [a])

                        new_absolute_numbers.append(a)
                        new_episode_numbers.extend(e)
                        new_season_numbers.append(s)

                elif best_result.season_number and len(best_result.episode_numbers) and not self.testing:
                    for epNo in best_result.episode_numbers:
                        s = best_result.season_number
                        e = epNo

                        if self.convert and show_obj.is_scene:
                            (s, e) = scene_numbering.get_indexer_numbering(
                                show_obj.tvid, show_obj.prodid, best_result.season_number, epNo)
                        if show_obj.is_anime:
                            a = helpers.get_absolute_number_from_season_and_episode(show_obj, s, e)
                            if a:
                                new_absolute_numbers.append(a)

                        new_episode_numbers.append(e)
                        new_season_numbers.append(s)

                # need to do a quick sanity check here.  It's possible that we now have episodes
                # from more than one season (by tvdb numbering), and this is just too much, so flag it.
                new_season_numbers = list(set(new_season_numbers))  # remove duplicates
                if 1 < len(new_season_numbers):
                    raise InvalidNameException('Scene numbering results episodes from '
                                               'seasons %s, (i.e. more than one) and '
                                               'SickGear does not support this.  '
                                               'Sorry.' % (str(new_season_numbers)))

                # I guess it's possible that we'd have duplicate episodes too, so lets
                # eliminate them
                new_episode_numbers = list(set(new_episode_numbers))
                new_episode_numbers.sort()

                # maybe even duplicate absolute numbers so why not do them as well
                new_absolute_numbers = list(set(new_absolute_numbers))
                new_absolute_numbers.sort()

                if len(new_absolute_numbers):
                    best_result.ab_episode_numbers = new_absolute_numbers

                if len(new_season_numbers) and len(new_episode_numbers):
                    best_result.episode_numbers = new_episode_numbers
                    best_result.season_number = new_season_numbers[0]

                if self.convert and show_obj.is_scene:
                    logger.log(u'Converted parsed result %s into %s'
                               % (best_result.original_name, decode_str(str(best_result), errors='xmlcharrefreplace')),
                               logger.DEBUG)

                helpers.cpu_sleep()

                return best_result
Example #19
0
    def _ep_data(self, ep_obj):
        # type: (sickbeard.tv.TVEpisode) -> Optional[Union[bool, AnyStr]]
        """
        Creates a key value structure for a Tivo episode metadata file and
        returns the resulting data object.

        ep_obj: a TVEpisode instance to create the metadata file for.

        Lookup the show in http://thetvdb.com/ using the python library:

        https://github.com/dbr/indexer_api/

        The results are saved in the object show_info.

        The key values for the tivo metadata file are from:

        http://pytivo.sourceforge.net/wiki/index.php/Metadata
        """

        data = ''

        ep_obj_list_to_write = [ep_obj] + ep_obj.related_ep_obj

        show_lang = ep_obj.show_obj.lang

        try:
            tvinfo_config = sickbeard.TVInfoAPI(
                ep_obj.show_obj.tvid).api_params.copy()

            tvinfo_config['actors'] = True

            if show_lang and not 'en' == show_lang:
                tvinfo_config['language'] = show_lang

            if 0 != ep_obj.show_obj.dvdorder:
                tvinfo_config['dvdorder'] = True

            t = sickbeard.TVInfoAPI(
                ep_obj.show_obj.tvid).setup(**tvinfo_config)
            show_info = t[ep_obj.show_obj.prodid]
        except BaseTVinfoShownotfound as e:
            raise exceptions_helper.ShowNotFoundException(ex(e))
        except BaseTVinfoError as e:
            logger.log(
                "Unable to connect to %s while creating meta files - skipping - %s"
                % (sickbeard.TVInfoAPI(ep_obj.show_obj.tvid).name, ex(e)),
                logger.ERROR)
            return False

        if not self._valid_show(show_info, ep_obj.show_obj):
            return

        for cur_ep_obj in ep_obj_list_to_write:

            try:
                ep_info = show_info[cur_ep_obj.season][cur_ep_obj.episode]
            except (BaseException, Exception):
                logger.log(
                    "Unable to find episode %sx%s on %s... has it been removed? Should I delete from db?"
                    % (cur_ep_obj.season, cur_ep_obj.episode,
                       sickbeard.TVInfoAPI(ep_obj.show_obj.tvid).name))
                return None

            if None is getattr(ep_info, 'firstaired',
                               None) and 0 == ep_obj.season:
                ep_info["firstaired"] = str(datetime.date.fromordinal(1))

            if None is getattr(ep_info, 'episodename',
                               None) or None is getattr(
                                   ep_info, 'firstaired', None):
                return None

            if None is not getattr(show_info, 'seriesname', None):
                data += ("title : " + show_info["seriesname"] + "\n")
                data += ("seriesTitle : " + show_info["seriesname"] + "\n")

            data += ("episodeTitle : " +
                     cur_ep_obj._format_pattern('%Sx%0E %EN') + "\n")

            # This should be entered for episodic shows and omitted for movies. The standard tivo format is to enter
            # the season number followed by the episode number for that season. For example, enter 201 for season 2
            # episode 01.

            # This only shows up if you go into the Details from the Program screen.

            # This seems to disappear once the video is transferred to TiVo.

            # NOTE: May not be correct format, missing season, but based on description from wiki leaving as is.
            data += ("episodeNumber : " + str(cur_ep_obj.episode) + "\n")

            # Must be entered as true or false. If true, the year from originalAirDate will be shown in parentheses
            # after the episode's title and before the description on the Program screen.

            # FIXME: Hardcode isEpisode to true for now, not sure how to handle movies
            data += "isEpisode : true\n"

            # Write the synopsis of the video here
            # Micrsoft Word's smartquotes can die in a fire.
            sanitizedDescription = cur_ep_obj.description
            # Replace double curly quotes
            sanitizedDescription = sanitizedDescription.replace(
                u"\u201c", "\"").replace(u"\u201d", "\"")
            # Replace single curly quotes
            sanitizedDescription = sanitizedDescription.replace(
                u"\u2018", "'").replace(u"\u2019",
                                        "'").replace(u"\u02BC", "'")

            data += ("description : " + sanitizedDescription + "\n")

            # Usually starts with "SH" and followed by 6-8 digits.
            # Tivo uses zap2it for thier data, so the series id is the zap2it_id.
            if None is not getattr(show_info, 'zap2it_id', None):
                data += ("seriesId : " + show_info["zap2it_id"] + "\n")

            # This is the call sign of the channel the episode was recorded from.
            if None is not getattr(show_info, 'network', None):
                data += ("callsign : " + show_info["network"] + "\n")

            # This must be entered as yyyy-mm-ddThh:mm:ssZ (the t is capitalized and never changes, the Z is also
            # capitalized and never changes). This is the original air date of the episode.
            # NOTE: Hard coded the time to T00:00:00Z as we really don't know when during the day the first run happened
            if cur_ep_obj.airdate != datetime.date.fromordinal(1):
                data += ("originalAirDate : " + str(cur_ep_obj.airdate) +
                         "T00:00:00Z\n")

            # This shows up at the beginning of the description on the Program screen and on the Details screen.
            for actor in getattr(show_info, 'actors', []):
                data += ('vActor : %s\n' % actor['character']['name']
                         and actor['person']['name'] and
                         actor['character']['name'] != actor['person']['name']
                         and '%s (%s)' %
                         (actor['character']['name'], actor['person']['name'])
                         or actor['person']['name']
                         or actor['character']['name'])

            # This is shown on both the Program screen and the Details screen.
            if None is not getattr(ep_info, 'rating', None):
                try:
                    rating = float(ep_info['rating'])
                except ValueError:
                    rating = 0.0
                # convert 10 to 4 star rating. 4 * rating / 10
                # only whole numbers or half numbers work. multiply by 2, round, divide by 2.0
                rating = round(8 * rating / 10) / 2.0
                data += ("starRating : " + str(rating) + "\n")

            # This is shown on both the Program screen and the Details screen.
            # It uses the standard TV rating system of: TV-Y7, TV-Y, TV-G, TV-PG, TV-14, TV-MA and TV-NR.
            if None is not getattr(show_info, 'contentrating', None):
                data += ("tvRating : " + str(show_info["contentrating"]) +
                         "\n")

            # This field can be repeated as many times as necessary or omitted completely.
            if ep_obj.show_obj.genre:
                for genre in ep_obj.show_obj.genre.split('|'):
                    if genre:
                        data += ("vProgramGenre : " + str(genre) + "\n")

                        # NOTE: The following are metadata keywords are not used
                        # displayMajorNumber
                        # showingBits
                        # displayMinorNumber
                        # colorCode
                        # vSeriesGenre
                        # vGuestStar, vDirector, vExecProducer, vProducer, vWriter, vHost, vChoreographer
                        # partCount
                        # partIndex

        return data
Example #20
0
    def _ep_data(self, ep_obj):
        # type: (sickbeard.tv.TVEpisode) -> Optional[Union[bool, etree.Element]]
        """
        Creates an elementTree XML structure for a WDTV style episode.xml
        and returns the resulting data object.

        ep_obj: a TVShow instance to create the NFO for
        """

        ep_obj_list_to_write = [ep_obj] + ep_obj.related_ep_obj

        show_lang = ep_obj.show_obj.lang

        try:
            tvinfo_config = sickbeard.TVInfoAPI(
                ep_obj.show_obj.tvid).api_params.copy()

            tvinfo_config['actors'] = True

            if show_lang and not 'en' == show_lang:
                tvinfo_config['language'] = show_lang

            if 0 != ep_obj.show_obj.dvdorder:
                tvinfo_config['dvdorder'] = True

            t = sickbeard.TVInfoAPI(
                ep_obj.show_obj.tvid).setup(**tvinfo_config)
            show_info = t[ep_obj.show_obj.prodid]
        except BaseTVinfoShownotfound as e:
            raise exceptions_helper.ShowNotFoundException(ex(e))
        except BaseTVinfoError as e:
            logger.log(
                "Unable to connect to %s while creating meta files - skipping - %s"
                % (sickbeard.TVInfoAPI(ep_obj.show_obj.tvid).name, ex(e)),
                logger.ERROR)
            return False

        if not self._valid_show(show_info, ep_obj.show_obj):
            return

        rootNode = etree.Element("details")
        data = None

        # write an WDTV XML containing info for all matching episodes
        for cur_ep_obj in ep_obj_list_to_write:

            try:
                ep_info = show_info[cur_ep_obj.season][cur_ep_obj.episode]
            except (BaseException, Exception):
                logger.log(
                    "Unable to find episode %sx%s on %s... has it been removed? Should I delete from db?"
                    % (cur_ep_obj.season, cur_ep_obj.episode,
                       sickbeard.TVInfoAPI(ep_obj.show_obj.tvid).name))
                return None

            if None is getattr(ep_info, 'firstaired',
                               None) and 0 == ep_obj.season:
                ep_info["firstaired"] = str(datetime.date.fromordinal(1))

            if None is getattr(ep_info, 'episodename',
                               None) or None is getattr(
                                   ep_info, 'firstaired', None):
                return None

            if 1 < len(ep_obj_list_to_write):
                episode = etree.SubElement(rootNode, "details")
            else:
                episode = rootNode

            episodeID = etree.SubElement(episode, "id")
            episodeID.text = str(cur_ep_obj.epid)

            title = etree.SubElement(episode, "title")
            title.text = '%s' % ep_obj.pretty_name()

            seriesName = etree.SubElement(episode, "series_name")
            if None is not getattr(show_info, 'seriesname', None):
                seriesName.text = '%s' % show_info["seriesname"]

            episodeName = etree.SubElement(episode, "episode_name")
            if None is not cur_ep_obj.name:
                episodeName.text = '%s' % cur_ep_obj.name

            seasonNumber = etree.SubElement(episode, "season_number")
            seasonNumber.text = str(cur_ep_obj.season)

            episodeNum = etree.SubElement(episode, "episode_number")
            episodeNum.text = str(cur_ep_obj.episode)

            firstAired = etree.SubElement(episode, "firstaired")

            if cur_ep_obj.airdate != datetime.date.fromordinal(1):
                firstAired.text = str(cur_ep_obj.airdate)

            year = etree.SubElement(episode, "year")
            year_text = self.get_show_year(ep_obj.show_obj, show_info)
            if year_text:
                year.text = '%s' % year_text

            runtime = etree.SubElement(episode, "runtime")
            if 0 != cur_ep_obj.season:
                if None is not getattr(show_info, 'runtime', None):
                    runtime.text = '%s' % show_info["runtime"]

            genre = etree.SubElement(episode, "genre")
            if None is not getattr(show_info, 'genre', None):
                genre.text = " / ".join(
                    [x for x in show_info["genre"].split('|') if x])

            director = etree.SubElement(episode, "director")
            director_text = getattr(ep_info, 'director', None)
            if None is not director_text:
                director.text = '%s' % director_text

            for actor in getattr(show_info, 'actors', []):
                cur_actor = etree.SubElement(episode, 'actor')

                cur_actor_name = etree.SubElement(cur_actor, 'name')
                cur_actor_name.text = '%s' % actor['person']['name']

                cur_actor_role = etree.SubElement(cur_actor, 'role')
                cur_actor_role_text = '%s' % actor['character']['name']
                if cur_actor_role_text:
                    cur_actor_role.text = '%s' % cur_actor_role_text

            overview = etree.SubElement(episode, "overview")
            if None is not cur_ep_obj.description:
                overview.text = '%s' % cur_ep_obj.description

            # Make it purdy
            sg_helpers.indent_xml(rootNode)
            data = etree.ElementTree(rootNode)

        return data
Example #21
0
def retrieve_exceptions():
    """
    Looks up the exceptions on github, parses them into a dict, and inserts them into the
    scene_exceptions table in cache.db. Also clears the scene name cache.
    """
    global exception_dict, anidb_exception_dict, xem_exception_dict

    # exceptions are stored on github pages
    for tvid in sickbeard.TVInfoAPI().sources:
        if should_refresh(sickbeard.TVInfoAPI(tvid).name):
            logger.log(u'Checking for scene exception updates for %s' %
                       sickbeard.TVInfoAPI(tvid).name)

            url = sickbeard.TVInfoAPI(tvid).config['scene_url']

            url_data = helpers.get_url(url)
            if None is url_data:
                # When None is urlData, trouble connecting to github
                logger.log(
                    u'Check scene exceptions update failed. Unable to get URL: %s'
                    % url, logger.ERROR)
                continue

            else:
                set_last_refresh(sickbeard.TVInfoAPI(tvid).name)

                # each exception is on one line with the format indexer_id: 'show name 1', 'show name 2', etc
                for cur_line in url_data.splitlines():
                    cur_line = cur_line
                    prodid, sep, aliases = cur_line.partition(':')

                    if not aliases:
                        continue

                    prodid = int(prodid)

                    # regex out the list of shows, taking \' into account
                    # alias_list = [re.sub(r'\\(.)', r'\1', x) for x in re.findall(r"'(.*?)(?<!\\)',?", aliases)]
                    alias_list = [{
                        re.sub(r'\\(.)', r'\1', x): -1
                    } for x in re.findall(r"'(.*?)(?<!\\)',?", aliases)]
                    exception_dict[(tvid, prodid)] = alias_list
                    del alias_list
                del url_data

    # XEM scene exceptions
    _xem_exceptions_fetcher()
    for xem_ex in xem_exception_dict:
        if xem_ex in exception_dict:
            exception_dict[
                xem_ex] = exception_dict[xem_ex] + xem_exception_dict[xem_ex]
        else:
            exception_dict[xem_ex] = xem_exception_dict[xem_ex]

    # AniDB scene exceptions
    _anidb_exceptions_fetcher()
    for anidb_ex in anidb_exception_dict:
        if anidb_ex in exception_dict:
            exception_dict[anidb_ex] = exception_dict[
                anidb_ex] + anidb_exception_dict[anidb_ex]
        else:
            exception_dict[anidb_ex] = anidb_exception_dict[anidb_ex]

    changed_exceptions = False

    # write all the exceptions we got off the net into the database
    my_db = db.DBConnection()
    cl = []
    for cur_tvid_prodid in exception_dict:

        # get a list of the existing exceptions for this ID
        existing_exceptions = [
            x['show_name'] for x in my_db.select(
                'SELECT show_name'
                ' FROM scene_exceptions'
                ' WHERE indexer = ? AND indexer_id = ?', list(cur_tvid_prodid))
        ]

        if cur_tvid_prodid not in exception_dict:
            continue

        for cur_exception_dict in exception_dict[cur_tvid_prodid]:
            try:
                cur_exception, cur_season = next(iteritems(cur_exception_dict))
            except (BaseException, Exception):
                logger.log('scene exception error', logger.ERROR)
                logger.log(traceback.format_exc(), logger.ERROR)
                continue

            # if this exception isn't already in the DB then add it
            if cur_exception not in existing_exceptions:

                if PY2 and not isinstance(cur_exception, text_type):
                    cur_exception = text_type(cur_exception, 'utf-8',
                                              'replace')

                cl.append([
                    'INSERT INTO scene_exceptions'
                    ' (indexer, indexer_id, show_name, season) VALUES (?,?,?,?)',
                    list(cur_tvid_prodid) + [cur_exception, cur_season]
                ])
                changed_exceptions = True

    if cl:
        my_db.mass_action(cl)

    # since this could invalidate the results of the cache we clear it out after updating
    if changed_exceptions:
        logger.log(u'Updated scene exceptions')
    else:
        logger.log(u'No scene exceptions update needed')

    # cleanup
    exception_dict.clear()
    anidb_exception_dict.clear()
    xem_exception_dict.clear()
Example #22
0
    def _show_data(self, show_obj):
        # type: (sickbeard.tv.TVShow) -> Optional[Union[bool, etree.Element]]
        """
        Creates an elementTree XML structure for a MediaBrowser-style series.xml
        returns the resulting data object.

        show_obj: a TVShow instance to create the NFO for
        """

        show_lang = show_obj.lang
        tvinfo_config = sickbeard.TVInfoAPI(show_obj.tvid).api_params.copy()

        tvinfo_config['actors'] = True

        if show_lang and not 'en' == show_lang:
            tvinfo_config['language'] = show_lang

        if 0 != show_obj.dvdorder:
            tvinfo_config['dvdorder'] = True

        t = sickbeard.TVInfoAPI(show_obj.tvid).setup(**tvinfo_config)

        rootNode = etree.Element('details')
        tv_node = etree.SubElement(rootNode, 'movie')
        tv_node.attrib['isExtra'] = 'false'
        tv_node.attrib['isSet'] = 'false'
        tv_node.attrib['isTV'] = 'true'

        try:
            show_info = t[int(show_obj.prodid)]
        except BaseTVinfoShownotfound as e:
            logger.log(
                u'Unable to find show with id ' + str(show_obj.prodid) +
                ' on tvdb, skipping it', logger.ERROR)
            raise e
        except BaseTVinfoError as e:
            logger.log(u'TVDB is down, can\'t use its data to make the NFO',
                       logger.ERROR)
            raise e

        if not self._valid_show(show_info, show_obj):
            return

        # check for title and id
        try:
            if None is show_info['seriesname'] \
                    or '' == show_info['seriesname'] \
                    or None is show_info['id'] \
                    or '' == show_info['id']:
                logger.log(
                    'Incomplete info for show with id %s on %s, skipping it' %
                    (show_obj.prodid, sickbeard.TVInfoAPI(show_obj.tvid).name),
                    logger.ERROR)
                return False
        except BaseTVinfoAttributenotfound:
            logger.log(
                'Incomplete info for show with id %s on %s, skipping it' %
                (show_obj.prodid, sickbeard.TVInfoAPI(show_obj.tvid).name),
                logger.ERROR)
            return False

        SeriesName = etree.SubElement(tv_node, 'title')
        if None is not show_info['seriesname']:
            SeriesName.text = '%s' % show_info['seriesname']
        else:
            SeriesName.text = ''

        Genres = etree.SubElement(tv_node, 'genres')
        if None is not show_info['genre']:
            for genre in show_info['genre'].split('|'):
                if genre and genre.strip():
                    cur_genre = etree.SubElement(Genres, 'Genre')
                    cur_genre.text = '%s' % genre.strip()

        FirstAired = etree.SubElement(tv_node, 'premiered')
        if None is not show_info['firstaired']:
            FirstAired.text = '%s' % show_info['firstaired']

        year = etree.SubElement(tv_node, 'year')
        year_text = self.get_show_year(show_obj, show_info)
        if year_text:
            year.text = '%s' % year_text

        if None is not show_info['rating']:
            try:
                rating = int((float(show_info['rating']) * 10))
            except ValueError:
                rating = 0
            Rating = etree.SubElement(tv_node, 'rating')
            rating_text = str(rating)
            if None is not rating_text:
                Rating.text = '%s' % rating_text

        Status = etree.SubElement(tv_node, 'status')
        if None is not show_info['status']:
            Status.text = '%s' % show_info['status']

        mpaa = etree.SubElement(tv_node, 'mpaa')
        if None is not show_info['contentrating']:
            mpaa.text = '%s' % show_info['contentrating']

        IMDB_ID = etree.SubElement(tv_node, 'id')
        if None is not show_info['imdb_id']:
            IMDB_ID.attrib['moviedb'] = 'imdb'
            IMDB_ID.text = '%s' % show_info['imdb_id']

        prodid = etree.SubElement(tv_node, 'indexerid')
        if None is not show_info['id']:
            prodid.text = '%s' % show_info['id']

        Runtime = etree.SubElement(tv_node, 'runtime')
        if None is not show_info['runtime']:
            Runtime.text = '%s' % show_info['runtime']

        cast = etree.SubElement(tv_node, 'cast')
        self.add_actor_element(show_info, etree, cast)

        sg_helpers.indent_xml(rootNode)

        data = etree.ElementTree(rootNode)

        return data
Example #23
0
def _get_proper_list(aired_since_shows,  # type: datetime.datetime
                     recent_shows,  # type: List[Tuple[int, int]]
                     recent_anime,  # type:  List[Tuple[int, int]]
                     proper_dict=None  # type:  Dict[AnyStr, List[Proper]]
                     ):
    # type: (...) -> List[Proper]
    """

    :param aired_since_shows: date since aired
    :param recent_shows: list of recent shows
    :param recent_anime: list of recent anime shows
    :param proper_dict: dict with provider keys containing Proper objects
    :return: list of propers
    """
    propers = {}
    # make sure the episode has been downloaded before
    history_limit = datetime.datetime.now() - datetime.timedelta(days=30)

    my_db = db.DBConnection()
    # for each provider get a list of arbitrary Propers
    orig_thread_name = threading.currentThread().name
    # filter provider list for:
    # 1. from recent search: recent search enabled providers
    # 2. native proper search: active search enabled providers
    provider_list = filter_list(
        lambda p: p.is_active() and (p.enable_recentsearch, p.enable_backlog)[None is proper_dict],
        sickbeard.providers.sortedProviderList())
    search_threads = []

    if None is proper_dict:
        # if not a recent proper search create a thread per provider to search for Propers
        proper_dict = {}
        for cur_provider in provider_list:
            if not recent_anime and cur_provider.anime_only:
                continue

            provider_id = cur_provider.get_id()

            logger.log('Searching for new Proper releases at [%s]' % cur_provider.name)
            proper_dict[provider_id] = []

            search_threads.append(threading.Thread(target=_search_provider,
                                                   kwargs={'cur_provider': cur_provider,
                                                           'provider_propers': proper_dict[provider_id],
                                                           'aired_since_shows': aired_since_shows,
                                                           'recent_shows': recent_shows,
                                                           'recent_anime': recent_anime},
                                                   name='%s :: [%s]' % (orig_thread_name, cur_provider.name)))

            search_threads[-1].start()

        # wait for all searches to finish
        for cur_thread in search_threads:
            cur_thread.join()

    for cur_provider in provider_list:
        if not recent_anime and cur_provider.anime_only:
            continue

        found_propers = proper_dict.get(cur_provider.get_id(), [])
        if not found_propers:
            continue

        # if they haven't been added by a different provider than add the Proper to the list
        for cur_proper in found_propers:
            name = _generic_name(cur_proper.name)
            if name in propers:
                continue

            try:
                np = NameParser(False, show_obj=cur_proper.parsed_show_obj, indexer_lookup=False)
                parse_result = np.parse(cur_proper.name)
            except (InvalidNameException, InvalidShowException, Exception):
                continue

            # get the show object
            cur_proper.parsed_show_obj = (cur_proper.parsed_show_obj
                                          or helpers.find_show_by_id(parse_result.show_obj.tvid_prodid))
            if None is cur_proper.parsed_show_obj:
                logger.log('Skip download; cannot find show with ID [%s] at %s' %
                           (cur_proper.prodid, sickbeard.TVInfoAPI(cur_proper.tvid).name), logger.ERROR)
                continue

            cur_proper.tvid = cur_proper.parsed_show_obj.tvid
            cur_proper.prodid = cur_proper.parsed_show_obj.prodid

            if not (-1 != cur_proper.prodid and parse_result.series_name and parse_result.episode_numbers
                    and (cur_proper.tvid, cur_proper.prodid) in recent_shows + recent_anime):
                continue

            # only get anime Proper if it has release group and version
            if parse_result.is_anime and not parse_result.release_group and -1 == parse_result.version:
                logger.log('Ignored Proper with no release group and version in name [%s]' % cur_proper.name,
                           logger.DEBUG)
                continue

            if not show_name_helpers.pass_wordlist_checks(cur_proper.name, parse=False, indexer_lookup=False,
                                                          show_obj=cur_proper.parsed_show_obj):
                logger.log('Ignored unwanted Proper [%s]' % cur_proper.name, logger.DEBUG)
                continue

            re_x = dict(re_prefix='.*', re_suffix='.*')
            result = show_name_helpers.contains_any(cur_proper.name, cur_proper.parsed_show_obj.rls_ignore_words,
                                                    rx=cur_proper.parsed_show_obj.rls_ignore_words_regex, **re_x)
            if None is not result and result:
                logger.log('Ignored Proper containing ignore word [%s]' % cur_proper.name, logger.DEBUG)
                continue

            result = show_name_helpers.contains_any(cur_proper.name, cur_proper.parsed_show_obj.rls_require_words,
                                                    rx=cur_proper.parsed_show_obj.rls_require_words_regex, **re_x)
            if None is not result and not result:
                logger.log('Ignored Proper for not containing any required word [%s]' % cur_proper.name, logger.DEBUG)
                continue

            cur_size = getattr(cur_proper, 'size', None)
            if failed_history.has_failed(cur_proper.name, cur_size, cur_provider.name):
                continue

            cur_proper.season = parse_result.season_number if None is not parse_result.season_number else 1
            cur_proper.episode = parse_result.episode_numbers[0]
            # check if we actually want this Proper (if it's the right quality)
            sql_result = my_db.select(
                'SELECT release_group, status, version, release_name'
                ' FROM tv_episodes'
                ' WHERE indexer = ? AND showid = ?'
                ' AND season = ? AND episode = ?'
                ' LIMIT 1',
                [cur_proper.tvid, cur_proper.prodid,
                 cur_proper.season, cur_proper.episode])
            if not sql_result:
                continue

            # only keep the Proper if we already retrieved the same quality ep (don't get better/worse ones)
            # check if we want this release: same quality as current, current has correct status
            # restrict other release group releases to Proper's
            old_status, old_quality = Quality.splitCompositeStatus(int(sql_result[0]['status']))
            cur_proper.quality = Quality.nameQuality(cur_proper.name, parse_result.is_anime)
            cur_proper.is_repack, cur_proper.properlevel = Quality.get_proper_level(
                parse_result.extra_info_no_name(), parse_result.version, parse_result.is_anime, check_is_repack=True)
            cur_proper.proper_level = cur_proper.properlevel    # local non global value
            if old_status in SNATCHED_ANY:
                old_release_group = ''
                # noinspection SqlResolve
                history_results = my_db.select(
                    'SELECT resource FROM history'
                    ' WHERE indexer = ? AND showid = ?'
                    ' AND season = ? AND episode = ? AND quality = ? AND date >= ?'
                    ' AND (%s) ORDER BY date DESC LIMIT 1' % ' OR '.join(
                        ['action = "%d%02d"' % (old_quality, x) for x in SNATCHED_ANY]),
                    [cur_proper.tvid, cur_proper.prodid,
                     cur_proper.season, cur_proper.episode, cur_proper.quality,
                     history_limit.strftime(history.dateFormat)])
                if len(history_results):
                    try:
                        old_release_group = np.parse(history_results[0]['resource']).release_group
                    except (BaseException, Exception):
                        pass
            else:
                old_release_group = sql_result[0]['release_group']
            try:
                same_release_group = parse_result.release_group.lower() == old_release_group.lower()
            except (BaseException, Exception):
                same_release_group = parse_result.release_group == old_release_group
            if old_status not in SNATCHED_ANY + [DOWNLOADED, ARCHIVED] \
                    or cur_proper.quality != old_quality \
                    or (cur_proper.is_repack and not same_release_group):
                continue

            np = NameParser(False, show_obj=cur_proper.parsed_show_obj, indexer_lookup=False)
            try:
                extra_info = np.parse(sql_result[0]['release_name']).extra_info_no_name()
            except (BaseException, Exception):
                extra_info = None
            # don't take Proper of the same level we already downloaded
            old_proper_level, old_extra_no_name, old_name = \
                get_old_proper_level(cur_proper.parsed_show_obj, cur_proper.tvid, cur_proper.prodid,
                                     cur_proper.season, parse_result.episode_numbers,
                                     old_status, cur_proper.quality, extra_info,
                                     parse_result.version, parse_result.is_anime)
            if cur_proper.proper_level <= old_proper_level:
                continue

            is_web = (old_quality in (Quality.HDWEBDL, Quality.FULLHDWEBDL, Quality.UHD4KWEB) or
                      (old_quality == Quality.SDTV and
                       isinstance(sql_result[0]['release_name'], string_types) and
                       re.search(r'\Wweb.?(dl|rip|.([hx]\W?26[45]|hevc))\W', sql_result[0]['release_name'], re.I)))

            if is_web:
                old_name = (old_name, sql_result[0]['release_name'])[old_name in ('', None)]
                old_webdl_type = get_webdl_type(old_extra_no_name, old_name)
                new_webdl_type = get_webdl_type(parse_result.extra_info_no_name(), cur_proper.name)
                if old_webdl_type != new_webdl_type:
                    logger.log('Ignored Proper webdl source [%s], does not match existing webdl source [%s] for [%s]'
                               % (old_webdl_type, new_webdl_type, cur_proper.name), logger.DEBUG)
                    continue

            # for webdls, prevent Propers from different groups
            log_same_grp = 'Ignored Proper from release group [%s] does not match existing group [%s] for [%s]' \
                           % (parse_result.release_group, old_release_group, cur_proper.name)
            if sickbeard.PROPERS_WEBDL_ONEGRP and is_web and not same_release_group:
                logger.log(log_same_grp, logger.DEBUG)
                continue

            # check if we actually want this Proper (if it's the right release group and a higher version)
            if parse_result.is_anime:
                old_version = int(sql_result[0]['version'])
                if not (-1 < old_version < parse_result.version):
                    continue
                if not same_release_group:
                    logger.log(log_same_grp, logger.DEBUG)
                    continue
                found_msg = 'Found anime Proper v%s to replace v%s' % (parse_result.version, old_version)
            else:
                found_msg = 'Found Proper [%s]' % cur_proper.name

            # noinspection SqlResolve
            history_results = my_db.select(
                'SELECT resource FROM history'
                ' WHERE indexer = ? AND showid = ?'
                ' AND season = ? AND episode = ? AND quality = ? AND date >= ?'
                ' AND (%s)' % ' OR '.join(['action LIKE "%%%02d"' % x for x in SNATCHED_ANY + [DOWNLOADED, ARCHIVED]]),
                [cur_proper.tvid, cur_proper.prodid,
                 cur_proper.season, cur_proper.episode, cur_proper.quality,
                 history_limit.strftime(history.dateFormat)])

            # skip if the episode has never downloaded, because a previous quality is required to match the Proper
            if not len(history_results):
                logger.log('Ignored Proper cannot find a recent history item for [%s]' % cur_proper.name, logger.DEBUG)
                continue

            # make sure that none of the existing history downloads are the same Proper as the download candidate
            clean_proper_name = _generic_name(helpers.remove_non_release_groups(
                cur_proper.name, cur_proper.parsed_show_obj.is_anime))
            is_same = False
            for hitem in history_results:
                # if the result exists in history already we need to skip it
                if clean_proper_name == _generic_name(helpers.remove_non_release_groups(
                        ek.ek(os.path.basename, hitem['resource']))):
                    is_same = True
                    break
            if is_same:
                logger.log('Ignored Proper already in history [%s]' % cur_proper.name)
                continue

            logger.log(found_msg, logger.DEBUG)

            # finish populating the Proper instance
            # cur_proper.show_obj = cur_proper.parsed_show_obj.prodid
            cur_proper.provider = cur_provider
            cur_proper.extra_info = parse_result.extra_info
            cur_proper.extra_info_no_name = parse_result.extra_info_no_name
            cur_proper.release_group = parse_result.release_group

            cur_proper.is_anime = parse_result.is_anime
            cur_proper.version = parse_result.version

            propers[name] = cur_proper

        cur_provider.log_result('Propers', len(propers), '%s' % cur_provider.name)

    return list_values(propers)
Example #24
0
def send_nzb(search_result):
    """

    :param search_result: search result
    :type search_result: NZBSearchResult or NZBDataSearchResult
    :return:
    """
    result = False
    add_to_top = False
    nzbget_prio = 0

    authed, auth_msg, rpc_client = test_nzbget(sickbeard.NZBGET_HOST,
                                               sickbeard.NZBGET_USE_HTTPS,
                                               sickbeard.NZBGET_USERNAME,
                                               sickbeard.NZBGET_PASSWORD)

    if not authed:
        return authed

    dupekey = ''
    dupescore = 0
    # if it aired recently make it high priority and generate DupeKey/Score
    for cur_ep_obj in search_result.ep_obj_list:
        if '' == dupekey:
            dupekey = 'SickGear-%s%s' % (sickbeard.TVInfoAPI(
                cur_ep_obj.show_obj.tvid).config.get(
                    'dupekey', ''), cur_ep_obj.show_obj.prodid)
        dupekey += '-%s.%s' % (cur_ep_obj.season, cur_ep_obj.episode)

    if 1 == search_result.priority:
        add_to_top = True
        nzbget_prio = sickbeard.NZBGET_PRIORITY

    if Quality.UNKNOWN != search_result.quality:
        dupescore = search_result.quality * 100

    dupescore += (0,
                  9 + search_result.properlevel)[0 < search_result.properlevel]

    nzbcontent64 = None
    if 'nzbdata' == search_result.resultType:
        data = search_result.get_data()
        if not data:
            return False
        nzbcontent64 = b64encodestring(data, keep_eol=True)
    elif 'Anizb' == search_result.provider.name and 'nzb' == search_result.resultType:
        gen_provider = GenericProvider('')
        data = gen_provider.get_url(search_result.url)
        if None is data:
            return result
        nzbcontent64 = b64encodestring(data, keep_eol=True)

    logger.log(u'Sending NZB to NZBGet: %s' % search_result.name)

    try:
        # Find out if nzbget supports priority (Version 9.0+), old versions beginning with a 0.x will use the old cmd
        nzbget_version_str = rpc_client.version()
        nzbget_version = try_int(
            nzbget_version_str[:nzbget_version_str.find('.')])

        # v13+ has a combined append method that accepts both (url and content)
        # also the return value has changed from boolean to integer
        # (Positive number representing NZBID of the queue item. 0 and negative numbers represent error codes.)
        if 13 <= nzbget_version:
            nzbget_result = 0 < rpc_client.append(
                '%s.nzb' % search_result.name,
                (nzbcontent64, search_result.url)[None is nzbcontent64],
                sickbeard.NZBGET_CATEGORY, nzbget_prio, False, False, dupekey,
                dupescore, 'score')

        elif 12 == nzbget_version:
            if None is not nzbcontent64:
                nzbget_result = rpc_client.append(
                    '%s.nzb' % search_result.name, sickbeard.NZBGET_CATEGORY,
                    nzbget_prio, False, nzbcontent64, False, dupekey,
                    dupescore, 'score')
            else:
                nzbget_result = rpc_client.appendurl(
                    '%s.nzb' % search_result.name, sickbeard.NZBGET_CATEGORY,
                    nzbget_prio, False, search_result.url, False, dupekey,
                    dupescore, 'score')
        elif 0 == nzbget_version:
            if None is not nzbcontent64:
                nzbget_result = rpc_client.append(
                    '%s.nzb' % search_result.name, sickbeard.NZBGET_CATEGORY,
                    add_to_top, nzbcontent64)
            else:
                if 'nzb' == search_result.resultType:
                    gen_provider = GenericProvider('')
                    data = gen_provider.get_url(search_result.url)
                    if None is data:
                        return result

                    nzbcontent64 = b64encodestring(data, keep_eol=True)
                nzbget_result = rpc_client.append(
                    '%s.nzb' % search_result.name, sickbeard.NZBGET_CATEGORY,
                    add_to_top, nzbcontent64)
        else:
            if None is not nzbcontent64:
                nzbget_result = rpc_client.append(
                    '%s.nzb' % search_result.name, sickbeard.NZBGET_CATEGORY,
                    nzbget_prio, False, nzbcontent64)
            else:
                nzbget_result = rpc_client.appendurl(
                    '%s.nzb' % search_result.name, sickbeard.NZBGET_CATEGORY,
                    nzbget_prio, False, search_result.url)

        if nzbget_result:
            logger.log(u'NZB sent to NZBGet successfully', logger.DEBUG)
            result = True
        else:
            logger.log(
                u'NZBGet could not add %s.nzb to the queue' %
                search_result.name, logger.ERROR)
    except (BaseException, Exception):
        logger.log(
            u'Connect Error to NZBGet: could not add %s.nzb to the queue' %
            search_result.name, logger.ERROR)

    return result
Example #25
0
    def _show_data(self, show_obj):
        # type: (sickbeard.tv.TVShow) -> Optional[Union[bool, etree.Element]]
        """
        Creates an elementTree XML structure for a MediaBrowser-style series.xml
        returns the resulting data object.

        show_obj: a TVShow instance to create the NFO for
        """

        show_lang = show_obj.lang

        # There's gotta be a better way of doing this but we don't wanna
        # change the language value elsewhere
        tvinfo_config = sickbeard.TVInfoAPI(show_obj.tvid).api_params.copy()

        tvinfo_config['actors'] = True

        if show_lang and not 'en' == show_lang:
            tvinfo_config['language'] = show_lang

        if 0 != show_obj.dvdorder:
            tvinfo_config['dvdorder'] = True

        t = sickbeard.TVInfoAPI(show_obj.tvid).setup(**tvinfo_config)

        tv_node = etree.Element("Series")

        try:
            show_info = t[int(show_obj.prodid)]
        except Exception as e:
            if check_exception_type(e, ExceptionTuples.tvinfo_shownotfound):
                logger.log(
                    "Unable to find show with id %s on %s, skipping it" %
                    (show_obj.prodid, sickbeard.TVInfoAPI(show_obj.tvid).name),
                    logger.ERROR)
                raise

            elif check_exception_type(e, ExceptionTuples.tvinfo_error):
                logger.log(
                    "%s is down, can't use its data to make the NFO" %
                    sickbeard.TVInfoAPI(show_obj.tvid).name, logger.ERROR)
                raise
            else:
                raise e

        if not self._valid_show(show_info, show_obj):
            return

        # check for title and id
        if None is getattr(show_info, 'seriesname', None) or None is getattr(
                show_info, 'id', None):
            logger.log(
                "Incomplete info for show with id %s on %s, skipping it" %
                (show_obj.prodid, sickbeard.TVInfoAPI(show_obj.tvid).name),
                logger.ERROR)
            return False

        prodid = etree.SubElement(tv_node, "id")
        if None is not getattr(show_info, 'id', None):
            prodid.text = str(show_info['id'])

        tvid = etree.SubElement(tv_node, "indexer")
        if None is not show_obj.tvid:
            tvid.text = str(show_obj.tvid)

        SeriesName = etree.SubElement(tv_node, "SeriesName")
        if None is not getattr(show_info, 'seriesname', None):
            SeriesName.text = '%s' % show_info['seriesname']

        Status = etree.SubElement(tv_node, "Status")
        if None is not getattr(show_info, 'status', None):
            Status.text = '%s' % show_info['status']

        Network = etree.SubElement(tv_node, "Network")
        if None is not getattr(show_info, 'network', None):
            Network.text = '%s' % show_info['network']

        Airs_Time = etree.SubElement(tv_node, "Airs_Time")
        if None is not getattr(show_info, 'airs_time', None):
            Airs_Time.text = '%s' % show_info['airs_time']

        Airs_DayOfWeek = etree.SubElement(tv_node, "Airs_DayOfWeek")
        if None is not getattr(show_info, 'airs_dayofweek', None):
            Airs_DayOfWeek.text = '%s' % show_info['airs_dayofweek']

        FirstAired = etree.SubElement(tv_node, "FirstAired")
        if None is not getattr(show_info, 'firstaired', None):
            FirstAired.text = '%s' % show_info['firstaired']

        ContentRating = etree.SubElement(tv_node, "ContentRating")
        MPAARating = etree.SubElement(tv_node, "MPAARating")
        certification = etree.SubElement(tv_node, "certification")
        if None is not getattr(show_info, 'contentrating', None):
            ContentRating.text = '%s' % show_info['contentrating']
            MPAARating.text = '%s' % show_info['contentrating']
            certification.text = '%s' % show_info['contentrating']

        MetadataType = etree.SubElement(tv_node, "Type")
        MetadataType.text = "Series"

        Overview = etree.SubElement(tv_node, "Overview")
        if None is not getattr(show_info, 'overview', None):
            Overview.text = '%s' % show_info['overview']

        PremiereDate = etree.SubElement(tv_node, "PremiereDate")
        if None is not getattr(show_info, 'firstaired', None):
            PremiereDate.text = '%s' % show_info['firstaired']

        Rating = etree.SubElement(tv_node, "Rating")
        if None is not getattr(show_info, 'rating', None):
            Rating.text = '%s' % show_info['rating']

        ProductionYear = etree.SubElement(tv_node, "ProductionYear")
        year_text = self.get_show_year(show_obj, show_info)
        if year_text:
            ProductionYear.text = '%s' % year_text

        RunningTime = etree.SubElement(tv_node, "RunningTime")
        Runtime = etree.SubElement(tv_node, "Runtime")
        if None is not getattr(show_info, 'runtime', None):
            RunningTime.text = '%s' % show_info['runtime']
            Runtime.text = '%s' % show_info['runtime']

        IMDB_ID = etree.SubElement(tv_node, "IMDB_ID")
        IMDB = etree.SubElement(tv_node, "IMDB")
        IMDbId = etree.SubElement(tv_node, "IMDbId")
        if None is not getattr(show_info, 'imdb_id', None):
            IMDB_ID.text = '%s' % show_info['imdb_id']
            IMDB.text = '%s' % show_info['imdb_id']
            IMDbId.text = '%s' % show_info['imdb_id']

        Zap2ItId = etree.SubElement(tv_node, "Zap2ItId")
        if None is not getattr(show_info, 'zap2it_id', None):
            Zap2ItId.text = '%s' % show_info['zap2it_id']

        Genres = etree.SubElement(tv_node, "Genres")
        for genre in show_info['genre'].split('|'):
            if genre:
                cur_genre = etree.SubElement(Genres, "Genre")
                cur_genre.text = '%s' % genre

        Genre = etree.SubElement(tv_node, "Genre")
        if None is not getattr(show_info, 'genre', None):
            Genre.text = "|".join(
                [x for x in show_info["genre"].split('|') if x])

        Studios = etree.SubElement(tv_node, "Studios")
        Studio = etree.SubElement(Studios, "Studio")
        if None is not getattr(show_info, 'network', None):
            Studio.text = '%s' % show_info['network']

        Persons = etree.SubElement(tv_node, 'Persons')
        for actor in getattr(show_info, 'actors', []):
            cur_actor = etree.SubElement(Persons, 'Person')

            cur_actor_name = etree.SubElement(cur_actor, 'Name')
            cur_actor_name.text = '%s' % actor['person']['name']

            cur_actor_type = etree.SubElement(cur_actor, 'Type')
            cur_actor_type.text = 'Actor'

            cur_actor_role = etree.SubElement(cur_actor, 'Role')
            cur_actor_role_text = '%s' % actor['character']['name']
            if cur_actor_role_text:
                cur_actor_role.text = '%s' % cur_actor_role_text

        helpers.indent_xml(tv_node)

        data = etree.ElementTree(tv_node)

        return data
Example #26
0
    def _ep_data(self, ep_obj):
        # type: (sickbeard.tv.TVEpisode) -> Optional[Union[bool, etree.Element]]
        """
        Creates an elementTree XML structure for a MediaBrowser style episode.xml
        and returns the resulting data object.

        show_obj: a TVShow instance to create the NFO for
        """

        ep_obj_list_to_write = [ep_obj] + ep_obj.related_ep_obj

        persons_dict = {'Director': [], 'GuestStar': [], 'Writer': []}

        show_lang = ep_obj.show_obj.lang

        try:
            tvinfo_config = sickbeard.TVInfoAPI(
                ep_obj.show_obj.tvid).api_params.copy()

            tvinfo_config['actors'] = True

            if show_lang and not 'en' == show_lang:
                tvinfo_config['language'] = show_lang

            if 0 != ep_obj.show_obj.dvdorder:
                tvinfo_config['dvdorder'] = True

            t = sickbeard.TVInfoAPI(
                ep_obj.show_obj.tvid).setup(**tvinfo_config)

            show_info = t[ep_obj.show_obj.prodid]
        except Exception as e:
            if check_exception_type(e, ExceptionTuples.tvinfo_shownotfound):
                raise exceptions_helper.ShowNotFoundException(ex(e))
            elif check_exception_type(e, ExceptionTuples.tvinfo_error):
                logger.log(
                    "Unable to connect to %s while creating meta files - skipping - %s"
                    % (sickbeard.TVInfoAPI(ep_obj.show_obj.tvid).name, ex(e)),
                    logger.ERROR)
                return False
            else:
                raise e

        if not self._valid_show(show_info, ep_obj.show_obj):
            return

        rootNode = etree.Element("Item")

        # write an MediaBrowser XML containing info for all matching episodes
        for cur_ep_obj in ep_obj_list_to_write:

            try:
                ep_info = show_info[cur_ep_obj.season][cur_ep_obj.episode]
            except (BaseException, Exception):
                logger.log(
                    "Unable to find episode %sx%s on %s.. has it been removed? Should I delete from db?"
                    % (cur_ep_obj.season, cur_ep_obj.episode,
                       sickbeard.TVInfoAPI(ep_obj.show_obj.tvid).name))
                return None

            if cur_ep_obj == ep_obj:
                # root (or single) episode

                # default to today's date for specials if firstaired is not set
                if None is getattr(ep_info, 'firstaired',
                                   None) and 0 == ep_obj.season:
                    ep_info['firstaired'] = str(datetime.date.fromordinal(1))

                if None is getattr(ep_info, 'episodename',
                                   None) or None is getattr(
                                       ep_info, 'firstaired', None):
                    return None

                episode = rootNode

                EpisodeName = etree.SubElement(episode, "EpisodeName")
                if None is not cur_ep_obj.name:
                    EpisodeName.text = '%s' % cur_ep_obj.name
                else:
                    EpisodeName.text = ""

                EpisodeNumber = etree.SubElement(episode, "EpisodeNumber")
                EpisodeNumber.text = str(ep_obj.episode)

                if ep_obj.related_ep_obj:
                    EpisodeNumberEnd = etree.SubElement(
                        episode, "EpisodeNumberEnd")
                    EpisodeNumberEnd.text = str(cur_ep_obj.episode)

                SeasonNumber = etree.SubElement(episode, "SeasonNumber")
                SeasonNumber.text = str(cur_ep_obj.season)

                if not ep_obj.related_ep_obj:
                    absolute_number = etree.SubElement(episode,
                                                       "absolute_number")
                    if None is not getattr(ep_info, 'absolute_number', None):
                        absolute_number.text = '%s' % ep_info['absolute_number']

                FirstAired = etree.SubElement(episode, "FirstAired")
                if cur_ep_obj.airdate != datetime.date.fromordinal(1):
                    FirstAired.text = str(cur_ep_obj.airdate)
                else:
                    FirstAired.text = ""

                MetadataType = etree.SubElement(episode, "Type")
                MetadataType.text = "Episode"

                Overview = etree.SubElement(episode, "Overview")
                if None is not cur_ep_obj.description:
                    Overview.text = '%s' % cur_ep_obj.description
                else:
                    Overview.text = ""

                if not ep_obj.related_ep_obj:
                    Rating = etree.SubElement(episode, "Rating")
                    if None is not getattr(ep_info, 'rating', None):
                        Rating.text = '%s' % ep_info['rating']

                    IMDB_ID = etree.SubElement(episode, "IMDB_ID")
                    IMDB = etree.SubElement(episode, "IMDB")
                    IMDbId = etree.SubElement(episode, "IMDbId")
                    if None is not getattr(show_info, 'imdb_id', None):
                        IMDB_ID.text = '%s' % show_info['imdb_id']
                        IMDB.text = '%s' % show_info['imdb_id']
                        IMDbId.text = '%s' % show_info['imdb_id']

                prodid = etree.SubElement(episode, "id")
                prodid.text = str(cur_ep_obj.show_obj.prodid)

                tvid = etree.SubElement(episode, "indexer")
                tvid.text = str(cur_ep_obj.show_obj.tvid)

                Persons = etree.SubElement(episode, "Persons")

                Language = etree.SubElement(episode, "Language")
                try:
                    Language.text = '%s' % cur_ep_obj.show_obj.lang
                except (BaseException, Exception):
                    Language.text = 'en'  # tvrage api doesn't provide language so we must assume a value here

                thumb = etree.SubElement(episode, "filename")
                # TODO: See what this is needed for.. if its still needed
                # just write this to the NFO regardless of whether it actually exists or not
                # note: renaming files after nfo generation will break this, tough luck
                thumb_text = self.get_episode_thumb_path(ep_obj)
                if thumb_text:
                    thumb.text = '%s' % thumb_text

            else:
                # append data from (if any) related episodes
                EpisodeNumberEnd.text = str(cur_ep_obj.episode)

                if cur_ep_obj.name:
                    if not EpisodeName.text:
                        EpisodeName.text = '%s' % cur_ep_obj.name
                    else:
                        EpisodeName.text = '%s, %s' % (EpisodeName.text,
                                                       cur_ep_obj.name)

                if cur_ep_obj.description:
                    if not Overview.text:
                        Overview.text = '%s' % cur_ep_obj.description
                    else:
                        Overview.text = '%s\r%s' % (Overview.text,
                                                    cur_ep_obj.description)

            # collect all directors, guest stars and writers
            if None is not getattr(ep_info, 'director', None):
                persons_dict['Director'] += [
                    x.strip() for x in ep_info['director'].split('|') if x
                ]
            if None is not getattr(ep_info, 'gueststars', None):
                persons_dict['GuestStar'] += [
                    x.strip() for x in ep_info['gueststars'].split('|') if x
                ]
            if None is not getattr(ep_info, 'writer', None):
                persons_dict['Writer'] += [
                    x.strip() for x in ep_info['writer'].split('|') if x
                ]

        # fill in Persons section with collected directors, guest starts and writers
        for person_type, names in iteritems(persons_dict):
            # remove doubles
            names = list(set(names))
            for cur_name in names:
                Person = etree.SubElement(Persons, "Person")
                cur_person_name = etree.SubElement(Person, "Name")
                cur_person_name.text = '%s' % cur_name
                cur_person_type = etree.SubElement(Person, "Type")
                cur_person_type.text = '%s' % person_type

        helpers.indent_xml(rootNode)
        data = etree.ElementTree(rootNode)

        return data
Example #27
0
    def _ep_data(self, ep_obj):
        # type: (sickbeard.tv.TVEpisode) -> Optional[Union[bool, etree.Element]]
        """
        Creates an elementTree XML structure for an XBMC-style episode.nfo and
        returns the resulting data object.
            show_obj: a TVEpisode instance to create the NFO for
        """

        ep_obj_list_to_write = [ep_obj] + ep_obj.related_ep_obj

        show_lang = ep_obj.show_obj.lang

        tvinfo_config = sickbeard.TVInfoAPI(
            ep_obj.show_obj.tvid).api_params.copy()

        tvinfo_config['actors'] = True

        if show_lang and not 'en' == show_lang:
            tvinfo_config['language'] = show_lang

        if 0 != ep_obj.show_obj.dvdorder:
            tvinfo_config['dvdorder'] = True

        try:
            t = sickbeard.TVInfoAPI(
                ep_obj.show_obj.tvid).setup(**tvinfo_config)
            show_info = t[ep_obj.show_obj.prodid]
        except BaseTVinfoShownotfound as e:
            raise exceptions_helper.ShowNotFoundException(ex(e))
        except BaseTVinfoError as e:
            logger.log(
                'Unable to connect to %s while creating meta files - skipping - %s'
                % (sickbeard.TVInfoAPI(ep_obj.show_obj.tvid).name, ex(e)),
                logger.ERROR)
            return

        if not self._valid_show(show_info, ep_obj.show_obj):
            return

        if 1 < len(ep_obj_list_to_write):
            rootNode = etree.Element('xbmcmultiepisode')
        else:
            rootNode = etree.Element('episodedetails')

        # write an NFO containing info for all matching episodes
        for cur_ep_obj in ep_obj_list_to_write:

            try:
                ep_info = show_info[cur_ep_obj.season][cur_ep_obj.episode]
            except (BaseTVinfoEpisodenotfound, BaseTVinfoSeasonnotfound) as e:
                logger.log(
                    'Unable to find episode %sx%s on %s.. has it been removed? Should I delete from db?'
                    % (cur_ep_obj.season, cur_ep_obj.episode,
                       sickbeard.TVInfoAPI(ep_obj.show_obj.tvid).name))
                return None
            except (BaseException, Exception):
                logger.log(
                    u'Not generating nfo because failed to fetched tv info data at this time',
                    logger.DEBUG)
                return None

            if None is getattr(ep_info, 'firstaired', None):
                ep_info['firstaired'] = str(datetime.date.fromordinal(1))

            if None is getattr(ep_info, 'episodename', None):
                logger.log(u'Not generating nfo because the ep has no title',
                           logger.DEBUG)
                return None

            logger.log(
                u'Creating metadata for episode ' + str(ep_obj.season) + 'x' +
                str(ep_obj.episode), logger.DEBUG)

            if 1 < len(ep_obj_list_to_write):
                episode = etree.SubElement(rootNode, 'episodedetails')
            else:
                episode = rootNode

            title = etree.SubElement(episode, 'title')
            if None is not cur_ep_obj.name:
                title.text = '%s' % cur_ep_obj.name

            showtitle = etree.SubElement(episode, 'showtitle')
            if None is not cur_ep_obj.show_obj.name:
                showtitle.text = '%s' % cur_ep_obj.show_obj.name

            season = etree.SubElement(episode, 'season')
            season.text = str(cur_ep_obj.season)

            episodenum = etree.SubElement(episode, 'episode')
            episodenum.text = str(cur_ep_obj.episode)

            uniqueid = etree.SubElement(episode, 'uniqueid')
            uniqueid.text = str(cur_ep_obj.epid)

            aired = etree.SubElement(episode, 'aired')
            if cur_ep_obj.airdate != datetime.date.fromordinal(1):
                aired.text = str(cur_ep_obj.airdate)
            else:
                aired.text = ''

            plot = etree.SubElement(episode, 'plot')
            if None is not cur_ep_obj.description:
                plot.text = '%s' % cur_ep_obj.description

            runtime = etree.SubElement(episode, 'runtime')
            if 0 != cur_ep_obj.season:
                if None is not getattr(show_info, 'runtime', None):
                    runtime.text = '%s' % show_info['runtime']

            displayseason = etree.SubElement(episode, 'displayseason')
            if None is not getattr(ep_info, 'airsbefore_season', None):
                displayseason_text = ep_info['airsbefore_season']
                if None is not displayseason_text:
                    displayseason.text = '%s' % displayseason_text

            displayepisode = etree.SubElement(episode, 'displayepisode')
            if None is not getattr(ep_info, 'airsbefore_episode', None):
                displayepisode_text = ep_info['airsbefore_episode']
                if None is not displayepisode_text:
                    displayepisode.text = '%s' % displayepisode_text

            thumb = etree.SubElement(episode, 'thumb')
            thumb_text = getattr(ep_info, 'filename', None)
            if None is not thumb_text:
                thumb.text = '%s' % thumb_text

            watched = etree.SubElement(episode, 'watched')
            watched.text = 'false'

            credits = etree.SubElement(episode, 'credits')
            credits_text = getattr(ep_info, 'writer', None)
            if None is not credits_text:
                credits.text = '%s' % credits_text

            director = etree.SubElement(episode, 'director')
            director_text = getattr(ep_info, 'director', None)
            if None is not director_text:
                director.text = '%s' % director_text

            rating = etree.SubElement(episode, 'rating')
            rating_text = getattr(ep_info, 'rating', None)
            if None is not rating_text:
                rating.text = '%s' % rating_text

            gueststar_text = getattr(ep_info, 'gueststars', None)
            if isinstance(gueststar_text, string_types):
                for actor in (x.strip() for x in gueststar_text.split('|')
                              if x.strip()):
                    cur_actor = etree.SubElement(episode, 'actor')
                    cur_actor_name = etree.SubElement(cur_actor, 'name')
                    cur_actor_name.text = '%s' % actor

            self.add_actor_element(show_info, etree, episode)

        # Make it purdy
        sg_helpers.indent_xml(rootNode)

        data = etree.ElementTree(rootNode)

        return data
Example #28
0
    def _ep_data(self, ep_obj):
        # type: (sickbeard.tv.TVEpisode) -> Optional[Union[bool, etree.Element]]
        """
        Creates an elementTree XML structure for a MediaBrowser style episode.xml
        and returns the resulting data object.

        show_obj: a TVShow instance to create the NFO for
        """

        ep_obj_list_to_write = [ep_obj] + ep_obj.related_ep_obj

        show_lang = ep_obj.show_obj.lang

        try:
            # There's gotta be a better way of doing this but we don't wanna
            # change the language value elsewhere
            tvinfo_config = sickbeard.TVInfoAPI(
                ep_obj.show_obj.tvid).api_params.copy()

            if show_lang and not 'en' == show_lang:
                tvinfo_config['language'] = show_lang

            if 0 != ep_obj.show_obj.dvdorder:
                tvinfo_config['dvdorder'] = True

            t = sickbeard.TVInfoAPI(
                ep_obj.show_obj.tvid).setup(**tvinfo_config)
            show_info = t[ep_obj.show_obj.prodid]
        except BaseTVinfoShownotfound as e:
            raise exceptions_helper.ShowNotFoundException(ex(e))
        except BaseTVinfoError as e:
            logger.log(
                'Unable to connect to %s while creating meta files - skipping - %s'
                % (sickbeard.TVInfoAPI(ep_obj.show_obj.tvid).name, ex(e)),
                logger.ERROR)
            return False

        if not self._valid_show(show_info, ep_obj.show_obj):
            return

        rootNode = etree.Element('details')
        movie = etree.SubElement(rootNode, 'movie')

        movie.attrib['isExtra'] = 'false'
        movie.attrib['isSet'] = 'false'
        movie.attrib['isTV'] = 'true'

        # write an MediaBrowser XML containing info for all matching episodes
        for cur_ep_obj in ep_obj_list_to_write:

            try:
                ep_info = show_info[cur_ep_obj.season][cur_ep_obj.episode]
            except (BaseException, Exception):
                logger.log(
                    u'Unable to find episode %sx%s on tvdb... has it been removed? Should I delete from db?'
                    % (cur_ep_obj.season, cur_ep_obj.episode))
                return None

            if cur_ep_obj == ep_obj:
                # root (or single) episode

                # default to today's date for specials if firstaired is not set
                if None is ep_info['firstaired'] and 0 == ep_obj.season:
                    ep_info['firstaired'] = str(datetime.date.fromordinal(1))

                if None is ep_info['episodename'] or None is ep_info[
                        'firstaired']:
                    return None

                episode = movie

                EpisodeName = etree.SubElement(episode, 'title')
                if None is not cur_ep_obj.name:
                    EpisodeName.text = '%s' % cur_ep_obj.name
                else:
                    EpisodeName.text = ''

                SeasonNumber = etree.SubElement(episode, 'season')
                SeasonNumber.text = str(cur_ep_obj.season)

                EpisodeNumber = etree.SubElement(episode, 'episode')
                EpisodeNumber.text = str(ep_obj.episode)

                year = etree.SubElement(episode, 'year')
                year_text = self.get_show_year(ep_obj.show_obj, show_info)
                if year_text:
                    year.text = '%s' % year_text

                plot = etree.SubElement(episode, 'plot')
                if None is not show_info['overview']:
                    plot.text = '%s' % show_info['overview']

                Overview = etree.SubElement(episode, 'episodeplot')
                if None is not cur_ep_obj.description:
                    Overview.text = '%s' % cur_ep_obj.description
                else:
                    Overview.text = ''

                mpaa = etree.SubElement(episode, 'mpaa')
                if None is not show_info['contentrating']:
                    mpaa.text = '%s' % show_info['contentrating']

                if not ep_obj.related_ep_obj:
                    if None is not ep_info['rating']:
                        try:
                            rating = int((float(ep_info['rating']) * 10))
                        except ValueError:
                            rating = 0
                        Rating = etree.SubElement(episode, 'rating')
                        rating_text = str(rating)
                        if None is not rating_text:
                            Rating.text = '%s' % rating_text

                director = etree.SubElement(episode, 'director')
                director_text = ep_info['director']
                if None is not director_text:
                    director.text = '%s' % director_text

                credits = etree.SubElement(episode, 'credits')
                credits_text = ep_info['writer']
                if None is not credits_text:
                    credits.text = '%s' % credits_text

                cast = etree.SubElement(episode, 'cast')
                self.add_actor_element(show_info, etree, cast)

            else:
                # append data from (if any) related episodes

                if cur_ep_obj.name:
                    if not EpisodeName.text:
                        EpisodeName.text = '%s' % cur_ep_obj.name
                    else:
                        EpisodeName.text = '%s, %s' % (EpisodeName.text,
                                                       cur_ep_obj.name)

                if cur_ep_obj.description:
                    if not Overview.text:
                        Overview.text = '%s' % cur_ep_obj.description
                    else:
                        Overview.text = '%s\r%s' % (Overview.text,
                                                    cur_ep_obj.description)

        sg_helpers.indent_xml(rootNode)
        data = etree.ElementTree(rootNode)

        return data
Example #29
0
    def _show_data(self, show_obj):
        # type: (sickbeard.tv.TVShow) -> Optional[Union[bool, etree.Element]]
        """
        Creates an elementTree XML structure for an XBMC-style tvshow.nfo and
        returns the resulting data object.

        show_obj: a TVShow instance to create the NFO for
        """

        show_id = show_obj.prodid

        show_lang = show_obj.lang
        tvinfo_config = sickbeard.TVInfoAPI(show_obj.tvid).api_params.copy()

        tvinfo_config['actors'] = True

        if show_lang and not 'en' == show_lang:
            tvinfo_config['language'] = show_lang

        if 0 != show_obj.dvdorder:
            tvinfo_config['dvdorder'] = True

        t = sickbeard.TVInfoAPI(show_obj.tvid).setup(**tvinfo_config)

        tv_node = etree.Element('tvshow')

        try:
            show_info = t[int(show_id)]
        except BaseTVinfoShownotfound as e:
            logger.log(
                'Unable to find show with id %s on %s, skipping it' %
                (show_id, sickbeard.TVInfoAPI(show_obj.tvid).name),
                logger.ERROR)
            raise e
        except BaseTVinfoError as e:
            logger.log(
                '%s is down, can\'t use its data to add this show' %
                sickbeard.TVInfoAPI(show_obj.tvid).name, logger.ERROR)
            raise e

        if not self._valid_show(show_info, show_obj):
            return

        # check for title and id
        if None is getattr(show_info, 'seriesname', None) or None is getattr(
                show_info, 'id', None):
            logger.log(
                'Incomplete info for show with id %s on %s, skipping it' %
                (show_id, sickbeard.TVInfoAPI(show_obj.tvid).name),
                logger.ERROR)
            return False

        title = etree.SubElement(tv_node, 'title')
        if None is not getattr(show_info, 'seriesname', None):
            title.text = '%s' % show_info['seriesname']

        rating = etree.SubElement(tv_node, 'rating')
        if None is not getattr(show_info, 'rating', None):
            rating.text = '%s' % show_info['rating']

        year = etree.SubElement(tv_node, 'year')
        year_text = self.get_show_year(show_obj, show_info)
        if year_text:
            year.text = '%s' % year_text

        plot = etree.SubElement(tv_node, 'plot')
        if None is not getattr(show_info, 'overview', None):
            plot.text = '%s' % show_info['overview']

        episodeguide = etree.SubElement(tv_node, 'episodeguide')
        episodeguideurl = etree.SubElement(episodeguide, 'url')
        episodeguideurl2 = etree.SubElement(tv_node, 'episodeguideurl')
        if None is not getattr(show_info, 'id', None):
            showurl = sickbeard.TVInfoAPI(
                show_obj.tvid).config['base_url'] + str(
                    show_info['id']) + '/all/en.zip'
            episodeguideurl.text = '%s' % showurl
            episodeguideurl2.text = '%s' % showurl

        mpaa = etree.SubElement(tv_node, 'mpaa')
        if None is not getattr(show_info, 'contentrating', None):
            mpaa.text = '%s' % show_info['contentrating']

        prodid = etree.SubElement(tv_node, 'id')
        if None is not getattr(show_info, 'id', None):
            prodid.text = str(show_info['id'])

        tvid = etree.SubElement(tv_node, 'indexer')
        if None is not show_obj.tvid:
            tvid.text = str(show_obj.tvid)

        genre = etree.SubElement(tv_node, 'genre')
        if None is not getattr(show_info, 'genre', None):
            if isinstance(show_info['genre'], string_types):
                genre.text = ' / '.join(x.strip()
                                        for x in show_info['genre'].split('|')
                                        if x.strip())

        premiered = etree.SubElement(tv_node, 'premiered')
        if None is not getattr(show_info, 'firstaired', None):
            premiered.text = '%s' % show_info['firstaired']

        studio = etree.SubElement(tv_node, 'studio')
        if None is not getattr(show_info, 'network', None):
            studio.text = '%s' % show_info['network']

        self.add_actor_element(show_info, etree, tv_node)

        # Make it purdy
        sg_helpers.indent_xml(tv_node)

        data = etree.ElementTree(tv_node)

        return data
Example #30
0
    def _retrieve_show_image(self, image_type, show_obj, which=None):
        # type: (AnyStr, sickbeard.tv.TVShow, bool) -> Optional[bytes, List[AnyStr]]
        """
        Gets an image URL from theTVDB.com, fanart.tv and TMDB.com, downloads it and returns the data.
        If type is fanart, multiple image src urls are returned instead of a single data image.

        image_type: type of image to retrieve (currently supported: fanart, poster, banner, poster_thumb, banner_thumb)
        show_obj: a TVShow object to use when searching for the image
        which: optional, a specific numbered poster to look for

        Returns: the binary image data if available, or else None
        """
        show_lang = show_obj.lang

        try:
            # There's gotta be a better way of doing this but we don't wanna
            # change the language value elsewhere
            tvinfo_config = sickbeard.TVInfoAPI(show_obj.tvid).api_params.copy()
            if image_type.startswith('fanart'):
                tvinfo_config['fanart'] = True
            elif image_type.startswith('poster'):
                tvinfo_config['posters'] = True
            else:
                tvinfo_config['banners'] = True
            tvinfo_config['dvdorder'] = 0 != show_obj.dvdorder

            if show_lang and not 'en' == show_lang:
                tvinfo_config['language'] = show_lang

            t = sickbeard.TVInfoAPI(show_obj.tvid).setup(**tvinfo_config)
            show_info = t[show_obj.prodid, False]
        except (BaseTVinfoError, IOError) as e:
            logger.log(u"Unable to look up show on " + sickbeard.TVInfoAPI(
                show_obj.tvid).name + ", not downloading images: " + ex(e), logger.WARNING)
            return None

        if not self._valid_show(show_info, show_obj):
            return None

        return_links = False
        if 'fanart_all' == image_type:
            return_links = True
            image_type = 'fanart'

        if image_type not in ('poster', 'banner', 'fanart', 'poster_thumb', 'banner_thumb'):
            logger.log(u"Invalid image type " + str(image_type) + ", couldn't find it in the " + sickbeard.TVInfoAPI(
                show_obj.tvid).name + " object", logger.ERROR)
            return None

        image_urls = []
        init_url = None
        alt_tvdb_urls = []

        def build_url(s_o, image_mode):
            _urls = [[], []]
            _url = s_o[image_mode]
            if _url and _url.startswith('http'):
                if 'poster' == image_mode:
                    _url = re.sub('posters', '_cache/posters', _url)
                elif 'banner' == image_mode:
                    _url = re.sub('graphical', '_cache/graphical', _url)
                _urls[0].append(_url)

                try:
                    alt_url = '%swww.%s%s' % re.findall(
                        r'(https?://)(?:artworks\.)?(thetvdb\.[^/]+/banners/[^\d]+[^.]+)(?:_t)(.*)', _url)[0][0:3]
                    if alt_url not in _urls[0]:
                        _urls[1].append(alt_url)
                except (IndexError, Exception):
                    try:
                        alt_url = '%sartworks.%s_t%s' % re.findall(
                            r'(https?://)(?:www\.)?(thetvdb\.[^/]+/banners/[^\d]+[^.]+)(.*)', _url)[0][0:3]
                        if alt_url not in _urls[0]:
                            _urls[1].append(alt_url)
                    except (IndexError, Exception):
                        pass
            return _urls

        if 'poster_thumb' == image_type:
            if None is not getattr(show_info, image_type, None):
                image_urls, alt_tvdb_urls = build_url(show_info, image_type)
            elif None is not getattr(show_info, 'poster', None):
                image_urls, alt_tvdb_urls = build_url(show_info, 'poster')

            for item in self._fanart_urls_from_show(show_obj, image_type, show_lang, True) or []:
                image_urls.append(item[2])
            if 0 == len(image_urls):
                for item in self._tmdb_image_url(show_obj, image_type) or []:
                    image_urls.append(item[2])

        elif 'banner_thumb' == image_type:
            if None is not getattr(show_info, image_type, None):
                image_urls, alt_tvdb_urls = build_url(show_info, image_type)
            elif None is not getattr(show_info, 'banner', None):
                image_urls, alt_tvdb_urls = build_url(show_info, 'banner')
            for item in self._fanart_urls_from_show(show_obj, image_type, show_lang, True) or []:
                image_urls.append(item[2])
        else:
            for item in self._fanart_urls_from_show(show_obj, image_type, show_lang) or []:
                image_urls.append(item[2])

            if None is not getattr(show_info, image_type, None):
                image_url = show_info[image_type]
                if image_url:
                    image_urls.append(image_url)
                    if 'poster' == image_type:
                        init_url = image_url

            # check extra provided images in '_banners' key
            if None is not getattr(show_info, '_banners', None) and \
                    isinstance(show_info['_banners'].get(image_type, None), (list, dict)):
                for res, value in iteritems(show_info['_banners'][image_type]):
                    for item in itervalues(value):
                        image_urls.append(item['bannerpath'])

            if 0 == len(image_urls) or 'fanart' == image_type:
                for item in self._tmdb_image_url(show_obj, image_type) or []:
                    image_urls.append('%s?%s' % (item[2], item[0]))

        img_data = len(image_urls) or None
        if img_data:
            # remove duplicates, keeping order
            image_urls = list(OrderedDict.fromkeys(image_urls))
            if return_links:
                return image_urls
            else:
                img_data = metadata_helpers.getShowImage(
                    (init_url, image_urls[0])[None is init_url], which, show_obj.name, bool(len(alt_tvdb_urls)))
                if None is img_data and len(alt_tvdb_urls):
                    for url in alt_tvdb_urls:
                        image_data = metadata_helpers.getShowImage(
                            (init_url, url)[None is init_url], which, show_obj.name)
                        if None is not image_data:
                            break

        if None is not img_data:
            return img_data

        if None is not img_data:
            return img_data