Exemplo n.º 1
0
def refresh_subtitles(episode_info, existing_subtitles):
    try:
        video = get_video(episode_info['location'].encode(
            sickbeard.SYS_ENCODING))
    except UnicodeEncodeError as error:
        logger.log(
            u'An error occurred while encoding \'{}\' with your current locale. '
            'Rename the file or try a different locale. Error: {}'.format(
                episode_info['location'], ex(error)), logger.WARNING)
        return existing_subtitles, None

    if not video:
        logger.log(
            u'Exception caught in subliminal.scan_video, subtitles couldn\'t be refreshed',
            logger.DEBUG)
        return existing_subtitles, None
    current_subtitles = get_subtitles(video)
    if existing_subtitles == current_subtitles:
        logger.log(
            u'No changed subtitles for {} {}'.format(
                episode_info['show_name'],
                episode_num(episode_info['season'], episode_info['episode'])
                or episode_num(episode_info['season'],
                               episode_info['episode'],
                               numbering='absolute')), logger.DEBUG)
        return existing_subtitles, None
    else:
        return current_subtitles, True
Exemplo n.º 2
0
def setEpisodeToWanted(show, s, e):
    """
    Sets an episode to wanted, only if it is currently skipped
    """
    epObj = show.getEpisode(s, e)
    if epObj:

        with epObj.lock:
            if epObj.status != SKIPPED or epObj.airdate == datetime.date.fromordinal(
                    1):
                return

            logger.log("Setting episode {show} {ep} to wanted".format(
                show=show.name, ep=episode_num(s, e)))
            # figure out what segment the episode is in and remember it so we can backlog it

            epObj.status = WANTED
            epObj.saveToDB()

        cur_backlog_queue_item = search_queue.BacklogQueueItem(show, [epObj])
        sickbeard.searchQueueScheduler.action.add_item(cur_backlog_queue_item)

        logger.log(
            "Starting backlog search for {show} {ep} because some episodes were set to wanted"
            .format(show=show.name, ep=episode_num(s, e)))
Exemplo n.º 3
0
def refresh_subtitles(episode):
    video = get_video(episode.location)
    if not video:
        logger.log("Exception caught in subliminal.scan_video, subtitles couldn't be refreshed", logger.DEBUG)
        return episode.subtitles, None
    current_subtitles = get_subtitles(video)
    if episode.subtitles == current_subtitles:
        logger.log('No changed subtitles for {0} {1}'.format
                   (episode.show.name, episode_num(episode.season, episode.episode) or
                    episode_num(episode.season, episode.episode, numbering='absolute')), logger.DEBUG)
        return episode.subtitles, None
    else:
        return current_subtitles, True
Exemplo n.º 4
0
def refresh_subtitles(episode_info, existing_subtitles):
    video = get_video(episode_info['location'])
    if not video:
        logger.log(u'Exception caught in subliminal.scan_video, subtitles couldn\'t be refreshed', logger.DEBUG)
        return existing_subtitles, None
    current_subtitles = get_subtitles(video)
    if existing_subtitles == current_subtitles:
        logger.log(u'No changed subtitles for {} {}'.format
                   (episode_info['show_name'], episode_num(episode_info['season'], episode_info['episode']) or
                    episode_num(episode_info['season'], episode_info['episode'], numbering='absolute')), logger.DEBUG)
        return existing_subtitles, None
    else:
        return current_subtitles, True
Exemplo n.º 5
0
def refresh_subtitles(episode):
    video = get_video(episode.location)
    if not video:
        logger.log("Exception caught in subliminal.scan_video, subtitles couldn't be refreshed", logger.DEBUG)
        return episode.subtitles, None
    current_subtitles = get_subtitles(video)
    if episode.subtitles == current_subtitles:
        logger.log('No changed subtitles for {0} {1}'.format
                   (episode.show.name, episode_num(episode.season, episode.episode) or
                    episode_num(episode.season, episode.episode, numbering='absolute')), logger.DEBUG)
        return episode.subtitles, None
    else:
        return current_subtitles, True
Exemplo n.º 6
0
def download_subtitles(video_path, show_name, season, episode, episode_name, show_indexerid, release_name, status,
                       existing_subtitles):  # pylint: disable=too-many-locals, too-many-branches, too-many-statements
    """Download missing subtitles for the given episode.

    Checks whether subtitles are needed or not

    :param video_path: the video path
    :type video_path: str
    :param show_name: the show name
    :type show_name: str
    :param season: the season number
    :type season: int
    :param episode: the episode number
    :type episode: int
    :param episode_name: the episode name
    :type episode_name: str
    :param show_indexerid: the show indexerid
    :type show_indexerid: int
    :param release_name: the release name
    :type release_name: str
    :param status: the show status
    :type status: int
    :param existing_subtitles: list of existing subtitles (opensubtitles codes)
    :type existing_subtitles: list of str
    :return: a sorted list of the opensubtitles codes for the downloaded subtitles
    :rtype: list of str
    """
    ep_num = episode_num(season, episode) or episode_num(season, episode, numbering='absolute')
    languages = get_needed_languages(existing_subtitles)

    if not languages:
        logger.debug(u'Episode already has all needed subtitles, skipping %s %s', show_name, ep_num)
        return []

    logger.debug(u'Checking subtitle candidates for %s %s (%s)', show_name, ep_num, os.path.basename(video_path))

    subtitles_dir = get_subtitles_dir(video_path)
    found_subtitles = download_best_subs(video_path, subtitles_dir, release_name, languages)

    for subtitle in found_subtitles:
        if sickbeard.SUBTITLES_EXTRA_SCRIPTS and isMediaFile(video_path):
            subtitle_path = compute_subtitle_path(subtitle, video_path, subtitles_dir)
            run_subs_extra_scripts(video_path=video_path, subtitle_path=subtitle_path,
                                   subtitle_language=subtitle.language, show_name=show_name, season=season,
                                   episode=episode, episode_name=episode_name, show_indexerid=show_indexerid)
        if sickbeard.SUBTITLES_HISTORY:
            logger.debug(u'history.logSubtitle %s, %s', subtitle.provider_name, subtitle.language.opensubtitles)
            history.logSubtitle(show_indexerid, season, episode, status, subtitle)

    return sorted({subtitle.language.opensubtitles for subtitle in found_subtitles})
Exemplo n.º 7
0
    def addEpisodeToTraktWatchList(self):
        if sickbeard.TRAKT_SYNC_WATCHLIST and sickbeard.USE_TRAKT:
            logger.log(u"WATCHLIST::ADD::START - Look for Episodes to Add to Trakt Watchlist", logger.DEBUG)

            main_db_con = db.DBConnection()
            sql_selection = 'select tv_shows.indexer, tv_shows.startyear, showid, show_name, season, episode from tv_episodes,tv_shows where tv_shows.indexer_id = tv_episodes.showid and tv_episodes.status in (' + ','.join([str(x) for x in Quality.SNATCHED + Quality.SNATCHED_PROPER + [WANTED]]) + ')'
            episodes = main_db_con.select(sql_selection)

            if episodes is not None:
                trakt_data = []

                for cur_episode in episodes:
                    trakt_id = sickbeard.indexerApi(cur_episode["indexer"]).config['trakt_id']

                    if not self._checkInList(trakt_id, str(cur_episode["showid"]), str(cur_episode["season"]), str(cur_episode["episode"])):
                        logger.log(u"Adding Episode {show} {ep} to watchlist".format
                                   (show=cur_episode["show_name"],
                                    ep=episode_num(cur_episode["season"], cur_episode["episode"])),
                                   logger.DEBUG)
                        trakt_data.append((cur_episode["showid"], cur_episode["indexer"], cur_episode["show_name"], cur_episode["startyear"], cur_episode["season"],
                                           cur_episode["episode"]))

                if trakt_data:
                    try:
                        data = self.trakt_bulk_data_generate(trakt_data)
                        self.trakt_api.traktRequest("sync/watchlist", data, method='POST')
                        self._getEpisodeWatchlist()
                    except traktException as e:
                        logger.log(u"Could not connect to Trakt service. Error {0!s}".format(ex(e)), logger.WARNING)

            logger.log(u"WATCHLIST::ADD::FINISH - Look for Episodes to Add to Trakt Watchlist", logger.DEBUG)
Exemplo n.º 8
0
    def removeEpisodeFromTraktCollection(self):
        if sickbeard.TRAKT_SYNC_REMOVE and sickbeard.TRAKT_SYNC and sickbeard.USE_TRAKT:
            logger.log(u"COLLECTION::REMOVE::START - Look for Episodes to Remove From Trakt Collection", logger.DEBUG)

            main_db_con = db.DBConnection()
            sql_selection = 'select tv_shows.indexer, tv_shows.startyear, showid, show_name, season, episode, tv_episodes.status, tv_episodes.location from tv_episodes,tv_shows where tv_shows.indexer_id = tv_episodes.showid'
            episodes = main_db_con.select(sql_selection)

            if episodes is not None:
                trakt_data = []

                for cur_episode in episodes:
                    trakt_id = sickbeard.indexerApi(cur_episode["indexer"]).config['trakt_id']

                    if self._checkInList(trakt_id, str(cur_episode["showid"]), str(cur_episode["season"]), str(cur_episode["episode"]), List='Collection'):
                        if cur_episode["location"] == '':
                            logger.log(u"Removing Episode {show} {ep} from collection".format
                                       (show=cur_episode["show_name"],
                                        ep=episode_num(cur_episode["season"], cur_episode["episode"])),
                                       logger.DEBUG)
                            trakt_data.append((cur_episode["showid"], cur_episode["indexer"], cur_episode["show_name"], cur_episode["startyear"], cur_episode["season"], cur_episode["episode"]))

                if trakt_data:
                    try:
                        data = self.trakt_bulk_data_generate(trakt_data)
                        self.trakt_api.traktRequest("sync/collection/remove", data, method='POST')
                        self._getShowCollection()
                    except traktException as e:
                        logger.log(u"Could not connect to Trakt service. Error: {0!s}".format(ex(e)), logger.WARNING)

            logger.log(u"COLLECTION::REMOVE::FINISH - Look for Episodes to Remove From Trakt Collection", logger.DEBUG)
Exemplo n.º 9
0
    def removeEpisodeFromTraktWatchList(self):
        if sickbeard.TRAKT_SYNC_WATCHLIST and sickbeard.USE_TRAKT:
            logger.log("WATCHLIST::REMOVE::START - Look for Episodes to Remove from Trakt Watchlist", logger.DEBUG)

            main_db_con = db.DBConnection()
            sql_selection = 'select tv_shows.indexer, tv_shows.startyear, showid, show_name, season, episode, tv_episodes.status from tv_episodes,tv_shows where tv_shows.indexer_id = tv_episodes.showid'
            episodes = main_db_con.select(sql_selection)

            if episodes is not None:
                trakt_data = []

                for cur_episode in episodes:
                    trakt_id = sickbeard.indexerApi(cur_episode[b"indexer"]).config['trakt_id']

                    if self._checkInList(trakt_id, str(cur_episode[b"showid"]), str(cur_episode[b"season"]), str(cur_episode[b"episode"])):
                        if cur_episode[b"status"] not in Quality.SNATCHED + Quality.SNATCHED_PROPER + [UNKNOWN] + [WANTED]:
                            logger.log("Removing Episode {show} {ep} from watchlist".format
                                       (show=cur_episode[b"show_name"],
                                        ep=episode_num(cur_episode[b"season"], cur_episode[b"episode"])),
                                       logger.DEBUG)
                            trakt_data.append((cur_episode[b"showid"], cur_episode[b"indexer"], cur_episode[b"show_name"], cur_episode[b"startyear"], cur_episode[b"season"], cur_episode[b"episode"]))

                if trakt_data:
                    try:
                        data = self.trakt_bulk_data_generate(trakt_data)
                        self.trakt_api.traktRequest("sync/watchlist/remove", data, method='POST')
                        self._getEpisodeWatchlist()
                    except traktException as e:
                        logger.log("Could not connect to Trakt service. Error: {0}".format(ex(e)), logger.WARNING)

                logger.log("WATCHLIST::REMOVE::FINISH - Look for Episodes to Remove from Trakt Watchlist", logger.DEBUG)
