Esempio n. 1
0
def _get_custom_exceptions(force):
    custom_exceptions = defaultdict(dict)

    if force or should_refresh('custom_exceptions'):
        for indexer in indexerApi().indexers:
            location = indexerApi(indexer).config['scene_loc']
            logger.info(
                'Checking for scene exception updates from {location}',
                {'location': location}
            )
            try:
                # When any Medusa Safe session exception, session returns None and then AttributeError when json()
                jdata = safe_session.get(location, timeout=60).json()
            except (ValueError, AttributeError) as error:
                logger.debug(
                    'Check scene exceptions update failed. Unable to '
                    'update from {location}. Error: {error}',
                    {'location': location, 'error': error}
                )
                # If unable to get scene exceptions, assume we can't connect to CDN so we don't `continue`
                return custom_exceptions

            indexer_ids = jdata[indexerApi(indexer).config['identifier']]
            for indexer_id in indexer_ids:
                indexer_exceptions = indexer_ids[indexer_id]
                alias_list = [{exception: int(season)}
                              for season in indexer_exceptions
                              for exception in indexer_exceptions[season]]
                custom_exceptions[indexer][indexer_id] = alias_list

            set_last_refresh('custom_exceptions')

    return custom_exceptions
Esempio n. 2
0
def _get_custom_exceptions(force):
    custom_exceptions = defaultdict(dict)

    if force or should_refresh('custom_exceptions'):
        for indexer in indexerApi().indexers:
            location = indexerApi(indexer).config['scene_loc']
            logger.info(
                'Checking for scene exception updates from {location}',
                {'location': location}
            )
            try:
                # When any Medusa Safe session exception, session returns None and then AttributeError when json()
                jdata = safe_session.get(location, timeout=60).json()
            except (ValueError, AttributeError) as error:
                logger.debug(
                    'Check scene exceptions update failed. Unable to '
                    'update from {location}. Error: {error}',
                    {'location': location, 'error': error}
                )
                # If unable to get scene exceptions, assume we can't connect to CDN so we don't `continue`
                return custom_exceptions

            indexer_ids = jdata[indexerApi(indexer).config['identifier']]
            for indexer_id in indexer_ids:
                indexer_exceptions = indexer_ids[indexer_id]
                alias_list = [{exception: int(season)}
                              for season in indexer_exceptions
                              for exception in indexer_exceptions[season]]
                custom_exceptions[indexer][indexer_id] = alias_list

            set_last_refresh('custom_exceptions')

    return custom_exceptions
Esempio n. 3
0
def get_externals(show=None, indexer=None, indexed_show=None):
    """Use as much as possible sources to map known id's.

    Provide the external id's you have in a dictionary, and use as much available resources as possible to retrieve
    external id's.
    :param show: Series object.
    :param indexer: Indexer id. For example 1 for tvdb or 4 for tmdb.
    :param indexed_show: The result of a fully indexed shows. For example after an t['12345']
    """
    if show:
        indexer = show.indexer
        new_show_externals = show.externals
    else:
        if not indexer or not indexed_show:
            raise Exception(
                'Need a minimum of a show object or an indexer + indexer_api '
                '(Show searched through indexerApi.')
        new_show_externals = getattr(indexed_show, 'externals', {})

    # For this show let's get all externals, and use them.
    mappings = {
        indexer: indexerConfig[indexer]['mapped_to']
        for indexer in indexerConfig
    }
    other_indexers = [
        mapped_indexer for mapped_indexer in mappings
        if mapped_indexer != indexer
    ]

    # We for example want to add through tmdb, but the show is already added through tvdb.
    # If tmdb doesn't have a mapping to imdb, but tvmaze does, there is a small chance we can use that.

    for other_indexer in other_indexers:
        lindexer_api_pararms = indexerApi(other_indexer).api_params.copy()
        try:
            t = indexerApi(other_indexer).indexer(**lindexer_api_pararms)
        except IndexerUnavailable:
            continue
        if hasattr(t, 'get_id_by_external'):
            log.debug(u"Trying other indexer: {indexer} get_id_by_external",
                      {'indexer': indexerApi(other_indexer).name})
            # Call the get_id_by_external and pass all the externals we have,
            # except for the indexers own.
            try:
                new_show_externals.update(
                    t.get_id_by_external(**new_show_externals))
            except IndexerException as error:
                log.warning(
                    u'Error getting external ids for other'
                    u' indexer {name}: {reason}', {
                        'name': indexerApi(show.indexer).name,
                        'reason': error.message
                    })

    # Try to update with the Trakt externals.
    if app.USE_TRAKT:
        new_show_externals.update(get_trakt_externals(new_show_externals))

    return new_show_externals
Esempio n. 4
0
def _get_xem_exceptions(force):
    xem_exceptions = defaultdict(dict)
    xem_url = 'http://thexem.de/map/allNames?origin={0}&seasonNumbers=1'

    if force or should_refresh('xem'):
        for indexer in indexerApi().indexers:
            indexer_api = indexerApi(indexer)

            # Not query XEM for unsupported indexers
            if not indexer_api.config.get('xem_origin'):
                continue

            logger.info(
                'Checking for XEM scene exceptions updates for'
                ' {indexer_name}'.format(indexer_name=indexer_api.name))

            url = xem_url.format(indexer_api.config['xem_origin'])
            response = helpers.get_url(url,
                                       session=xem_session,
                                       timeout=60,
                                       returns='response')
            try:
                jdata = response.json()
            except (ValueError, AttributeError) as error:
                logger.debug(
                    'Check scene exceptions update failed for {indexer}.'
                    ' Unable to get URL: {url} Error: {error}'.format(
                        indexer=indexer_api.name,
                        url=url,
                        error=error,
                    ))
                continue

            if not jdata['data'] or jdata['result'] == 'failure':
                logger.debug(
                    'No data returned from XEM while checking for scene'
                    ' exceptions. Update failed for {indexer}'.format(
                        indexer=indexer_api.name))
                continue

            for indexer_id, exceptions in iteritems(jdata['data']):
                try:
                    xem_exceptions[indexer][indexer_id] = exceptions
                except Exception as error:
                    logger.warning(
                        'XEM: Rejected entry: Indexer ID: {indexer_id},'
                        ' Exceptions: {e}'.format(indexer_id=indexer_id,
                                                  e=exceptions))
                    logger.warning('XEM: Rejected entry error message:'
                                   ' {error}'.format(error=error))

        set_last_refresh('xem')

    return xem_exceptions
Esempio n. 5
0
def check_existing_shows(indexed_show, indexer):
    """Check if the searched show already exists in the current library.

    :param indexed_show: (Indexer Show object) The indexed show from -for example- tvdb. It might already have some
    externals like imdb_id which can be used to search at tmdb, tvmaze or trakt.
    :param indexer: (int) The indexer id, which has been used to search the indexed_show with.
    :return: Raises the exception IndexerShowAllreadyInLibrary() when the show is already in your library.
    """
    # For this show let's get all externals, and use them.
    mappings = {indexer: indexerConfig[indexer]['mapped_to'] for indexer in indexerConfig}
    other_indexers = [mapped_indexer for mapped_indexer in mappings if mapped_indexer != indexer]

    new_show_externals = get_externals(indexer=indexer, indexed_show=indexed_show)

    # Iterate through all shows in library, and see if one of our externals matches it's indexer_id
    # Or one of it's externals.
    for show in app.showList:

        # Check if the new shows indexer id matches the external for the show
        # in library
        if show.externals.get(mappings[indexer]) and indexed_show['id'] == show.externals.get(mappings[indexer]):
            log.debug(u'Show already in database. [{id}] {name}',
                      {'name': show.name, 'id': indexed_show['id']})
            raise IndexerShowAllreadyInLibrary('The show {0} has already been added by the indexer {1}. '
                                               'Please remove the show, before you can add it through {2}.'
                                               .format(show.name, indexerApi(show.indexer).name,
                                                       indexerApi(indexer).name))

        for new_show_external_key in list(new_show_externals):
            if show.indexer not in other_indexers:
                continue

            # Check if one of the new shows externals matches one of the
            # externals for the show in library.
            if not new_show_externals.get(new_show_external_key) or not show.externals.get(new_show_external_key):
                continue

            if new_show_externals.get(new_show_external_key) == show.externals.get(new_show_external_key):
                log.debug(
                    u'Show already in database under external ID ({existing})'
                    u' for ({id}) {name}', {
                        'name': show.name,
                        'id': show.externals.get(new_show_external_key),
                        'existing': new_show_external_key,
                    }
                )
                raise IndexerShowAllreadyInLibrary('The show {0} has already been added by the indexer {1}. '
                                                   'Please remove the show, before you can add it through {2}.'
                                                   .format(show.name, indexerApi(show.indexer).name,
                                                           indexerApi(indexer).name))
Esempio n. 6
0
def check_existing_shows(indexed_show, indexer):
    """Check if the searched show already exists in the current library.

    :param indexed_show: (Indexer Show object) The indexed show from -for example- tvdb. It might already have some
    externals like imdb_id which can be used to search at tmdb, tvmaze or trakt.
    :param indexer: (int) The indexer id, which has been used to search the indexed_show with.
    :return: Raises the exception IndexerShowAlreadyInLibrary() when the show is already in your library.
    """
    # For this show let's get all externals, and use them.
    mappings = {indexer: indexerConfig[indexer]['mapped_to'] for indexer in indexerConfig}
    other_indexers = [mapped_indexer for mapped_indexer in mappings if mapped_indexer != indexer]

    new_show_externals = get_externals(indexer=indexer, indexed_show=indexed_show)

    # Iterate through all shows in library, and see if one of our externals matches it's indexer_id
    # Or one of it's externals.
    for show in app.showList:

        # Check if the new shows indexer id matches the external for the show
        # in library
        if show.externals.get(mappings[indexer]) and indexed_show['id'] == show.externals.get(mappings[indexer]):
            log.debug(u'Show already in database. [{id}] {name}',
                      {'name': show.name, 'id': indexed_show['id']})
            raise IndexerShowAlreadyInLibrary('The show {0} has already been added by the indexer {1}. '
                                              'Please remove the show, before you can add it through {2}.'
                                              .format(show.name, indexerApi(show.indexer).name,
                                                      indexerApi(indexer).name))

        for new_show_external_key in list(new_show_externals):
            if show.indexer not in other_indexers:
                continue

            # Check if one of the new shows externals matches one of the
            # externals for the show in library.
            if not new_show_externals.get(new_show_external_key) or not show.externals.get(new_show_external_key):
                continue

            if new_show_externals.get(new_show_external_key) == show.externals.get(new_show_external_key):
                log.debug(
                    u'Show already in database under external ID ({existing})'
                    u' for ({id}) {name}', {
                        'name': show.name,
                        'id': show.externals.get(new_show_external_key),
                        'existing': new_show_external_key,
                    }
                )
                raise IndexerShowAlreadyInLibrary('The show {0} has already been added by the indexer {1}. '
                                                  'Please remove the show, before you can add it through {2}.'
                                                  .format(show.name, indexerApi(show.indexer).name,
                                                          indexerApi(indexer).name))
Esempio n. 7
0
def get_externals(show=None, indexer=None, indexed_show=None):
    """Use as much as possible sources to map known id's.

    Provide the external id's you have in a dictionary, and use as much available resources as possible to retrieve
    external id's.
    :param show: Series object.
    :param indexer: Indexer id. For example 1 for tvdb or 4 for tmdb.
    :param indexed_show: The result of a fully indexed shows. For example after an t['12345']
    """
    if show:
        indexer = show.indexer
        new_show_externals = show.externals
    else:
        if not indexer or not indexed_show:
            raise Exception('Need a minimum of a show object or an indexer + indexer_api '
                            '(Show searched through indexerApi.')
        new_show_externals = getattr(indexed_show, 'externals', {})

    # For this show let's get all externals, and use them.
    mappings = {indexer: indexerConfig[indexer]['mapped_to'] for indexer in indexerConfig}
    other_indexers = [mapped_indexer for mapped_indexer in mappings if mapped_indexer != indexer]

    # We for example want to add through tmdb, but the show is already added through tvdb.
    # If tmdb doesn't have a mapping to imdb, but tvmaze does, there is a small chance we can use that.

    for other_indexer in other_indexers:
        lindexer_api_pararms = indexerApi(other_indexer).api_params.copy()
        try:
            t = indexerApi(other_indexer).indexer(**lindexer_api_pararms)
        except IndexerUnavailable:
            continue
        if hasattr(t, 'get_id_by_external'):
            log.debug(u'Trying other indexer: {indexer} get_id_by_external',
                      {'indexer': indexerApi(other_indexer).name})
            # Call the get_id_by_external and pass all the externals we have,
            # except for the indexers own.
            try:
                new_show_externals.update(t.get_id_by_external(**new_show_externals))
            except (IndexerException, RequestException) as error:
                log.warning(
                    u'Error getting external ids for other'
                    u' indexer {name}: {reason!r}',
                    {'name': indexerApi(other_indexer).name, 'reason': error})

    # Try to update with the Trakt externals.
    if app.USE_TRAKT:
        new_show_externals.update(get_trakt_externals(new_show_externals))

    return new_show_externals
Esempio n. 8
0
 def __init__(self):
     """Initialize the trakt recommended list object."""
     self.cache_subfolder = __name__.split(
         '.')[-1] if '.' in __name__ else __name__
     self.recommender = "Trakt Popular"
     self.default_img_src = 'trakt-default.png'
     self.tvdb_api_v2 = indexerApi(INDEXER_TVDBV2).indexer()
Esempio n. 9
0
    def _get_show_data(self, show_obj):
        """
        Retrieve show data from the indexer.

        Try to reuse the indexer_api class instance attribute.
        As we are reusing the indexers results, we need to do a full index including actors and images.

        :param show_obj: A TVshow object.
        :return: A re-indexed show object.
        """

        show_id = show_obj.indexerid

        try:
            if not (show_obj.indexer_api and all([
                    show_obj.indexer_api.config[u'banners_enabled'],
                    show_obj.indexer_api.config[u'actors_enabled']
            ])):
                show_obj.create_indexer(banners=True, actors=True)

            self.indexer_api = show_obj.indexer_api
            my_show = self.indexer_api[int(show_id)]
        except IndexerShowNotFound:
            log.warning(u'Unable to find {indexer} show {id}, skipping it', {
                u'indexer': indexerApi(show_obj.indexer).name,
                u'id': show_id
            })
            return False

        except (IndexerException, RequestException):
            log.warning(
                u'{indexer} is down, cannot use its data to add this show',
                {u'indexer': indexerApi(show_obj.indexer).name})
            return False

        # check for title and id
        if not (getattr(my_show, u'seriesname', None)
                and getattr(my_show, u'id', None)):
            log.warning(
                u'Incomplete info for {indexer} show {id}, skipping it', {
                    u'indexer': indexerApi(show_obj.indexer).name,
                    u'id': show_id
                })
            return False

        return my_show
Esempio n. 10
0
 def __init__(self):
     """Initialize the trakt recommended list object."""
     self.cache_subfolder = __name__.split(
         '.')[-1] if '.' in __name__ else __name__
     self.session = requests.Session()
     self.recommender = "Trakt Popular"
     self.default_img_src = 'trakt-default.png'
     self.anidb = Anidb(cache_dir=app.CACHE_DIR)
     self.tvdb_api_v2 = indexerApi(INDEXER_TVDBV2).indexer()
Esempio n. 11
0
def _get_custom_exceptions(force):
    custom_exceptions = defaultdict(dict)

    if force or should_refresh('custom_exceptions'):
        for indexer in indexerApi().indexers:
            try:
                location = indexerApi(indexer).config['scene_loc']
                logger.info(
                    'Checking for scene exception updates from {location}',
                    location=location
                )

                try:
                    jdata = indexerApi(indexer).session.get(location, timeout=60).json()
                except (ValueError, AttributeError, RequestException) as error:
                    logger.debug(
                        'Check scene exceptions update failed. Unable to '
                        'update from {location}. Error: {error}'.format(
                            location=location, error=error
                        )
                    )
                    return custom_exceptions

                indexer_ids = jdata[indexerApi(indexer).config['identifier']]
                for indexer_id in indexer_ids:
                    indexer_exceptions = indexer_ids[indexer_id]
                    alias_list = [{exception: int(season)}
                                  for season in indexer_exceptions
                                  for exception in indexer_exceptions[season]]
                    custom_exceptions[indexer][indexer_id] = alias_list
            except Exception as error:
                logger.error(
                    'Unable to update scene exceptions for {indexer}.'
                    ' Error: {error}'.format(
                        indexer=indexer, error=error
                    )
                )
                continue

            set_last_refresh('custom_exceptions')

    return custom_exceptions
