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
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)))
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
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
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})
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)
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)
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)
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)
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)
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
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)
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)
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)
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)
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
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
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')
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)))
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
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
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
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
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
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
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
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
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)
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
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
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
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
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
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 []
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