Exemplo n.º 10
0
    def addEpisodeToTraktCollection(self):
        if sickbeard.TRAKT_SYNC and sickbeard.USE_TRAKT:
            logger.log("COLLECTION::ADD::START - Look for Episodes to Add to Trakt Collection", logger.DEBUG)

            main_db_con = db.DBConnection()
            sql_selection = 'select tv_shows.indexer, tv_shows.startyear, showid, show_name, season, episode from tv_episodes,tv_shows where tv_shows.indexer_id = tv_episodes.showid and tv_episodes.status in (' + ','.join([str(x) for x in Quality.DOWNLOADED + Quality.ARCHIVED]) + ')'
            episodes = main_db_con.select(sql_selection)

            if episodes is not None:
                trakt_data = []

                for cur_episode in episodes:
                    trakt_id = sickbeard.indexerApi(cur_episode[b"indexer"]).config['trakt_id']

                    if not self._checkInList(trakt_id, str(cur_episode[b"showid"]), str(cur_episode[b"season"]), str(cur_episode[b"episode"]), List='Collection'):
                        logger.log("Adding Episode {show} {ep} to collection".format
                                   (show=cur_episode[b"show_name"],
                                    ep=episode_num(cur_episode[b"season"], cur_episode[b"episode"])),
                                   logger.DEBUG)
                        trakt_data.append((cur_episode[b"showid"], cur_episode[b"indexer"], cur_episode[b"show_name"], cur_episode[b"startyear"], cur_episode[b"season"], cur_episode[b"episode"]))

                if trakt_data:
                    try:
                        data = self.trakt_bulk_data_generate(trakt_data)
                        self.trakt_api.traktRequest("sync/collection", data, method='POST')
                        self._getShowCollection()
                    except traktException as e:
                        logger.log("Could not connect to Trakt service. Error: {0}".format(ex(e)), logger.WARNING)

            logger.log("COLLECTION::ADD::FINISH - Look for Episodes to Add to Trakt Collection", logger.DEBUG)
Exemplo n.º 11
0
    def remove_episode_trakt_collection(self):
        if sickbeard.TRAKT_SYNC_REMOVE and sickbeard.TRAKT_SYNC and sickbeard.USE_TRAKT:

            main_db_con = db.DBConnection()
            sql_selection = 'select tv_shows.indexer, tv_shows.startyear, showid, show_name, season, episode, tv_episodes.status, tv_episodes.location from tv_episodes,tv_shows where tv_shows.indexer_id = tv_episodes.showid'
            episodes = main_db_con.select(sql_selection)

            if episodes:
                trakt_data = []

                for cur_episode in episodes:
                    trakt_id = sickbeard.indexerApi(cur_episode["indexer"]).config['trakt_id']

                    if self._check_list(trakt_id, cur_episode["showid"], cur_episode["season"], cur_episode["episode"], List='Collection'):
                        if cur_episode["location"] == '':
                            logger.log(u"Removing Episode {show} {ep} from collection".format
                                       (show=cur_episode["show_name"],
                                        ep=episode_num(cur_episode["season"], cur_episode["episode"])),
                                       logger.DEBUG)
                            trakt_data.append((cur_episode["showid"], cur_episode["indexer"], cur_episode["show_name"], cur_episode["startyear"], cur_episode["season"], cur_episode["episode"]))

                if trakt_data:
                    try:
                        data = self.trakt_bulk_data_generate(trakt_data)
                        self.trakt_api.traktRequest("sync/collection/remove", data, method='POST')
                        self._get_show_collection()
                    except traktException as e:
                        logger.log(u"Could not connect to Trakt. Error: {}".format(ex(e)), logger.WARNING)
Exemplo n.º 12
0
    def _get_episode_search_strings(self, ep_obj, add_string=''):

        if not ep_obj:
            return [{}]

        to_return = []
        search_params = {'category': 'Episode'}

        # episode
        if ep_obj.show.air_by_date or ep_obj.show.sports:
            date_str = str(ep_obj.airdate)

            # BTN uses dots in dates, we just search for the date since that
            # combined with the series identifier should result in just one episode
            search_params['name'] = date_str.replace('-', '.')
        elif ep_obj.show.anime:
            search_params['name'] = "{0:d}".format(int(ep_obj.scene_absolute_number))
        else:
            # Do a general name search for the episode, formatted like SXXEYY
            search_params['name'] = "{ep}".format(ep=episode_num(ep_obj.scene_season, ep_obj.scene_episode))

        # search
        if ep_obj.show.indexer == 1:
            search_params['tvdb'] = ep_obj.show.indexerid
            to_return.append(search_params)
        else:
            # add new query string for every exception
            name_exceptions = list(
                set(scene_exceptions.get_scene_exceptions(ep_obj.show.indexerid) + [ep_obj.show.name]))
            for cur_exception in name_exceptions:
                search_params['series'] = sanitizeSceneName(cur_exception)
                to_return.append(search_params)

        return to_return
Exemplo n.º 13
0
    def removeEpisodeFromTraktCollection(self):
        if sickbeard.TRAKT_SYNC_REMOVE and sickbeard.TRAKT_SYNC and sickbeard.USE_TRAKT:
            logger.log("COLLECTION::REMOVE::START - Look for Episodes to Remove From Trakt Collection", logger.DEBUG)

            main_db_con = db.DBConnection()
            sql_selection = 'select tv_shows.indexer, tv_shows.startyear, showid, show_name, season, episode, tv_episodes.status, tv_episodes.location from tv_episodes,tv_shows where tv_shows.indexer_id = tv_episodes.showid'
            episodes = main_db_con.select(sql_selection)

            if episodes is not None:
                trakt_data = []

                for cur_episode in episodes:
                    trakt_id = sickbeard.indexerApi(cur_episode[b"indexer"]).config[b'trakt_id']

                    if self._checkInList(trakt_id, str(cur_episode[b"showid"]), str(cur_episode[b"season"]), str(cur_episode[b"episode"]), List='Collection'):
                        if cur_episode[b"location"] == '':
                            logger.log("Removing Episode {show} {ep} from collection".format(
                                show=cur_episode[b"show_name"], ep=episode_num(cur_episode[b"season"], cur_episode[b"episode"])), logger.DEBUG
                            )
                            trakt_data.append((cur_episode[b"showid"], cur_episode[b"indexer"], cur_episode[b"show_name"], cur_episode[b"startyear"],
                                               cur_episode[b"season"], cur_episode[b"episode"]))

                if trakt_data:
                    try:
                        data = self.trakt_bulk_data_generate(trakt_data)
                        self.trakt_api.traktRequest("sync/collection/remove", data, method='POST')
                        self._getShowCollection()
                    except traktException as e:
                        logger.log("Could not connect to Trakt service. Error: {0}".format(ex(e)), logger.WARNING)

            logger.log("COLLECTION::REMOVE::FINISH - Look for Episodes to Remove From Trakt Collection", logger.DEBUG)
Exemplo n.º 14
0
    def addEpisodeToTraktWatchList(self):
        if sickbeard.TRAKT_SYNC_WATCHLIST and sickbeard.USE_TRAKT:
            logger.log("WATCHLIST::ADD::START - Look for Episodes to Add to Trakt Watchlist", logger.DEBUG)

            main_db_con = db.DBConnection()
            sql_selection = 'select tv_shows.indexer, tv_shows.startyear, showid, show_name, season, episode from tv_episodes,tv_shows where tv_shows.indexer_id = tv_episodes.showid and tv_episodes.status in (' + ','.join([str(x) for x in Quality.SNATCHED + Quality.SNATCHED_PROPER + [WANTED]]) + ')'
            episodes = main_db_con.select(sql_selection)

            if episodes is not None:
                trakt_data = []

                for cur_episode in episodes:
                    trakt_id = sickbeard.indexerApi(cur_episode[b"indexer"]).config['trakt_id']

                    if not self._checkInList(trakt_id, str(cur_episode[b"showid"]), str(cur_episode[b"season"]), str(cur_episode[b"episode"])):
                        logger.log("Adding Episode {show} {ep} to watchlist".format
                                   (show=cur_episode[b"show_name"],
                                    ep=episode_num(cur_episode[b"season"], cur_episode[b"episode"])),
                                   logger.DEBUG)
                        trakt_data.append((cur_episode[b"showid"], cur_episode[b"indexer"], cur_episode[b"show_name"], cur_episode[b"startyear"], cur_episode[b"season"],
                                           cur_episode[b"episode"]))

                if trakt_data:
                    try:
                        data = self.trakt_bulk_data_generate(trakt_data)
                        self.trakt_api.traktRequest("sync/watchlist", data, method='POST')
                        self._getEpisodeWatchlist()
                    except traktException as e:
                        logger.log("Could not connect to Trakt service. Error {0}".format(ex(e)), logger.WARNING)

            logger.log("WATCHLIST::ADD::FINISH - Look for Episodes to Add to Trakt Watchlist", logger.DEBUG)
Exemplo n.º 15
0
    def removeEpisodeFromTraktWatchList(self):
        if sickbeard.TRAKT_SYNC_WATCHLIST and sickbeard.USE_TRAKT:
            logger.log(u"WATCHLIST::REMOVE::START - Look for Episodes to Remove from Trakt Watchlist", logger.DEBUG)

            main_db_con = db.DBConnection()
            sql_selection = 'select tv_shows.indexer, tv_shows.startyear, showid, show_name, season, episode, tv_episodes.status from tv_episodes,tv_shows where tv_shows.indexer_id = tv_episodes.showid'
            episodes = main_db_con.select(sql_selection)

            if episodes is not None:
                trakt_data = []

                for cur_episode in episodes:
                    trakt_id = sickbeard.indexerApi(cur_episode["indexer"]).config['trakt_id']

                    if self._checkInList(trakt_id, str(cur_episode["showid"]), str(cur_episode["season"]), str(cur_episode["episode"])):
                        if cur_episode["status"] not in Quality.SNATCHED + Quality.SNATCHED_PROPER + [UNKNOWN] + [WANTED]:
                            logger.log(u"Removing Episode {show} {ep} from watchlist".format
                                       (show=cur_episode["show_name"],
                                        ep=episode_num(cur_episode["season"], cur_episode["episode"])),
                                       logger.DEBUG)
                            trakt_data.append((cur_episode["showid"], cur_episode["indexer"], cur_episode["show_name"], cur_episode["startyear"], cur_episode["season"], cur_episode["episode"]))

                if trakt_data:
                    try:
                        data = self.trakt_bulk_data_generate(trakt_data)
                        self.trakt_api.traktRequest("sync/watchlist/remove", data, method='POST')
                        self._getEpisodeWatchlist()
                    except traktException as e:
                        logger.log(u"Could not connect to Trakt service. Error: {0}".format(ex(e)), logger.WARNING)

                logger.log(u"WATCHLIST::REMOVE::FINISH - Look for Episodes to Remove from Trakt Watchlist", logger.DEBUG)
Exemplo n.º 16
0
    def addEpisodeToTraktCollection(self):
        if sickbeard.TRAKT_SYNC and sickbeard.USE_TRAKT:
            logger.log(u"COLLECTION::ADD::START - Look for Episodes to Add to Trakt Collection", logger.DEBUG)

            main_db_con = db.DBConnection()
            sql_selection = 'select tv_shows.indexer, tv_shows.startyear, showid, show_name, season, episode from tv_episodes,tv_shows where tv_shows.indexer_id = tv_episodes.showid and tv_episodes.status in (' + ','.join([str(x) for x in Quality.DOWNLOADED + Quality.ARCHIVED]) + ')'
            episodes = main_db_con.select(sql_selection)

            if episodes is not None:
                trakt_data = []

                for cur_episode in episodes:
                    trakt_id = sickbeard.indexerApi(cur_episode["indexer"]).config['trakt_id']

                    if not self._checkInList(trakt_id, str(cur_episode["showid"]), str(cur_episode["season"]), str(cur_episode["episode"]), List='Collection'):
                        logger.log(u"Adding Episode {show} {ep} to collection".format
                                   (show=cur_episode["show_name"],
                                    ep=episode_num(cur_episode["season"], cur_episode["episode"])),
                                   logger.DEBUG)
                        trakt_data.append((cur_episode["showid"], cur_episode["indexer"], cur_episode["show_name"], cur_episode["startyear"], cur_episode["season"], cur_episode["episode"]))

                if trakt_data:
                    try:
                        data = self.trakt_bulk_data_generate(trakt_data)
                        self.trakt_api.traktRequest("sync/collection", data, method='POST')
                        self._getShowCollection()
                    except traktException as e:
                        logger.log(u"Could not connect to Trakt service. Error: {0}".format(ex(e)), logger.WARNING)

            logger.log(u"COLLECTION::ADD::FINISH - Look for Episodes to Add to Trakt Collection", logger.DEBUG)