Esempio n. 12
0
    def _retrieve_show_image(self,
                             image_type,
                             show_obj,
                             episode=None,
                             which=None):
        """
        Get an image URL from theTVDB.com and TMDB.com, download it and returns the data.

        :param image_type: type of image to retrieve (currently supported: fanart, poster, banner)
        :param show_obj: a Series object to use when searching for the image
        :param episode: Episode object (only needed for episode thumbnails)
        :param which: optional, a specific numbered poster to look for
        :return: the binary image data if available, or else None
        """
        image_url = None

        indexer_show_obj = self._get_show_data(show_obj)

        if image_type not in (u'fanart', u'poster', u'banner', u'thumbnail',
                              u'poster_thumb', u'banner_thumb'):
            log.error(
                u'Invalid {image}, unable to find it in the {indexer}', {
                    u'image': image_type,
                    u'indexer': indexerApi(show_obj.indexer).name
                })
            return None

        if image_type == u'thumbnail' and episode:
            image_url = self._get_episode_thumb_url(indexer_show_obj, episode)
        elif image_type == u'poster_thumb':
            if getattr(indexer_show_obj, u'poster', None):
                image_url = re.sub(u'posters', u'_cache/posters',
                                   indexer_show_obj[u'poster'])
            if not image_url:
                # Try and get images from TMDB
                image_url = self._retrieve_show_images_from_tmdb(
                    show_obj, image_type)
        elif image_type == u'banner_thumb':
            if getattr(indexer_show_obj, u'banner', None):
                image_url = re.sub(u'graphical', u'_cache/graphical',
                                   indexer_show_obj[u'banner'])
        else:
            if getattr(indexer_show_obj, image_type, None):
                image_url = indexer_show_obj[image_type]
            if not image_url and show_obj.indexer != 4:
                # Try and get images from TMDB
                image_url = self._retrieve_show_images_from_tmdb(
                    show_obj, image_type)

        if image_url:
            image_data = get_image(image_url, which)
            return image_data

        return None
Esempio n. 13
0
    def searchIndexersForShowName(search_term, lang=None, indexer=None):
        if not lang or lang == 'null':
            lang = app.INDEXER_DEFAULT_LANGUAGE

        search_term = search_term.encode('utf-8')

        search_terms = [search_term]

        # If search term ends with what looks like a year, enclose it in ()
        matches = re.match(r'^(.+ |)([12][0-9]{3})$', search_term)
        if matches:
            search_terms.append('%s(%s)' % (matches.group(1), matches.group(2)))

        for searchTerm in search_terms:
            # If search term begins with an article, let's also search for it without
            matches = re.match(r'^(?:a|an|the) (.+)$', searchTerm, re.I)
            if matches:
                search_terms.append(matches.group(1))

        results = {}
        final_results = []

        # Query Indexers for each search term and build the list of results
        for indexer in indexerApi().indexers if not int(indexer) else [int(indexer)]:
            l_indexer_api_parms = indexerApi(indexer).api_params.copy()
            l_indexer_api_parms['language'] = lang
            l_indexer_api_parms['custom_ui'] = classes.AllShowsListUI
            try:
                indexer_api = indexerApi(indexer).indexer(**l_indexer_api_parms)
            except IndexerUnavailable as msg:
                logger.log(u'Could not initialize Indexer {indexer}: {error}'.
                           format(indexer=indexerApi(indexer).name, error=msg))
                continue

            logger.log(u'Searching for Show with searchterm(s): %s on Indexer: %s' % (
                search_terms, indexerApi(indexer).name), logger.DEBUG)
            for searchTerm in search_terms:
                try:
                    indexer_results = indexer_api[searchTerm]
                    # add search results
                    results.setdefault(indexer, []).extend(indexer_results)
                except IndexerException as e:
                    logger.log(u'Error searching for show: {error}'.format(error=e.message))

        for i, shows in iteritems(results):
            final_results.extend({(indexerApi(i).name, i, indexerApi(i).config['show_url'], int(show['id']),
                                   show['seriesname'].encode('utf-8'), show['firstaired'] or 'N/A',
                                   show.get('network', '').encode('utf-8') or 'N/A') for show in shows})

        lang_id = indexerApi().config['langabbv_to_id'][lang]
        return json.dumps({'results': final_results, 'langid': lang_id})
Esempio n. 14
0
    def newShow(self, show_to_add=None, other_shows=None, search_string=None):
        """
        Display the new show page which collects a tvdb id, folder, and extra options and
        posts them to addNewShow
        """
        t = PageTemplate(rh=self, filename='addShows_newShow.mako')

        indexer, show_dir, indexer_id, show_name = self.split_extra_show(
            show_to_add)
        use_provided_info = bool(indexer_id and indexer and show_name)

        # use the given show_dir for the indexer search if available
        if not show_dir:
            if search_string:
                default_show_name = search_string
            else:
                default_show_name = ''

        elif not show_name:
            default_show_name = re.sub(
                r' \(\d{4}\)', '',
                os.path.basename(os.path.normpath(show_dir)))
        else:
            default_show_name = show_name

        # carry a list of other dirs if given
        if not other_shows:
            other_shows = []
        elif not isinstance(other_shows, list):
            other_shows = [other_shows]

        provided_indexer_id = int(indexer_id or 0)
        provided_indexer_name = show_name

        provided_indexer = int(indexer or app.INDEXER_DEFAULT)

        return t.render(enable_anime_options=True,
                        use_provided_info=use_provided_info,
                        default_show_name=default_show_name,
                        other_shows=other_shows,
                        provided_show_dir=show_dir,
                        provided_indexer_id=provided_indexer_id,
                        provided_indexer_name=provided_indexer_name,
                        provided_indexer=provided_indexer,
                        indexers=indexerApi().indexers,
                        whitelist=[],
                        blacklist=[],
                        groups=[],
                        title='New Show',
                        header='New Show',
                        topmenu='home',
                        controller='addShows',
                        action='newShow')
Esempio n. 15
0
def _get_custom_exceptions():
    custom_exceptions = {}

    if should_refresh('custom_exceptions'):
        for indexer in indexerApi().indexers:
            try:
                location = indexerApi(indexer).config['scene_loc']
                logger.log('Checking for scene exception updates from {0}'.format(location))

                response = helpers.getURL(location, session=indexerApi(indexer).session,
                                          timeout=60, returns='response')
                try:
                    jdata = response.json()
                except (ValueError, AttributeError) as error:
                    logger.log('Check scene exceptions update failed. Unable to update from {0}. Error: {1}'.format
                               (location, error), logger.DEBUG)
                    return custom_exceptions

                if indexer not in custom_exceptions:
                    custom_exceptions[indexer] = {}

                for indexer_id in jdata[indexerApi(indexer).config['identifier']]:
                    alias_list = [{scene_exception: int(scene_season)}
                                  for scene_season in jdata[indexerApi(indexer).config['identifier']][indexer_id]
                                  for scene_exception in jdata[indexerApi(indexer).config
                                  ['identifier']][indexer_id][scene_season]]
                    custom_exceptions[indexer][indexer_id] = alias_list
            except Exception as error:
                logger.log('Unable to update scene exceptions for {0}. Error: {1}'.format
                           (indexer, error), logger.ERROR)
                continue

            set_last_refresh('custom_exceptions')

    return custom_exceptions
Esempio n. 16
0
    def newShow(self, show_to_add=None, other_shows=None, search_string=None):
        """
        Display the new show page which collects a tvdb id, folder, and extra options and
        posts them to addNewShow
        """
        t = PageTemplate(rh=self, filename='addShows_newShow.mako')

        indexer, show_dir, indexer_id, show_name = self.split_extra_show(show_to_add)
        use_provided_info = bool(indexer_id and indexer and show_name)

        # use the given show_dir for the indexer search if available
        if not show_dir:
            if search_string:
                default_show_name = search_string
            else:
                default_show_name = ''

        elif not show_name:
            default_show_name = re.sub(r' \(\d{4}\)', '',
                                       os.path.basename(os.path.normpath(show_dir)))
        else:
            default_show_name = show_name

        # carry a list of other dirs if given
        if not other_shows:
            other_shows = []
        elif not isinstance(other_shows, list):
            other_shows = [other_shows]

        other_shows = decode_shows(other_shows)
        provided_indexer_id = int(indexer_id or 0)
        provided_indexer_name = show_name
        provided_indexer = int(indexer or app.INDEXER_DEFAULT)

        return t.render(
            enable_anime_options=True, use_provided_info=use_provided_info,
            default_show_name=default_show_name, other_shows=other_shows,
            provided_show_dir=show_dir, provided_indexer_id=provided_indexer_id,
            provided_indexer_name=provided_indexer_name, provided_indexer=provided_indexer,
            indexers=indexerApi().indexers, whitelist=[], blacklist=[], groups=[],
            controller='addShows', action='newShow'
        )
Esempio n. 17
0
def _get_xem_exceptions():
    xem_exceptions = {}

    if should_refresh('xem'):
        for indexer in indexerApi().indexers:

            # Not query XEM for unsupported indexers
            if not indexerApi(indexer).config.get('xem_origin'):
                continue

            logger.log('Checking for XEM scene exceptions updates for {0}'.format
                       (indexerApi(indexer).name))

            if indexer not in xem_exceptions:
                xem_exceptions[indexer] = {}

            xem_url = 'http://thexem.de/map/allNames?origin={0}&seasonNumbers=1'.format(
                indexerApi(indexer).config['xem_origin'])

            response = helpers.getURL(xem_url, session=xem_session, timeout=60, returns='response')
            try:
                jdata = response.json()
            except (ValueError, AttributeError) as error:
                logger.log('Check scene exceptions update failed for {0}. Unable to get URL: {1}'.format
                           (indexerApi(indexer).name, xem_url), logger.DEBUG)
                continue

            if not jdata['data'] or jdata['result'] == 'failure':
                logger.log('No data returned from XEM while checking for scene exceptions. '
                           'Update failed for {0}'.format(indexerApi(indexer).name), logger.DEBUG)
                continue

            for indexer_id, exceptions in iteritems(jdata['data']):
                try:
                    xem_exceptions[indexer][indexer_id] = exceptions
                except Exception as error:
                    logger.log('XEM: Rejected entry: Indexer ID: {0}, Exceptions: {1}'.format
                               (indexer_id, exceptions), logger.WARNING)
                    logger.log('XEM: Rejected entry error message: {0}'.format(error), logger.ERROR)

        set_last_refresh('xem')

    return xem_exceptions
Esempio n. 18
0
    def run(self):

        ShowQueueItem.run(self)

        logger.log(
            u'{id}: Beginning update of {show}'.format(id=self.show.indexerid,
                                                       show=self.show.name),
            logger.DEBUG)

        logger.log(
            u'{id}: Retrieving show info from {indexer}'.format(
                id=self.show.indexerid,
                indexer=indexerApi(self.show.indexer).name), logger.DEBUG)
        try:
            # Let's make sure we refresh the indexer_api object attached to the show object.
            self.show.create_indexer()
            self.show.load_from_indexer()
        except IndexerError as e:
            logger.log(
                u'{id}: Unable to contact {indexer}. Aborting: {error_msg}'.
                format(id=self.show.indexerid,
                       indexer=indexerApi(self.show.indexer).name,
                       error_msg=e.message), logger.WARNING)
            return
        except IndexerAttributeNotFound as e:
            logger.log(
                u'{id}: Data retrieved from {indexer} was incomplete. Aborting: {error_msg}'
                .format(id=self.show.indexerid,
                        indexer=indexerApi(self.show.indexer).name,
                        error_msg=e.message), logger.WARNING)
            return
        except IndexerShowNotFoundInLanguage as e:
            logger.log(
                u'{id}: Data retrieved from {indexer} was incomplete. The indexer does not provide '
                u'show information in the searched language {language}. Aborting: {error_msg}'
                .format(id=self.show.indexerid,
                        indexer=indexerApi(self.show.indexer).name,
                        language=e.language,
                        error_msg=e.message), logger.WARNING)
            ui.notifications.error(
                'Error changing language show!',
                'Unable to change language for show {show_name} on {indexer} to language: {language}'
                .format(show_name=self.show.name,
                        indexer=indexerApi(self.show.indexer).name,
                        language=e.language))
            return

        logger.log(
            u'{id}: Retrieving show info from IMDb'.format(
                id=self.show.indexerid), logger.DEBUG)
        try:
            self.show.load_imdb_info()
        except ImdbAPIError as e:
            logger.log(
                u'{id}: Something wrong on IMDb api: {error_msg}'.format(
                    id=self.show.indexerid, error_msg=e.message), logger.INFO)
        except Exception as e:
            logger.log(
                u'{id}: Error loading IMDb info: {error_msg}'.format(
                    id=self.show.indexerid, error_msg=e.message),
                logger.WARNING)

        # have to save show before reading episodes from db
        try:
            logger.log(
                u'{id}: Saving new IMDb show info to database'.format(
                    id=self.show.indexerid), logger.DEBUG)
            self.show.save_to_db()
        except Exception as e:
            logger.log(
                u"{id}: Error saving new IMDb show info to database: {error_msg}"
                .format(id=self.show.indexerid,
                        error_msg=e.message), logger.WARNING)
            logger.log(traceback.format_exc(), logger.ERROR)

        # get episode list from DB
        try:
            episodes_from_db = self.show.load_episodes_from_db()
        except IndexerException as e:
            logger.log(
                u'{id}: Unable to contact {indexer}. Aborting: {error_msg}'.
                format(id=self.show.indexerid,
                       indexer=indexerApi(self.show.indexer).name,
                       error_msg=e.message), logger.WARNING)
            return

        # get episode list from the indexer
        try:
            episodes_from_indexer = self.show.load_episodes_from_indexer()
        except IndexerException as e:
            logger.log(
                u'{id}: Unable to get info from {indexer}. The show info will not be refreshed. '
                u'Error: {error_msg}'.format(
                    id=self.show.indexerid,
                    indexer=indexerApi(self.show.indexer).name,
                    error_msg=e.message), logger.WARNING)
            episodes_from_indexer = None

        if episodes_from_indexer is None:
            logger.log(
                u'{id}: No data returned from {indexer} during full show update. Unable to update this show'
                .format(id=self.show.indexerid,
                        indexer=indexerApi(self.show.indexer).name),
                logger.WARNING)
        else:
            # for each ep we found on the Indexer delete it from the DB list
            for cur_season in episodes_from_indexer:
                for cur_episode in episodes_from_indexer[cur_season]:
                    if cur_season in episodes_from_db and cur_episode in episodes_from_db[
                            cur_season]:
                        del episodes_from_db[cur_season][cur_episode]

            # remaining episodes in the DB list are not on the indexer, just delete them from the DB
            for cur_season in episodes_from_db:
                for cur_episode in episodes_from_db[cur_season]:
                    logger.log(
                        u'{id}: Permanently deleting episode {show} {ep} from the database'
                        .format(id=self.show.indexerid,
                                show=self.show.name,
                                ep=episode_num(cur_season,
                                               cur_episode)), logger.DEBUG)
                    # Create the ep object only because Im going to delete it
                    ep_obj = self.show.get_episode(cur_season, cur_episode)
                    try:
                        ep_obj.delete_episode()
                    except EpisodeDeletedException:
                        logger.log(
                            u'{id}: Episode {show} {ep} successfully deleted from the database'
                            .format(id=self.show.indexerid,
                                    show=self.show.name,
                                    ep=episode_num(cur_season,
                                                   cur_episode)), logger.DEBUG)

        # Save only after all changes were applied
        try:
            logger.log(
                u'{id}: Saving all updated show info to database'.format(
                    id=self.show.indexerid), logger.DEBUG)
            self.show.save_to_db()
        except Exception as e:
            logger.log(
                u'{id}: Error saving all updated show info to database: {error_msg}'
                .format(id=self.show.indexerid,
                        error_msg=e.message), logger.WARNING)
            logger.log(traceback.format_exc(), logger.ERROR)

        logger.log(
            u'{id}: Finished update of {show}'.format(id=self.show.indexerid,
                                                      show=self.show.name),
            logger.DEBUG)

        # Refresh show needs to be forced since current execution locks the queue
        app.show_queue_scheduler.action.refreshShow(self.show, True)
        self.finish()
