def check_sonarr_rootfolder(): get_sonarr_rootfolder() rootfolder = TableShowsRootfolder.select(TableShowsRootfolder.id, TableShowsRootfolder.path).dicts() for item in rootfolder: root_path = item['path'] if not root_path.endswith(('/', '\\')): if root_path.startswith('/'): root_path += '/' else: root_path += '\\' if not os.path.isdir(path_mappings.path_replace(root_path)): TableShowsRootfolder.update({TableShowsRootfolder.accessible: 0, TableShowsRootfolder.error: 'This Sonarr root directory does not seems to ' 'be accessible by Bazarr. Please check path ' 'mapping.'})\ .where(TableShowsRootfolder.id == item['id'])\ .execute() elif not os.access(path_mappings.path_replace(root_path), os.W_OK): TableShowsRootfolder.update({TableShowsRootfolder.accessible: 0, TableShowsRootfolder.error: 'Bazarr cannot write to this directory.'}) \ .where(TableShowsRootfolder.id == item['id']) \ .execute() else: TableShowsRootfolder.update({TableShowsRootfolder.accessible: 1, TableShowsRootfolder.error: ''}) \ .where(TableShowsRootfolder.id == item['id']) \ .execute()
def path_replace(self, values_dict): if type(values_dict) is list: for item in values_dict: item['path'] = path_mappings.path_replace(item['path']) elif type(values_dict) is dict: values_dict['path'] = path_mappings.path_replace(values_dict['path']) else: return path_mappings.path_replace(values_dict)
def postprocessEpisode(item): postprocess(item) if 'audio_language' in item and item['audio_language'] is not None: item['audio_language'] = get_audio_profile_languages(episode_id=item['sonarrEpisodeId']) if 'subtitles' in item: if item['subtitles'] is None: raw_subtitles = [] else: raw_subtitles = ast.literal_eval(item['subtitles']) subtitles = [] for subs in raw_subtitles: subtitle = subs[0].split(':') sub = {"name": language_from_alpha2(subtitle[0]), "code2": subtitle[0], "code3": alpha3_from_alpha2(subtitle[0]), "path": path_mappings.path_replace(subs[1]), "forced": False, "hi": False} if len(subtitle) > 1: sub["forced"] = True if subtitle[1] == 'forced' else False sub["hi"] = True if subtitle[1] == 'hi' else False subtitles.append(sub) item.update({"subtitles": subtitles}) # Parse missing subtitles if 'missing_subtitles' in item: if item['missing_subtitles'] is None: item['missing_subtitles'] = [] else: item['missing_subtitles'] = ast.literal_eval(item['missing_subtitles']) for i, subs in enumerate(item['missing_subtitles']): subtitle = subs.split(':') item['missing_subtitles'][i] = {"name": language_from_alpha2(subtitle[0]), "code2": subtitle[0], "code3": alpha3_from_alpha2(subtitle[0]), "forced": False, "hi": False} if len(subtitle) > 1: item['missing_subtitles'][i].update({ "forced": True if subtitle[1] == 'forced' else False, "hi": True if subtitle[1] == 'hi' else False }) if 'scene_name' in item: item["sceneName"] = item["scene_name"] del item["scene_name"] if 'path' in item and item['path']: # Provide mapped path item['path'] = path_mappings.path_replace(item['path'])
def get_health_issues(): # this function must return a list of dictionaries consisting of to keys: object and issue health_issues = [] # get Sonarr rootfolder issues if settings.general.getboolean('use_sonarr'): rootfolder = TableShowsRootfolder.select(TableShowsRootfolder.path, TableShowsRootfolder.accessible, TableShowsRootfolder.error)\ .where(TableShowsRootfolder.accessible == 0)\ .dicts() for item in rootfolder: health_issues.append({ 'object': path_mappings.path_replace(item['path']), 'issue': item['error'] }) # get Radarr rootfolder issues if settings.general.getboolean('use_radarr'): rootfolder = TableMoviesRootfolder.select(TableMoviesRootfolder.path, TableMoviesRootfolder.accessible, TableMoviesRootfolder.error)\ .where(TableMoviesRootfolder.accessible == 0)\ .dicts() for item in rootfolder: health_issues.append({ 'object': path_mappings.path_replace_movie(item['path']), 'issue': item['error'] }) return health_issues
def delete(self): sonarrSeriesId = request.args.get('seriesid') sonarrEpisodeId = request.args.get('episodeid') episodeInfo = TableEpisodes.select(TableEpisodes.title, TableEpisodes.path, TableEpisodes.scene_name, TableEpisodes.audio_language)\ .where(TableEpisodes.sonarrEpisodeId == sonarrEpisodeId)\ .dicts()\ .get() episodePath = path_mappings.path_replace(episodeInfo['path']) language = request.form.get('language') forced = request.form.get('forced') hi = request.form.get('hi') subtitlesPath = request.form.get('path') subtitlesPath = path_mappings.path_replace_reverse(subtitlesPath) delete_subtitles(media_type='series', language=language, forced=forced, hi=hi, media_path=episodePath, subtitles_path=subtitlesPath, sonarr_series_id=sonarrSeriesId, sonarr_episode_id=sonarrEpisodeId) return '', 204
def get(self): # Manual Search sonarrEpisodeId = request.args.get('episodeid') episodeInfo = TableEpisodes.select(TableEpisodes.title, TableEpisodes.path, TableEpisodes.scene_name, TableShows.profileId) \ .join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId))\ .where(TableEpisodes.sonarrEpisodeId == sonarrEpisodeId) \ .dicts() \ .get() title = episodeInfo['title'] episodePath = path_mappings.path_replace(episodeInfo['path']) sceneName = episodeInfo['scene_name'] profileId = episodeInfo['profileId'] if sceneName is None: sceneName = "None" providers_list = get_providers() providers_auth = get_providers_auth() data = manual_search(episodePath, profileId, providers_list, providers_auth, sceneName, title, 'series') if not data: data = [] return jsonify(data=data)
def post(self): sonarr_series_id = int(request.args.get('seriesid')) sonarr_episode_id = int(request.args.get('episodeid')) provider = request.form.get('provider') subs_id = request.form.get('subs_id') language = request.form.get('language') episodeInfo = TableEpisodes.select(TableEpisodes.path)\ .where(TableEpisodes.sonarrEpisodeId == sonarr_episode_id)\ .dicts()\ .get_or_none() if not episodeInfo: return 'Episode not found', 500 media_path = episodeInfo['path'] subtitles_path = request.form.get('subtitles_path') blacklist_log(sonarr_series_id=sonarr_series_id, sonarr_episode_id=sonarr_episode_id, provider=provider, subs_id=subs_id, language=language) delete_subtitles(media_type='series', language=language, forced=False, hi=False, media_path=path_mappings.path_replace(media_path), subtitles_path=subtitles_path, sonarr_series_id=sonarr_series_id, sonarr_episode_id=sonarr_episode_id) episode_download_subtitles(sonarr_episode_id) event_stream(type='episode-history') return '', 200
def series_full_scan_subtitles(): episodes = database.execute("SELECT path FROM table_episodes") for i, episode in enumerate(episodes, 1): store_subtitles(episode['path'], path_mappings.path_replace(episode['path'])) gc.collect()
def postprocessSeries(item): postprocess(item) # Parse audio language if 'audio_language' in item and item['audio_language'] is not None: item['audio_language'] = get_audio_profile_languages(series_id=item['sonarrSeriesId']) if 'alternateTitles' in item: if item['alternateTitles'] is None: item['alternativeTitles'] = [] else: item['alternativeTitles'] = ast.literal_eval(item['alternateTitles']) del item["alternateTitles"] # Parse seriesType if 'seriesType' in item and item['seriesType'] is not None: item['seriesType'] = item['seriesType'].capitalize() if 'path' in item: item['path'] = path_mappings.path_replace(item['path']) # map poster and fanart to server proxy if 'poster' in item: poster = item['poster'] item['poster'] = f"{base_url}/images/series{poster}" if poster else None if 'fanart' in item: fanart = item['fanart'] item['fanart'] = f"{base_url}/images/series{fanart}" if fanart else None
def series_scan_subtitles(no): episodes = TableEpisodes.select(TableEpisodes.path)\ .where(TableEpisodes.sonarrSeriesId == no)\ .order_by(TableEpisodes.sonarrEpisodeId)\ .dicts() for episode in episodes: store_subtitles(episode['path'], path_mappings.path_replace(episode['path']), use_cache=False)
def series_scan_subtitles(no): episodes = database.execute( "SELECT path FROM table_episodes WHERE sonarrSeriesId=? ORDER BY sonarrEpisodeId", (no, )) for episode in episodes: store_subtitles(episode['path'], path_mappings.path_replace(episode['path']))
def patch(self): sonarrSeriesId = request.args.get('seriesid') sonarrEpisodeId = request.args.get('episodeid') episodeInfo = TableEpisodes.select(TableEpisodes.path, TableEpisodes.scene_name, TableEpisodes.audio_language, TableShows.title) \ .join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId)) \ .where(TableEpisodes.sonarrEpisodeId == sonarrEpisodeId)\ .dicts()\ .get_or_none() if not episodeInfo: return 'Episode not found', 500 title = episodeInfo['title'] episodePath = path_mappings.path_replace(episodeInfo['path']) sceneName = episodeInfo['scene_name'] or "None" language = request.form.get('language') hi = request.form.get('hi').capitalize() forced = request.form.get('forced').capitalize() audio_language_list = get_audio_profile_languages(episode_id=sonarrEpisodeId) if len(audio_language_list) > 0: audio_language = audio_language_list[0]['name'] else: audio_language = None try: result = list(generate_subtitles(episodePath, [(language, hi, forced)], audio_language, sceneName, title, 'series', profile_id=get_profile_id(episode_id=sonarrEpisodeId))) if result: result = result[0] message = result[0] path = result[1] forced = result[5] if result[8]: language_code = result[2] + ":hi" elif forced: language_code = result[2] + ":forced" else: language_code = result[2] provider = result[3] score = result[4] subs_id = result[6] subs_path = result[7] history_log(1, sonarrSeriesId, sonarrEpisodeId, message, path, language_code, provider, score, subs_id, subs_path) send_notifications(sonarrSeriesId, sonarrEpisodeId, message) store_subtitles(path, episodePath) else: event_stream(type='episode', payload=sonarrEpisodeId) except OSError: pass return '', 204
def series_full_scan_subtitles(): use_ffprobe_cache = settings.sonarr.getboolean('use_ffprobe_cache') episodes = TableEpisodes.select(TableEpisodes.path).dicts() count_episodes = len(episodes) for i, episode in enumerate(episodes): show_progress(id='episodes_disk_scan', header='Full disk scan...', name='Episodes subtitles', value=i, count=count_episodes) store_subtitles(episode['path'], path_mappings.path_replace(episode['path']), use_cache=use_ffprobe_cache) hide_progress(id='episodes_disk_scan') gc.collect()
def delete_subtitles(media_type, language, forced, hi, media_path, subtitles_path, sonarr_series_id=None, sonarr_episode_id=None, radarr_id=None): if not subtitles_path.endswith('.srt'): logging.error('BAZARR can only delete .srt files.') return False language_log = language language_string = language_from_alpha2(language) if hi in [True, 'true', 'True']: language_log += ':hi' language_string += ' HI' elif forced in [True, 'true', 'True']: language_log += ':forced' language_string += ' forced' result = language_string + " subtitles deleted from disk." if media_type == 'series': try: os.remove(path_mappings.path_replace(subtitles_path)) except OSError: logging.exception('BAZARR cannot delete subtitles file: ' + subtitles_path) store_subtitles(path_mappings.path_replace_reverse(media_path), media_path) return False else: history_log(0, sonarr_series_id, sonarr_episode_id, result, language=language_log, video_path=path_mappings.path_replace_reverse(media_path), subtitles_path=path_mappings.path_replace_reverse(subtitles_path)) store_subtitles(path_mappings.path_replace_reverse(media_path), media_path) notify_sonarr(sonarr_series_id) else: try: os.remove(path_mappings.path_replace_movie(subtitles_path)) except OSError: logging.exception('BAZARR cannot delete subtitles file: ' + subtitles_path) store_subtitles_movie(path_mappings.path_replace_reverse_movie(media_path), media_path) return False else: history_log_movie(0, radarr_id, result, language=language_log, video_path=path_mappings.path_replace_reverse_movie(media_path), subtitles_path=path_mappings.path_replace_reverse_movie(subtitles_path)) store_subtitles_movie(path_mappings.path_replace_reverse_movie(media_path), media_path) notify_radarr(radarr_id) return True
def sync_episodes(): logging.debug('BAZARR Starting episodes sync from Sonarr.') apikey_sonarr = settings.sonarr.apikey # Get current episodes id in DB current_episodes_db = database.execute("SELECT sonarrEpisodeId, path, sonarrSeriesId FROM table_episodes") current_episodes_db_list = [x['sonarrEpisodeId'] for x in current_episodes_db] current_episodes_sonarr = [] episodes_to_update = [] episodes_to_add = [] altered_episodes = [] # Get sonarrId for each series from database seriesIdList = database.execute("SELECT sonarrSeriesId, title FROM table_shows") for i, seriesId in enumerate(seriesIdList): # Get episodes data for a series from Sonarr url_sonarr_api_episode = url_sonarr() + "/api/episode?seriesId=" + str(seriesId['sonarrSeriesId']) + "&apikey=" + apikey_sonarr try: r = requests.get(url_sonarr_api_episode, timeout=60, verify=False) r.raise_for_status() except requests.exceptions.HTTPError as errh: logging.exception("BAZARR Error trying to get episodes from Sonarr. Http error.") return except requests.exceptions.ConnectionError as errc: logging.exception("BAZARR Error trying to get episodes from Sonarr. Connection Error.") return except requests.exceptions.Timeout as errt: logging.exception("BAZARR Error trying to get episodes from Sonarr. Timeout Error.") return except requests.exceptions.RequestException as err: logging.exception("BAZARR Error trying to get episodes from Sonarr.") return else: for episode in r.json(): if 'hasFile' in episode: if episode['hasFile'] is True: if 'episodeFile' in episode: if episode['episodeFile']['size'] > 20480: # Add shows in Sonarr to current shows list if 'sceneName' in episode['episodeFile']: sceneName = episode['episodeFile']['sceneName'] else: sceneName = None try: format, resolution = episode['episodeFile']['quality']['quality']['name'].split('-') except: format = episode['episodeFile']['quality']['quality']['name'] try: resolution = str(episode['episodeFile']['quality']['quality']['resolution']) + 'p' except: resolution = None if 'mediaInfo' in episode['episodeFile']: if 'videoCodec' in episode['episodeFile']['mediaInfo']: videoCodec = episode['episodeFile']['mediaInfo']['videoCodec'] videoCodec = SonarrFormatVideoCodec(videoCodec) else: videoCodec = None if 'audioCodec' in episode['episodeFile']['mediaInfo']: audioCodec = episode['episodeFile']['mediaInfo']['audioCodec'] audioCodec = SonarrFormatAudioCodec(audioCodec) else: audioCodec = None else: videoCodec = None audioCodec = None audio_language = None if 'language' in episode['episodeFile'] and len(episode['episodeFile']['language']): item = episode['episodeFile']['language'] if isinstance(item, dict): if 'name' in item: audio_language = item['name'] else: audio_language = database.execute("SELECT audio_language FROM table_shows WHERE " "sonarrSeriesId=?", (episode['seriesId'],), only_one=True)['audio_language'] # Add episodes in sonarr to current episode list current_episodes_sonarr.append(episode['id']) if episode['id'] in current_episodes_db_list: episodes_to_update.append({'sonarrSeriesId': episode['seriesId'], 'sonarrEpisodeId': episode['id'], 'title': episode['title'], 'path': episode['episodeFile']['path'], 'season': episode['seasonNumber'], 'episode': episode['episodeNumber'], 'scene_name': sceneName, 'monitored': str(bool(episode['monitored'])), 'format': format, 'resolution': resolution, 'video_codec': videoCodec, 'audio_codec': audioCodec, 'episode_file_id': episode['episodeFile']['id'], 'audio_language': audio_language}) else: episodes_to_add.append({'sonarrSeriesId': episode['seriesId'], 'sonarrEpisodeId': episode['id'], 'title': episode['title'], 'path': episode['episodeFile']['path'], 'season': episode['seasonNumber'], 'episode': episode['episodeNumber'], 'scene_name': sceneName, 'monitored': str(bool(episode['monitored'])), 'format': format, 'resolution': resolution, 'video_codec': videoCodec, 'audio_codec': audioCodec, 'episode_file_id': episode['episodeFile']['id'], 'audio_language': audio_language}) # Remove old episodes from DB removed_episodes = list(set(current_episodes_db_list) - set(current_episodes_sonarr)) for removed_episode in removed_episodes: episode_to_delete = database.execute("SELECT sonarrSeriesId, sonarrEpisodeId FROM table_episodes WHERE " "sonarrEpisodeId=?", (removed_episode,), only_one=True) database.execute("DELETE FROM table_episodes WHERE sonarrEpisodeId=?", (removed_episode,)) event_stream(type='episode', action='delete', series=episode_to_delete['sonarrSeriesId'], episode=episode_to_delete['sonarrEpisodeId']) # Update existing episodes in DB episode_in_db_list = [] episodes_in_db = database.execute("SELECT sonarrSeriesId, sonarrEpisodeId, title, path, season, episode, " "scene_name, monitored, format, resolution, video_codec, audio_codec, " "episode_file_id, audio_language FROM table_episodes") for item in episodes_in_db: episode_in_db_list.append(item) episodes_to_update_list = [i for i in episodes_to_update if i not in episode_in_db_list] for updated_episode in episodes_to_update_list: query = dict_converter.convert(updated_episode) database.execute('''UPDATE table_episodes SET ''' + query.keys_update + ''' WHERE sonarrEpisodeId = ?''', query.values + (updated_episode['sonarrEpisodeId'],)) altered_episodes.append([updated_episode['sonarrEpisodeId'], updated_episode['path'], updated_episode['sonarrSeriesId']]) # Insert new episodes in DB for added_episode in episodes_to_add: query = dict_converter.convert(added_episode) result = database.execute( '''INSERT OR IGNORE INTO table_episodes(''' + query.keys_insert + ''') VALUES(''' + query.question_marks + ''')''', query.values) if result > 0: altered_episodes.append([added_episode['sonarrEpisodeId'], added_episode['path'], added_episode['monitored']]) event_stream(type='episode', action='insert', series=added_episode['sonarrSeriesId'], episode=added_episode['sonarrEpisodeId']) else: logging.debug('BAZARR unable to insert this episode into the database:{}'.format(path_mappings.path_replace(added_episode['path']))) # Store subtitles for added or modified episodes for i, altered_episode in enumerate(altered_episodes, 1): store_subtitles(altered_episode[1], path_mappings.path_replace(altered_episode[1])) logging.debug('BAZARR All episodes synced from Sonarr into database.') # Search for desired subtitles if no more than 5 episodes have been added. if len(altered_episodes) <= 5: logging.debug("BAZARR No more than 5 episodes were added during this sync then we'll search for subtitles.") for altered_episode in altered_episodes: data = database.execute("SELECT table_episodes.sonarrEpisodeId, table_episodes.monitored, table_shows.tags," " table_shows.seriesType FROM table_episodes LEFT JOIN table_shows on " "table_episodes.sonarrSeriesId = table_shows.sonarrSeriesId WHERE " "sonarrEpisodeId = ?" + get_exclusion_clause('series'), (altered_episode[0],), only_one=True) if data: episode_download_subtitles(data['sonarrEpisodeId']) else: logging.debug("BAZARR skipping download for this episode as it is excluded.") else: logging.debug("BAZARR More than 5 episodes were added during this sync then we wont search for subtitles right now.")
def update_series(): apikey_sonarr = settings.sonarr.apikey if apikey_sonarr is None: return sonarr_version = get_sonarr_version() serie_default_enabled = settings.general.getboolean( 'serie_default_enabled') if serie_default_enabled is True: serie_default_profile = settings.general.serie_default_profile if serie_default_profile == '': serie_default_profile = None else: serie_default_profile = None audio_profiles = get_profile_list() tagsDict = get_tags() # Get shows data from Sonarr url_sonarr_api_series = url_sonarr( ) + "/api/series?apikey=" + apikey_sonarr try: r = requests.get(url_sonarr_api_series, timeout=60, verify=False) r.raise_for_status() except requests.exceptions.HTTPError: logging.exception( "BAZARR Error trying to get series from Sonarr. Http error.") return except requests.exceptions.ConnectionError: logging.exception( "BAZARR Error trying to get series from Sonarr. Connection Error.") return except requests.exceptions.Timeout: logging.exception( "BAZARR Error trying to get series from Sonarr. Timeout Error.") return except requests.exceptions.RequestException: logging.exception("BAZARR Error trying to get series from Sonarr.") return # Get current shows in DB current_shows_db = database.execute( "SELECT sonarrSeriesId FROM table_shows") current_shows_db_list = [x['sonarrSeriesId'] for x in current_shows_db] current_shows_sonarr = [] series_to_update = [] series_to_add = [] series_list_length = len(r.json()) for i, show in enumerate(r.json(), 1): overview = show['overview'] if 'overview' in show else '' poster = '' fanart = '' for image in show['images']: if image['coverType'] == 'poster': poster_big = image['url'].split('?')[0] poster = os.path.splitext( poster_big)[0] + '-250' + os.path.splitext(poster_big)[1] if image['coverType'] == 'fanart': fanart = image['url'].split('?')[0] alternate_titles = None if show['alternateTitles'] is not None: alternate_titles = str( [item['title'] for item in show['alternateTitles']]) audio_language = [] if sonarr_version.startswith('2'): audio_language = profile_id_to_language(show['qualityProfileId'], audio_profiles) else: audio_language = profile_id_to_language(show['languageProfileId'], audio_profiles) tags = [d['label'] for d in tagsDict if d['id'] in show['tags']] imdbId = show['imdbId'] if 'imdbId' in show else None # Add shows in Sonarr to current shows list current_shows_sonarr.append(show['id']) if show['id'] in current_shows_db_list: series_to_update.append({ 'title': show["title"], 'path': show["path"], 'tvdbId': int(show["tvdbId"]), 'sonarrSeriesId': int(show["id"]), 'overview': overview, 'poster': poster, 'fanart': fanart, 'audio_language': str(audio_language), 'sortTitle': show['sortTitle'], 'year': str(show['year']), 'alternateTitles': alternate_titles, 'tags': str(tags), 'seriesType': show['seriesType'], 'imdbId': imdbId }) else: series_to_add.append({ 'title': show["title"], 'path': show["path"], 'tvdbId': show["tvdbId"], 'sonarrSeriesId': show["id"], 'overview': overview, 'poster': poster, 'fanart': fanart, 'audio_language': str(audio_language), 'sortTitle': show['sortTitle'], 'year': str(show['year']), 'alternateTitles': alternate_titles, 'tags': str(tags), 'seriesType': show['seriesType'], 'imdbId': imdbId, 'profileId': serie_default_profile }) # Remove old series from DB removed_series = list( set(current_shows_db_list) - set(current_shows_sonarr)) for series in removed_series: database.execute("DELETE FROM table_shows WHERE sonarrSeriesId=?", (series, )) event_stream(type='series', action='delete', series=series) # Update existing series in DB series_in_db_list = [] series_in_db = database.execute( "SELECT title, path, tvdbId, sonarrSeriesId, overview, poster, fanart, " "audio_language, sortTitle, year, alternateTitles, tags, seriesType, imdbId FROM table_shows" ) for item in series_in_db: series_in_db_list.append(item) series_to_update_list = [ i for i in series_to_update if i not in series_in_db_list ] for updated_series in series_to_update_list: query = dict_converter.convert(updated_series) database.execute( '''UPDATE table_shows SET ''' + query.keys_update + ''' WHERE sonarrSeriesId = ?''', query.values + (updated_series['sonarrSeriesId'], )) event_stream(type='series', action='update', series=updated_series['sonarrSeriesId']) # Insert new series in DB for added_series in series_to_add: query = dict_converter.convert(added_series) result = database.execute( '''INSERT OR IGNORE INTO table_shows(''' + query.keys_insert + ''') VALUES(''' + query.question_marks + ''')''', query.values) if result: list_missing_subtitles(no=added_series['sonarrSeriesId']) else: logging.debug( 'BAZARR unable to insert this series into the database:', path_mappings.path_replace(added_series['path'])) event_stream(type='series', action='insert', series=added_series['sonarrSeriesId']) logging.debug( 'BAZARR All series synced from Sonarr into database.')
def patch(self): action = request.args.get('action') language = request.form.get('language') subtitles_path = request.form.get('path') media_type = request.form.get('type') id = request.form.get('id') if media_type == 'episode': subtitles_path = path_mappings.path_replace(subtitles_path) metadata = TableEpisodes.select(TableEpisodes.path, TableEpisodes.sonarrSeriesId)\ .where(TableEpisodes.sonarrEpisodeId == id)\ .dicts()\ .get_or_none() if not metadata: return 'Episode not found', 500 video_path = path_mappings.path_replace(metadata['path']) else: subtitles_path = path_mappings.path_replace_movie(subtitles_path) metadata = TableMovies.select(TableMovies.path).where( TableMovies.radarrId == id).dicts().get_or_none() if not metadata: return 'Movie not found', 500 video_path = path_mappings.path_replace_movie(metadata['path']) if action == 'sync': subsync = SubSyncer() if media_type == 'episode': subsync.sync(video_path=video_path, srt_path=subtitles_path, srt_lang=language, media_type='series', sonarr_series_id=metadata['sonarrSeriesId'], sonarr_episode_id=int(id)) else: subsync.sync(video_path=video_path, srt_path=subtitles_path, srt_lang=language, media_type='movies', radarr_id=id) del subsync gc.collect() elif action == 'translate': dest_language = language forced = True if request.form.get('forced') == 'true' else False hi = True if request.form.get('hi') == 'true' else False result = translate_subtitles_file(video_path=video_path, source_srt_file=subtitles_path, to_lang=dest_language, forced=forced, hi=hi) if result: if media_type == 'episode': store_subtitles( path_mappings.path_replace_reverse(video_path), video_path) else: store_subtitles_movie( path_mappings.path_replace_reverse_movie(video_path), video_path) return '', 200 else: return '', 404 else: subtitles_apply_mods(language, subtitles_path, [action]) # apply chmod if required chmod = int(settings.general.chmod, 8) if not sys.platform.startswith( 'win') and settings.general.getboolean('chmod_enabled') else None if chmod: os.chmod(subtitles_path, chmod) return '', 204
def series_download_subtitles(no): conditions = [(TableEpisodes.sonarrSeriesId == no), (TableEpisodes.missing_subtitles != '[]')] conditions += get_exclusion_clause('series') episodes_details = TableEpisodes.select(TableEpisodes.path, TableEpisodes.missing_subtitles, TableEpisodes.monitored, TableEpisodes.sonarrEpisodeId, TableEpisodes.scene_name, TableShows.tags, TableShows.seriesType, TableEpisodes.audio_language, TableShows.title, TableEpisodes.season, TableEpisodes.episode, TableEpisodes.title.alias('episodeTitle')) \ .join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId)) \ .where(reduce(operator.and_, conditions)) \ .dicts() if not episodes_details: logging.debug( "BAZARR no episode for that sonarrSeriesId have been found in database or they have all been " "ignored because of monitored status, series type or series tags: {}" .format(no)) return count_episodes_details = len(episodes_details) for i, episode in enumerate(episodes_details): providers_list = get_providers() if providers_list: show_progress(id='series_search_progress_{}'.format(no), header='Searching missing subtitles...', name='{0} - S{1:02d}E{2:02d} - {3}'.format( episode['title'], episode['season'], episode['episode'], episode['episodeTitle']), value=i, count=count_episodes_details) audio_language_list = get_audio_profile_languages( episode_id=episode['sonarrEpisodeId']) if len(audio_language_list) > 0: audio_language = audio_language_list[0]['name'] else: audio_language = 'None' languages = [] for language in ast.literal_eval(episode['missing_subtitles']): # confirm if language is still missing or if cutoff have been reached confirmed_missing_subs = TableEpisodes.select(TableEpisodes.missing_subtitles) \ .where(TableEpisodes.sonarrEpisodeId == episode['sonarrEpisodeId']) \ .dicts() \ .get_or_none() if not confirmed_missing_subs: continue if language not in ast.literal_eval( confirmed_missing_subs['missing_subtitles']): continue if language is not None: hi_ = "True" if language.endswith(':hi') else "False" forced_ = "True" if language.endswith( ':forced') else "False" languages.append((language.split(":")[0], hi_, forced_)) if not languages: continue for result in generate_subtitles( path_mappings.path_replace(episode['path']), languages, audio_language, str(episode['scene_name']), episode['title'], 'series'): if result: message = result[0] path = result[1] forced = result[5] if result[8]: language_code = result[2] + ":hi" elif forced: language_code = result[2] + ":forced" else: language_code = result[2] provider = result[3] score = result[4] subs_id = result[6] subs_path = result[7] store_subtitles( episode['path'], path_mappings.path_replace(episode['path'])) history_log(1, no, episode['sonarrEpisodeId'], message, path, language_code, provider, score, subs_id, subs_path) send_notifications(no, episode['sonarrEpisodeId'], message) else: logging.info("BAZARR All providers are throttled") break hide_progress(id='series_search_progress_{}'.format(no))
def post(self): # Manual Download sonarrSeriesId = request.args.get('seriesid') sonarrEpisodeId = request.args.get('episodeid') episodeInfo = TableEpisodes.select(TableEpisodes.title, TableEpisodes.path, TableEpisodes.scene_name) \ .where(TableEpisodes.sonarrEpisodeId == sonarrEpisodeId) \ .dicts() \ .get() title = episodeInfo['title'] episodePath = path_mappings.path_replace(episodeInfo['path']) sceneName = episodeInfo['scene_name'] if sceneName is None: sceneName = "None" language = request.form.get('language') hi = request.form.get('hi').capitalize() forced = request.form.get('forced').capitalize() selected_provider = request.form.get('provider') subtitle = request.form.get('subtitle') providers_auth = get_providers_auth() audio_language_list = get_audio_profile_languages( episode_id=sonarrEpisodeId) if len(audio_language_list) > 0: audio_language = audio_language_list[0]['name'] else: audio_language = 'None' try: result = manual_download_subtitle( episodePath, language, audio_language, hi, forced, subtitle, selected_provider, providers_auth, sceneName, title, 'series', profile_id=get_profile_id(episode_id=sonarrEpisodeId)) if result is not None: message = result[0] path = result[1] forced = result[5] if result[8]: language_code = result[2] + ":hi" elif forced: language_code = result[2] + ":forced" else: language_code = result[2] provider = result[3] score = result[4] subs_id = result[6] subs_path = result[7] history_log(2, sonarrSeriesId, sonarrEpisodeId, message, path, language_code, provider, score, subs_id, subs_path) if not settings.general.getboolean( 'dont_notify_manual_actions'): send_notifications(sonarrSeriesId, sonarrEpisodeId, message) store_subtitles(path, episodePath) return result, 201 except OSError: pass return '', 204
def update_one_series(series_id, action): logging.debug('BAZARR syncing this specific series from Sonarr: {}'.format(series_id)) # Check if there's a row in database for this series ID existing_series = TableShows.select(TableShows.path)\ .where(TableShows.sonarrSeriesId == series_id)\ .dicts()\ .get_or_none() # Delete series from DB if action == 'deleted' and existing_series: try: TableShows.delete().where(TableShows.sonarrSeriesId == int(series_id)).execute() except Exception as e: logging.error(f"BAZARR cannot delete series with sonarrSeriesId {series_id} because of {e}") else: TableEpisodes.delete().where(TableEpisodes.sonarrSeriesId == int(series_id)).execute() event_stream(type='series', action='delete', payload=int(series_id)) return serie_default_enabled = settings.general.getboolean('serie_default_enabled') if serie_default_enabled is True: serie_default_profile = settings.general.serie_default_profile if serie_default_profile == '': serie_default_profile = None else: serie_default_profile = None audio_profiles = get_profile_list() tagsDict = get_tags() try: # Get series data from sonarr api series = None series_data = get_series_from_sonarr_api(url=url_sonarr(), apikey_sonarr=settings.sonarr.apikey, sonarr_series_id=int(series_id)) if not series_data: return else: if action == 'updated' and existing_series: series = seriesParser(series_data, action='update', tags_dict=tagsDict, serie_default_profile=serie_default_profile, audio_profiles=audio_profiles) elif action == 'updated' and not existing_series: series = seriesParser(series_data, action='insert', tags_dict=tagsDict, serie_default_profile=serie_default_profile, audio_profiles=audio_profiles) except Exception: logging.debug('BAZARR cannot parse series returned by SignalR feed.') return # Update existing series in DB if action == 'updated' and existing_series: try: TableShows.update(series).where(TableShows.sonarrSeriesId == series['sonarrSeriesId']).execute() except IntegrityError as e: logging.error(f"BAZARR cannot update series {series['path']} because of {e}") else: sync_episodes(series_id=int(series_id), send_event=True) event_stream(type='series', action='update', payload=int(series_id)) logging.debug('BAZARR updated this series into the database:{}'.format(path_mappings.path_replace( series['path']))) # Insert new series in DB elif action == 'updated' and not existing_series: try: TableShows.insert(series).on_conflict(action='IGNORE').execute() except IntegrityError as e: logging.error(f"BAZARR cannot insert series {series['path']} because of {e}") else: event_stream(type='series', action='update', payload=int(series_id)) logging.debug('BAZARR inserted this series into the database:{}'.format(path_mappings.path_replace( series['path'])))
def update_series(send_event=True): check_sonarr_rootfolder() apikey_sonarr = settings.sonarr.apikey if apikey_sonarr is None: return serie_default_enabled = settings.general.getboolean('serie_default_enabled') if serie_default_enabled is True: serie_default_profile = settings.general.serie_default_profile if serie_default_profile == '': serie_default_profile = None else: serie_default_profile = None audio_profiles = get_profile_list() tagsDict = get_tags() # Get shows data from Sonarr series = get_series_from_sonarr_api(url=url_sonarr(), apikey_sonarr=apikey_sonarr) if not series: return else: # Get current shows in DB current_shows_db = TableShows.select(TableShows.sonarrSeriesId).dicts() current_shows_db_list = [x['sonarrSeriesId'] for x in current_shows_db] current_shows_sonarr = [] series_to_update = [] series_to_add = [] series_count = len(series) for i, show in enumerate(series): if send_event: show_progress(id='series_progress', header='Syncing series...', name=show['title'], value=i, count=series_count) # Add shows in Sonarr to current shows list current_shows_sonarr.append(show['id']) if show['id'] in current_shows_db_list: series_to_update.append(seriesParser(show, action='update', tags_dict=tagsDict, serie_default_profile=serie_default_profile, audio_profiles=audio_profiles)) else: series_to_add.append(seriesParser(show, action='insert', tags_dict=tagsDict, serie_default_profile=serie_default_profile, audio_profiles=audio_profiles)) if send_event: hide_progress(id='series_progress') # Remove old series from DB removed_series = list(set(current_shows_db_list) - set(current_shows_sonarr)) for series in removed_series: try: TableShows.delete().where(TableShows.sonarrSeriesId == series).execute() except Exception as e: logging.error(f"BAZARR cannot delete series with sonarrSeriesId {series} because of {e}") continue else: if send_event: event_stream(type='series', action='delete', payload=series) # Update existing series in DB series_in_db_list = [] series_in_db = TableShows.select(TableShows.title, TableShows.path, TableShows.tvdbId, TableShows.sonarrSeriesId, TableShows.overview, TableShows.poster, TableShows.fanart, TableShows.audio_language, TableShows.sortTitle, TableShows.year, TableShows.alternateTitles, TableShows.tags, TableShows.seriesType, TableShows.imdbId).dicts() for item in series_in_db: series_in_db_list.append(item) series_to_update_list = [i for i in series_to_update if i not in series_in_db_list] for updated_series in series_to_update_list: try: TableShows.update(updated_series).where(TableShows.sonarrSeriesId == updated_series['sonarrSeriesId']).execute() except IntegrityError as e: logging.error(f"BAZARR cannot update series {updated_series['path']} because of {e}") continue else: if send_event: event_stream(type='series', payload=updated_series['sonarrSeriesId']) # Insert new series in DB for added_series in series_to_add: try: result = TableShows.insert(added_series).on_conflict(action='IGNORE').execute() except IntegrityError as e: logging.error(f"BAZARR cannot insert series {added_series['path']} because of {e}") continue else: if result: list_missing_subtitles(no=added_series['sonarrSeriesId']) else: logging.debug('BAZARR unable to insert this series into the database:', path_mappings.path_replace(added_series['path'])) if send_event: event_stream(type='series', action='update', payload=added_series['sonarrSeriesId']) logging.debug('BAZARR All series synced from Sonarr into database.')
def sync_episodes(series_id=None, send_event=True): logging.debug('BAZARR Starting episodes sync from Sonarr.') apikey_sonarr = settings.sonarr.apikey # Get current episodes id in DB current_episodes_db = TableEpisodes.select(TableEpisodes.sonarrEpisodeId, TableEpisodes.path, TableEpisodes.sonarrSeriesId)\ .where((TableEpisodes.sonarrSeriesId == series_id) if series_id else None)\ .dicts() current_episodes_db_list = [ x['sonarrEpisodeId'] for x in current_episodes_db ] current_episodes_sonarr = [] episodes_to_update = [] episodes_to_add = [] altered_episodes = [] # Get sonarrId for each series from database seriesIdList = get_series_from_sonarr_api( series_id=series_id, url=url_sonarr(), apikey_sonarr=apikey_sonarr, ) series_count = len(seriesIdList) for i, seriesId in enumerate(seriesIdList): if send_event: show_progress(id='episodes_progress', header='Syncing episodes...', name=seriesId['title'], value=i, count=series_count) # Get episodes data for a series from Sonarr episodes = get_episodes_from_sonarr_api( url=url_sonarr(), apikey_sonarr=apikey_sonarr, series_id=seriesId['sonarrSeriesId']) if not episodes: continue else: # For Sonarr v3, we need to update episodes to integrate the episodeFile API endpoint results if not get_sonarr_info.is_legacy(): episodeFiles = get_episodesFiles_from_sonarr_api( url=url_sonarr(), apikey_sonarr=apikey_sonarr, series_id=seriesId['sonarrSeriesId']) for episode in episodes: if episode['hasFile']: item = [ x for x in episodeFiles if x['id'] == episode['episodeFileId'] ] if item: episode['episodeFile'] = item[0] for episode in episodes: if 'hasFile' in episode: if episode['hasFile'] is True: if 'episodeFile' in episode: if episode['episodeFile']['size'] > 20480: # Add episodes in sonarr to current episode list current_episodes_sonarr.append(episode['id']) # Parse episode data if episode['id'] in current_episodes_db_list: episodes_to_update.append( episodeParser(episode)) else: episodes_to_add.append( episodeParser(episode)) if send_event: hide_progress(id='episodes_progress') # Remove old episodes from DB removed_episodes = list( set(current_episodes_db_list) - set(current_episodes_sonarr)) for removed_episode in removed_episodes: episode_to_delete = TableEpisodes.select(TableEpisodes.sonarrSeriesId, TableEpisodes.sonarrEpisodeId)\ .where(TableEpisodes.sonarrEpisodeId == removed_episode)\ .dicts()\ .get() TableEpisodes.delete().where( TableEpisodes.sonarrEpisodeId == removed_episode).execute() if send_event: event_stream(type='episode', action='delete', payload=episode_to_delete['sonarrEpisodeId']) # Update existing episodes in DB episode_in_db_list = [] episodes_in_db = TableEpisodes.select( TableEpisodes.sonarrSeriesId, TableEpisodes.sonarrEpisodeId, TableEpisodes.title, TableEpisodes.path, TableEpisodes.season, TableEpisodes.episode, TableEpisodes.scene_name, TableEpisodes.monitored, TableEpisodes.format, TableEpisodes.resolution, TableEpisodes.video_codec, TableEpisodes.audio_codec, TableEpisodes.episode_file_id, TableEpisodes.audio_language, TableEpisodes.file_size).dicts() for item in episodes_in_db: episode_in_db_list.append(item) episodes_to_update_list = [ i for i in episodes_to_update if i not in episode_in_db_list ] for updated_episode in episodes_to_update_list: TableEpisodes.update(updated_episode).where( TableEpisodes.sonarrEpisodeId == updated_episode['sonarrEpisodeId']).execute() altered_episodes.append([ updated_episode['sonarrEpisodeId'], updated_episode['path'], updated_episode['sonarrSeriesId'] ]) # Insert new episodes in DB for added_episode in episodes_to_add: result = TableEpisodes.insert(added_episode).on_conflict( action='IGNORE').execute() if result > 0: altered_episodes.append([ added_episode['sonarrEpisodeId'], added_episode['path'], added_episode['monitored'] ]) if send_event: event_stream(type='episode', payload=added_episode['sonarrEpisodeId']) else: logging.debug( 'BAZARR unable to insert this episode into the database:{}'. format(path_mappings.path_replace(added_episode['path']))) # Store subtitles for added or modified episodes for i, altered_episode in enumerate(altered_episodes, 1): store_subtitles(altered_episode[1], path_mappings.path_replace(altered_episode[1])) logging.debug('BAZARR All episodes synced from Sonarr into database.')
def sync_one_episode(episode_id): logging.debug( 'BAZARR syncing this specific episode from Sonarr: {}'.format( episode_id)) url = url_sonarr() apikey_sonarr = settings.sonarr.apikey # Check if there's a row in database for this episode ID try: existing_episode = TableEpisodes.select(TableEpisodes.path, TableEpisodes.episode_file_id)\ .where(TableEpisodes.sonarrEpisodeId == episode_id)\ .dicts()\ .get() except DoesNotExist: existing_episode = None try: # Get episode data from sonarr api episode = None episode_data = get_episodes_from_sonarr_api( url=url, apikey_sonarr=apikey_sonarr, episode_id=episode_id) if not episode_data: return else: # For Sonarr v3, we need to update episodes to integrate the episodeFile API endpoint results if not get_sonarr_info.is_legacy( ) and existing_episode and episode_data['hasFile']: episode_data['episodeFile'] = \ get_episodesFiles_from_sonarr_api(url=url, apikey_sonarr=apikey_sonarr, episode_file_id=existing_episode['episode_file_id']) episode = episodeParser(episode_data) except Exception: logging.debug( 'BAZARR cannot get episode returned by SignalR feed from Sonarr API.' ) return # Drop useless events if not episode and not existing_episode: return # Remove episode from DB if not episode and existing_episode: TableEpisodes.delete().where( TableEpisodes.sonarrEpisodeId == episode_id).execute() event_stream(type='episode', action='delete', payload=int(episode_id)) logging.debug( 'BAZARR deleted this episode from the database:{}'.format( path_mappings.path_replace(existing_episode['path']))) return # Update existing episodes in DB elif episode and existing_episode: TableEpisodes.update(episode).where( TableEpisodes.sonarrEpisodeId == episode_id).execute() event_stream(type='episode', action='update', payload=int(episode_id)) logging.debug( 'BAZARR updated this episode into the database:{}'.format( path_mappings.path_replace(episode['path']))) # Insert new episodes in DB elif episode and not existing_episode: TableEpisodes.insert(episode).on_conflict(action='IGNORE').execute() event_stream(type='episode', action='update', payload=int(episode_id)) logging.debug( 'BAZARR inserted this episode into the database:{}'.format( path_mappings.path_replace(episode['path']))) # Storing existing subtitles logging.debug('BAZARR storing subtitles for this episode: {}'.format( path_mappings.path_replace(episode['path']))) store_subtitles(episode['path'], path_mappings.path_replace(episode['path'])) # Downloading missing subtitles logging.debug( 'BAZARR downloading missing subtitles for this episode: {}'.format( path_mappings.path_replace(episode['path']))) episode_download_subtitles(episode_id)
def post(self): sonarrSeriesId = request.args.get('seriesid') sonarrEpisodeId = request.args.get('episodeid') episodeInfo = TableEpisodes.select(TableEpisodes.title, TableEpisodes.path, TableEpisodes.scene_name, TableEpisodes.audio_language)\ .where(TableEpisodes.sonarrEpisodeId == sonarrEpisodeId)\ .dicts()\ .get() title = episodeInfo['title'] episodePath = path_mappings.path_replace(episodeInfo['path']) sceneName = episodeInfo['scene_name'] audio_language = episodeInfo['audio_language'] if sceneName is None: sceneName = "None" language = request.form.get('language') forced = True if request.form.get('forced') == 'true' else False hi = True if request.form.get('hi') == 'true' else False subFile = request.files.get('file') _, ext = os.path.splitext(subFile.filename) if ext not in SUBTITLE_EXTENSIONS: raise ValueError('A subtitle of an invalid format was uploaded.') try: result = manual_upload_subtitle(path=episodePath, language=language, forced=forced, hi=hi, title=title, scene_name=sceneName, media_type='series', subtitle=subFile, audio_language=audio_language) if result is not None: message = result[0] path = result[1] subs_path = result[2] if hi: language_code = language + ":hi" elif forced: language_code = language + ":forced" else: language_code = language provider = "manual" score = 360 history_log(4, sonarrSeriesId, sonarrEpisodeId, message, path, language_code, provider, score, subtitles_path=subs_path) if not settings.general.getboolean( 'dont_notify_manual_actions'): send_notifications(sonarrSeriesId, sonarrEpisodeId, message) store_subtitles(path, episodePath) except OSError: pass return '', 204
def _wanted_episode(episode): audio_language_list = get_audio_profile_languages(episode_id=episode['sonarrEpisodeId']) if len(audio_language_list) > 0: audio_language = audio_language_list[0]['name'] else: audio_language = 'None' languages = [] for language in ast.literal_eval(episode['missing_subtitles']): # confirm if language is still missing or if cutoff have been reached confirmed_missing_subs = TableEpisodes.select(TableEpisodes.missing_subtitles) \ .where(TableEpisodes.sonarrEpisodeId == episode['sonarrEpisodeId']) \ .dicts() \ .get_or_none() if not confirmed_missing_subs: continue if language not in ast.literal_eval(confirmed_missing_subs['missing_subtitles']): continue if is_search_active(desired_language=language, attempt_string=episode['failedAttempts']): TableEpisodes.update({TableEpisodes.failedAttempts: updateFailedAttempts(desired_language=language, attempt_string=episode['failedAttempts'])}) \ .where(TableEpisodes.sonarrEpisodeId == episode['sonarrEpisodeId']) \ .execute() hi_ = "True" if language.endswith(':hi') else "False" forced_ = "True" if language.endswith(':forced') else "False" languages.append((language.split(":")[0], hi_, forced_)) else: logging.debug( f"BAZARR Search is throttled by adaptive search for this episode {episode['path']} and " f"language: {language}") for result in generate_subtitles(path_mappings.path_replace(episode['path']), languages, audio_language, str(episode['scene_name']), episode['title'], 'series'): if result: message = result[0] path = result[1] forced = result[5] if result[8]: language_code = result[2] + ":hi" elif forced: language_code = result[2] + ":forced" else: language_code = result[2] provider = result[3] score = result[4] subs_id = result[6] subs_path = result[7] store_subtitles(episode['path'], path_mappings.path_replace(episode['path'])) history_log(1, episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message, path, language_code, provider, score, subs_id, subs_path) event_stream(type='series', action='update', payload=episode['sonarrSeriesId']) event_stream(type='episode-wanted', action='delete', payload=episode['sonarrEpisodeId']) send_notifications(episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message)
def get(self): start = request.args.get('start') or 0 length = request.args.get('length') or -1 episodeid = request.args.get('episodeid') upgradable_episodes_not_perfect = [] if settings.general.getboolean('upgrade_subs'): days_to_upgrade_subs = settings.general.days_to_upgrade_subs minimum_timestamp = ( (datetime.datetime.now() - timedelta(days=int(days_to_upgrade_subs))) - datetime.datetime(1970, 1, 1)).total_seconds() if settings.general.getboolean('upgrade_manual'): query_actions = [1, 2, 3, 6] else: query_actions = [1, 3] upgradable_episodes_conditions = [ (TableHistory.action.in_(query_actions)), (TableHistory.timestamp > minimum_timestamp), (TableHistory.score is not None) ] upgradable_episodes_conditions += get_exclusion_clause('series') upgradable_episodes = TableHistory.select(TableHistory.video_path, fn.MAX(TableHistory.timestamp).alias('timestamp'), TableHistory.score, TableShows.tags, TableEpisodes.monitored, TableShows.seriesType)\ .join(TableEpisodes, on=(TableHistory.sonarrEpisodeId == TableEpisodes.sonarrEpisodeId))\ .join(TableShows, on=(TableHistory.sonarrSeriesId == TableShows.sonarrSeriesId))\ .where(reduce(operator.and_, upgradable_episodes_conditions))\ .group_by(TableHistory.video_path)\ .dicts() upgradable_episodes = list(upgradable_episodes) for upgradable_episode in upgradable_episodes: if upgradable_episode['timestamp'] > minimum_timestamp: try: int(upgradable_episode['score']) except ValueError: pass else: if int(upgradable_episode['score']) < 360: upgradable_episodes_not_perfect.append( upgradable_episode) query_conditions = [(TableEpisodes.title is not None)] if episodeid: query_conditions.append( (TableEpisodes.sonarrEpisodeId == episodeid)) query_condition = reduce(operator.and_, query_conditions) episode_history = TableHistory.select(TableHistory.id, TableShows.title.alias('seriesTitle'), TableEpisodes.monitored, TableEpisodes.season.concat('x').concat(TableEpisodes.episode).alias('episode_number'), TableEpisodes.title.alias('episodeTitle'), TableHistory.timestamp, TableHistory.subs_id, TableHistory.description, TableHistory.sonarrSeriesId, TableEpisodes.path, TableHistory.language, TableHistory.score, TableShows.tags, TableHistory.action, TableHistory.subtitles_path, TableHistory.sonarrEpisodeId, TableHistory.provider, TableShows.seriesType)\ .join(TableShows, on=(TableHistory.sonarrSeriesId == TableShows.sonarrSeriesId))\ .join(TableEpisodes, on=(TableHistory.sonarrEpisodeId == TableEpisodes.sonarrEpisodeId))\ .where(query_condition)\ .order_by(TableHistory.timestamp.desc())\ .limit(length)\ .offset(start)\ .dicts() episode_history = list(episode_history) blacklist_db = TableBlacklist.select(TableBlacklist.provider, TableBlacklist.subs_id).dicts() blacklist_db = list(blacklist_db) for item in episode_history: # Mark episode as upgradable or not item.update({"upgradable": False}) if { "video_path": str(item['path']), "timestamp": float(item['timestamp']), "score": str(item['score']), "tags": str(item['tags']), "monitored": str(item['monitored']), "seriesType": str(item['seriesType']) } in upgradable_episodes_not_perfect: if os.path.isfile( path_mappings.path_replace(item['subtitles_path'])): item.update({"upgradable": True}) del item['path'] postprocessEpisode(item) if item['score']: item['score'] = str(round( (int(item['score']) * 100 / 360), 2)) + "%" # Make timestamp pretty if item['timestamp']: item["raw_timestamp"] = int(item['timestamp']) item["parsed_timestamp"] = datetime.datetime.fromtimestamp( int(item['timestamp'])).strftime('%x %X') item['timestamp'] = pretty.date(item["raw_timestamp"]) # Check if subtitles is blacklisted item.update({"blacklisted": False}) if item['action'] not in [0, 4, 5]: for blacklisted_item in blacklist_db: if blacklisted_item['provider'] == item['provider'] and \ blacklisted_item['subs_id'] == item['subs_id']: item.update({"blacklisted": True}) break count = TableHistory.select()\ .join(TableEpisodes, on=(TableHistory.sonarrEpisodeId == TableEpisodes.sonarrEpisodeId))\ .where(TableEpisodes.title is not None).count() return jsonify(data=episode_history, total=count)