Exemplo n.º 17
0
def refresh_subtitles(episode_info, existing_subtitles):
    video = get_video(episode_info["location"])
    if not video:
        logger.log(u"Exception caught in subliminal.scan_video, subtitles couldn't be refreshed", logger.DEBUG)
        return existing_subtitles, None
    current_subtitles = get_subtitles(video)
    if existing_subtitles == current_subtitles:
        logger.log(
            u"No changed subtitles for {0} {1}".format(
                episode_info["show_name"],
                episode_num(episode_info["season"], episode_info["episode"])
                or episode_num(episode_info["season"], episode_info["episode"], numbering="absolute"),
            ),
            logger.DEBUG,
        )
        return existing_subtitles, None
    else:
        return current_subtitles, True
Exemplo n.º 18
0
def refresh_subtitles(episode_info, existing_subtitles):
    try:
        video = get_video(episode_info['location'].encode(sickbeard.SYS_ENCODING))
    except UnicodeEncodeError as error:
        logger.log(u'An error occurred while encoding \'{}\' with your current locale. '
                   'Rename the file or try a different locale. Error: {}'.format
                   (episode_info['location'], ex(error)), logger.WARNING)
        return existing_subtitles, None

    if not video:
        logger.log(u'Exception caught in subliminal.scan_video, subtitles couldn\'t be refreshed', logger.DEBUG)
        return existing_subtitles, None
    current_subtitles = get_subtitles(video)
    if existing_subtitles == current_subtitles:
        logger.log(u'No changed subtitles for {} {}'.format
                   (episode_info['show_name'], episode_num(episode_info['season'], episode_info['episode']) or
                    episode_num(episode_info['season'], episode_info['episode'], numbering='absolute')), logger.DEBUG)
        return existing_subtitles, None
    else:
        return current_subtitles, True
Exemplo n.º 19
0
    def backlogOverview(self):
        t = PageTemplate(rh=self, filename='manage_backlogOverview.mako')

        show_counts = {}
        show_cats = {}
        show_sql_results = {}

        main_db_con = db.DBConnection()
        for cur_show in sickbeard.showList:

            ep_counts = {
                Overview.SKIPPED: 0,
                Overview.WANTED: 0,
                Overview.QUAL: 0,
                Overview.GOOD: 0,
                Overview.UNAIRED: 0,
                Overview.SNATCHED: 0,
                Overview.SNATCHED_PROPER: 0,
                Overview.SNATCHED_BEST: 0
            }
            ep_cats = {}

            sql_results = main_db_con.select(
                """
                SELECT status, season, episode, name, airdate
                FROM tv_episodes
                WHERE tv_episodes.season IS NOT NULL AND
                      tv_episodes.showid IN (SELECT tv_shows.indexer_id
                                             FROM tv_shows
                                             WHERE tv_shows.indexer_id = ? AND
                                                   paused = 0)
                ORDER BY tv_episodes.season DESC, tv_episodes.episode DESC
                """,
                [cur_show.indexerid]
            )

            for cur_result in sql_results:
                cur_ep_cat = cur_show.getOverview(cur_result[b'status'])
                if cur_ep_cat:
                    ep_cats[u'{ep}'.format(ep=episode_num(cur_result[b'season'], cur_result[b'episode']))] = cur_ep_cat
                    ep_counts[cur_ep_cat] += 1

            show_counts[cur_show.indexerid] = ep_counts
            show_cats[cur_show.indexerid] = ep_cats
            show_sql_results[cur_show.indexerid] = sql_results

        return t.render(
            showCounts=show_counts, showCats=show_cats,
            showSQLResults=show_sql_results, controller='manage',
            action='backlogOverview', title='Backlog Overview',
            header='Backlog Overview', topmenu='manage')
Exemplo n.º 20
0
def setEpisodeToWanted(show, s, e):
    """
    Sets an episode to wanted, only if it is currently skipped
    """
    epObj = show.getEpisode(s, e)
    if epObj:

        with epObj.lock:
            if epObj.status != SKIPPED or epObj.airdate == datetime.date.fromordinal(1):
                return

            logger.log("Setting episode {show} {ep} to wanted".format
                       (show=show.name, ep=episode_num(s, e)))
            # figure out what segment the episode is in and remember it so we can backlog it

            epObj.status = WANTED
            epObj.saveToDB()

        cur_backlog_queue_item = search_queue.BacklogQueueItem(show, [epObj])
        sickbeard.searchQueueScheduler.action.add_item(cur_backlog_queue_item)

        logger.log("Starting backlog search for {show} {ep} because some episodes were set to wanted".format
                   (show=show.name, ep=episode_num(s, e)))