Esempio n. 19
0
    def run(self):

        ShowQueueItem.run(self)

        logger.log(u"Starting to add show {0}".format(
            "by ShowDir: {0}".format(self.showDir) if self.
            showDir else u"by Indexer Id: {0}".format(self.indexer_id)))

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

            logger.log(u"" + str(indexerApi(self.indexer).name) + ": " +
                       repr(l_indexer_api_params))

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

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

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

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

                ui.notifications.error(
                    'Unable to add show',
                    'Show in {0} has no name on {1}, probably the wrong language. \
                                       Delete .nfo and manually add the correct language.'
                    .format(self.showDir,
                            indexerApi(self.indexer).name))
                self._finishEarly()
                return
            # if the show has no episodes/seasons
            if not s:
                logger.log(u"Show " + str(s['seriesname']) + u" is on " +
                           str(indexerApi(self.indexer).name) +
                           u" but contains no season/episode data.")
                ui.notifications.error(
                    "Unable to add show",
                    "Show {0} is on {1} but contains no season/episode data.".
                    format(s['seriesname'],
                           indexerApi(self.indexer).name))
                self._finishEarly()

                return

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

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

        # TODO: Add more specific indexer exceptions, that should provide the user with some accurate feedback.
        except IndexerShowIncomplete as e:
            logger.log(
                u"%s Error while loading information from indexer %s. "
                u"Error: %s" %
                (self.indexer_id, indexerApi(self.indexer).name, e.message),
                logger.WARNING)
            ui.notifications.error(
                "Unable to add show",
                "Unable to look up the show in {0} on {1} using ID {2} "
                "Reason: {3}".format(self.showDir,
                                     indexerApi(self.indexer).name,
                                     self.indexer_id, e.message))
            self._finishEarly()
            return
        except IndexerShowNotFoundInLanguage as e:
            logger.log(
                u'{id}: Data retrieved from {indexer} was incomplete. The indexer does not provide '
                u'show information in the searched language {language}. Aborting: {error_msg}'
                .format(id=self.indexer_id,
                        indexer=indexerApi(self.indexer).name,
                        language=e.language,
                        error_msg=e.message), logger.WARNING)
            ui.notifications.error(
                'Error adding show!',
                'Unable to add show {indexer_id} on {indexer} with this language: {language}'
                .format(indexer_id=self.indexer_id,
                        indexer=indexerApi(self.indexer).name,
                        language=e.language))
            self._finishEarly()
            return
        except Exception as e:
            logger.log(
                u"%s Error while loading information from indexer %s. "
                u"Error: %r" %
                (self.indexer_id, indexerApi(self.indexer).name, e.message),
                logger.ERROR)
            ui.notifications.error(
                "Unable to add show",
                "Unable to look up the show in {0} on {1} using ID {2}, not using the NFO. "
                "Delete .nfo and try adding manually again.".format(
                    self.showDir,
                    indexerApi(self.indexer).name, self.indexer_id))
            self._finishEarly()
            return

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

            self.show = newShow

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

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

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

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

        except IndexerException as e:
            logger.log(
                u"Unable to add show due to an error with " +
                indexerApi(self.indexer).name + ": " + e.message, logger.ERROR)
            if self.show:
                ui.notifications.error("Unable to add " + str(self.show.name) +
                                       " due to an error with " +
                                       indexerApi(self.indexer).name + "")
            else:
                ui.notifications.error(
                    "Unable to add show due to an error with " +
                    indexerApi(self.indexer).name + "")
            self._finishEarly()
            return

        except MultipleShowObjectsException:
            logger.log(
                u"The show in " + self.showDir +
                " is already in your show list, skipping", logger.WARNING)
            ui.notifications.error(
                'Show skipped', "The show in " + self.showDir +
                " is already in your show list")
            self._finishEarly()
            return

        except Exception as e:
            logger.log(u"Error trying to add show: " + e.message, logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)
            self._finishEarly()
            raise

        logger.log(u"Retrieving show info from IMDb", logger.DEBUG)
        try:
            self.show.load_imdb_info()
        except ImdbAPIError as e:
            logger.log(u"Something wrong on IMDb api: " + e.message,
                       logger.INFO)
        except Exception as e:
            logger.log(u"Error loading IMDb info: " + e.message, logger.ERROR)

        try:
            self.show.save_to_db()
        except Exception as e:
            logger.log(u"Error saving the show to the database: " + e.message,
                       logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)
            self._finishEarly()
            raise

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

        try:
            self.show.load_episodes_from_indexer(tvapi=indexer_api)
        except Exception as e:
            logger.log(
                u"Error with " + indexerApi(self.show.indexer).name +
                ", not creating episode list: " + e.message, logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)

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

        try:
            self.show.load_episodes_from_dir()
        except Exception as e:
            logger.log(u"Error searching dir for episodes: " + e.message,
                       logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)

        # if they set default ep status to WANTED then run the backlog to search for episodes
        # FIXME: This needs to be a backlog queue item!!!
        if self.show.default_ep_status == WANTED:
            logger.log(
                u"Launching backlog for this show since its episodes are WANTED"
            )
            app.backlog_search_scheduler.action.search_backlog([self.show])

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

        self.show.flush_episodes()

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

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

            if app.TRAKT_SYNC_WATCHLIST:
                logger.log(u"update watchlist")
                notifiers.trakt_notifier.update_watchlist(show_obj=self.show)

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

        # check if show has XEM mapping so we can determine if searches
        # should go by scene numbering or indexer numbering.
        if not self.scene and scene_numbering.get_xem_numbering_for_show(
                self.show.indexerid, self.show.indexer):
            self.show.scene = 1

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

        self.finish()
Esempio n. 20
0
    def resource_search_indexers_for_show_name(self):
        """
        Search indexers for show name.

        Query parameters:
        :param query: Search term
        :param indexerId: Indexer to search, defined by ID. '0' for all indexers.
        :param language: 2-letter language code to search the indexer(s) in
        """
        query = self.get_argument('query', '').strip()
        indexer_id = self.get_argument('indexerId', '0').strip()
        language = self.get_argument('language', '').strip()

        if not query:
            return self._bad_request('No search term provided.')

        enabled_indexers = indexerApi().indexers
        indexer_id = try_int(indexer_id)
        if indexer_id > 0 and indexer_id not in enabled_indexers:
            return self._bad_request('Invalid indexer id.')

        if not language or language == 'null':
            language = app.INDEXER_DEFAULT_LANGUAGE

        search_terms = [query]

        # If search term ends with what looks like a year, enclose it in ()
        matches = re.match(r'^(.+ |)([12][0-9]{3})$', query)
        if matches:
            search_terms.append('{0}({1})'.format(matches.group(1),
                                                  matches.group(2)))

        for search_term in search_terms:
            # If search term begins with an article, let's also search for it without
            matches = re.match(r'^(?:a|an|the) (.+)$', search_term, re.I)
            if matches:
                search_terms.append(matches.group(1))

        results = {}
        final_results = []

        # Query indexers for each search term and build the list of results
        for indexer in enabled_indexers if indexer_id == 0 else [indexer_id]:
            indexer_instance = indexerApi(indexer)
            custom_api_params = indexer_instance.api_params.copy()
            custom_api_params['language'] = language
            custom_api_params['custom_ui'] = classes.AllShowsListUI

            try:
                indexer_api = indexer_instance.indexer(**custom_api_params)
            except IndexerUnavailable as msg:
                log.info('Could not initialize indexer {indexer}: {error}', {
                    'indexer': indexer_instance.name,
                    'error': msg
                })
                continue

            log.debug(
                'Searching for show with search term(s): {terms} on indexer: {indexer}',
                {
                    'terms': search_terms,
                    'indexer': indexer_api.name
                })
            for search_term in search_terms:
                try:
                    indexer_results = indexer_api[search_term]
                    # add search results
                    results.setdefault(indexer, []).extend(indexer_results)
                except IndexerException as error:
                    log.info(
                        'Error searching for show. term(s): {terms} indexer: {indexer} error: {error}',
                        {
                            'terms': search_terms,
                            'indexer': indexer_api.name,
                            'error': error
                        })
                except Exception as error:
                    log.error(
                        'Internal Error searching for show. term(s): {terms} indexer: {indexer} error: {error}',
                        {
                            'terms': search_terms,
                            'indexer': indexer_api.name,
                            'error': error
                        })

        # Get all possible show ids
        all_show_ids = {}
        for show in app.showList:
            for ex_indexer_name, ex_show_id in iteritems(show.externals):
                ex_indexer_id = reverse_mappings.get(ex_indexer_name)
                if not ex_indexer_id:
                    continue
                all_show_ids[(ex_indexer_id, ex_show_id)] = (show.indexer_name,
                                                             show.series_id)

        for indexer, shows in iteritems(results):
            indexer_api = indexerApi(indexer)
            indexer_results_set = set()
            for show in shows:
                show_id = int(show['id'])
                indexer_results_set.add(
                    (indexer_api.name, indexer, indexer_api.config['show_url'],
                     show_id, show['seriesname'], show['firstaired']
                     or 'N/A', show.get('network', '') or 'N/A',
                     sanitize_filename(show['seriesname']),
                     all_show_ids.get((indexer, show_id), False)))

            final_results.extend(indexer_results_set)

        language_id = indexerApi().config['langabbv_to_id'][language]
        data = {'results': final_results, 'languageId': language_id}
        return self._ok(data=data)
Esempio n. 21
0
    def _ep_data(self, ep_obj):
        """
        Creates an elementTree XML structure for a WDTV style episode.xml
        and returns the resulting data object.

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

        eps_to_write = [ep_obj] + ep_obj.related_episodes

        my_show = self._get_show_data(ep_obj.series)
        if not my_show:
            return None

        root_node = etree.Element('details')

        # write an WDTV XML containing info for all matching episodes
        for ep_to_write in eps_to_write:

            try:
                my_ep = my_show[ep_to_write.season][ep_to_write.episode]
            except (IndexerEpisodeNotFound, IndexerSeasonNotFound):
                log.info(
                    'Unable to find episode {number} on {indexer}... has it been removed? Should I delete from db?', {
                        'number': ep_num(ep_to_write.season, ep_to_write.episode),
                        'indexer': indexerApi(ep_obj.series.indexer).name,
                    }
                )
                return None

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

            if not (getattr(my_ep, 'episodename', None) and getattr(my_ep, 'firstaired', None)):
                return None

            if len(eps_to_write) > 1:
                episode = etree.SubElement(root_node, 'details')
            else:
                episode = root_node

            # TODO: get right EpisodeID
            episode_id = etree.SubElement(episode, 'id')
            episode_id.text = str(ep_to_write.indexerid)

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

            if getattr(my_show, 'seriesname', None):
                series_name = etree.SubElement(episode, 'series_name')
                series_name.text = my_show['seriesname']

            if ep_to_write.name:
                episode_name = etree.SubElement(episode, 'episode_name')
                episode_name.text = ep_to_write.name

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

            episode_num = etree.SubElement(episode, 'episode_number')
            episode_num.text = str(ep_to_write.episode)

            first_aired = etree.SubElement(episode, 'firstaired')

            if ep_to_write.airdate != datetime.date.fromordinal(1):
                first_aired.text = str(ep_to_write.airdate)

            if getattr(my_show, 'firstaired', None):
                try:
                    year_text = str(datetime.datetime.strptime(my_show['firstaired'], dateFormat).year)
                    if year_text:
                        year = etree.SubElement(episode, 'year')
                        year.text = year_text
                except Exception:
                    pass

            if ep_to_write.season != 0 and getattr(my_show, 'runtime', None):
                runtime = etree.SubElement(episode, 'runtime')
                runtime.text = str(my_show['runtime'])

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

            if getattr(my_ep, 'director', None):
                director = etree.SubElement(episode, 'director')
                director.text = my_ep['director']

            if getattr(my_show, '_actors', None):
                for actor in my_show['_actors']:
                    if not ('name' in actor and actor['name'].strip()):
                        continue

                    cur_actor = etree.SubElement(episode, 'actor')

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

                    if 'role' in actor and actor['role'].strip():
                        cur_actor_role = etree.SubElement(cur_actor, 'role')
                        cur_actor_role.text = actor['role'].strip()

            if ep_to_write.description:
                overview = etree.SubElement(episode, 'overview')
                overview.text = ep_to_write.description

            # Make it purdy
            helpers.indent_xml(root_node)
            data = etree.ElementTree(root_node)

        return data
Esempio n. 22
0
    def _ep_data(self, ep_obj):
        """
        Creates an elementTree XML structure for a MediaBrowser style episode.xml
        and returns the resulting data object.

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

        eps_to_write = [ep_obj] + ep_obj.related_episodes

        my_show = self._get_show_data(ep_obj.series)
        if not my_show:
            return None

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

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

        # write an MediaBrowser XML containing info for all matching episodes
        for ep_to_write in eps_to_write:

            try:
                my_ep = my_show[ep_to_write.season][ep_to_write.episode]
            except (IndexerEpisodeNotFound, IndexerSeasonNotFound):
                log.info(
                    'Unable to find episode {ep_num} on {indexer}...'
                    ' has it been removed? Should I delete from db?', {
                        'ep_num': episode_num(ep_to_write.season, ep_to_write.episode),
                        'indexer': indexerApi(ep_obj.series.indexer).name,
                    }
                )
                return None

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

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

                if not (getattr(my_ep, 'episodename', None) and getattr(my_ep, 'firstaired', None)):
                    return None

                episode = movie

                if ep_to_write.name:
                    episode_name = etree.SubElement(episode, 'title')
                    episode_name.text = ep_to_write.name

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

                episode_number = etree.SubElement(episode, 'episode')
                episode_number.text = str(ep_to_write.episode)

                if getattr(my_show, 'firstaired', None):
                    try:
                        year_text = str(datetime.datetime.strptime(my_show['firstaired'], dateFormat).year)
                        if year_text:
                            year = etree.SubElement(episode, 'year')
                            year.text = year_text
                    except Exception:
                        pass

                if getattr(my_show, 'overview', None):
                    plot = etree.SubElement(episode, 'plot')
                    plot.text = my_show['overview']

                if ep_to_write.description:
                    overview = etree.SubElement(episode, 'episodeplot')
                    overview.text = ep_to_write.description

                if getattr(my_show, 'contentrating', None):
                    mpaa = etree.SubElement(episode, 'mpaa')
                    mpaa.text = my_show['contentrating']

                if not ep_obj.related_episodes and getattr(my_ep, 'rating', None):
                    try:
                        rating = int((float(my_ep['rating']) * 10))
                    except ValueError:
                        rating = 0

                    if rating:
                        rating = etree.SubElement(episode, 'rating')
                        rating.text = str(rating)

                if getattr(my_ep, 'director', None):
                    director = etree.SubElement(episode, 'director')
                    director.text = my_ep['director']

                if getattr(my_ep, 'writer', None):
                    writer = etree.SubElement(episode, 'credits')
                    writer.text = my_ep['writer']

                if getattr(my_show, '_actors', None) or getattr(my_ep, 'gueststars', None):
                    cast = etree.SubElement(episode, 'cast')
                    if getattr(my_ep, 'gueststars', None) and isinstance(my_ep['gueststars'], string_types):
                        for actor in (x.strip() for x in my_ep['gueststars'].split('|') if x.strip()):
                            cur_actor = etree.SubElement(cast, 'actor')
                            cur_actor.text = actor

                    if getattr(my_show, '_actors', None):
                        for actor in my_show['_actors']:
                            if 'name' in actor and actor['name'].strip():
                                cur_actor = etree.SubElement(cast, 'actor')
                                cur_actor.text = actor['name'].strip()

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

                if ep_to_write.name:
                    if not episode_name.text:
                        episode_name.text = ep_to_write.name
                    else:
                        episode_name.text = ', '.join([episode_name.text, ep_to_write.name])

                if ep_to_write.description:
                    if not overview.text:
                        overview.text = ep_to_write.description
                    else:
                        overview.text = '\r'.join([overview.text, ep_to_write.description])

        # Make it purdy
        helpers.indent_xml(root_node)

        data = etree.ElementTree(root_node)

        return data
Esempio n. 23
0
    def _ep_data(self, ep_obj):
        """
        Creates an elementTree XML structure for a MediaBrowser style episode.xml
        and returns the resulting data object.

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

        eps_to_write = [ep_obj] + ep_obj.related_episodes

        my_show = self._get_show_data(ep_obj.series)
        if not my_show:
            return None

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

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

        # write an MediaBrowser XML containing info for all matching episodes
        for ep_to_write in eps_to_write:

            try:
                my_ep = my_show[ep_to_write.season][ep_to_write.episode]
            except (IndexerEpisodeNotFound, IndexerSeasonNotFound):
                log.info(
                    'Unable to find episode {ep_num} on {indexer}...'
                    ' has it been removed? Should I delete from db?', {
                        'ep_num':
                        episode_num(ep_to_write.season, ep_to_write.episode),
                        'indexer':
                        indexerApi(ep_obj.series.indexer).name,
                    })
                return None

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

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

                if not (getattr(my_ep, 'episodename', None)
                        and getattr(my_ep, 'firstaired', None)):
                    return None

                episode = movie

                if ep_to_write.name:
                    episode_name = etree.SubElement(episode, 'title')
                    episode_name.text = ep_to_write.name

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

                episode_number = etree.SubElement(episode, 'episode')
                episode_number.text = str(ep_to_write.episode)

                if getattr(my_show, 'firstaired', None):
                    try:
                        year_text = str(
                            datetime.datetime.strptime(my_show['firstaired'],
                                                       dateFormat).year)
                        if year_text:
                            year = etree.SubElement(episode, 'year')
                            year.text = year_text
                    except Exception:
                        pass

                if getattr(my_show, 'overview', None):
                    plot = etree.SubElement(episode, 'plot')
                    plot.text = my_show['overview']

                if ep_to_write.description:
                    overview = etree.SubElement(episode, 'episodeplot')
                    overview.text = ep_to_write.description

                if getattr(my_show, 'contentrating', None):
                    mpaa = etree.SubElement(episode, 'mpaa')
                    mpaa.text = my_show['contentrating']

                if not ep_obj.related_episodes and getattr(
                        my_ep, 'rating', None):
                    try:
                        rating = int((float(my_ep['rating']) * 10))
                    except ValueError:
                        rating = 0

                    if rating:
                        rating = etree.SubElement(episode, 'rating')
                        rating.text = str(rating)

                if getattr(my_ep, 'director', None):
                    director = etree.SubElement(episode, 'director')
                    director.text = my_ep['director']

                if getattr(my_ep, 'writer', None):
                    writer = etree.SubElement(episode, 'credits')
                    writer.text = my_ep['writer']

                if getattr(my_show, '_actors', None) or getattr(
                        my_ep, 'gueststars', None):
                    cast = etree.SubElement(episode, 'cast')
                    if getattr(my_ep, 'gueststars', None) and isinstance(
                            my_ep['gueststars'], string_types):
                        for actor in (x.strip()
                                      for x in my_ep['gueststars'].split('|')
                                      if x.strip()):
                            cur_actor = etree.SubElement(cast, 'actor')
                            cur_actor.text = actor

                    if getattr(my_show, '_actors', None):
                        for actor in my_show['_actors']:
                            if 'name' in actor and actor['name'].strip():
                                cur_actor = etree.SubElement(cast, 'actor')
                                cur_actor.text = actor['name'].strip()

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

                if ep_to_write.name:
                    if not episode_name.text:
                        episode_name.text = ep_to_write.name
                    else:
                        episode_name.text = ', '.join(
                            [episode_name.text, ep_to_write.name])

                if ep_to_write.description:
                    if not overview.text:
                        overview.text = ep_to_write.description
                    else:
                        overview.text = '\r'.join(
                            [overview.text, ep_to_write.description])

        # Make it purdy
        helpers.indent_xml(root_node)

        data = etree.ElementTree(root_node)

        return data
Esempio n. 24
0
def _get_xem_exceptions(force):
    xem_exceptions = defaultdict(dict)
    url = 'http://thexem.de/map/allNames'
    params = {
        'origin': None,
        'seasonNumbers': 1,
    }

    if force or should_refresh('xem'):
        for indexer in indexerApi().indexers:
            indexer_api = indexerApi(indexer)

            try:
                # Get XEM origin for indexer
                origin = indexer_api.config['xem_origin']
                if origin not in VALID_XEM_ORIGINS:
                    msg = 'invalid origin for XEM: {0}'.format(origin)
                    raise ValueError(msg)
            except KeyError:
                # Indexer has no XEM origin
                continue
            except ValueError as error:
                # XEM origin for indexer is invalid
                logger.error(
                    'Error getting XEM scene exceptions for {indexer}:'
                    ' {error}', {'indexer': indexer_api.name, 'error': error}
                )
                continue
            else:
                # XEM origin for indexer is valid
                params['origin'] = origin

            logger.info(
                'Checking for XEM scene exceptions updates for'
                ' {indexer_name}', {'indexer_name': indexer_api.name}
            )

            response = safe_session.get(url, params=params, timeout=60)
            try:
                jdata = response.json()
            except (ValueError, AttributeError) as error:
                logger.debug(
                    'Check scene exceptions update failed for {indexer}.'
                    ' Unable to get URL: {url} Error: {error}', {'indexer': indexer_api.name, 'url': url, 'error': error}
                )
                continue

            if not jdata['data'] or jdata['result'] == 'failure':
                logger.debug(
                    'No data returned from XEM while checking for scene'
                    ' exceptions. Update failed for {indexer}', {'indexer': indexer_api.name}
                )
                continue

            for indexer_id, exceptions in iteritems(jdata['data']):
                try:
                    xem_exceptions[indexer][indexer_id] = exceptions
                except Exception as error:
                    logger.warning(
                        'XEM: Rejected entry: Indexer ID: {indexer_id},'
                        ' Exceptions: {exceptions}', {'indexer_id': indexer_id, 'exceptions': exceptions}
                    )
                    logger.warning('XEM: Rejected entry error message: {error}', {'error': error})

        set_last_refresh('xem')

    return xem_exceptions
Esempio n. 25
0
    def _parse_string(self, name):
        guess = guessit.guessit(name, dict(show_type=self.show_type))
        result = self.to_parse_result(name, guess)

        search_series = helpers.get_show(result.series_name, self.try_indexers) if not self.naming_pattern else None

        # confirm passed in show object indexer id matches result show object indexer id
        series_obj = None if search_series and self.series and search_series.indexerid != self.series.indexerid else search_series
        result.series = series_obj or self.series

        # if this is a naming pattern test or result doesn't have a show object then return best result
        if not result.series or self.naming_pattern:
            return result

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

        # if we have an air-by-date show and the result is air-by-date,
        # then get the real season/episode numbers
        if result.series.air_by_date and result.is_air_by_date:
            log.debug('Series {name} is air by date', {'name': result.series.name})
            airdate = result.air_date.toordinal()
            main_db_con = db.DBConnection()
            sql_result = main_db_con.select(
                b'SELECT season, episode FROM tv_episodes WHERE indexer = ? AND showid = ? AND airdate = ?',
                [result.series.indexer, result.series.series_id, airdate])

            season_number = None
            episode_numbers = []

            if sql_result:
                season_number = int(sql_result[0][0])
                episode_numbers = [int(sql_result[0][1])]

                # Use the next query item if we have multiple results
                # and the current one is a special episode (season 0)
                if season_number == 0 and len(sql_result) > 1:
                    season_number = int(sql_result[1][0])
                    episode_numbers = [int(sql_result[1][1])]

                log.debug(
                    'Database info for series {name}: Season: {season} Episode(s): {episodes}', {
                        'name': result.series.name,
                        'season': season_number,
                        'episodes': episode_numbers
                    }
                )

            if season_number is None or not episode_numbers:
                log.debug('Series {name} has no season or episodes, using indexer',
                          {'name': result.series.name})
                try:
                    indexer_api_params = indexerApi(result.series.indexer).api_params.copy()

                    if result.series.lang:
                        indexer_api_params['language'] = result.series.lang

                    indexer_api = indexerApi(result.series.indexer).indexer(**indexer_api_params)
                    tv_episode = indexer_api[result.series.indexerid].aired_on(result.air_date)[0]

                    season_number = int(tv_episode['seasonnumber'])
                    episode_numbers = [int(tv_episode['episodenumber'])]
                    log.debug(
                        'Indexer info for series {name}: {ep}', {
                            'name': result.series.name,
                            'ep': episode_num(season_number, episode_numbers[0]),
                        }
                    )
                except IndexerEpisodeNotFound:
                    log.warning(
                        'Unable to find episode with date {date} for series {name}. Skipping',
                        {'date': result.air_date, 'name': result.series.name}
                    )
                    episode_numbers = []
                except IndexerError as error:
                    log.warning(
                        'Unable to contact {indexer_api.name}: {error}',
                        {'indexer_api': indexer_api, 'error': error.message}
                    )
                    episode_numbers = []
                except IndexerException as error:
                    log.warning(
                        'Indexer exception: {indexer_api.name}: {error}',
                        {'indexer_api': indexer_api, 'error': error.message}
                    )
                    episode_numbers = []

            for episode_number in episode_numbers:
                season = season_number
                episode = episode_number

                if result.series.is_scene:
                    (season, episode) = scene_numbering.get_indexer_numbering(
                        result.series,
                        season_number,
                        episode_number,
                    )
                    log.debug(
                        'Scene numbering enabled series {name}, using indexer numbering: {ep}',
                        {'name': result.series.name, 'ep': episode_num(season, episode)}
                    )
                new_episode_numbers.append(episode)
                new_season_numbers.append(season)

        elif result.series.is_anime and result.is_anime:
            log.debug('Scene numbering enabled series {name} is anime',
                      {'name': result.series.name})
            scene_season = scene_exceptions.get_scene_exceptions_by_name(result.series_name)[0][1]

            for absolute_episode in result.ab_episode_numbers:
                a = absolute_episode

                # Apparently we got a scene_season using the season scene exceptions. If we also do not have a season
                # parsed, guessit made a 'mistake' and it should have set the season with the value.
                # This is required for titles like: '[HorribleSubs].Kekkai.Sensen.&.Beyond.-.01.[1080p].mkv'
                if result.season_number is None and scene_season > 0:
                    season = scene_season
                    episode = [a]
                    log.debug(
                        'Detected a season scene exception [{series_name} -> {scene_season}] without a '
                        'season number in the title, '
                        'assuming the episode # [{scene_absolute}] is the scene_absolute #.',
                        {'series_name': result.series_name, 'scene_season': scene_season, 'scene_absolute': a}
                    )
                else:
                    if result.series.is_scene:
                        a = scene_numbering.get_indexer_absolute_numbering(result.series, absolute_episode,
                                                                           True, scene_season)

                    # Translate the absolute episode number, back to the indexers season and episode.
                    (season, episode) = helpers.get_all_episodes_from_absolute_number(result.series, [a])
                    log.debug(
                        'Scene numbering enabled series {name} using indexer for absolute {absolute}: {ep}',
                        {'name': result.series.name, 'absolute': a, 'ep': episode_num(season, episode, 'absolute')}
                    )

                new_absolute_numbers.append(a)
                new_episode_numbers.extend(episode)
                new_season_numbers.append(season)

        elif result.season_number and result.episode_numbers:
            for episode_number in result.episode_numbers:
                season = result.season_number
                episode = episode_number

                if result.series.is_scene:
                    (season, episode) = scene_numbering.get_indexer_numbering(
                        result.series,
                        result.season_number,
                        episode_number
                    )
                    log.debug(
                        'Scene numbering enabled series {name} using indexer numbering: {ep}',
                        {'name': result.series.name, 'ep': episode_num(season, episode)}
                    )

                if result.series.is_anime:
                    a = helpers.get_absolute_number_from_season_and_episode(result.series, season, episode)
                    if a:
                        new_absolute_numbers.append(a)
                        log.debug(
                            'Scene numbering enabled anime {name} using indexer with absolute {absolute}: {ep}',
                            {'name': result.series.name, 'absolute': a, 'ep': episode_num(season, episode, 'absolute')}
                        )

                new_episode_numbers.append(episode)
                new_season_numbers.append(season)

        # need to do a quick sanity check heregex.  It's possible that we now have episodes
        # from more than one season (by tvdb numbering), and this is just too much
        # for the application, so we'd need to flag it.
        new_season_numbers = sorted(set(new_season_numbers))  # remove duplicates
        if len(new_season_numbers) > 1:
            raise InvalidNameException('Scene numbering results episodes from seasons {seasons}, (i.e. more than one) '
                                       'and Medusa does not support this. Sorry.'.format(seasons=new_season_numbers))

        # If guess it's possible that we'd have duplicate episodes too,
        # so lets eliminate them
        new_episode_numbers = sorted(set(new_episode_numbers))

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

        if new_absolute_numbers:
            result.ab_episode_numbers = new_absolute_numbers

        if new_season_numbers and new_episode_numbers:
            result.episode_numbers = new_episode_numbers
            result.season_number = new_season_numbers[0]

        # For anime that we still couldn't get a season, let's assume we should use 1.
        if result.series.is_anime and result.season_number is None and result.episode_numbers:
            result.season_number = 1
            log.warning(
                'Unable to parse season number for anime {name}, '
                'assuming absolute numbered anime with season 1',
                {'name': result.series.name}
            )

        if result.series.is_scene:
            log.debug(
                'Converted parsed result {original} into {result}',
                {'original': result.original_name, 'result': result}
            )

        return result
Esempio n. 26
0
    def run(self, force=False):

        self.amActive = True
        refresh_shows = []  # A list of shows, that need to be refreshed
        season_updates = []  # A list of show seasons that have passed their next_update timestamp
        update_max_weeks = 12

        network_timezones.update_network_dict()

        # Refresh the exceptions_cache from db.
        refresh_exceptions_cache()

        logger.info(u'Started periodic show updates')

        # Cache for the indexers list of updated show
        indexer_updated_shows = {}
        # Cache for the last indexer update timestamp
        last_updates = {}

        # Loop through the list of shows, and per show evaluate if we can use the .get_last_updated_seasons()
        for show in app.showList:
            if show.paused:
                logger.info(u'The show {show} is paused, not updating it.', show=show.name)
                continue

            indexer_api_params = indexerApi(show.indexer).api_params.copy()
            try:
                indexer_api = indexerApi(show.indexer).indexer(**indexer_api_params)
            except IndexerUnavailable:
                logger.warning(u'Problem running show_updater, Indexer {indexer_name} seems to be having '
                               u'connectivity issues. While trying to look for show updates on show: {show}',
                               indexer_name=indexerApi(show.indexer).name, show=show.name)
                continue

            # Get the lastUpdate timestamp for this indexer.
            if indexerApi(show.indexer).name not in last_updates:
                last_updates[indexerApi(show.indexer).name] = \
                    self.update_cache.get_last_indexer_update(indexerApi(show.indexer).name)
            last_update = last_updates[indexerApi(show.indexer).name]

            # Get a list of updated shows from the indexer, since last update.
            # Use the list, to limit the shows for which are requested for the last updated seasons.
            if last_update and last_update > time.time() - (604800 * update_max_weeks):
                if show.indexer not in indexer_updated_shows:
                    try:
                        indexer_updated_shows[show.indexer] = indexer_api.get_last_updated_series(
                            last_update, update_max_weeks
                        )
                    except IndexerUnavailable:
                        logger.warning(u'Problem running show_updater, Indexer {indexer_name} seems to be having '
                                       u'connectivity issues while trying to look for show updates on show: {show}',
                                       indexer_name=indexerApi(show.indexer).name, show=show.name)
                        continue
                    except IndexerException as error:
                        logger.warning(u'Problem running show_updater, Indexer {indexer_name} seems to be having '
                                       u'issues while trying to get updates for show {show}. Cause: {cause}',
                                       indexer_name=indexerApi(show.indexer).name, show=show.name, cause=error.message)
                        continue
                    except RequestException as error:
                        logger.warning(u'Problem running show_updater, Indexer {indexer_name} seems to be having '
                                       u'issues while trying to get updates for show {show}. ',
                                       indexer_name=indexerApi(show.indexer).name, show=show.name)

                        if isinstance(error, HTTPError):
                            if error.response.status_code == 503:
                                logger.warning(u'API Service offline: '
                                               u'This service is temporarily offline, try again later.')
                            elif error.response.status_code == 429:
                                logger.warning(u'Your request count (#) is over the allowed limit of (40).')

                        logger.warning(u'Cause: {cause}.', cause=error)
                        continue
                    except Exception as error:
                        logger.exception(u'Problem running show_updater, Indexer {indexer_name} seems to be having '
                                         u'issues while trying to get updates for show {show}. Cause: {cause}.',
                                         indexer_name=indexerApi(show.indexer).name, show=show.name, cause=error)
                        continue

                # If the current show is not in the list, move on to the next.
                # Only do this for shows, if the indexer has had a successful update run within the last 12 weeks.
                if all([isinstance(indexer_updated_shows[show.indexer], list),
                        show.indexerid not in indexer_updated_shows.get(show.indexer)]):
                    logger.debug(u'Skipping show update for {show}. As the show is not '
                                 u'in the indexers {indexer_name} list with updated '
                                 u'shows within the last {weeks} weeks.', show=show.name,
                                 indexer_name=indexerApi(show.indexer).name, weeks=update_max_weeks)
                    continue

            # These are the criteria for performing a full show refresh.
            if any([not hasattr(indexer_api, 'get_last_updated_seasons'),
                    not last_update,
                    last_update < time.time() - 604800 * update_max_weeks]):
                # no entry in lastUpdate, or last update was too long ago,
                # let's refresh the show for this indexer
                logger.debug(u'Trying to update {show}. Your lastUpdate for {indexer_name} is older then {weeks} weeks,'
                             u" or the indexer doesn't support per season updates. Doing a full update.",
                             show=show.name, indexer_name=indexerApi(show.indexer).name,
                             weeks=update_max_weeks)
                refresh_shows.append(show)

            # Else fall back to per season updates.
            elif hasattr(indexer_api, 'get_last_updated_seasons'):
                # Get updated seasons and add them to the season update list.
                try:
                    updated_seasons = indexer_api.get_last_updated_seasons([show.indexerid], last_update,
                                                                           update_max_weeks)
                except IndexerUnavailable:
                    logger.warning(u'Problem running show_updater, Indexer {indexer_name} seems to be having '
                                   u'connectivity issues while trying to look for showupdates on show: {show}',
                                   indexer_name=indexerApi(show.indexer).name, show=show.name)
                    continue
                except IndexerException as e:
                    logger.warning(u'Problem running show_updater, Indexer {indexer_name} seems to be having '
                                   u'issues while trying to get updates for show {show}. Cause: {cause}',
                                   indexer_name=indexerApi(show.indexer).name, show=show.name, cause=e.message)
                    continue
                except Exception as e:
                    logger.exception(u'Problem running show_updater, Indexer {indexer_name} seems to be having '
                                     u'issues while trying to get updates for show {show}. Cause: {cause}',
                                     indexer_name=indexerApi(show.indexer).name, show=show.name, cause=e)
                    continue

                if updated_seasons[show.indexerid]:
                    logger.info(u'{show_name}: Adding the following seasons for update to queue: {seasons}',
                                show_name=show.name, seasons=updated_seasons[show.indexerid])
                    for season in updated_seasons[show.indexerid]:
                        season_updates.append((show.indexer, show, season))

        pi_list = []

        # Full refreshes
        for show in refresh_shows:
            # If the cur_show is not 'paused' then add to the show_queue_scheduler
            if not show.paused:
                logger.info(u'Full update on show: {show}', show=show.name)
                try:
                    pi_list.append(app.show_queue_scheduler.action.updateShow(show))
                except (CantUpdateShowException, CantRefreshShowException) as e:
                    logger.warning(u'Automatic update failed. Error: {error}', error=e)
                except Exception as e:
                    logger.error(u'Automatic update failed: Error: {error}', error=e)
            else:
                logger.info(u'Show update skipped, show: {show} is paused.', show=show.name)

        # Only update expired season
        for show in season_updates:
            # If the cur_show is not 'paused' then add to the show_queue_scheduler
            if not show[1].paused:
                logger.info(u'Updating season {season} for show: {show}.', season=show[2], show=show[1].name)
                try:
                    pi_list.append(app.show_queue_scheduler.action.updateShow(show[1], season=show[2]))
                except CantUpdateShowException as e:
                    logger.warning(u'Automatic update failed. Error: {error}', error=e)
                except Exception as e:
                    logger.error(u'Automatic update failed: Error: {error}', error=e)
            else:
                logger.info(u'Show update skipped, show: {show} is paused.', show=show[1].name)

        ui.ProgressIndicators.setIndicator('dailyUpdate', ui.QueueProgressIndicator('Daily Update', pi_list))

        # Only refresh updated shows that have been updated using the season updates.
        # The full refreshed shows, are updated from the queueItem.
        for show in set(show[1] for show in season_updates):
            if not show.paused:
                try:
                    app.show_queue_scheduler.action.refreshShow(show, True)
                except CantRefreshShowException as e:
                    logger.warning(u'Show refresh on show {show_name} failed. Error: {error}',
                                   show_name=show.name, error=e)
                except Exception as e:
                    logger.error(u'Show refresh on show {show_name} failed: Unexpected Error: {error}',
                                 show_name=show.name, error=e)
            else:
                logger.info(u'Show refresh skipped, show: {show_name} is paused.', show_name=show.name)

        if refresh_shows or season_updates:
            for indexer in set([show.indexer for show in refresh_shows] + [s[1].indexer for s in season_updates]):
                indexer_api = indexerApi(indexer)
                self.update_cache.set_last_indexer_update(indexer_api.name)
                logger.info(u'Updated lastUpdate timestamp for {indexer_name}', indexer_name=indexer_api.name)
            logger.info(u'Completed scheduling updates on shows')
        else:
            logger.info(u'Completed but there was nothing to update')

        self.amActive = False
Esempio n. 27
0
    def _ep_data(self, ep_obj):
        """
        Creates an elementTree XML structure for an KODI-style episode.nfo and
        returns the resulting data object.

        show_obj: a Episode instance to create the NFO for
        """
        eps_to_write = [ep_obj] + ep_obj.related_episodes

        my_show = self._get_show_data(ep_obj.series)
        if not my_show:
            return None

        if len(eps_to_write) > 1:
            root_node = etree.Element('kodimultiepisode')
        else:
            root_node = etree.Element('episodedetails')

        # write an NFO containing info for all matching episodes
        for ep_to_write in eps_to_write:

            try:
                my_ep = my_show[ep_to_write.season][ep_to_write.episode]
            except (IndexerEpisodeNotFound, IndexerSeasonNotFound):
                log.info(
                    u'Unable to find episode {ep_num} on {indexer}...'
                    u' has it been removed? Should I delete from db?', {
                        'ep_num':
                        episode_num(ep_to_write.season, ep_to_write.episode),
                        'indexer':
                        indexerApi(ep_obj.series.indexer).name,
                    })
                return None

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

            if not getattr(my_ep, 'episodename', None):
                log.debug(u'Not generating nfo because the ep has no title')
                return None

            log.debug(u'Creating metadata for episode {0}',
                      episode_num(ep_obj.season, ep_obj.episode))

            if len(eps_to_write) > 1:
                episode = etree.SubElement(root_node, 'episodedetails')
            else:
                episode = root_node

            if getattr(my_ep, 'episodename', None):
                title = etree.SubElement(episode, 'title')
                title.text = my_ep['episodename']

            if getattr(my_show, 'seriesname', None):
                showtitle = etree.SubElement(episode, 'showtitle')
                showtitle.text = my_show['seriesname']

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

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

            uniqueid = etree.SubElement(episode, 'uniqueid')
            uniqueid.text = str(ep_to_write.indexerid)

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

            if getattr(my_ep, 'overview', None):
                plot = etree.SubElement(episode, 'plot')
                plot.text = my_ep['overview']

            if ep_to_write.season and getattr(my_show, 'runtime', None):
                runtime = etree.SubElement(episode, 'runtime')
                runtime.text = my_show['runtime']

            if getattr(my_ep, 'airsbefore_season', None):
                displayseason = etree.SubElement(episode, 'displayseason')
                displayseason.text = my_ep['airsbefore_season']

            if getattr(my_ep, 'airsbefore_episode', None):
                displayepisode = etree.SubElement(episode, 'displayepisode')
                displayepisode.text = my_ep['airsbefore_episode']

            if getattr(my_ep, 'filename', None):
                thumb = etree.SubElement(episode, 'thumb')
                thumb.text = my_ep['filename'].strip()

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

            if getattr(my_ep, 'rating', None):
                rating = etree.SubElement(episode, 'rating')
                rating.text = my_ep['rating']

            if getattr(my_ep, 'writer', None) and isinstance(
                    my_ep['writer'], string_types):
                for writer in self._split_info(my_ep['writer']):
                    cur_writer = etree.SubElement(episode, 'credits')
                    cur_writer.text = writer

            if getattr(my_ep, 'director', None) and isinstance(
                    my_ep['director'], string_types):
                for director in self._split_info(my_ep['director']):
                    cur_director = etree.SubElement(episode, 'director')
                    cur_director.text = director

            if getattr(my_ep, 'gueststars', None) and isinstance(
                    my_ep['gueststars'], string_types):
                for actor in self._split_info(my_ep['gueststars']):
                    cur_actor = etree.SubElement(episode, 'actor')
                    cur_actor_name = etree.SubElement(cur_actor, 'name')
                    cur_actor_name.text = actor

            if getattr(my_show, '_actors', None):
                for actor in my_show['_actors']:
                    cur_actor = etree.SubElement(episode, 'actor')

                    if 'name' in actor and actor['name'].strip():
                        cur_actor_name = etree.SubElement(cur_actor, 'name')
                        cur_actor_name.text = actor['name'].strip()
                    else:
                        continue

                    if 'role' in actor and actor['role'].strip():
                        cur_actor_role = etree.SubElement(cur_actor, 'role')
                        cur_actor_role.text = actor['role'].strip()

                    if 'image' in actor and actor['image'].strip():
                        cur_actor_thumb = etree.SubElement(cur_actor, 'thumb')
                        cur_actor_thumb.text = actor['image'].strip()

        # Make it purdy
        helpers.indent_xml(root_node)

        data = etree.ElementTree(root_node)

        return data
Esempio n. 28
0
    def _parse_air_by_date(self, result):
        """
        Parse anime season episode results.

        Translate scene episode and season numbering to indexer numbering,
        using an air date to indexer season/episode translation.

        :param result: Guessit parse result object.
        :return: tuple of found indexer episode numbers and indexer season numbers
        """
        log.debug('Series {name} is air by date', {'name': result.series.name})

        new_episode_numbers = []
        new_season_numbers = []

        episode_by_air_date = self._get_episodes_by_air_date(result)

        season_number = None
        episode_numbers = []

        if episode_by_air_date:
            season_number = int(episode_by_air_date[0]['season'])
            episode_numbers = [int(episode_by_air_date[0]['episode'])]

            # Use the next query item if we have multiple results
            # and the current one is a special episode (season 0)
            if season_number == 0 and len(episode_by_air_date) > 1:
                season_number = int(episode_by_air_date[1]['season'])
                episode_numbers = [int(episode_by_air_date[1]['episode'])]

            log.debug(
                'Database info for series {name}: Season: {season} Episode(s): {episodes}', {
                    'name': result.series.name,
                    'season': season_number,
                    'episodes': episode_numbers
                }
            )

        if season_number is None or not episode_numbers:
            log.debug('Series {name} has no season or episodes, using indexer',
                      {'name': result.series.name})

            indexer_api_params = indexerApi(result.series.indexer).api_params.copy()
            indexer_api = indexerApi(result.series.indexer).indexer(**indexer_api_params)
            try:
                if result.series.lang:
                    indexer_api_params['language'] = result.series.lang

                tv_episode = indexer_api[result.series.indexerid].aired_on(result.air_date)[0]

                season_number = int(tv_episode['seasonnumber'])
                episode_numbers = [int(tv_episode['episodenumber'])]
                log.debug(
                    'Indexer info for series {name}: {ep}', {
                        'name': result.series.name,
                        'ep': episode_num(season_number, episode_numbers[0]),
                    }
                )
            except IndexerEpisodeNotFound:
                log.warning(
                    'Unable to find episode with date {date} for series {name}. Skipping',
                    {'date': result.air_date, 'name': result.series.name}
                )
                episode_numbers = []
            except IndexerError as error:
                log.warning(
                    'Unable to contact {indexer_api.name}: {error!r}',
                    {'indexer_api': indexer_api, 'error': error}
                )
                episode_numbers = []
            except IndexerException as error:
                log.warning(
                    'Indexer exception: {indexer_api.name}: {error!r}',
                    {'indexer_api': indexer_api, 'error': error}
                )
                episode_numbers = []

        for episode_number in episode_numbers:
            season = season_number
            episode = episode_number

            if result.series.is_scene:
                (season, episode) = scene_numbering.get_indexer_numbering(
                    result.series,
                    season_number,
                    episode_number,
                )
                log.debug(
                    'Scene numbering enabled series {name}, using indexer numbering: {ep}',
                    {'name': result.series.name, 'ep': episode_num(season, episode)}
                )
            new_episode_numbers.append(episode)
            new_season_numbers.append(season)

        return new_episode_numbers, new_season_numbers
Esempio n. 29
0
    def run(self):

        ShowQueueItem.run(self)

        logger.log(
            u'{id}: Beginning update of {show}{season}'.format(
                id=self.show.indexerid,
                show=self.show.name,
                season=u' with season(s) [{0}]'.format(u','.join(
                    str(s) for s in self.seasons) if self.seasons else u'')),
            logger.INFO)

        logger.log(
            u'{id}: Retrieving show info from {indexer}'.format(
                id=self.show.indexerid,
                indexer=indexerApi(self.show.indexer).name), logger.DEBUG)
        try:
            # Let's make sure we refresh the indexer_api object attached to the show object.
            self.show.create_indexer()
            self.show.load_from_indexer()
        except IndexerError as e:
            logger.log(
                u'{id}: Unable to contact {indexer}. Aborting: {error_msg}'.
                format(id=self.show.indexerid,
                       indexer=indexerApi(self.show.indexer).name,
                       error_msg=e.message), logger.WARNING)
            return
        except IndexerAttributeNotFound as e:
            logger.log(
                u'{id}: Data retrieved from {indexer} was incomplete. Aborting: {error_msg}'
                .format(id=self.show.indexerid,
                        indexer=indexerApi(self.show.indexer).name,
                        error_msg=e.message), logger.WARNING)
            return

        logger.log(
            u'{id}: Retrieving show info from IMDb'.format(
                id=self.show.indexerid), logger.DEBUG)
        try:
            self.show.load_imdb_info()
        except ImdbAPIError as e:
            logger.log(
                u'{id}: Something wrong on IMDb api: {error_msg}'.format(
                    id=self.show.indexerid, error_msg=e.message), logger.INFO)
        except Exception as e:
            logger.log(
                u'{id}: Error loading IMDb info: {error_msg}'.format(
                    id=self.show.indexerid, error_msg=e.message),
                logger.WARNING)

        # have to save show before reading episodes from db
        try:
            logger.log(
                u'{id}: Saving new IMDb show info to database'.format(
                    id=self.show.indexerid), logger.DEBUG)
            self.show.save_to_db()
        except Exception as e:
            logger.log(
                u"{id}: Error saving new IMDb show info to database: {error_msg}"
                .format(id=self.show.indexerid,
                        error_msg=e.message), logger.WARNING)
            logger.log(traceback.format_exc(), logger.ERROR)

        # get episode list from DB
        try:
            episodes_from_db = self.show.load_episodes_from_db(self.seasons)
        except IndexerException as e:
            logger.log(
                u'{id}: Unable to contact {indexer}. Aborting: {error_msg}'.
                format(id=self.show.indexerid,
                       indexer=indexerApi(self.show.indexer).name,
                       error_msg=e.message), logger.WARNING)
            return

        # get episode list from the indexer
        try:
            episodes_from_indexer = self.show.load_episodes_from_indexer(
                self.seasons)
        except IndexerException as e:
            logger.log(
                u'{id}: Unable to get info from {indexer}. The show info will not be refreshed. '
                u'Error: {error_msg}'.format(
                    id=self.show.indexerid,
                    indexer=indexerApi(self.show.indexer).name,
                    error_msg=e.message), logger.WARNING)
            episodes_from_indexer = None

        if episodes_from_indexer is None:
            logger.log(
                u'{id}: No data returned from {indexer} during season show update. Unable to update this show'
                .format(id=self.show.indexerid,
                        indexer=indexerApi(self.show.indexer).name),
                logger.WARNING)
        else:
            # for each ep we found on the Indexer delete it from the DB list
            for cur_season in episodes_from_indexer:
                for cur_episode in episodes_from_indexer[cur_season]:
                    if cur_season in episodes_from_db and cur_episode in episodes_from_db[
                            cur_season]:
                        del episodes_from_db[cur_season][cur_episode]

            # remaining episodes in the DB list are not on the indexer, just delete them from the DB
            for cur_season in episodes_from_db:
                for cur_episode in episodes_from_db[cur_season]:
                    logger.log(
                        u'{id}: Permanently deleting episode {show} {ep} from the database'
                        .format(id=self.show.indexerid,
                                show=self.show.name,
                                ep=episode_num(cur_season,
                                               cur_episode)), logger.DEBUG)
                    # Create the ep object only because Im going to delete it
                    ep_obj = self.show.get_episode(cur_season, cur_episode)
                    try:
                        ep_obj.delete_episode()
                    except EpisodeDeletedException:
                        logger.log(
                            u'{id}: Episode {show} {ep} successfully deleted from the database'
                            .format(id=self.show.indexerid,
                                    show=self.show.name,
                                    ep=episode_num(cur_season,
                                                   cur_episode)), logger.DEBUG)

        # Save only after all changes were applied
        try:
            logger.log(
                u'{id}: Saving all updated show info to database'.format(
                    id=self.show.indexerid), logger.DEBUG)
            self.show.save_to_db()
        except Exception as e:
            logger.log(
                u'{id}: Error saving all updated show info to database: {error_msg}'
                .format(id=self.show.indexerid,
                        error_msg=e.message), logger.WARNING)
            logger.log(traceback.format_exc(), logger.ERROR)

        logger.log(
            u'{id}: Finished update of {show}'.format(id=self.show.indexerid,
                                                      show=self.show.name),
            logger.INFO)

        self.finish()
Esempio n. 30
0
    def _ep_data(self, ep_obj):
        """
        Creates an elementTree XML structure for a MediaBrowser style episode.xml
        and returns the resulting data object.

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

        eps_to_write = [ep_obj] + ep_obj.related_episodes

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

        my_show = self._get_show_data(ep_obj.series)
        if not my_show:
            return None

        root_node = etree.Element(u'Item')

        # write an MediaBrowser XML containing info for all matching episodes
        for ep_to_write in eps_to_write:

            try:
                my_ep = my_show[ep_to_write.season][ep_to_write.episode]
            except (IndexerEpisodeNotFound, IndexerSeasonNotFound):
                log.info(
                    u'Unable to find episode {number} on {indexer}... has it been removed? Should I delete from db?', {
                        u'number': episode_num(ep_to_write.season, ep_to_write.episode),
                        u'indexer': indexerApi(ep_obj.series.indexer).name
                    }
                )
                return None

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

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

                if not (getattr(my_ep, u'episodename', None) and getattr(my_ep, u'firstaired', None)):
                    return None

                episode = root_node

                if ep_to_write.name:
                    episode_name = etree.SubElement(episode, u'EpisodeName')
                    episode_name.text = ep_to_write.name

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

                if ep_obj.related_episodes:
                    episode_number_end = etree.SubElement(episode, u'EpisodeNumberEnd')
                    episode_number_end.text = str(ep_to_write.episode)

                season_number = etree.SubElement(episode, u'SeasonNumber')
                season_number.text = str(ep_to_write.season)

                if not ep_obj.related_episodes and getattr(my_ep, u'absolute_number', None):
                    absolute_number = etree.SubElement(episode, u'absolute_number')
                    absolute_number.text = str(my_ep[u'absolute_number'])

                if ep_to_write.airdate != datetime.date.fromordinal(1):
                    first_aired = etree.SubElement(episode, u'FirstAired')
                    first_aired.text = str(ep_to_write.airdate)

                metadata_type = etree.SubElement(episode, u'Type')
                metadata_type.text = u'Episode'

                if ep_to_write.description:
                    overview = etree.SubElement(episode, u'Overview')
                    overview.text = ep_to_write.description

                if not ep_obj.related_episodes:
                    if getattr(my_ep, u'rating', None):
                        rating = etree.SubElement(episode, u'Rating')
                        rating.text = str(my_ep[u'rating'])

                    if getattr(my_show, u'imdb_id', None):
                        IMDB_ID = etree.SubElement(episode, u'IMDB_ID')
                        IMDB_ID.text = my_show[u'imdb_id']

                        IMDB = etree.SubElement(episode, u'IMDB')
                        IMDB.text = my_show[u'imdb_id']

                        IMDbId = etree.SubElement(episode, u'IMDbId')
                        IMDbId.text = my_show[u'imdb_id']

                indexer_id = etree.SubElement(episode, u'id')
                indexer_id.text = str(ep_to_write.indexerid)

                persons = etree.SubElement(episode, u'Persons')

                if getattr(my_show, u'_actors', None):
                    for actor in my_show[u'_actors']:
                        if not (u'name' in actor and actor[u'name'].strip()):
                            continue

                        cur_actor = etree.SubElement(persons, u'Person')

                        cur_actor_name = etree.SubElement(cur_actor, u'Name')
                        cur_actor_name.text = actor[u'name'].strip()

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

                        if u'role' in actor and actor[u'role'].strip():
                            cur_actor_role = etree.SubElement(cur_actor, u'Role')
                            cur_actor_role.text = actor[u'role'].strip()

                language = etree.SubElement(episode, u'Language')
                try:
                    language.text = my_ep[u'language']
                except Exception:
                    language.text = app.INDEXER_DEFAULT_LANGUAGE  # tvrage api doesn't provide language so we must assume a value here

                thumb = etree.SubElement(episode, u'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 = thumb_text

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

                if ep_to_write.name:
                    if not episode_name.text:
                        episode_name.text = ep_to_write.name
                    else:
                        episode_name.text = u', '.join([episode_name.text, ep_to_write.name])

                if ep_to_write.description:
                    if not overview.text:
                        overview.text = ep_to_write.description
                    else:
                        overview.text = u'\r'.join([overview.text, ep_to_write.description])

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

        # 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, u'Person')
                cur_person_name = etree.SubElement(person, u'Name')
                cur_person_name.text = cur_name
                cur_person_type = etree.SubElement(person, u'Type')
                cur_person_type.text = person_type

        # Make it purdy
        helpers.indent_xml(root_node)
        data = etree.ElementTree(root_node)

        return data
Esempio n. 31
0
 def __init__(self):
     """Initialize the trakt recommended list object."""
     self.cache_subfolder = __name__.split('.')[-1] if '.' in __name__ else __name__
     self.recommender = 'Trakt Popular'
     self.default_img_src = 'trakt-default.png'
     self.tvdb_api_v2 = indexerApi(INDEXER_TVDBV2).indexer()
Esempio n. 32
0
    def _ep_data(self, ep_obj):
        """
        Creates an elementTree XML structure for an KODI-style episode.nfo and
        returns the resulting data object.

        show_obj: a Episode instance to create the NFO for
        """
        eps_to_write = [ep_obj] + ep_obj.related_episodes

        series_obj = self._get_show_data(ep_obj.series)
        if not series_obj:
            return None

        if len(eps_to_write) > 1:
            root_node = etree.Element('kodimultiepisode')
        else:
            root_node = etree.Element('episodedetails')

        # write an NFO containing info for all matching episodes
        for ep_to_write in eps_to_write:

            try:
                my_ep = series_obj[ep_to_write.season][ep_to_write.episode]
            except (IndexerEpisodeNotFound, IndexerSeasonNotFound):
                log.info(
                    u'Unable to find episode {ep_num} on {indexer}...'
                    u' has it been removed? Should I delete from db?', {
                        'ep_num': episode_num(ep_to_write.season, ep_to_write.episode),
                        'indexer': indexerApi(ep_obj.series.indexer).name,
                    }
                )
                return None

            if not getattr(my_ep, 'firstaired', None):
                my_ep['firstaired'] = text_type(datetime.date.fromordinal(1))

            if not getattr(my_ep, 'episodename', None):
                log.debug(u'Not generating nfo because the ep has no title')
                return None

            log.debug(u'Creating metadata for episode {0}',
                      episode_num(ep_obj.season, ep_obj.episode))

            if len(eps_to_write) > 1:
                episode = etree.SubElement(root_node, 'episodedetails')
            else:
                episode = root_node

            if getattr(my_ep, 'episodename', None):
                title = etree.SubElement(episode, 'title')
                title.text = my_ep['episodename']

            if getattr(series_obj, 'seriesname', None):
                showtitle = etree.SubElement(episode, 'showtitle')
                showtitle.text = series_obj['seriesname']

            season = etree.SubElement(episode, 'season')
            season.text = text_type(ep_to_write.season)

            episodenum = etree.SubElement(episode, 'episode')
            episodenum.text = text_type(ep_to_write.episode)

            uniqueid = etree.SubElement(episode, 'uniqueid')
            uniqueid.text = text_type(ep_to_write.indexerid)

            if ep_to_write.airdate != datetime.date.fromordinal(1):
                aired = etree.SubElement(episode, 'aired')
                aired.text = text_type(ep_to_write.airdate)

            if getattr(my_ep, 'overview', None):
                plot = etree.SubElement(episode, 'plot')
                plot.text = my_ep['overview']

            if ep_to_write.season and getattr(series_obj, 'runtime', None):
                runtime = etree.SubElement(episode, 'runtime')
                runtime.text = text_type(series_obj['runtime'])

            if getattr(my_ep, 'airsbefore_season', None):
                displayseason = etree.SubElement(episode, 'displayseason')
                displayseason.text = my_ep['airsbefore_season']

            if getattr(my_ep, 'airsbefore_episode', None):
                displayepisode = etree.SubElement(episode, 'displayepisode')
                displayepisode.text = my_ep['airsbefore_episode']

            if getattr(my_ep, 'filename', None):
                thumb = etree.SubElement(episode, 'thumb')
                thumb.text = my_ep['filename'].strip()

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

            if getattr(my_ep, 'rating', None):
                rating = etree.SubElement(episode, 'rating')
                rating.text = text_type(my_ep['rating'])

            if getattr(my_ep, 'writer', None) and isinstance(my_ep['writer'], string_types):
                for writer in self._split_info(my_ep['writer']):
                    cur_writer = etree.SubElement(episode, 'credits')
                    cur_writer.text = writer

            if getattr(my_ep, 'director', None) and isinstance(my_ep['director'], string_types):
                for director in self._split_info(my_ep['director']):
                    cur_director = etree.SubElement(episode, 'director')
                    cur_director.text = director

            if getattr(my_ep, 'gueststars', None) and isinstance(my_ep['gueststars'], string_types):
                for actor in self._split_info(my_ep['gueststars']):
                    cur_actor = etree.SubElement(episode, 'actor')
                    cur_actor_name = etree.SubElement(cur_actor, 'name')
                    cur_actor_name.text = actor

            if getattr(series_obj, '_actors', None):
                for actor in series_obj['_actors']:
                    cur_actor = etree.SubElement(episode, 'actor')

                    if 'name' in actor and actor['name'].strip():
                        cur_actor_name = etree.SubElement(cur_actor, 'name')
                        cur_actor_name.text = actor['name'].strip()
                    else:
                        continue

                    if 'role' in actor and actor['role'].strip():
                        cur_actor_role = etree.SubElement(cur_actor, 'role')
                        cur_actor_role.text = actor['role'].strip()

                    if 'image' in actor and actor['image'].strip():
                        cur_actor_thumb = etree.SubElement(cur_actor, 'thumb')
                        cur_actor_thumb.text = actor['image'].strip()

        # Make it purdy
        helpers.indent_xml(root_node)

        data = etree.ElementTree(root_node)

        return data
Esempio n. 33
0
def _get_xem_exceptions(force):
    xem_exceptions = defaultdict(dict)
    url = 'http://thexem.de/map/allNames'
    params = {
        'origin': None,
        'seasonNumbers': 1,
    }

    if force or should_refresh('xem'):
        for indexer in indexerApi().indexers:
            indexer_api = indexerApi(indexer)

            try:
                # Get XEM origin for indexer
                origin = indexer_api.config['xem_origin']
                if origin not in VALID_XEM_ORIGINS:
                    msg = 'invalid origin for XEM: {0}'.format(origin)
                    raise ValueError(msg)
            except KeyError:
                # Indexer has no XEM origin
                continue
            except ValueError as error:
                # XEM origin for indexer is invalid
                logger.error(
                    'Error getting XEM scene exceptions for {indexer}:'
                    ' {error}', {'indexer': indexer_api.name, 'error': error}
                )
                continue
            else:
                # XEM origin for indexer is valid
                params['origin'] = origin

            logger.info(
                'Checking for XEM scene exceptions updates for'
                ' {indexer_name}', {'indexer_name': indexer_api.name}
            )

            response = safe_session.get(url, params=params, timeout=60)
            try:
                jdata = response.json()
            except (ValueError, AttributeError) as error:
                logger.debug(
                    'Check scene exceptions update failed for {indexer}.'
                    ' Unable to get URL: {url} Error: {error}', {'indexer': indexer_api.name, 'url': url, 'error': error}
                )
                continue

            if not jdata['data'] or jdata['result'] == 'failure':
                logger.debug(
                    'No data returned from XEM while checking for scene'
                    ' exceptions. Update failed for {indexer}', {'indexer': indexer_api.name}
                )
                continue

            for indexer_id, exceptions in iteritems(jdata['data']):
                try:
                    xem_exceptions[indexer][indexer_id] = exceptions
                except Exception as error:
                    logger.warning(
                        'XEM: Rejected entry: Indexer ID: {indexer_id},'
                        ' Exceptions: {exceptions}', {'indexer_id': indexer_id, 'exceptions': exceptions}
                    )
                    logger.warning('XEM: Rejected entry error message: {error}', {'error': error})

        set_last_refresh('xem')

    return xem_exceptions
Esempio n. 34
0
    def _parse_air_by_date(self, result):
        """
        Parse anime season episode results.

        Translate scene episode and season numbering to indexer numbering,
        using an air date to indexer season/episode translation.

        :param result: Guessit parse result object.
        :return: tuple of found indexer episode numbers and indexer season numbers
        """
        log.debug('Series {name} is air by date', {'name': result.series.name})

        new_episode_numbers = []
        new_season_numbers = []

        episode_by_air_date = self._get_episodes_by_air_date(result)

        season_number = None
        episode_numbers = []

        if episode_by_air_date:
            season_number = int(episode_by_air_date[0]['season'])
            episode_numbers = [int(episode_by_air_date[0]['episode'])]

            # Use the next query item if we have multiple results
            # and the current one is a special episode (season 0)
            if season_number == 0 and len(episode_by_air_date) > 1:
                season_number = int(episode_by_air_date[1]['season'])
                episode_numbers = [int(episode_by_air_date[1]['episode'])]

            log.debug(
                'Database info for series {name}: Season: {season} Episode(s): {episodes}',
                {
                    'name': result.series.name,
                    'season': season_number,
                    'episodes': episode_numbers
                })

        if season_number is None or not episode_numbers:
            log.debug('Series {name} has no season or episodes, using indexer',
                      {'name': result.series.name})

            indexer_api_params = indexerApi(
                result.series.indexer).api_params.copy()
            indexer_api = indexerApi(
                result.series.indexer).indexer(**indexer_api_params)
            try:
                if result.series.lang:
                    indexer_api_params['language'] = result.series.lang

                tv_episode = indexer_api[result.series.indexerid].aired_on(
                    result.air_date)[0]

                season_number = int(tv_episode['seasonnumber'])
                episode_numbers = [int(tv_episode['episodenumber'])]
                log.debug(
                    'Indexer info for series {name}: {ep}', {
                        'name': result.series.name,
                        'ep': episode_num(season_number, episode_numbers[0]),
                    })
            except IndexerEpisodeNotFound:
                log.warning(
                    'Unable to find episode with date {date} for series {name}. Skipping',
                    {
                        'date': result.air_date,
                        'name': result.series.name
                    })
                episode_numbers = []
            except IndexerError as error:
                log.warning('Unable to contact {indexer_api.name}: {error!r}',
                            {
                                'indexer_api': indexer_api,
                                'error': error
                            })
                episode_numbers = []
            except IndexerException as error:
                log.warning('Indexer exception: {indexer_api.name}: {error!r}',
                            {
                                'indexer_api': indexer_api,
                                'error': error
                            })
                episode_numbers = []

        for episode_number in episode_numbers:
            season = season_number
            episode = episode_number

            if result.series.is_scene:
                (season, episode) = scene_numbering.get_indexer_numbering(
                    result.series,
                    season_number,
                    episode_number,
                )
                log.debug(
                    'Scene numbering enabled series {name}, using indexer numbering: {ep}',
                    {
                        'name': result.series.name,
                        'ep': episode_num(season, episode)
                    })
            new_episode_numbers.append(episode)
            new_season_numbers.append(season)

        return new_episode_numbers, new_season_numbers
Esempio n. 35
0
def xem_refresh(series_obj, force=False):
    """
    Refresh data from xem for a tv show

    :param indexer_id: int
    """
    if not series_obj or series_obj.series_id < 1:
        return

    indexer_id = series_obj.indexer
    series_id = series_obj.series_id

    MAX_REFRESH_AGE_SECS = 86400  # 1 day

    main_db_con = db.DBConnection()
    rows = main_db_con.select(
        "SELECT last_refreshed FROM xem_refresh WHERE indexer = ? and indexer_id = ?",
        [indexer_id, series_id])
    if rows:
        lastRefresh = int(rows[0]['last_refreshed'])
        refresh = int(time.mktime(datetime.datetime.today().timetuple())
                      ) > lastRefresh + MAX_REFRESH_AGE_SECS
    else:
        refresh = True

    if refresh or force:
        logger.log(
            u'Looking up XEM scene mapping for show ID {0} on {1}'.format(
                series_id, series_obj.indexer_name), logger.DEBUG)

        # mark refreshed
        main_db_con.upsert(
            "xem_refresh", {
                'last_refreshed':
                int(time.mktime(datetime.datetime.today().timetuple()))
            }, {
                'indexer': indexer_id,
                'indexer_id': series_id
            })

        try:
            if not indexerApi(indexer_id).config.get('xem_origin'):
                logger.log(
                    u'{0} is an unsupported indexer in XEM'.format(
                        indexerApi(indexer_id).name), logger.DEBUG)
                return
            # XEM MAP URL
            url = "http://thexem.de/map/havemap?origin={0}".format(
                indexerApi(indexer_id).config['xem_origin'])
            parsed_json = safe_session.get_json(url)
            if not parsed_json or 'result' not in parsed_json or 'success' not in parsed_json[
                    'result'] or 'data' not in parsed_json or str(
                        indexer_id) not in parsed_json['data']:
                logger.log(
                    u'No XEM data for show ID {0} on {1}'.format(
                        series_id, series_obj.indexer_name), logger.DEBUG)
                return

            # XEM API URL
            url = "http://thexem.de/map/all?id={0}&origin={1}&destination=scene".format(
                indexer_id,
                indexerApi(indexer_id).config['xem_origin'])
            parsed_json = safe_session.get_json(url)
            if not parsed_json or 'result' not in parsed_json or 'success' not in parsed_json[
                    'result']:
                logger.log(
                    u'No XEM data for show ID {0} on {1}'.format(
                        indexer_id, series_obj.indexer_name), logger.DEBUG)
                return

            cl = []
            for entry in parsed_json['data']:
                if 'scene' in entry:
                    cl.append([
                        "UPDATE tv_episodes SET scene_season = ?, scene_episode = ?, scene_absolute_number = ? WHERE indexer = ? AND showid = ? AND season = ? AND episode = ?",
                        [
                            entry['scene']['season'],
                            entry['scene']['episode'],
                            entry['scene']['absolute'], indexer_id, series_id,
                            entry[indexerApi(indexer_id).config['xem_origin']]
                            ['season'], entry[indexerApi(
                                indexer_id).config['xem_origin']]['episode']
                        ]
                    ])
                    cl.append([
                        "UPDATE tv_episodes SET absolute_number = ? WHERE indexer = ? AND showid = ? AND season = ? AND episode = ? AND absolute_number = 0",
                        [
                            entry[indexerApi(
                                indexer_id).config['xem_origin']]['absolute'],
                            indexer_id, series_id, entry[indexerApi(
                                indexer_id).config['xem_origin']]['season'],
                            entry[indexerApi(
                                indexer_id).config['xem_origin']]['episode']
                        ]
                    ])
                if 'scene_2' in entry:  # for doubles
                    cl.append([
                        "UPDATE tv_episodes SET scene_season = ?, scene_episode = ?, scene_absolute_number = ? WHERE indexer = ? AND showid = ? AND season = ? AND episode = ?",
                        [
                            entry['scene_2']['season'],
                            entry['scene_2']['episode'],
                            entry['scene_2']['absolute'], indexer_id,
                            series_id, entry[indexerApi(
                                indexer_id).config['xem_origin']]['season'],
                            entry[indexerApi(
                                indexer_id).config['xem_origin']]['episode']
                        ]
                    ])

            if cl:
                main_db_con = db.DBConnection()
                main_db_con.mass_action(cl)

        except Exception as e:
            logger.log(
                u"Exception while refreshing XEM data for show ID {0} on {1}: {2}"
                .format(series_id, series_obj.indexer_name,
                        ex(e)), logger.WARNING)
            logger.log(traceback.format_exc(), logger.DEBUG)
Esempio n. 36
0
    def _ep_data(self, ep_obj):
        """
        Creates an elementTree XML structure for a WDTV style episode.xml
        and returns the resulting data object.

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

        eps_to_write = [ep_obj] + ep_obj.related_episodes

        my_show = self._get_show_data(ep_obj.series)
        if not my_show:
            return None

        root_node = etree.Element('details')

        # write an WDTV XML containing info for all matching episodes
        for ep_to_write in eps_to_write:

            try:
                my_ep = my_show[ep_to_write.season][ep_to_write.episode]
            except (IndexerEpisodeNotFound, IndexerSeasonNotFound):
                log.info(
                    'Unable to find episode {number} on {indexer}... has it been removed? Should I delete from db?',
                    {
                        'number': ep_num(ep_to_write.season,
                                         ep_to_write.episode),
                        'indexer': indexerApi(ep_obj.series.indexer).name,
                    })
                return None

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

            if not (getattr(my_ep, 'episodename', None)
                    and getattr(my_ep, 'firstaired', None)):
                return None

            if len(eps_to_write) > 1:
                episode = etree.SubElement(root_node, 'details')
            else:
                episode = root_node

            # TODO: get right EpisodeID
            episode_id = etree.SubElement(episode, 'id')
            episode_id.text = str(ep_to_write.indexerid)

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

            if getattr(my_show, 'seriesname', None):
                series_name = etree.SubElement(episode, 'series_name')
                series_name.text = my_show['seriesname']

            if ep_to_write.name:
                episode_name = etree.SubElement(episode, 'episode_name')
                episode_name.text = ep_to_write.name

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

            episode_num = etree.SubElement(episode, 'episode_number')
            episode_num.text = str(ep_to_write.episode)

            first_aired = etree.SubElement(episode, 'firstaired')

            if ep_to_write.airdate != datetime.date.fromordinal(1):
                first_aired.text = str(ep_to_write.airdate)

            if getattr(my_show, 'firstaired', None):
                try:
                    year_text = str(
                        datetime.datetime.strptime(my_show['firstaired'],
                                                   dateFormat).year)
                    if year_text:
                        year = etree.SubElement(episode, 'year')
                        year.text = year_text
                except Exception:
                    pass

            if ep_to_write.season != 0 and getattr(my_show, 'runtime', None):
                runtime = etree.SubElement(episode, 'runtime')
                runtime.text = my_show['runtime']

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

            if getattr(my_ep, 'director', None):
                director = etree.SubElement(episode, 'director')
                director.text = my_ep['director']

            if getattr(my_show, '_actors', None):
                for actor in my_show['_actors']:
                    if not ('name' in actor and actor['name'].strip()):
                        continue

                    cur_actor = etree.SubElement(episode, 'actor')

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

                    if 'role' in actor and actor['role'].strip():
                        cur_actor_role = etree.SubElement(cur_actor, 'role')
                        cur_actor_role.text = actor['role'].strip()

            if ep_to_write.description:
                overview = etree.SubElement(episode, 'overview')
                overview.text = ep_to_write.description

            # Make it purdy
            helpers.indent_xml(root_node)
            data = etree.ElementTree(root_node)

        return data
Esempio n. 37
0
    def _ep_data(self, ep_obj):
        """
        Creates an elementTree XML structure for a MediaBrowser style episode.xml
        and returns the resulting data object.

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

        eps_to_write = [ep_obj] + ep_obj.related_episodes

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

        my_show = self._get_show_data(ep_obj.series)
        if not my_show:
            return None

        root_node = etree.Element(u'Item')

        # write an MediaBrowser XML containing info for all matching episodes
        for ep_to_write in eps_to_write:

            try:
                my_ep = my_show[ep_to_write.season][ep_to_write.episode]
            except (IndexerEpisodeNotFound, IndexerSeasonNotFound):
                log.info(
                    u'Unable to find episode {number} on {indexer}... has it been removed? Should I delete from db?',
                    {
                        u'number':
                        episode_num(ep_to_write.season, ep_to_write.episode),
                        u'indexer':
                        indexerApi(ep_obj.series.indexer).name
                    })
                return None

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

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

                if not (getattr(my_ep, u'episodename', None)
                        and getattr(my_ep, u'firstaired', None)):
                    return None

                episode = root_node

                if ep_to_write.name:
                    episode_name = etree.SubElement(episode, u'EpisodeName')
                    episode_name.text = ep_to_write.name

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

                if ep_obj.related_episodes:
                    episode_number_end = etree.SubElement(
                        episode, u'EpisodeNumberEnd')
                    episode_number_end.text = str(ep_to_write.episode)

                season_number = etree.SubElement(episode, u'SeasonNumber')
                season_number.text = str(ep_to_write.season)

                if not ep_obj.related_episodes and getattr(
                        my_ep, u'absolute_number', None):
                    absolute_number = etree.SubElement(episode,
                                                       u'absolute_number')
                    absolute_number.text = str(my_ep[u'absolute_number'])

                if ep_to_write.airdate != datetime.date.fromordinal(1):
                    first_aired = etree.SubElement(episode, u'FirstAired')
                    first_aired.text = str(ep_to_write.airdate)

                metadata_type = etree.SubElement(episode, u'Type')
                metadata_type.text = u'Episode'

                if ep_to_write.description:
                    overview = etree.SubElement(episode, u'Overview')
                    overview.text = ep_to_write.description

                if not ep_obj.related_episodes:
                    if getattr(my_ep, u'rating', None):
                        rating = etree.SubElement(episode, u'Rating')
                        rating.text = my_ep[u'rating']

                    if getattr(my_show, u'imdb_id', None):
                        IMDB_ID = etree.SubElement(episode, u'IMDB_ID')
                        IMDB_ID.text = my_show[u'imdb_id']

                        IMDB = etree.SubElement(episode, u'IMDB')
                        IMDB.text = my_show[u'imdb_id']

                        IMDbId = etree.SubElement(episode, u'IMDbId')
                        IMDbId.text = my_show[u'imdb_id']

                indexer_id = etree.SubElement(episode, u'id')
                indexer_id.text = str(ep_to_write.indexerid)

                persons = etree.SubElement(episode, u'Persons')

                if getattr(my_show, u'_actors', None):
                    for actor in my_show[u'_actors']:
                        if not (u'name' in actor and actor[u'name'].strip()):
                            continue

                        cur_actor = etree.SubElement(persons, u'Person')

                        cur_actor_name = etree.SubElement(cur_actor, u'Name')
                        cur_actor_name.text = actor[u'name'].strip()

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

                        if u'role' in actor and actor[u'role'].strip():
                            cur_actor_role = etree.SubElement(
                                cur_actor, u'Role')
                            cur_actor_role.text = actor[u'role'].strip()

                language = etree.SubElement(episode, u'Language')
                try:
                    language.text = my_ep[u'language']
                except Exception:
                    language.text = app.INDEXER_DEFAULT_LANGUAGE  # tvrage api doesn't provide language so we must assume a value here

                thumb = etree.SubElement(episode, u'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 = thumb_text

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

                if ep_to_write.name:
                    if not episode_name.text:
                        episode_name.text = ep_to_write.name
                    else:
                        episode_name.text = u', '.join(
                            [episode_name.text, ep_to_write.name])

                if ep_to_write.description:
                    if not overview.text:
                        overview.text = ep_to_write.description
                    else:
                        overview.text = u'\r'.join(
                            [overview.text, ep_to_write.description])

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

        # 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, u'Person')
                cur_person_name = etree.SubElement(person, u'Name')
                cur_person_name.text = cur_name
                cur_person_type = etree.SubElement(person, u'Type')
                cur_person_type.text = person_type

        # Make it purdy
        helpers.indent_xml(root_node)
        data = etree.ElementTree(root_node)

        return data
Esempio n. 38
0
    def run(self, force=False):

        self.amActive = True
        refresh_shows = []  # A list of shows, that need to be refreshed
        season_updates = []  # A list of show seasons that have passed their next_update timestamp
        update_max_weeks = 12

        network_timezones.update_network_dict()

        # Refresh the exceptions_cache from db.
        refresh_exceptions_cache()

        logger.info(u'Started periodic show updates')

        # Cache for the indexers list of updated show
        indexer_updated_shows = {}
        # Cache for the last indexer update timestamp
        last_updates = {}

        # Loop through the list of shows, and per show evaluate if we can use the .get_last_updated_seasons()
        for show in app.showList:
            if show.paused:
                logger.info(u'The show {show} is paused, not updating it.', show=show.name)
                continue

            indexer_api_params = indexerApi(show.indexer).api_params.copy()
            try:
                indexer_api = indexerApi(show.indexer).indexer(**indexer_api_params)
            except IndexerUnavailable:
                logger.warning(u'Problem running show_updater, Indexer {indexer_name} seems to be having '
                               u'connectivity issues. While trying to look for show updates on show: {show}',
                               indexer_name=indexerApi(show.indexer).name, show=show.name)
                continue

            # Get the lastUpdate timestamp for this indexer.
            if indexerApi(show.indexer).name not in last_updates:
                last_updates[indexerApi(show.indexer).name] = \
                    self.update_cache.get_last_indexer_update(indexerApi(show.indexer).name)
            last_update = last_updates[indexerApi(show.indexer).name]

            # Get a list of updated shows from the indexer, since last update.
            # Use the list, to limit the shows for which are requested for the last updated seasons.
            if last_update and last_update > time.time() - (604800 * update_max_weeks):
                if show.indexer not in indexer_updated_shows:
                    try:
                        indexer_updated_shows[show.indexer] = indexer_api.get_last_updated_series(
                            last_update, update_max_weeks
                        )
                    except IndexerUnavailable:
                        logger.warning(u'Problem running show_updater, Indexer {indexer_name} seems to be having '
                                       u'connectivity issues while trying to look for show updates on show: {show}',
                                       indexer_name=indexerApi(show.indexer).name, show=show.name)
                        continue
                    except IndexerException as error:
                        logger.warning(u'Problem running show_updater, Indexer {indexer_name} seems to be having '
                                       u'issues while trying to get updates for show {show}. Cause: {cause!r}',
                                       indexer_name=indexerApi(show.indexer).name, show=show.name, cause=error)
                        continue
                    except RequestException as error:
                        logger.warning(u'Problem running show_updater, Indexer {indexer_name} seems to be having '
                                       u'issues while trying to get updates for show {show}. Cause: {cause!r}',
                                       indexer_name=indexerApi(show.indexer).name, show=show.name, cause=error)

                        if isinstance(error, HTTPError):
                            if error.response.status_code == 503:
                                logger.warning(u'API Service offline: '
                                               u'This service is temporarily offline, try again later.')
                            elif error.response.status_code == 429:
                                logger.warning(u'Your request count (#) is over the allowed limit of (40).')
                        continue
                    except Exception as error:
                        logger.exception(u'Problem running show_updater, Indexer {indexer_name} seems to be having '
                                         u'issues while trying to get updates for show {show}. Cause: {cause!r}.',
                                         indexer_name=indexerApi(show.indexer).name, show=show.name, cause=error)
                        continue

                # If the current show is not in the list, move on to the next.
                # Only do this for shows, if the indexer has had a successful update run within the last 12 weeks.
                if all([isinstance(indexer_updated_shows[show.indexer], list),
                        show.indexerid not in indexer_updated_shows.get(show.indexer)]):
                    logger.debug(u'Skipping show update for {show}. As the show is not '
                                 u'in the indexers {indexer_name} list with updated '
                                 u'shows within the last {weeks} weeks.', show=show.name,
                                 indexer_name=indexerApi(show.indexer).name, weeks=update_max_weeks)
                    continue

            # These are the criteria for performing a full show refresh.
            if any([not hasattr(indexer_api, 'get_last_updated_seasons'),
                    not last_update or last_update < time.time() - 604800 * update_max_weeks]):
                # no entry in lastUpdate, or last update was too long ago,
                # let's refresh the show for this indexer
                logger.debug(u'Trying to update {show}. Your lastUpdate for {indexer_name} is older then {weeks} weeks,'
                             u" or the indexer doesn't support per season updates. Doing a full update.",
                             show=show.name, indexer_name=indexerApi(show.indexer).name,
                             weeks=update_max_weeks)
                refresh_shows.append(show)

            # Else fall back to per season updates.
            elif hasattr(indexer_api, 'get_last_updated_seasons'):
                # Get updated seasons and add them to the season update list.
                try:
                    updated_seasons = indexer_api.get_last_updated_seasons([show.indexerid], last_update,
                                                                           update_max_weeks)
                except IndexerUnavailable:
                    logger.warning(u'Problem running show_updater, Indexer {indexer_name} seems to be having '
                                   u'connectivity issues while trying to look for showupdates on show: {show}',
                                   indexer_name=indexerApi(show.indexer).name, show=show.name)
                    continue
                except IndexerException as error:
                    logger.warning(u'Problem running show_updater, Indexer {indexer_name} seems to be having '
                                   u'issues while trying to get updates for show {show}. Cause: {cause!r}',
                                   indexer_name=indexerApi(show.indexer).name, show=show.name, cause=error)
                    continue
                except Exception as error:
                    logger.exception(u'Problem running show_updater, Indexer {indexer_name} seems to be having '
                                     u'issues while trying to get updates for show {show}. Cause: {cause!r}',
                                     indexer_name=indexerApi(show.indexer).name, show=show.name, cause=error)
                    continue

                if updated_seasons[show.indexerid]:
                    logger.info(u'{show_name}: Adding the following seasons for update to queue: {seasons}',
                                show_name=show.name, seasons=updated_seasons[show.indexerid])
                    for season in updated_seasons[show.indexerid]:
                        season_updates.append((show.indexer, show, season))

        pi_list = []

        # Full refreshes
        for show in refresh_shows:
            # If the cur_show is not 'paused' then add to the show_queue_scheduler
            if not show.paused:
                logger.info(u'Full update on show: {show}', show=show.name)
                try:
                    pi_list.append(app.show_queue_scheduler.action.updateShow(show))
                except (CantUpdateShowException, CantRefreshShowException) as e:
                    logger.warning(u'Automatic update failed. Error: {error}', error=e)
                except Exception as e:
                    logger.error(u'Automatic update failed: Error: {error}', error=e)
            else:
                logger.info(u'Show update skipped, show: {show} is paused.', show=show.name)

        # Only update expired season
        for show in season_updates:
            # If the cur_show is not 'paused' then add to the show_queue_scheduler
            if not show[1].paused:
                logger.info(u'Updating season {season} for show: {show}.', season=show[2], show=show[1].name)
                try:
                    pi_list.append(app.show_queue_scheduler.action.updateShow(show[1], season=show[2]))
                except CantUpdateShowException as e:
                    logger.warning(u'Automatic update failed. Error: {error}', error=e)
                except Exception as e:
                    logger.error(u'Automatic update failed: Error: {error}', error=e)
            else:
                logger.info(u'Show update skipped, show: {show} is paused.', show=show[1].name)

        ui.ProgressIndicators.setIndicator('dailyUpdate', ui.QueueProgressIndicator('Daily Update', pi_list))

        # Only refresh updated shows that have been updated using the season updates.
        # The full refreshed shows, are updated from the queueItem.
        for show in set(show[1] for show in season_updates):
            if not show.paused:
                try:
                    app.show_queue_scheduler.action.refreshShow(show, True)
                except CantRefreshShowException as e:
                    logger.warning(u'Show refresh on show {show_name} failed. Error: {error}',
                                   show_name=show.name, error=e)
                except Exception as e:
                    logger.error(u'Show refresh on show {show_name} failed: Unexpected Error: {error}',
                                 show_name=show.name, error=e)
            else:
                logger.info(u'Show refresh skipped, show: {show_name} is paused.', show_name=show.name)

        if refresh_shows or season_updates:
            for indexer in set([show.indexer for show in refresh_shows] + [s[1].indexer for s in season_updates]):
                indexer_api = indexerApi(indexer)
                self.update_cache.set_last_indexer_update(indexer_api.name)
                logger.info(u'Updated lastUpdate timestamp for {indexer_name}', indexer_name=indexer_api.name)
            logger.info(u'Completed scheduling updates on shows')
        else:
            logger.info(u'Completed but there was nothing to update')

        self.amActive = False
Esempio n. 39
0
    def _ep_data(self, ep_obj):
        """
        Creates a key value structure for a Tivo episode metadata file and
        returns the resulting data object.

        ep_obj: a Episode 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 myShow.

        The key values for the tivo metadata file are from:

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

        data = ''

        eps_to_write = [ep_obj] + ep_obj.related_episodes

        my_show = self._get_show_data(ep_obj.series)
        if not my_show:
            return None

        for ep_to_write in eps_to_write:

            try:
                my_ep = my_show[ep_to_write.season][ep_to_write.episode]
            except (IndexerEpisodeNotFound, IndexerSeasonNotFound):
                log.debug(
                    u'Unable to find episode {number} on {indexer}... has it been removed? Should I delete from db?',
                    {
                        'number':
                        episode_num(ep_to_write.season, ep_to_write.episode),
                        'indexer':
                        indexerApi(ep_obj.series.indexer).name,
                    })
                return None

            if ep_obj.season == 0 and not getattr(my_ep, 'firstaired', None):
                my_ep['firstaired'] = text_type(datetime.date.fromordinal(1))

            if not (getattr(my_ep, 'episodename', None)
                    and getattr(my_ep, 'firstaired', None)):
                return None

            if getattr(my_show, 'seriesname', None):
                data += ('title : {title}\n'.format(
                    title=my_show['seriesname']))
                data += ('seriesTitle : {title}\n'.format(
                    title=my_show['seriesname']))

            data += ('episodeTitle : {title}\n'.format(
                title=ep_to_write._format_pattern('%Sx%0E %EN')))

            # 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 : {ep_num}\n'.format(
                ep_num=ep_to_write.episode))

            # 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.
            sanitized_description = ep_to_write.description
            # Replace double curly quotes
            sanitized_description = sanitized_description.replace(
                u'\u201c', '\'').replace(u'\u201d', '\'')
            # Replace single curly quotes
            sanitized_description = sanitized_description.replace(
                u'\u2018', '\'').replace(u'\u2019',
                                         '\'').replace(u'\u02BC', '\'')

            data += ('description : {desc}\n'.format(
                desc=sanitized_description))

            # Usually starts with 'SH' and followed by 6-8 digits.
            # TiVo uses zap2it for their data, so the series id is the zap2it_id.
            if getattr(my_show, 'zap2it_id', None):
                data += ('seriesId : {zap2it}\n'.format(
                    zap2it=my_show['zap2it_id']))

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

            # 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 ep_to_write.airdate != datetime.date.fromordinal(1):
                data += ('originalAirDate : {airdate}T00:00:00Z\n'.format(
                    airdate=ep_to_write.airdate))

            # This shows up at the beginning of the description on the Program screen and on the Details screen.
            if getattr(my_show, '_actors', None):
                for actor in my_show['_actors']:
                    if 'name' in actor and actor['name'].strip():
                        data += ('vActor : {actor}\n'.format(
                            actor=actor['name'].strip()))

            # This is shown on both the Program screen and the Details screen.
            if getattr(my_ep, 'rating', None):
                try:
                    rating = float(my_ep['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 : {rating}\n'.format(rating=rating))

            # 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 getattr(my_show, 'contentrating', None):
                data += ('tvRating : {rating}\n'.format(
                    rating=my_show['contentrating']))

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

                        # 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
Esempio n. 40
0
    def resource_search_indexers_for_show_name(self):
        """
        Search indexers for show name.

        Query parameters:
        :param query: Search term
        :param indexerId: Indexer to search, defined by ID. '0' for all indexers.
        :param language: 2-letter language code to search the indexer(s) in
        """
        query = self.get_argument('query', '').strip()
        indexer_id = self.get_argument('indexerId', '0').strip()
        language = self.get_argument('language', '').strip()

        if not query:
            return self._bad_request('No search term provided.')

        enabled_indexers = indexerApi().indexers
        indexer_id = try_int(indexer_id)
        if indexer_id > 0 and indexer_id not in enabled_indexers:
            return self._bad_request('Invalid indexer id.')

        if not language or language == 'null':
            language = app.INDEXER_DEFAULT_LANGUAGE

        search_terms = [query]

        # If search term ends with what looks like a year, enclose it in ()
        matches = re.match(r'^(.+ |)([12][0-9]{3})$', query)
        if matches:
            search_terms.append('{0}({1})'.format(matches.group(1), matches.group(2)))

        for search_term in search_terms:
            # If search term begins with an article, let's also search for it without
            matches = re.match(r'^(?:a|an|the) (.+)$', search_term, re.I)
            if matches:
                search_terms.append(matches.group(1))

        results = {}
        final_results = []

        # Query indexers for each search term and build the list of results
        for indexer in enabled_indexers if indexer_id == 0 else [indexer_id]:
            indexer_instance = indexerApi(indexer)
            custom_api_params = indexer_instance.api_params.copy()
            custom_api_params['language'] = language
            custom_api_params['custom_ui'] = classes.AllShowsListUI

            try:
                indexer_api = indexer_instance.indexer(**custom_api_params)
            except IndexerUnavailable as msg:
                log.info('Could not initialize indexer {indexer}: {error}',
                         {'indexer': indexer_instance.name, 'error': msg})
                continue

            log.debug('Searching for show with search term(s): {terms} on indexer: {indexer}',
                      {'terms': search_terms, 'indexer': indexer_api.name})
            for search_term in search_terms:
                try:
                    indexer_results = indexer_api[search_term]
                    # add search results
                    results.setdefault(indexer, []).extend(indexer_results)
                except IndexerException as error:
                    log.info('Error searching for show. term(s): {terms} indexer: {indexer} error: {error}',
                             {'terms': search_terms, 'indexer': indexer_api.name, 'error': error})
                except Exception as error:
                    log.error('Internal Error searching for show. term(s): {terms} indexer: {indexer} error: {error}',
                              {'terms': search_terms, 'indexer': indexer_api.name, 'error': error})

        # Get all possible show ids
        all_show_ids = {}
        for show in app.showList:
            for ex_indexer_name, ex_show_id in iteritems(show.externals):
                ex_indexer_id = reverse_mappings.get(ex_indexer_name)
                if not ex_indexer_id:
                    continue
                all_show_ids[(ex_indexer_id, ex_show_id)] = (show.indexer_name, show.series_id)

        for indexer, shows in iteritems(results):
            indexer_api = indexerApi(indexer)
            indexer_results_set = set()
            for show in shows:
                show_id = int(show['id'])
                indexer_results_set.add(
                    (
                        indexer_api.name,
                        indexer,
                        indexer_api.config['show_url'],
                        show_id,
                        show['seriesname'],
                        show['firstaired'] or 'N/A',
                        show.get('network', '') or 'N/A',
                        sanitize_filename(show['seriesname']),
                        all_show_ids.get((indexer, show_id), False)
                    )
                )

            final_results.extend(indexer_results_set)

        language_id = indexerApi().config['langabbv_to_id'][language]
        data = {
            'results': final_results,
            'languageId': language_id
        }
        return self._ok(data=data)
Esempio n. 41
0
    def run(self):

        ShowQueueItem.run(self)

        log.info(
            '{id}: Beginning update of {show}{season}', {
                'id':
                self.show.series_id,
                'show':
                self.show.name,
                'season':
                ' with season(s) [{0}]'.format(','.join(
                    text_type(s)
                    for s in self.seasons) if self.seasons else '')
            })

        log.debug(
            '{id}: Retrieving show info from {indexer}', {
                'id': self.show.series_id,
                'indexer': indexerApi(self.show.indexer).name
            })
        try:
            # Let's make sure we refresh the indexer_api object attached to the show object.
            self.show.create_indexer()
            self.show.load_from_indexer()
        except IndexerError as error:
            log.warning(
                '{id}: Unable to contact {indexer}. Aborting: {error_msg}', {
                    'id': self.show.series_id,
                    'indexer': indexerApi(self.show.indexer).name,
                    'error_msg': error
                })
            return
        except IndexerAttributeNotFound as error:
            log.warning(
                '{id}: Data retrieved from {indexer} was incomplete.'
                ' Aborting: {error_msg}', {
                    'id': self.show.series_id,
                    'indexer': indexerApi(self.show.indexer).name,
                    'error_msg': error
                })
            return

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

        # have to save show before reading episodes from db
        try:
            log.debug('{id}: Saving new IMDb show info to database',
                      {'id': self.show.series_id})
            self.show.save_to_db()
        except Exception as error:
            log.warning(
                '{id}: Error saving new IMDb show info to database: {error_msg}',
                {
                    'id': self.show.series_id,
                    'error_msg': error
                })
            log.error(traceback.format_exc())

        # get episode list from DB
        try:
            episodes_from_db = self.show.load_episodes_from_db(self.seasons)
        except IndexerException as error:
            log.warning(
                '{id}: Unable to contact {indexer}. Aborting: {error_msg}', {
                    'id': self.show.series_id,
                    'indexer': indexerApi(self.show.indexer).name,
                    'error_msg': error
                })
            return

        # get episode list from the indexer
        try:
            episodes_from_indexer = self.show.load_episodes_from_indexer(
                self.seasons)
        except IndexerException as error:
            log.warning(
                '{id}: Unable to get info from {indexer}. The show info will not be refreshed.'
                ' Error: {error_msg}', {
                    'id': self.show.series_id,
                    'indexer': indexerApi(self.show.indexer).name,
                    'error_msg': error
                })
            episodes_from_indexer = None

        if episodes_from_indexer is None:
            log.warning(
                '{id}: No data returned from {indexer} during season show update.'
                ' Unable to update this show', {
                    'id': self.show.series_id,
                    'indexer': indexerApi(self.show.indexer).name
                })
        else:
            # for each ep we found on the Indexer delete it from the DB list
            for cur_season in episodes_from_indexer:
                for cur_episode in episodes_from_indexer[cur_season]:
                    if cur_season in episodes_from_db and cur_episode in episodes_from_db[
                            cur_season]:
                        del episodes_from_db[cur_season][cur_episode]

            # remaining episodes in the DB list are not on the indexer, just delete them from the DB
            for cur_season in episodes_from_db:
                for cur_episode in episodes_from_db[cur_season]:
                    log.debug(
                        '{id}: Permanently deleting episode {show} {ep} from the database',
                        {
                            'id': self.show.series_id,
                            'show': self.show.name,
                            'ep': episode_num(cur_season, cur_episode)
                        })
                    # Create the ep object only because Im going to delete it
                    ep_obj = self.show.get_episode(cur_season, cur_episode)
                    try:
                        ep_obj.delete_episode()
                    except EpisodeDeletedException:
                        log.debug(
                            '{id}: Episode {show} {ep} successfully deleted from the database',
                            {
                                'id': self.show.series_id,
                                'show': self.show.name,
                                'ep': episode_num(cur_season, cur_episode)
                            })

        # Save only after all changes were applied
        try:
            log.debug('{id}: Saving all updated show info to database',
                      {'id': self.show.series_id})
            self.show.save_to_db()
        except Exception as error:
            log.warning(
                '{id}: Error saving all updated show info to database: {error_msg}',
                {
                    'id': self.show.series_id,
                    'error_msg': error
                })
            log.error(traceback.format_exc())

        log.info('{id}: Finished update of {show}', {
            'id': self.show.series_id,
            'show': self.show.name
        })

        self.finish()
Esempio n. 42
0
    def run(self):

        ShowQueueItem.run(self)

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

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

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

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

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

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

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

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

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

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

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

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

            self.show = newShow

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

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

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

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

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

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

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

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

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

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

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

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

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

        # if they set default ep status to WANTED then run the backlog to search for episodes
        # FIXME: This needs to be a backlog queue item!!!
        if self.show.default_ep_status == WANTED:
            log.info(
                'Launching backlog for this show since its episodes are WANTED'
            )
            app.backlog_search_scheduler.action.search_backlog([self.show])

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

        self.show.flush_episodes()

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

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

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

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

        # check if show has XEM mapping so we can determine if searches
        # should go by scene numbering or indexer numbering.
        if not self.scene and scene_numbering.get_xem_numbering_for_show(
                self.show):
            self.show.scene = 1

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

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

        self.finish()
Esempio n. 43
0
    def getIndexerLanguages():
        result = indexerApi().config['valid_languages']

        return json.dumps({'results': result})
Esempio n. 44
0
def xem_refresh(series_obj, force=False):
    """
    Refresh data from xem for a tv show

    :param indexer_id: int
    """
    if not series_obj or series_obj.series_id < 1:
        return

    indexer_id = series_obj.indexer
    series_id = series_obj.series_id

    MAX_REFRESH_AGE_SECS = 86400  # 1 day

    main_db_con = db.DBConnection()
    rows = main_db_con.select('SELECT last_refreshed FROM xem_refresh WHERE indexer = ? and indexer_id = ?',
                              [indexer_id, series_id])
    if rows:
        lastRefresh = int(rows[0]['last_refreshed'])
        refresh = int(time.mktime(datetime.datetime.today().timetuple())) > lastRefresh + MAX_REFRESH_AGE_SECS
    else:
        refresh = True

    if refresh or force:
        logger.log(
            u'Looking up XEM scene mapping for show ID {0} on {1}'.format(series_id, series_obj.indexer_name), logger.DEBUG)

        # mark refreshed
        main_db_con.upsert(
            'xem_refresh',
            {'last_refreshed': int(time.mktime(datetime.datetime.today().timetuple()))},
            {'indexer': indexer_id, 'indexer_id': series_id}
        )

        try:
            if not indexerApi(indexer_id).config.get('xem_origin'):
                logger.log(u'{0} is an unsupported indexer in XEM'.format(indexerApi(indexer_id).name), logger.DEBUG)
                return
            # XEM MAP URL
            url = 'http://thexem.de/map/havemap?origin={0}'.format(indexerApi(indexer_id).config['xem_origin'])
            parsed_json = safe_session.get_json(url)
            if not parsed_json or 'result' not in parsed_json or 'success' not in parsed_json['result'] or 'data' not in parsed_json or str(series_id) not in parsed_json['data']:
                logger.log(u'No XEM data for show ID {0} on {1}'.format(series_id, series_obj.indexer_name), logger.DEBUG)
                return

            # XEM API URL
            url = 'http://thexem.de/map/all?id={0}&origin={1}&destination=scene'.format(series_id, indexerApi(indexer_id).config['xem_origin'])
            parsed_json = safe_session.get_json(url)
            if not parsed_json or 'result' not in parsed_json or 'success' not in parsed_json['result']:
                logger.log(u'No XEM data for show ID {0} on {1}'.format(indexer_id, series_obj.indexer_name), logger.DEBUG)
                return

            cl = []
            for entry in parsed_json['data']:
                if 'scene' in entry:
                    cl.append([
                        'UPDATE tv_episodes SET scene_season = ?, scene_episode = ?, scene_absolute_number = ? '
                        'WHERE indexer = ? AND showid = ? AND season = ? AND episode = ?',
                        [entry['scene']['season'], entry['scene']['episode'],
                         entry['scene']['absolute'], indexer_id, series_id,
                         entry[indexerApi(indexer_id).config['xem_origin']]['season'],
                         entry[indexerApi(indexer_id).config['xem_origin']]['episode']]
                    ])
                    # Update the absolute_number from xem, but do not set it when it has already been set by tvdb.
                    # We want to prevent doubles and tvdb is leading in that case.
                    cl.append([
                        'UPDATE tv_episodes SET absolute_number = ? '
                        'WHERE indexer = ? AND showid = ? AND season = ? AND episode = ? AND absolute_number = 0 '
                        'AND {absolute_number} NOT IN '
                        '(SELECT absolute_number '
                        'FROM tv_episodes '
                        'WHERE absolute_number = ? AND indexer = ? AND showid = ?)'.format(
                            absolute_number=entry[indexerApi(indexer_id).config['xem_origin']]['absolute']
                        ),
                        [entry[indexerApi(indexer_id).config['xem_origin']]['absolute'], indexer_id, series_id,
                         entry[indexerApi(indexer_id).config['xem_origin']]['season'],
                         entry[indexerApi(indexer_id).config['xem_origin']]['episode'],
                         entry[indexerApi(indexer_id).config['xem_origin']]['absolute'],
                         indexer_id, series_id]
                    ])
                if 'scene_2' in entry:  # for doubles
                    cl.append([
                        'UPDATE tv_episodes SET scene_season = ?, scene_episode = ?, scene_absolute_number = ? '
                        'WHERE indexer = ? AND showid = ? AND season = ? AND episode = ?',
                        [entry['scene_2']['season'], entry['scene_2']['episode'],
                         entry['scene_2']['absolute'], indexer_id, series_id,
                         entry[indexerApi(indexer_id).config['xem_origin']]['season'],
                         entry[indexerApi(indexer_id).config['xem_origin']]['episode']]
                    ])

            if cl:
                main_db_con = db.DBConnection()
                main_db_con.mass_action(cl)

        except Exception as e:
            logger.log(u'Exception while refreshing XEM data for show ID {0} on {1}: {2}'.format
                       (series_id, series_obj.indexer_name, ex(e)), logger.WARNING)
            logger.log(traceback.format_exc(), logger.DEBUG)