Exemplo n.º 21
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 TVEpisode instance to create the NFO for
        """

        eps_to_write = [ep_obj] + ep_obj.relatedEps

        indexer_lang = ep_obj.show.lang

        # There's gotta be a better way of doing this but we don't wanna
        # change the language value elsewhere
        l_indexer_api_params = sickbeard.indexerApi(ep_obj.show.indexer).api_params.copy()

        l_indexer_api_params[b'actors'] = True

        if indexer_lang and not indexer_lang == sickbeard.INDEXER_DEFAULT_LANGUAGE:
            l_indexer_api_params[b'language'] = indexer_lang

        if ep_obj.show.dvdorder != 0:
            l_indexer_api_params[b'dvdorder'] = True

        try:
            t = sickbeard.indexerApi(ep_obj.show.indexer).indexer(**l_indexer_api_params)
            my_show = t[ep_obj.show.indexerid]
        except sickbeard.indexer_shownotfound as e:
            raise ShowNotFoundException(e.message)
        except sickbeard.indexer_error:
            logger.log(u'Unable to connect to {indexer} while creating meta files - skipping it.'.format
                       (indexer=sickbeard.indexerApi(ep_obj.show.indexer).name), logger.WARNING)
            return

        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 (sickbeard.indexer_episodenotfound, sickbeard.indexer_seasonnotfound):
                logger.log(u'Unable to find episode {ep_num} on {indexer}... '
                           u'has it been removed? Should I delete from db?'.format
                           (ep_num=episode_num(ep_to_write.season, ep_to_write.episode),
                            indexer=sickbeard.indexerApi(ep_obj.show.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):
                logger.log(u'Not generating nfo because the ep has no title', logger.DEBUG)
                return None

            logger.log(u'Creating metadata for episode {ep_num}'.format
                       (ep_num=episode_num(ep_obj.season, ep_obj.episode)), logger.DEBUG)

            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'], basestring):
                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'], basestring):
                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'], basestring):
                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.indentXML(root_node)

        data = etree.ElementTree(root_node)

        return data
Exemplo n.º 22
0
def download_subtitles(subtitles_info):  # pylint: disable=too-many-locals, too-many-branches, too-many-statements
    existing_subtitles = subtitles_info['subtitles']

    if not needs_subtitles(existing_subtitles):
        logger.log(u'Episode already has all needed subtitles, skipping {} {}'.format
                   (subtitles_info['show_name'], episode_num(subtitles_info['season'], subtitles_info['episode']) or
                    episode_num(subtitles_info['season'], subtitles_info['episode'], numbering='absolute')), logger.DEBUG)
        return existing_subtitles, None

    # Check if we really need subtitles
    languages = get_needed_languages(existing_subtitles)
    if not languages:
        logger.log(u'No subtitles needed for {} {}'.format
                   (subtitles_info['show_name'], episode_num(subtitles_info['season'], subtitles_info['episode']) or
                    episode_num(subtitles_info['season'], subtitles_info['episode'], numbering='absolute')), logger.DEBUG)
        return existing_subtitles, None

    subtitles_path = get_subtitles_path(subtitles_info['location'])
    video_path = subtitles_info['location']
    
    # Perfect match = hash score - hearing impaired score - resolution score (subtitle for 720p its the same for 1080p)
    # Perfect match = 215 -1 -1 = 213
    # No-perfect match = hash score - hearing impaired score - resolution score - release_group score
    # No-perfect match = 215 -1 -1 -9 = 204
    # From latest subliminal code:
    # episode_scores = {'hash': 215, 'series': 108, 'year': 54, 'season': 18, 'episode': 18, 'release_group': 9,
    #                   'format': 4, 'audio_codec': 2, 'resolution': 1, 'hearing_impaired': 1, 'video_codec': 1}
    user_score = 213 if sickbeard.SUBTITLES_PERFECT_MATCH else 204

    video = get_video(video_path, subtitles_path=subtitles_path)
    if not video:
        logger.log(u'Exception caught in subliminal.scan_video for {} {}'.format
                   (subtitles_info['show_name'], episode_num(subtitles_info['season'], subtitles_info['episode']) or
                    episode_num(subtitles_info['season'], subtitles_info['episode'], numbering='absolute')), logger.DEBUG)
        return existing_subtitles, None

    providers = enabled_service_list()
    provider_configs = {'addic7ed': {'username': sickbeard.ADDIC7ED_USER,
                                     'password': sickbeard.ADDIC7ED_PASS},
                        'legendastv': {'username': sickbeard.LEGENDASTV_USER,
                                       'password': sickbeard.LEGENDASTV_PASS},
                        'opensubtitles': {'username': sickbeard.OPENSUBTITLES_USER,
                                          'password': sickbeard.OPENSUBTITLES_PASS}}

    pool = ProviderPool(providers=providers, provider_configs=provider_configs)

    try:
        subtitles_list = pool.list_subtitles(video, languages)

        for provider in providers:
            if provider in pool.discarded_providers:
                logger.log(u'Could not search in {} provider. Discarding for now'.format(provider), logger.DEBUG)

        if not subtitles_list:
            logger.log(u'No subtitles found for {} {}'.format
                       (subtitles_info['show_name'], episode_num(subtitles_info['season'], subtitles_info['episode']) or
                        episode_num(subtitles_info['season'], subtitles_info['episode'], numbering='absolute')), logger.DEBUG)
            return existing_subtitles, None

        for subtitle in subtitles_list:
            score = subliminal.score.compute_score(subtitle, video, hearing_impaired=sickbeard.SUBTITLES_HEARING_IMPAIRED)
            logger.log(u'[{}] Subtitle score for {} is: {} (min={})'.format
                       (subtitle.provider_name, subtitle.id, score, user_score), logger.DEBUG)

        found_subtitles = pool.download_best_subtitles(subtitles_list, video, languages=languages,
                                                       hearing_impaired=sickbeard.SUBTITLES_HEARING_IMPAIRED,
                                                       min_score=user_score, only_one=not sickbeard.SUBTITLES_MULTI)

        subliminal.save_subtitles(video, found_subtitles, directory=subtitles_path,
                                  single=not sickbeard.SUBTITLES_MULTI)
    except IOError as error:
        if 'No space left on device' in ex(error):
            logger.log(u'Not enough space on the drive to save subtitles', logger.WARNING)
        else:
            logger.log(traceback.format_exc(), logger.WARNING)
    except Exception:
        logger.log(u'Error occurred when downloading subtitles for: {}'.format(video_path))
        logger.log(traceback.format_exc(), logger.ERROR)
        return existing_subtitles, None

    for subtitle in found_subtitles:
        subtitle_path = subliminal.subtitle.get_subtitle_path(video.name,
                                                              None if not sickbeard.SUBTITLES_MULTI else
                                                              subtitle.language)
        if subtitles_path is not None:
            subtitle_path = os.path.join(subtitles_path, os.path.split(subtitle_path)[1])

        sickbeard.helpers.chmodAsParent(subtitle_path)
        sickbeard.helpers.fixSetGroupID(subtitle_path)

        if sickbeard.SUBTITLES_HISTORY:
            logger.log(u'history.logSubtitle {}, {}'.format
                       (subtitle.provider_name, subtitle.language.opensubtitles), logger.DEBUG)

            history.logSubtitle(subtitles_info['show_indexerid'], subtitles_info['season'],
                                subtitles_info['episode'], subtitles_info['status'], subtitle)

        if sickbeard.SUBTITLES_EXTRA_SCRIPTS and isMediaFile(video_path) and not sickbeard.EMBEDDED_SUBTITLES_ALL:
            run_subs_extra_scripts(subtitles_info, subtitle, video, single=not sickbeard.SUBTITLES_MULTI)

    new_subtitles = sorted({subtitle.language.opensubtitles for subtitle in found_subtitles})
    current_subtitles = sorted({subtitle for subtitle in new_subtitles + existing_subtitles}) if existing_subtitles else new_subtitles
    if not sickbeard.SUBTITLES_MULTI and len(found_subtitles) == 1:
        new_code = found_subtitles[0].language.opensubtitles
        if new_code not in existing_subtitles:
            current_subtitles.remove(new_code)
        current_subtitles.append('und')

    return current_subtitles, new_subtitles
Exemplo 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 TVShow instance to create the NFO for
        """

        eps_to_write = [ep_obj] + ep_obj.relatedEps

        indexer_lang = ep_obj.show.lang

        # There's gotta be a better way of doing this but we don't wanna
        # change the language value elsewhere
        l_indexer_api_params = sickbeard.indexerApi(ep_obj.show.indexer).api_params.copy()

        if indexer_lang and not indexer_lang == sickbeard.INDEXER_DEFAULT_LANGUAGE:
            l_indexer_api_params[b'language'] = indexer_lang

        if ep_obj.show.dvdorder != 0:
            l_indexer_api_params[b'dvdorder'] = True

        try:
            t = sickbeard.indexerApi(ep_obj.show.indexer).indexer(**l_indexer_api_params)
            my_show = t[ep_obj.show.indexerid]
        except sickbeard.indexer_shownotfound as e:
            raise ShowNotFoundException(e.message)
        except sickbeard.indexer_error:
            logger.log(u'Unable to connect to {indexer} while creating meta files - skipping it.'.format
                       (indexer=sickbeard.indexerApi(ep_obj.show.indexer).name), logger.WARNING)
            return

        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 (sickbeard.indexer_episodenotfound, sickbeard.indexer_seasonnotfound):
                logger.log(u'Unable to find episode {ep_num} on {indexer}... '
                           u'has it been removed? Should I delete from db?'.format
                           (ep_num=episode_num(ep_to_write.season, ep_to_write.episode),
                            indexer=sickbeard.indexerApi(ep_obj.show.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.relatedEps 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'], basestring):
                        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 = 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])

        # Make it purdy
        helpers.indentXML(root_node)

        data = etree.ElementTree(root_node)

        return data
Exemplo n.º 24
0
def download_subtitles(subtitles_info):  # pylint: disable=too-many-locals, too-many-branches, too-many-statements
    existing_subtitles = subtitles_info['subtitles']

    if not needs_subtitles(existing_subtitles):
        logger.log(
            u'Episode already has all needed subtitles, skipping {0} {1}'.
            format(
                subtitles_info['show_name'],
                episode_num(subtitles_info['season'],
                            subtitles_info['episode'])
                or episode_num(subtitles_info['season'],
                               subtitles_info['episode'],
                               numbering='absolute')), logger.DEBUG)
        return existing_subtitles, None

    languages = get_needed_languages(existing_subtitles)
    if not languages:
        logger.log(
            u'No subtitles needed for {0} {1}'.format(
                subtitles_info['show_name'],
                episode_num(subtitles_info['season'],
                            subtitles_info['episode'])
                or episode_num(subtitles_info['season'],
                               subtitles_info['episode'],
                               numbering='absolute')), logger.DEBUG)
        return existing_subtitles, None

    subtitles_path = get_subtitles_path(subtitles_info['location'])
    video_path = subtitles_info['location']

    # Perfect match = hash score - hearing impaired score - resolution score (subtitle for 720p is the same as for 1080p)
    # Perfect match = 215 - 1 - 1 = 213
    # Non-perfect match = series + year + season + episode
    # Non-perfect match = 108 + 54 + 18 + 18 = 198
    # From latest subliminal code:
    # episode_scores = {'hash': 215, 'series': 108, 'year': 54, 'season': 18, 'episode': 18, 'release_group': 9,
    #                   'format': 4, 'audio_codec': 2, 'resolution': 1, 'hearing_impaired': 1, 'video_codec': 1}
    user_score = 213 if sickbeard.SUBTITLES_PERFECT_MATCH else 198

    video = get_video(video_path, subtitles_path=subtitles_path)
    if not video:
        logger.log(
            u'Exception caught in subliminal.scan_video for {0} {1}'.format(
                subtitles_info['show_name'],
                episode_num(subtitles_info['season'],
                            subtitles_info['episode'])
                or episode_num(subtitles_info['season'],
                               subtitles_info['episode'],
                               numbering='absolute')), logger.DEBUG)
        return existing_subtitles, None

    providers = enabled_service_list()
    pool = SubtitleProviderPool()

    try:
        subtitles_list = pool.list_subtitles(video, languages)

        for provider in providers:
            if provider in pool.discarded_providers:
                logger.log(
                    u'Could not search in {0} provider. Discarding for now'.
                    format(provider), logger.DEBUG)

        if not subtitles_list:
            logger.log(
                u'No subtitles found for {0} {1}'.format(
                    subtitles_info['show_name'],
                    episode_num(subtitles_info['season'],
                                subtitles_info['episode'])
                    or episode_num(subtitles_info['season'],
                                   subtitles_info['episode'],
                                   numbering='absolute')), logger.DEBUG)
            return existing_subtitles, None

        for subtitle in subtitles_list:
            score = subliminal.score.compute_score(
                subtitle,
                video,
                hearing_impaired=sickbeard.SUBTITLES_HEARING_IMPAIRED)
            logger.log(
                u'[{0}] Subtitle score for {1} is: {2} (min={3})'.format(
                    subtitle.provider_name, subtitle.id, score, user_score),
                logger.DEBUG)

        found_subtitles = pool.download_best_subtitles(
            subtitles_list,
            video,
            languages=languages,
            hearing_impaired=sickbeard.SUBTITLES_HEARING_IMPAIRED,
            min_score=user_score,
            only_one=not sickbeard.SUBTITLES_MULTI)

        subliminal.save_subtitles(video,
                                  found_subtitles,
                                  directory=subtitles_path,
                                  single=not sickbeard.SUBTITLES_MULTI)
    except IOError as error:
        if 'No space left on device' in ex(error):
            logger.log(u'Not enough space on the drive to save subtitles',
                       logger.WARNING)
        else:
            logger.log(traceback.format_exc(), logger.WARNING)
    except Exception:
        logger.log(
            u'Error occurred when downloading subtitles for: {0}'.format(
                video_path))
        logger.log(traceback.format_exc(), logger.ERROR)
        return existing_subtitles, None

    for subtitle in found_subtitles:
        subtitle_path = subliminal.subtitle.get_subtitle_path(
            video.name,
            None if not sickbeard.SUBTITLES_MULTI else subtitle.language)
        if subtitles_path is not None:
            subtitle_path = os.path.join(subtitles_path,
                                         os.path.split(subtitle_path)[1])

        sickbeard.helpers.chmodAsParent(subtitle_path)
        sickbeard.helpers.fixSetGroupID(subtitle_path)

        if sickbeard.SUBTITLES_HISTORY:
            logger.log(
                u'history.logSubtitle {0}, {1}'.format(
                    subtitle.provider_name, subtitle.language.opensubtitles),
                logger.DEBUG)

            history.logSubtitle(subtitles_info['show_indexerid'],
                                subtitles_info['season'],
                                subtitles_info['episode'],
                                subtitles_info['status'], subtitle)

        if sickbeard.SUBTITLES_EXTRA_SCRIPTS and isMediaFile(
                video_path) and not sickbeard.EMBEDDED_SUBTITLES_ALL:
            run_subs_extra_scripts(subtitles_info,
                                   subtitle,
                                   video,
                                   single=not sickbeard.SUBTITLES_MULTI)

    new_subtitles = sorted(
        {subtitle.language.opensubtitles
         for subtitle in found_subtitles})
    current_subtitles = sorted(
        {subtitle
         for subtitle in new_subtitles +
         existing_subtitles}) if existing_subtitles else new_subtitles
    if not sickbeard.SUBTITLES_MULTI and len(found_subtitles) == 1:
        new_code = found_subtitles[0].language.opensubtitles
        if new_code not in existing_subtitles:
            current_subtitles.remove(new_code)
        current_subtitles.append('und')

    return current_subtitles, new_subtitles
Exemplo n.º 25
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 TVShow instance to create the NFO for
        """

        eps_to_write = [ep_obj] + ep_obj.relatedEps

        indexer_lang = ep_obj.show.lang

        l_indexer_api_params = sickbeard.indexerApi(ep_obj.show.indexer).api_params.copy()

        l_indexer_api_params[b'actors'] = True

        if indexer_lang and not indexer_lang == sickbeard.INDEXER_DEFAULT_LANGUAGE:
            l_indexer_api_params[b'language'] = indexer_lang

        if ep_obj.show.dvdorder != 0:
            l_indexer_api_params[b'dvdorder'] = True

        try:
            t = sickbeard.indexerApi(ep_obj.show.indexer).indexer(**l_indexer_api_params)
            my_show = t[ep_obj.show.indexerid]
        except sickbeard.indexer_shownotfound as e:
            raise ShowNotFoundException(e.message)
        except sickbeard.indexer_error:
            logger.log(u'Unable to connect to {indexer} while creating meta files - skipping it.'.format
                       (indexer=sickbeard.indexerApi(ep_obj.show.indexer).name), logger.WARNING)
            return False

        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 (sickbeard.indexer_episodenotfound, sickbeard.indexer_seasonnotfound):
                logger.log(u'Unable to find episode {ep_num} on {indexer}... '
                           u'has it been removed? Should I delete from db?'.format
                           (ep_num=episode_num(ep_to_write.season, ep_to_write.episode),
                            indexer=sickbeard.indexerApi(ep_obj.show.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.prettyName()

            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.indentXML(root_node)
            data = etree.ElementTree(root_node)

        return data
Exemplo n.º 26
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 TVEpisode instance to create the metadata file for.

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

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

        The results are saved in the object 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.relatedEps

        indexer_lang = ep_obj.show.lang

        try:
            l_indexer_api_params = sickbeard.indexerApi(ep_obj.show.indexer).api_params.copy()

            l_indexer_api_params['actors'] = True

            if indexer_lang and not indexer_lang == sickbeard.INDEXER_DEFAULT_LANGUAGE:
                l_indexer_api_params['language'] = indexer_lang

            if ep_obj.show.dvdorder != 0:
                l_indexer_api_params['dvdorder'] = True

            t = sickbeard.indexerApi(ep_obj.show.indexer).indexer(**l_indexer_api_params)
            my_show = t[ep_obj.show.indexerid]
        except sickbeard.indexer_shownotfound as e:
            raise ShowNotFoundException(e.message)
        except sickbeard.indexer_error:
            logger.log(u'Unable to connect to {indexer} while creating meta files - skipping it.'.format
                       (indexer=sickbeard.indexerApi(ep_obj.show.indexer).name), logger.WARNING)
            return False

        for ep_to_write in eps_to_write:

            try:
                my_ep = my_show[ep_to_write.season][ep_to_write.episode]
            except (sickbeard.indexer_episodenotfound, sickbeard.indexer_seasonnotfound):
                logger.log(u'Unable to find episode {ep_num} on {indexer}... '
                           u'has it been removed? Should I delete from db?'.format
                           (ep_num=episode_num(ep_to_write.season, ep_to_write.episode),
                            indexer=sickbeard.indexerApi(ep_obj.show.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 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_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.show.genre:
                for genre in ep_obj.show.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
Exemplo n.º 27
0
def download_subtitles(episode, force_lang=None):  # pylint: disable=too-many-locals, too-many-branches, too-many-statements
    existing_subtitles = episode.subtitles

    if not needs_subtitles(existing_subtitles, force_lang):
        logger.log('Episode already has all needed subtitles, skipping {0} {1}'.format
                   (episode.show.name, episode_num(episode.season, episode.episode) or
                    episode_num(episode.season, episode.episode, numbering='absolute')), logger.DEBUG)
        return existing_subtitles, None

    if not force_lang:
        languages = get_needed_languages(existing_subtitles)
    else:
        languages = {from_code(force_lang)}

    if not languages:
        logger.log('No subtitles needed for {0} {1}'.format
                   (episode.show.name, episode_num(episode.season, episode.episode) or
                    episode_num(episode.season, episode.episode, numbering='absolute')), logger.DEBUG)
        return existing_subtitles, None

    subtitles_path = get_subtitles_path(episode.location)
    video_path = episode.location

    # Perfect match = hash score - hearing impaired score - resolution score
    # (subtitle for 720p is the same as for 1080p)
    # Perfect match = 215 - 1 - 1 = 213
    # Non-perfect match = series + year + season + episode
    # Non-perfect match = 108 + 54 + 18 + 18 = 198
    # From latest subliminal code:
    # episode_scores = {'hash': 215, 'series': 108, 'year': 54, 'season': 18, 'episode': 18, 'release_group': 9,
    #                   'format': 4, 'audio_codec': 2, 'resolution': 1, 'hearing_impaired': 1, 'video_codec': 1}
    user_score = 213 if sickbeard.SUBTITLES_PERFECT_MATCH else 198

    video = get_video(video_path, subtitles_path=subtitles_path, episode=episode)
    if not video:
        logger.log('Exception caught in subliminal.scan_video for {0} {1}'.format
                   (episode.show.name, episode_num(episode.season, episode.episode) or
                    episode_num(episode.season, episode.episode, numbering='absolute')), logger.DEBUG)
        return existing_subtitles, None

    providers = enabled_service_list()
    pool = SubtitleProviderPool()

    try:
        subtitles_list = pool.list_subtitles(video, languages)

        for provider in providers:
            if provider in pool.discarded_providers:
                logger.log('Could not search in {0} provider. Discarding for now'.format(provider), logger.DEBUG)

        if not subtitles_list:
            logger.log('No subtitles found for {0} {1}'.format
                       (episode.show.name, episode_num(episode.season, episode.episode) or
                        episode_num(episode.season, episode.episode, numbering='absolute')), logger.DEBUG)
            return existing_subtitles, None

        for subtitle in subtitles_list:
            score = subliminal.score.compute_score(subtitle, video,
                                                   hearing_impaired=sickbeard.SUBTITLES_HEARING_IMPAIRED)
            logger.log('[{0}] Subtitle score for {1} is: {2} (min={3})'.format
                       (subtitle.provider_name, subtitle.id, score, user_score), logger.DEBUG)

        found_subtitles = pool.download_best_subtitles(subtitles_list, video, languages=languages,
                                                       hearing_impaired=sickbeard.SUBTITLES_HEARING_IMPAIRED,
                                                       min_score=user_score, only_one=not sickbeard.SUBTITLES_MULTI)

        subliminal.save_subtitles(video, found_subtitles, directory=subtitles_path,
                                  single=not sickbeard.SUBTITLES_MULTI)
    except IOError as error:
        if 'No space left on device' in ex(error):
            logger.log('Not enough space on the drive to save subtitles', logger.WARNING)
        else:
            logger.log(traceback.format_exc(), logger.WARNING)
    except Exception:
        logger.log('Error occurred when downloading subtitles for: {0}'.format(video_path))
        logger.log(traceback.format_exc(), logger.ERROR)
        return existing_subtitles, None

    for subtitle in found_subtitles:
        subtitle_path = subliminal.subtitle.get_subtitle_path(video.name,
                                                              None if not sickbeard.SUBTITLES_MULTI else
                                                              subtitle.language)
        if subtitles_path is not None:
            subtitle_path = os.path.join(subtitles_path, os.path.split(subtitle_path)[1])

        sickbeard.helpers.chmodAsParent(subtitle_path)
        sickbeard.helpers.fixSetGroupID(subtitle_path)

        if sickbeard.SUBTITLES_HISTORY:
            logger.log('history.logSubtitle {0}, {1}'.format
                       (subtitle.provider_name, subtitle.language.opensubtitles), logger.DEBUG)

            history.logSubtitle(episode.show.indexerid, episode.season, episode.episode, episode.status, subtitle)

        if sickbeard.SUBTITLES_EXTRA_SCRIPTS and is_media_file(video_path) and not sickbeard.EMBEDDED_SUBTITLES_ALL:

            run_subs_extra_scripts(episode, subtitle, video, single=not sickbeard.SUBTITLES_MULTI)

    new_subtitles = sorted({subtitle.language.opensubtitles for subtitle in found_subtitles})
    current_subtitles = sorted({subtitle for subtitle in new_subtitles + existing_subtitles}) if existing_subtitles else new_subtitles
    if not sickbeard.SUBTITLES_MULTI and len(found_subtitles) == 1:
        new_code = found_subtitles[0].language.opensubtitles
        if new_code not in existing_subtitles:
            current_subtitles.remove(new_code)
        current_subtitles.append('und')

    return current_subtitles, new_subtitles
Exemplo n.º 28
0
    def run(self, force=False):  # pylint: disable=too-many-branches, too-many-statements

        if not sickbeard.USE_SUBTITLES:
            return

        if len(sickbeard.subtitles.enabled_service_list()) < 1:
            logger.log(u'Not enough services selected. At least 1 service is required to '
                       'search subtitles in the background', logger.WARNING)
            return

        self.amActive = True

        def dhm(td):
            days = td.days
            hours = td.seconds // 60 ** 2
            minutes = (td.seconds // 60) % 60
            ret = (u'', '{} days, '.format(days))[days > 0] + \
                (u'', '{} hours, '.format(hours))[hours > 0] + \
                (u'', '{} minutes'.format(minutes))[minutes > 0]
            if days == 1:
                ret = ret.replace('days', 'day')
            if hours == 1:
                ret = ret.replace('hours', 'hour')
            if minutes == 1:
                ret = ret.replace('minutes', 'minute')
            return ret.rstrip(', ')

        if sickbeard.SUBTITLES_DOWNLOAD_IN_PP:
            self.subtitles_download_in_pp()

        logger.log(u'Checking for missed subtitles', logger.INFO)

        statuses = list({status for status in Quality.DOWNLOADED + Quality.ARCHIVED})

        database = db.DBConnection()
        sql_results = database.select(
            "SELECT s.show_name, e.showid, e.season, e.episode, "
            "e.status, e.subtitles, e.subtitles_searchcount AS searchcount, "
            "e.subtitles_lastsearch AS lastsearch, e.location, (? - e.airdate) as age "
            "FROM tv_episodes AS e INNER JOIN tv_shows AS s "
            "ON (e.showid = s.indexer_id) "
            "WHERE s.subtitles = 1 AND e.subtitles NOT LIKE ? "
            "AND e.location != '' AND e.status IN (%s) ORDER BY age ASC" %
            ','.join(['?'] * len(statuses)),
            [datetime.datetime.now().toordinal(), wanted_languages(True)] + statuses
        )

        if not sql_results:
            logger.log(u'No subtitles to download', logger.INFO)
            self.amActive = False
            return

        for ep_to_sub in sql_results:
            try:
                # Encode path to system encoding.
                subtitle_path = ep_to_sub['location'].encode(sickbeard.SYS_ENCODING)
            except UnicodeEncodeError:
                # Fallback to UTF-8.
                subtitle_path = ep_to_sub['location'].encode('utf-8')
            if not os.path.isfile(subtitle_path):
                logger.log(u'Episode file does not exist, cannot download subtitles for {} {}'.format
                           (ep_to_sub['show_name'], episode_num(ep_to_sub['season'], ep_to_sub['episode']) or
                            episode_num(ep_to_sub['season'], ep_to_sub['episode'], numbering='absolute')), logger.DEBUG)
                continue

            if not needs_subtitles(ep_to_sub['subtitles']):
                logger.log(u'Episode already has all needed subtitles, skipping {} {}'.format
                           (ep_to_sub['show_name'], episode_num(ep_to_sub['season'], ep_to_sub['episode']) or
                            episode_num(ep_to_sub['season'], ep_to_sub['episode'], numbering='absolute')), logger.DEBUG)
                continue

            try:
                lastsearched = datetime.datetime.strptime(ep_to_sub['lastsearch'], dateTimeFormat)
            except ValueError:
                lastsearched = datetime.datetime.min

            try:
                if not force:
                    now = datetime.datetime.now()
                    days = int(ep_to_sub['age'])
                    delay_time = datetime.timedelta(hours=8 if days < 10 else 7 * 24 if days < 30 else 30 * 24)

                    # Search every hour for the first 24 hours since aired, then every 8 hours until 10 days passes
                    # After 10 days, search every 7 days, after 30 days search once a month
                    # Will always try an episode regardless of age at least 2 times
                    if lastsearched + delay_time > now and int(ep_to_sub['searchcount']) > 2 and days:
                        logger.log(u'Subtitle search for {} {} delayed for {}'.format
                                   (ep_to_sub['show_name'], episode_num(ep_to_sub['season'], ep_to_sub['episode']) or
                                    episode_num(ep_to_sub['season'], ep_to_sub['episode'], numbering='absolute'),
                                    dhm(lastsearched + delay_time - now)), logger.DEBUG)
                        continue

                logger.log(u'Searching for missing subtitles of {} {}'.format
                           (ep_to_sub['show_name'], episode_num(ep_to_sub['season'], ep_to_sub['episode']) or
                            episode_num(ep_to_sub['season'], ep_to_sub['episode'], numbering='absolute')), logger.INFO)

                show_object = Show.find(sickbeard.showList, int(ep_to_sub['showid']))
                if not show_object:
                    logger.log(u'Show with ID {} not found in the database'.format(ep_to_sub['showid']), logger.DEBUG)
                    continue

                episode_object = show_object.getEpisode(ep_to_sub['season'], ep_to_sub['episode'])
                if isinstance(episode_object, str):
                    logger.log(u'{} {} not found in the database'.format
                               (ep_to_sub['show_name'], episode_num(ep_to_sub['season'], ep_to_sub['episode']) or
                                episode_num(ep_to_sub['season'], ep_to_sub['episode'], numbering='absolute')), logger.DEBUG)
                    continue

                try:
                    new_subtitles = episode_object.download_subtitles()
                except Exception as error:
                    logger.log(u'Unable to find subtitles for {} {}. Error: {}'.format
                               (ep_to_sub['show_name'], episode_num(ep_to_sub['season'], ep_to_sub['episode']) or
                                episode_num(ep_to_sub['season'], ep_to_sub['episode'], numbering='absolute'), ex(error)), logger.ERROR)
                    continue

                if new_subtitles:
                    logger.log(u'Downloaded {} subtitles for {} {}'.format
                               (', '.join(new_subtitles), ep_to_sub['show_name'], episode_num(ep_to_sub['season'], ep_to_sub['episode']) or
                                episode_num(ep_to_sub['season'], ep_to_sub['episode'], numbering='absolute')))

            except Exception as error:
                logger.log(u'Error while searching subtitles for {} {}. Error: {}'.format
                           (ep_to_sub['show_name'], episode_num(ep_to_sub['season'], ep_to_sub['episode']) or
                            episode_num(ep_to_sub['season'], ep_to_sub['episode'], numbering='absolute'), ex(error)), logger.ERROR)
                continue

        logger.log(u'Finished checking for missed subtitles', logger.INFO)
        self.amActive = False
Exemplo n.º 29
0
    def test_episode_num(self):
        # Standard numbering
        self.assertEqual(episode_num(0, 1),
                         'S00E01')  # Seasons start at 0 for specials
        self.assertEqual(episode_num(1, 1), 'S01E01')

        # Absolute numbering
        self.assertEqual(episode_num(1, numbering='absolute'), '001')
        self.assertEqual(episode_num(0, 1, numbering='absolute'), '001')
        self.assertEqual(episode_num(1, 0, numbering='absolute'), '001')

        # Must have both season and episode for standard numbering
        self.assertEqual(episode_num(0), None)
        self.assertEqual(episode_num(1), None)

        # Episode numbering starts at 1
        self.assertEqual(episode_num(0, 0), None)
        self.assertEqual(episode_num(1, 0), None)

        # Absolute numbering starts at 1
        self.assertEqual(episode_num(0, 0, numbering='absolute'), None)

        # Absolute numbering can't have both season and episode
        self.assertEqual(episode_num(1, 1, numbering='absolute'), None)
Exemplo n.º 30
0
    def test_episode_num(self):
        # Standard numbering
        self.assertEqual(episode_num(0, 1), 'S00E01')  # Seasons start at 0 for specials
        self.assertEqual(episode_num(1, 1), 'S01E01')

        # Absolute numbering
        self.assertEqual(episode_num(1, numbering='absolute'), '001')
        self.assertEqual(episode_num(0, 1, numbering='absolute'), '001')
        self.assertEqual(episode_num(1, 0, numbering='absolute'), '001')

        # Must have both season and episode for standard numbering
        self.assertEqual(episode_num(0), None)
        self.assertEqual(episode_num(1), None)

        # Episode numbering starts at 1
        self.assertEqual(episode_num(0, 0), None)
        self.assertEqual(episode_num(1, 0), None)

        # Absolute numbering starts at 1
        self.assertEqual(episode_num(0, 0, numbering='absolute'), None)

        # Absolute numbering can't have both season and episode
        self.assertEqual(episode_num(1, 1, numbering='absolute'), None)
Exemplo n.º 31
0
def download_subtitles(subtitles_info):  # pylint: disable=too-many-locals, too-many-branches, too-many-statements
    existing_subtitles = subtitles_info['subtitles']

    if not needs_subtitles(existing_subtitles):
        logger.log(u'Episode already has all needed subtitles, skipping {} {}'.format
                   (subtitles_info['show_name'], episode_num(subtitles_info['season'], subtitles_info['episode']) or
                    episode_num(subtitles_info['season'], subtitles_info['episode'], numbering='absolute')), logger.DEBUG)
        return existing_subtitles, None

    # Check if we really need subtitles
    languages = get_needed_languages(existing_subtitles)
    if not languages:
        logger.log(u'No subtitles needed for {} {}'.format
                   (subtitles_info['show_name'], episode_num(subtitles_info['season'], subtitles_info['episode']) or
                    episode_num(subtitles_info['season'], subtitles_info['episode'], numbering='absolute')), logger.DEBUG)
        return existing_subtitles, None

    subtitles_path = get_subtitles_path(subtitles_info['location'])
    video_path = subtitles_info['location']
    user_score = 367 if sickbeard.SUBTITLES_PERFECT_MATCH else 352

    video = get_video(video_path, subtitles_path=subtitles_path)
    if not video:
        logger.log(u'Exception caught in subliminal.scan_video for {} {}'.format
                   (subtitles_info['show_name'], episode_num(subtitles_info['season'], subtitles_info['episode']) or
                    episode_num(subtitles_info['season'], subtitles_info['episode'], numbering='absolute')), logger.DEBUG)
        return existing_subtitles, None

    providers = enabled_service_list()
    provider_configs = {'addic7ed': {'username': sickbeard.ADDIC7ED_USER,
                                     'password': sickbeard.ADDIC7ED_PASS},
                        'legendastv': {'username': sickbeard.LEGENDASTV_USER,
                                       'password': sickbeard.LEGENDASTV_PASS},
                        'opensubtitles': {'username': sickbeard.OPENSUBTITLES_USER,
                                          'password': sickbeard.OPENSUBTITLES_PASS}}

    pool = ProviderPool(providers=providers, provider_configs=provider_configs)

    try:
        subtitles_list = pool.list_subtitles(video, languages)

        for provider in providers:
            if provider in pool.discarded_providers:
                logger.log(u'Could not search in {} provider. Discarding for now'.format(provider), logger.DEBUG)

        if not subtitles_list:
            logger.log(u'No subtitles found for {} {}'.format
                       (subtitles_info['show_name'], episode_num(subtitles_info['season'], subtitles_info['episode']) or
                        episode_num(subtitles_info['season'], subtitles_info['episode'], numbering='absolute')), logger.DEBUG)
            return existing_subtitles, None

        for subtitle in subtitles_list:
            score = subliminal.score.compute_score(subtitle, video, hearing_impaired=sickbeard.SUBTITLES_HEARING_IMPAIRED)
            logger.log(u'[{}] Subtitle score for {} is: {} (min={})'.format
                       (subtitle.provider_name, subtitle.id, score, user_score), logger.DEBUG)

        found_subtitles = pool.download_best_subtitles(subtitles_list, video, languages=languages,
                                                       hearing_impaired=sickbeard.SUBTITLES_HEARING_IMPAIRED,
                                                       min_score=user_score, only_one=not sickbeard.SUBTITLES_MULTI)

        subliminal.save_subtitles(video, found_subtitles, directory=subtitles_path,
                                  single=not sickbeard.SUBTITLES_MULTI)
    except IOError as error:
        if 'No space left on device' in ex(error):
            logger.log(u'Not enough space on the drive to save subtitles', logger.WARNING)
        else:
            logger.log(traceback.format_exc(), logger.WARNING)
    except Exception:
        logger.log(u'Error occurred when downloading subtitles for: {}'.format(video_path))
        logger.log(traceback.format_exc(), logger.ERROR)
        return existing_subtitles, None

    for subtitle in found_subtitles:
        subtitle_path = subliminal.subtitle.get_subtitle_path(video.name,
                                                              None if not sickbeard.SUBTITLES_MULTI else
                                                              subtitle.language)
        if subtitles_path is not None:
            subtitle_path = os.path.join(subtitles_path, os.path.split(subtitle_path)[1])

        sickbeard.helpers.chmodAsParent(subtitle_path)
        sickbeard.helpers.fixSetGroupID(subtitle_path)

        if sickbeard.SUBTITLES_HISTORY:
            logger.log(u'history.logSubtitle {}, {}'.format
                       (subtitle.provider_name, subtitle.language.opensubtitles), logger.DEBUG)

            history.logSubtitle(subtitles_info['show_indexerid'], subtitles_info['season'],
                                subtitles_info['episode'], subtitles_info['status'], subtitle)

        if sickbeard.SUBTITLES_EXTRA_SCRIPTS and isMediaFile(video_path) and not sickbeard.EMBEDDED_SUBTITLES_ALL:
            run_subs_extra_scripts(subtitles_info, subtitle, video, single=not sickbeard.SUBTITLES_MULTI)

    new_subtitles = sorted({subtitle.language.opensubtitles for subtitle in found_subtitles})
    current_subtitles = sorted({subtitle for subtitle in new_subtitles + existing_subtitles}) if existing_subtitles else new_subtitles
    if not sickbeard.SUBTITLES_MULTI and len(found_subtitles) == 1:
        new_code = found_subtitles[0].language.opensubtitles
        if new_code not in existing_subtitles:
            current_subtitles.remove(new_code)
        current_subtitles.append('und')

    return current_subtitles, new_subtitles
Exemplo n.º 32
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 TVShow instance to create the NFO for
        """

        eps_to_write = [ep_obj] + ep_obj.related_episodes

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

        indexer_lang = ep_obj.show.lang

        l_indexer_api_params = sickbeard.indexerApi(ep_obj.show.indexer).api_params.copy()

        l_indexer_api_params[b'actors'] = True

        if indexer_lang and not indexer_lang == sickbeard.INDEXER_DEFAULT_LANGUAGE:
            l_indexer_api_params[b'language'] = indexer_lang

        if ep_obj.show.dvdorder != 0:
            l_indexer_api_params[b'dvdorder'] = True

        try:
            t = sickbeard.indexerApi(ep_obj.show.indexer).indexer(**l_indexer_api_params)
            my_show = t[ep_obj.show.indexerid]
        except sickbeard.indexer_shownotfound as e:
            raise ShowNotFoundException(e.message)
        except sickbeard.indexer_error:
            logger.log(u'Unable to connect to {indexer} while creating meta files - skipping it.'.format
                       (indexer=sickbeard.indexerApi(ep_obj.show.indexer).name), logger.WARNING)
            return

        root_node = etree.Element('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 (sickbeard.indexer_episodenotfound, sickbeard.indexer_seasonnotfound):
                logger.log(u'Unable to find episode {ep_num} on {indexer}... '
                           u'has it been removed? Should I delete from db?'.format
                           (ep_num=episode_num(ep_to_write.season, ep_to_write.episode),
                            indexer=sickbeard.indexerApi(ep_obj.show.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 = root_node

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                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(persons, 'Person')

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

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

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

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

                thumb = etree.SubElement(episode, 'filename')
                # TODO: See what this is needed for.. if its still needed
                # just write this to the NFO regardless of whether it actually exists or not
                # note: renaming files after nfo generation will break this, tough luck
                thumb_text = self.get_episode_thumb_path(ep_obj)
                if thumb_text:
                    thumb.text = 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, 'director', None):
                persons_dict['Director'] += [x.strip() for x in my_ep['director'].split('|') if x.strip()]
            if getattr(my_ep, 'gueststars', None):
                persons_dict['GuestStar'] += [x.strip() for x in my_ep['gueststars'].split('|') if x.strip()]
            if getattr(my_ep, 'writer', None):
                persons_dict['Writer'] += [x.strip() for x in my_ep['writer'].split('|') 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, 'Person')
                cur_person_name = etree.SubElement(person, 'Name')
                cur_person_name.text = cur_name
                cur_person_type = etree.SubElement(person, 'Type')
                cur_person_type.text = person_type

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

        return data
Exemplo n.º 33
0
    def run(self, force=False):  # pylint: disable=too-many-branches, too-many-statements, too-many-locals
        """Check for needed subtitles for users' shows.

        :param force: True if a force search needs to be executed
        :type force: bool
        """
        if self.amActive:
            logger.log(u"Subtitle finder is still running, not starting it again", logger.DEBUG)
            return

        if not sickbeard.USE_SUBTITLES:
            logger.log(u"Subtitle search is disabled. Please enabled it", logger.WARNING)
            return

        if not sickbeard.subtitles.enabled_service_list():
            logger.warning(u'Not enough services selected. At least 1 service is required to search subtitles in the '
                           u'background')
            return

        self.amActive = True

        def dhm(td):
            days = td.days
            hours = td.seconds // 60 ** 2
            minutes = (td.seconds // 60) % 60
            ret = (u'', '{} days, '.format(days))[days > 0] + \
                  (u'', '{} hours, '.format(hours))[hours > 0] + \
                  (u'', '{} minutes'.format(minutes))[minutes > 0]
            if days == 1:
                ret = ret.replace('days', 'day')
            if hours == 1:
                ret = ret.replace('hours', 'hour')
            if minutes == 1:
                ret = ret.replace('minutes', 'minute')
            return ret.rstrip(', ')

        if sickbeard.SUBTITLES_DOWNLOAD_IN_PP:
            self.subtitles_download_in_pp()

        logger.info(u'Checking for missed subtitles')

        database = db.DBConnection()
        # Shows with air date <= 30 days, have a limit of 100 results
        # Shows with air date > 30 days, have a limit of 200 results
        sql_args = [{'age_comparison': '<=', 'limit': 100}, {'age_comparison': '>', 'limit': 200}]
        sql_like_languages = '%' + ','.join(sorted(wanted_languages())) + '%' if sickbeard.SUBTITLES_MULTI else '%und%'
        sql_results = []
        for args in sql_args:
            sql_results += database.select(
                "SELECT "
                "   s.show_name, "
                "   e.showid, "
                "   e.season, "
                "   e.episode,"
                "   e.release_name, "
                "   e.status, "
                "   e.subtitles, "
                "   e.subtitles_searchcount AS searchcount, "
                "   e.subtitles_lastsearch AS lastsearch, "
                "   e.location, (? - e.airdate) as age "
                "FROM "
                "   tv_episodes AS e "
                "INNER JOIN "
                "   tv_shows AS s "
                "ON (e.showid = s.indexer_id) "
                "WHERE"
                "   s.subtitles = 1 "
                "   AND (e.status LIKE '%4' OR e.status LIKE '%6') "
                "   AND e.season > 0 "
                "   AND e.location != '' "
                "   AND age {} 30 "
                "   AND e.subtitles NOT LIKE ? "
                "ORDER BY "
                "   lastsearch ASC "
                "LIMIT {}".format
                (args['age_comparison'], args['limit']), [datetime.datetime.now().toordinal(), sql_like_languages]
            )

        if not sql_results:
            logger.info(u'No subtitles to download')
            self.amActive = False
            return

        for ep_to_sub in sql_results:

            # give the CPU a break
            time.sleep(cpu_presets[sickbeard.CPU_PRESET])

            ep_num = episode_num(ep_to_sub['season'], ep_to_sub['episode']) or \
                     episode_num(ep_to_sub['season'], ep_to_sub['episode'], numbering='absolute')
            subtitle_path = _encode(ep_to_sub['location'], encoding=sickbeard.SYS_ENCODING, fallback='utf-8')
            if not os.path.isfile(subtitle_path):
                logger.debug(u'Episode file does not exist, cannot download subtitles for %s %s',
                             ep_to_sub['show_name'], ep_num)
                continue

            if not needs_subtitles(ep_to_sub['subtitles']):
                logger.debug(u'Episode already has all needed subtitles, skipping %s %s', ep_to_sub['show_name'], ep_num)
                continue

            try:
                lastsearched = datetime.datetime.strptime(ep_to_sub['lastsearch'], dateTimeFormat)
            except ValueError:
                lastsearched = datetime.datetime.min

            try:
                if not force:
                    now = datetime.datetime.now()
                    days = int(ep_to_sub['age'])
                    delay_time = datetime.timedelta(hours=1 if days <= 10 else 8 if days <= 30 else 30 * 24)
                    delay = lastsearched + delay_time - now

                    # Search every hour until 10 days pass
                    # After 10 days, search every 8 hours, after 30 days search once a month
                    # Will always try an episode regardless of age for 3 times
                    # The time resolution is minute
                    # Only delay is the it's bigger than one minute and avoid wrongly skipping the search slot.
                    if delay.total_seconds() > 60 and int(ep_to_sub['searchcount']) > 2:
                        logger.debug(u'Subtitle search for %s %s delayed for %s',
                                     ep_to_sub['show_name'], ep_num, dhm(delay))
                        continue

                show_object = Show.find(sickbeard.showList, int(ep_to_sub['showid']))
                if not show_object:
                    logger.debug(u'Show with ID %s not found in the database', ep_to_sub['showid'])
                    continue

                episode_object = show_object.getEpisode(ep_to_sub['season'], ep_to_sub['episode'])
                if isinstance(episode_object, str):
                    logger.debug(u'%s %s not found in the database', ep_to_sub['show_name'], ep_num)
                    continue

                try:
                    episode_object.download_subtitles()
                except Exception as error:
                    logger.error(u'Unable to find subtitles for %s %s. Error: %s',
                                 ep_to_sub['show_name'], ep_num, ex(error))
                    continue

            except Exception as error:
                logger.warning(u'Error while searching subtitles for %s %s. Error: %s',
                               ep_to_sub['show_name'], ep_num, ex(error))
                continue

        logger.info(u'Finished checking for missed subtitles')
        self.amActive = False
Exemplo n.º 34
0
    def run(self, force=False):  # pylint: disable=too-many-branches, too-many-statements, too-many-locals
        if not sickbeard.USE_SUBTITLES:
            return

        if not sickbeard.subtitles.enabled_service_list():
            logger.log(
                u'Not enough services selected. At least 1 service is required to '
                'search subtitles in the background', logger.WARNING)
            return

        self.amActive = True

        def dhm(td):
            days = td.days
            hours = td.seconds // 60**2
            minutes = (td.seconds // 60) % 60
            ret = (u'', '{0} days, '.format(days))[days > 0] + \
                (u'', '{0} hours, '.format(hours))[hours > 0] + \
                (u'', '{0} minutes'.format(minutes))[minutes > 0]
            if days == 1:
                ret = ret.replace('days', 'day')
            if hours == 1:
                ret = ret.replace('hours', 'hour')
            if minutes == 1:
                ret = ret.replace('minutes', 'minute')
            return ret.rstrip(', ')

        if sickbeard.SUBTITLES_DOWNLOAD_IN_PP:
            self.subtitles_download_in_pp()

        logger.log(u'Checking for missed subtitles', logger.INFO)

        database = db.DBConnection()
        sql_results = database.select(
            "SELECT s.show_name, e.showid, e.season, e.episode, "
            "e.status, e.subtitles, e.subtitles_searchcount AS searchcount, "
            "e.subtitles_lastsearch AS lastsearch, e.location, (? - e.airdate) as age "
            "FROM tv_episodes AS e INNER JOIN tv_shows AS s "
            "ON (e.showid = s.indexer_id) "
            "WHERE s.subtitles = 1 AND e.subtitles NOT LIKE ? "
            "AND e.location != '' AND e.status IN (%s) ORDER BY age ASC" %
            ','.join(['?'] * len(Quality.DOWNLOADED)),
            [datetime.datetime.now().toordinal(),
             wanted_languages(True)] + Quality.DOWNLOADED)

        if not sql_results:
            logger.log(u'No subtitles to download', logger.INFO)
            self.amActive = False
            return

        for ep_to_sub in sql_results:
            try:
                # Encode path to system encoding.
                subtitle_path = ep_to_sub['location'].encode(
                    sickbeard.SYS_ENCODING)
            except UnicodeEncodeError:
                # Fallback to UTF-8.
                subtitle_path = ep_to_sub['location'].encode('utf-8')
            if not os.path.isfile(subtitle_path):
                logger.log(
                    u'Episode file does not exist, cannot download subtitles for {0} {1}'
                    .format(
                        ep_to_sub['show_name'],
                        episode_num(ep_to_sub['season'], ep_to_sub['episode'])
                        or episode_num(ep_to_sub['season'],
                                       ep_to_sub['episode'],
                                       numbering='absolute')), logger.DEBUG)
                continue

            if not needs_subtitles(ep_to_sub['subtitles']):
                logger.log(
                    u'Episode already has all needed subtitles, skipping {0} {1}'
                    .format(
                        ep_to_sub['show_name'],
                        episode_num(ep_to_sub['season'], ep_to_sub['episode'])
                        or episode_num(ep_to_sub['season'],
                                       ep_to_sub['episode'],
                                       numbering='absolute')), logger.DEBUG)
                continue

            try:
                lastsearched = datetime.datetime.strptime(
                    ep_to_sub['lastsearch'], dateTimeFormat)
            except ValueError:
                lastsearched = datetime.datetime.min

            try:
                if not force:
                    now = datetime.datetime.now()
                    days = int(ep_to_sub['age'])
                    delay_time = datetime.timedelta(
                        hours=8 if days < 10 else 7 * 24 if days < 30 else 30 *
                        24)

                    # Search every hour for the first 24 hours since aired, then every 8 hours until 10 days passes
                    # After 10 days, search every 7 days, after 30 days search once a month
                    # Will always try an episode regardless of age at least 2 times
                    if lastsearched + delay_time > now and int(
                            ep_to_sub['searchcount']) > 2 and days:
                        logger.log(
                            u'Subtitle search for {0} {1} delayed for {2}'.
                            format(
                                ep_to_sub['show_name'],
                                episode_num(ep_to_sub['season'],
                                            ep_to_sub['episode'])
                                or episode_num(ep_to_sub['season'],
                                               ep_to_sub['episode'],
                                               numbering='absolute'),
                                dhm(lastsearched + delay_time - now)),
                            logger.DEBUG)
                        continue

                logger.log(
                    u'Searching for missing subtitles of {0} {1}'.format(
                        ep_to_sub['show_name'],
                        episode_num(ep_to_sub['season'], ep_to_sub['episode'])
                        or episode_num(ep_to_sub['season'],
                                       ep_to_sub['episode'],
                                       numbering='absolute')), logger.INFO)

                show_object = Show.find(sickbeard.showList,
                                        int(ep_to_sub['showid']))
                if not show_object:
                    logger.log(
                        u'Show with ID {0} not found in the database'.format(
                            ep_to_sub['showid']), logger.DEBUG)
                    continue

                episode_object = show_object.getEpisode(
                    ep_to_sub['season'], ep_to_sub['episode'])
                if isinstance(episode_object, str):
                    logger.log(
                        u'{0} {1} not found in the database'.format(
                            ep_to_sub['show_name'],
                            episode_num(ep_to_sub['season'],
                                        ep_to_sub['episode'])
                            or episode_num(ep_to_sub['season'],
                                           ep_to_sub['episode'],
                                           numbering='absolute')),
                        logger.DEBUG)
                    continue

                try:
                    new_subtitles = episode_object.download_subtitles()
                except Exception as error:
                    logger.log(
                        u'Unable to find subtitles for {0} {1}. Error: {2}'.
                        format(
                            ep_to_sub['show_name'],
                            episode_num(ep_to_sub['season'],
                                        ep_to_sub['episode'])
                            or episode_num(ep_to_sub['season'],
                                           ep_to_sub['episode'],
                                           numbering='absolute'), ex(error)),
                        logger.ERROR)
                    continue

                if new_subtitles:
                    logger.log(u'Downloaded {0} subtitles for {1} {2}'.format(
                        ', '.join(new_subtitles), ep_to_sub['show_name'],
                        episode_num(ep_to_sub['season'], ep_to_sub['episode'])
                        or episode_num(ep_to_sub['season'],
                                       ep_to_sub['episode'],
                                       numbering='absolute')))

            except Exception as error:
                logger.log(
                    u'Error while searching subtitles for {0} {1}. Error: {2}'.
                    format(
                        ep_to_sub['show_name'],
                        episode_num(ep_to_sub['season'], ep_to_sub['episode'])
                        or episode_num(ep_to_sub['season'],
                                       ep_to_sub['episode'],
                                       numbering='absolute'), ex(error)),
                    logger.ERROR)
                continue

        logger.log(u'Finished checking for missed subtitles', logger.INFO)
        self.amActive = False
Exemplo n.º 35
0
def download_subtitles(subtitles_info):  # pylint: disable=too-many-locals, too-many-branches, too-many-statements
    existing_subtitles = subtitles_info['subtitles']

    if not needs_subtitles(existing_subtitles):
        logger.log(u'Episode already has all needed subtitles, skipping {} {}'.format
                   (subtitles_info['show_name'], episode_num(subtitles_info['season'], subtitles_info['episode']) or
                    episode_num(subtitles_info['season'], subtitles_info['episode'], numbering='absolute')), logger.DEBUG)
        return existing_subtitles, None

    # Check if we really need subtitles
    languages = get_needed_languages(existing_subtitles)
    if not languages:
        logger.log(u'No subtitles needed for {} {}'.format
                   (subtitles_info['show_name'], episode_num(subtitles_info['season'], subtitles_info['episode']) or
                    episode_num(subtitles_info['season'], subtitles_info['episode'], numbering='absolute')), logger.DEBUG)
        return existing_subtitles, None

    try:
        subtitles_path = get_subtitles_path(subtitles_info['location']).encode(sickbeard.SYS_ENCODING)
        video_path = subtitles_info['location'].encode(sickbeard.SYS_ENCODING)
    except UnicodeEncodeError as error:
        logger.log(u'An error occurred while encoding \'{}\' with your current locale. '
                   'Rename the file or try a different locale. Error: {}'.format
                   (subtitles_info['location'], ex(error)), logger.WARNING)
        return existing_subtitles, None
    user_score = 132 if sickbeard.SUBTITLES_PERFECT_MATCH else 111

    video = get_video(video_path, subtitles_path=subtitles_path)
    if not video:
        logger.log(u'Exception caught in subliminal.scan_video for {} {}'.format
                   (subtitles_info['show_name'], episode_num(subtitles_info['season'], subtitles_info['episode']) or
                    episode_num(subtitles_info['season'], subtitles_info['episode'], numbering='absolute')), logger.DEBUG)
        return existing_subtitles, None

    providers = enabled_service_list()
    provider_configs = {'addic7ed': {'username': sickbeard.ADDIC7ED_USER,
                                     'password': sickbeard.ADDIC7ED_PASS},
                        'legendastv': {'username': sickbeard.LEGENDASTV_USER,
                                       'password': sickbeard.LEGENDASTV_PASS},
                        'opensubtitles': {'username': sickbeard.OPENSUBTITLES_USER,
                                          'password': sickbeard.OPENSUBTITLES_PASS}}

    pool = ProviderPool(providers=providers, provider_configs=provider_configs)

    try:
        subtitles_list = pool.list_subtitles(video, languages)
        if not subtitles_list:
            logger.log(u'No subtitles found for {} {}'.format
                       (subtitles_info['show_name'], episode_num(subtitles_info['season'], subtitles_info['episode']) or
                        episode_num(subtitles_info['season'], subtitles_info['episode'], numbering='absolute')), logger.DEBUG)
            return existing_subtitles, None

        for subtitle in subtitles_list:
            try:
                matches = subtitle.get_matches(video, hearing_impaired=sickbeard.SUBTITLES_HEARING_IMPAIRED)
            except ValueError as error:
                logger.log(u'An error occurred while getting a subtitle match for: {}. Error: {}'.format
                           (video.name, ex(error)), logger.WARNING)
                continue
            score = subliminal.subtitle.compute_score(matches, video)
            logger.log(u'[{}] Subtitle score for {} is: {} (min={})'.format
                       (subtitle.provider_name, subtitle.id, score, user_score), logger.DEBUG)

        found_subtitles = pool.download_best_subtitles(subtitles_list, video, languages=languages,
                                                       hearing_impaired=sickbeard.SUBTITLES_HEARING_IMPAIRED,
                                                       min_score=user_score, only_one=not sickbeard.SUBTITLES_MULTI)

        subliminal.save_subtitles(video, found_subtitles, directory=subtitles_path,
                                  single=not sickbeard.SUBTITLES_MULTI)
    except IOError as error:
        if 'No space left on device' in ex(error):
            logger.log(u'Not enough space on the drive to save subtitles', logger.WARNING)
        else:
            logger.log(traceback.format_exc(), logger.WARNING)
    except Exception:
        logger.log(u'Error occurred when downloading subtitles for: {}'.format(video_path))
        logger.log(traceback.format_exc(), logger.ERROR)
        return existing_subtitles, None

    for subtitle in found_subtitles:
        subtitle_path = subliminal.subtitle.get_subtitle_path(video.name,
                                                              None if not sickbeard.SUBTITLES_MULTI else
                                                              subtitle.language)
        if subtitles_path is not None:
            subtitle_path = ek(os.path.join, subtitles_path, ek(os.path.split, subtitle_path)[1])

        sickbeard.helpers.chmodAsParent(subtitle_path)
        sickbeard.helpers.fixSetGroupID(subtitle_path)

        if sickbeard.SUBTITLES_HISTORY:
            logger.log(u'history.logSubtitle {}, {}'.format
                       (subtitle.provider_name, subtitle.language.opensubtitles), logger.DEBUG)

            history.logSubtitle(subtitles_info['show_indexerid'], subtitles_info['season'],
                                subtitles_info['episode'], subtitles_info['status'], subtitle)

        if sickbeard.SUBTITLES_EXTRA_SCRIPTS and isMediaFile(video_path) and not sickbeard.EMBEDDED_SUBTITLES_ALL:
            run_subs_extra_scripts(subtitles_info, subtitle, video, single=not sickbeard.SUBTITLES_MULTI)

    new_subtitles = sorted({subtitle.language.opensubtitles for subtitle in found_subtitles})
    current_subtitles = sorted({subtitle for subtitle in new_subtitles + existing_subtitles}) if existing_subtitles else new_subtitles
    if not sickbeard.SUBTITLES_MULTI and len(found_subtitles) == 1:
        new_code = found_subtitles[0].language.opensubtitles
        if new_code not in existing_subtitles:
            current_subtitles.remove(new_code)
        current_subtitles.append('und')

    return current_subtitles, new_subtitles
Exemplo n.º 36
0
def download_best_subs(tv_episode, video_path, subtitles_dir, subtitles=True, embedded_subtitles=True):
    """Download the best subtitle for the given video.

    :param tv_episode:
    :type tv_episode: sickbeard.tv.TVEpisode
    :param video_path: the video path
    :type video_path: str
    :param subtitles_dir: the subtitles directory
    :type subtitles_dir: str
    :param subtitles: True if existing external subtitles should be taken into account
    :type subtitles: bool
    :param embedded_subtitles: True if embedded subtitles should be taken into account
    :type embedded_subtitles: bool
    :return: the downloaded subtitles
    :rtype: list of subliminal.subtitle.Subtitle
    """
    show_name = tv_episode.show.name
    season = tv_episode.season
    episode = tv_episode.episode
    release_name = tv_episode.release_name
    ep_num = episode_num(season, episode) or episode_num(season, episode, numbering='absolute')
    languages = get_needed_languages(tv_episode.subtitles)

    if not languages:
        logger.debug(u'Episode already has all needed subtitles, skipping %s %s', show_name, ep_num)
        return []

    logger.debug(u'Checking subtitle candidates for %s %s (%s)', show_name, ep_num, os.path.basename(video_path))

    try:
        video = get_video(tv_episode, video_path, subtitles_dir=subtitles_dir, subtitles=subtitles,
                          embedded_subtitles=embedded_subtitles, release_name=release_name)

        if not video:
            logger.info(u'Exception caught in subliminal.scan_video for %s', video_path)
            return []

        pool = get_provider_pool()

        if sickbeard.SUBTITLES_PRE_SCRIPTS:
            run_subs_pre_scripts(video_path)

        subtitles_list = pool.list_subtitles(video, languages)
        for provider in pool.providers:
            if provider in pool.discarded_providers:
                logger.debug(u'Could not search in %s provider. Discarding for now', provider)

        if not subtitles_list:
            logger.info(u'No subtitles found for %s', os.path.basename(video_path))
            return []

        min_score = get_min_score()
        scored_subtitles = sorted([(s, compute_score(s, video, hearing_impaired=sickbeard.SUBTITLES_HEARING_IMPAIRED))
                                  for s in subtitles_list], key=operator.itemgetter(1), reverse=True)
        for subtitle, score in scored_subtitles:
            logger.debug(u'[{0:>13s}:{1:<5s}] score = {2:3d}/{3:3d} for {4}'.format(
                subtitle.provider_name, subtitle.language, score, min_score, get_subtitle_description(subtitle)))

        found_subtitles = pool.download_best_subtitles(subtitles_list, video, languages=languages,
                                                       hearing_impaired=sickbeard.SUBTITLES_HEARING_IMPAIRED,
                                                       min_score=min_score, only_one=not sickbeard.SUBTITLES_MULTI)

        if not found_subtitles:
            logger.info(u'No subtitles found for %s with a minimum score of %d',
                        os.path.basename(video_path), min_score)
            return []

        save_subtitles(video, found_subtitles, directory=_encode(subtitles_dir), single=not sickbeard.SUBTITLES_MULTI)

        for subtitle in found_subtitles:
            logger.info(u'Found subtitle for %s in %s provider with language %s', os.path.basename(video_path),
                        subtitle.provider_name, subtitle.language.opensubtitles)
            subtitle_path = compute_subtitle_path(subtitle, video_path, subtitles_dir)

            sickbeard.helpers.chmodAsParent(subtitle_path)
            sickbeard.helpers.fixSetGroupID(subtitle_path)

        return found_subtitles
    except IOError as error:
        if 'No space left on device' in ex(error):
            logger.warning(u'Not enough space on the drive to save subtitles')
        else:
            logger.warning(traceback.format_exc())
    except Exception as error:
        logger.debug(u'Exception: %s', error)
        logger.info(u'Error occurred when downloading subtitles for: %s', video_path)
        logger.error(traceback.format_exc())

    return []
Exemplo n.º 37
0
    def run(self, force=False):  # pylint: disable=too-many-branches, too-many-statements, too-many-locals
        if not sickbeard.USE_SUBTITLES:
            return

        if not sickbeard.subtitles.enabled_service_list():
            logger.log(
                u"Not enough services selected. At least 1 service is required to "
                "search subtitles in the background",
                logger.WARNING,
            )
            return

        self.amActive = True

        def dhm(td):
            days = td.days
            hours = td.seconds // 60 ** 2
            minutes = (td.seconds // 60) % 60
            ret = (
                (u"", "{0} days, ".format(days))[days > 0]
                + (u"", "{0} hours, ".format(hours))[hours > 0]
                + (u"", "{0} minutes".format(minutes))[minutes > 0]
            )
            if days == 1:
                ret = ret.replace("days", "day")
            if hours == 1:
                ret = ret.replace("hours", "hour")
            if minutes == 1:
                ret = ret.replace("minutes", "minute")
            return ret.rstrip(", ")

        if sickbeard.SUBTITLES_DOWNLOAD_IN_PP:
            self.subtitles_download_in_pp()

        logger.log(u"Checking for missed subtitles", logger.INFO)

        database = db.DBConnection()
        sql_results = database.select(
            "SELECT s.show_name, e.showid, e.season, e.episode, "
            "e.status, e.subtitles, e.subtitles_searchcount AS searchcount, "
            "e.subtitles_lastsearch AS lastsearch, e.location, (? - e.airdate) as age "
            "FROM tv_episodes AS e INNER JOIN tv_shows AS s "
            "ON (e.showid = s.indexer_id) "
            "WHERE s.subtitles = 1 AND e.subtitles NOT LIKE ? "
            "AND e.location != '' AND e.status IN (%s) ORDER BY age ASC" % ",".join(["?"] * len(Quality.DOWNLOADED)),
            [datetime.datetime.now().toordinal(), wanted_languages(True)] + Quality.DOWNLOADED,
        )

        if not sql_results:
            logger.log(u"No subtitles to download", logger.INFO)
            self.amActive = False
            return

        for ep_to_sub in sql_results:
            try:
                # Encode path to system encoding.
                subtitle_path = ep_to_sub["location"].encode(sickbeard.SYS_ENCODING)
            except UnicodeEncodeError:
                # Fallback to UTF-8.
                subtitle_path = ep_to_sub["location"].encode("utf-8")
            if not os.path.isfile(subtitle_path):
                logger.log(
                    u"Episode file does not exist, cannot download subtitles for {0} {1}".format(
                        ep_to_sub["show_name"],
                        episode_num(ep_to_sub["season"], ep_to_sub["episode"])
                        or episode_num(ep_to_sub["season"], ep_to_sub["episode"], numbering="absolute"),
                    ),
                    logger.DEBUG,
                )
                continue

            if not needs_subtitles(ep_to_sub["subtitles"]):
                logger.log(
                    u"Episode already has all needed subtitles, skipping {0} {1}".format(
                        ep_to_sub["show_name"],
                        episode_num(ep_to_sub["season"], ep_to_sub["episode"])
                        or episode_num(ep_to_sub["season"], ep_to_sub["episode"], numbering="absolute"),
                    ),
                    logger.DEBUG,
                )
                continue

            try:
                lastsearched = datetime.datetime.strptime(ep_to_sub["lastsearch"], dateTimeFormat)
            except ValueError:
                lastsearched = datetime.datetime.min

            try:
                if not force:
                    now = datetime.datetime.now()
                    days = int(ep_to_sub["age"])
                    delay_time = datetime.timedelta(hours=8 if days < 10 else 7 * 24 if days < 30 else 30 * 24)

                    # Search every hour for the first 24 hours since aired, then every 8 hours until 10 days passes
                    # After 10 days, search every 7 days, after 30 days search once a month
                    # Will always try an episode regardless of age at least 2 times
                    if lastsearched + delay_time > now and int(ep_to_sub["searchcount"]) > 2 and days:
                        logger.log(
                            u"Subtitle search for {0} {1} delayed for {2}".format(
                                ep_to_sub["show_name"],
                                episode_num(ep_to_sub["season"], ep_to_sub["episode"])
                                or episode_num(ep_to_sub["season"], ep_to_sub["episode"], numbering="absolute"),
                                dhm(lastsearched + delay_time - now),
                            ),
                            logger.DEBUG,
                        )
                        continue

                logger.log(
                    u"Searching for missing subtitles of {0} {1}".format(
                        ep_to_sub["show_name"],
                        episode_num(ep_to_sub["season"], ep_to_sub["episode"])
                        or episode_num(ep_to_sub["season"], ep_to_sub["episode"], numbering="absolute"),
                    ),
                    logger.INFO,
                )

                show_object = Show.find(sickbeard.showList, int(ep_to_sub["showid"]))
                if not show_object:
                    logger.log(u"Show with ID {0} not found in the database".format(ep_to_sub["showid"]), logger.DEBUG)
                    continue

                episode_object = show_object.getEpisode(ep_to_sub["season"], ep_to_sub["episode"])
                if isinstance(episode_object, str):
                    logger.log(
                        u"{0} {1} not found in the database".format(
                            ep_to_sub["show_name"],
                            episode_num(ep_to_sub["season"], ep_to_sub["episode"])
                            or episode_num(ep_to_sub["season"], ep_to_sub["episode"], numbering="absolute"),
                        ),
                        logger.DEBUG,
                    )
                    continue

                try:
                    new_subtitles = episode_object.download_subtitles()
                except Exception as error:
                    logger.log(
                        u"Unable to find subtitles for {0} {1}. Error: {2}".format(
                            ep_to_sub["show_name"],
                            episode_num(ep_to_sub["season"], ep_to_sub["episode"])
                            or episode_num(ep_to_sub["season"], ep_to_sub["episode"], numbering="absolute"),
                            ex(error),
                        ),
                        logger.ERROR,
                    )
                    continue

                if new_subtitles:
                    logger.log(
                        u"Downloaded {0} subtitles for {1} {2}".format(
                            ", ".join(new_subtitles),
                            ep_to_sub["show_name"],
                            episode_num(ep_to_sub["season"], ep_to_sub["episode"])
                            or episode_num(ep_to_sub["season"], ep_to_sub["episode"], numbering="absolute"),
                        )
                    )

            except Exception as error:
                logger.log(
                    u"Error while searching subtitles for {0} {1}. Error: {2}".format(
                        ep_to_sub["show_name"],
                        episode_num(ep_to_sub["season"], ep_to_sub["episode"])
                        or episode_num(ep_to_sub["season"], ep_to_sub["episode"], numbering="absolute"),
                        ex(error),
                    ),
                    logger.ERROR,
                )
                continue

        logger.log(u"Finished checking for missed subtitles", logger.INFO)
        self.amActive = False