def check_radarr_rootfolder(): get_radarr_rootfolder() rootfolder = TableMoviesRootfolder.select(TableMoviesRootfolder.id, TableMoviesRootfolder.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_movie(root_path)): TableMoviesRootfolder.update({TableMoviesRootfolder.accessible: 0, TableMoviesRootfolder.error: 'This Radarr root directory does not seems to ' 'be accessible by Bazarr. Please check path ' 'mapping.'}) \ .where(TableMoviesRootfolder.id == item['id']) \ .execute() elif not os.access(path_mappings.path_replace_movie(root_path), os.W_OK): TableMoviesRootfolder.update({TableMoviesRootfolder.accessible: 0, TableMoviesRootfolder.error: 'Bazarr cannot write to this directory'}) \ .where(TableMoviesRootfolder.id == item['id']) \ .execute() else: TableMoviesRootfolder.update({TableMoviesRootfolder.accessible: 1, TableMoviesRootfolder.error: ''}) \ .where(TableMoviesRootfolder.id == item['id']) \ .execute()
def path_replace_movie(self, values_dict): if type(values_dict) is list: for item in values_dict: item['path'] = path_mappings.path_replace_movie(item['path']) elif type(values_dict) is dict: values_dict['path'] = path_mappings.path_replace_movie(values_dict['path']) else: return path_mappings.path_replace_movie(values_dict)
def delete(self): # Delete radarrId = request.args.get('radarrid') movieInfo = TableMovies.select(TableMovies.path) \ .where(TableMovies.radarrId == radarrId) \ .dicts() \ .get_or_none() if not movieInfo: return 'Movie not found', 500 moviePath = path_mappings.path_replace_movie(movieInfo['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_movie(subtitlesPath) result = delete_subtitles(media_type='movie', language=language, forced=forced, hi=hi, media_path=moviePath, subtitles_path=subtitlesPath, radarr_id=radarrId) if result: return '', 202 else: return '', 204
def post(self): radarr_id = int(request.args.get('radarrid')) provider = request.form.get('provider') subs_id = request.form.get('subs_id') language = request.form.get('language') # TODO forced = False hi = False data = TableMovies.select(TableMovies.path).where(TableMovies.radarrId == radarr_id).dicts().get() media_path = data['path'] subtitles_path = request.form.get('subtitles_path') blacklist_log_movie(radarr_id=radarr_id, provider=provider, subs_id=subs_id, language=language) delete_subtitles(media_type='movie', language=language, forced=forced, hi=hi, media_path=path_mappings.path_replace_movie(media_path), subtitles_path=subtitles_path, radarr_id=radarr_id) movies_download_subtitles(radarr_id) event_stream(type='movie-history') return '', 200
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 get(self): # Manual Search radarrId = request.args.get('radarrid') movieInfo = TableMovies.select(TableMovies.title, TableMovies.path, TableMovies.sceneName, TableMovies.profileId) \ .where(TableMovies.radarrId == radarrId) \ .dicts() \ .get_or_none() if not movieInfo: return 'Movie not found', 500 title = movieInfo['title'] moviePath = path_mappings.path_replace_movie(movieInfo['path']) sceneName = movieInfo['sceneName'] or "None" profileId = movieInfo['profileId'] providers_list = get_providers() data = manual_search(moviePath, profileId, providers_list, sceneName, title, 'movie') if not data: data = [] return jsonify(data=data)
def movies_full_scan_subtitles(): movies = database.execute("SELECT path FROM table_movies") for i, movie in enumerate(movies, 1): store_subtitles_movie(movie['path'], path_mappings.path_replace_movie(movie['path'])) gc.collect()
def movies_scan_subtitles(no): movies = TableMovies.select(TableMovies.path)\ .where(TableMovies.radarrId == no)\ .order_by(TableMovies.radarrId)\ .dicts() for movie in movies: store_subtitles_movie(movie['path'], path_mappings.path_replace_movie(movie['path']), use_cache=False)
def movies_scan_subtitles(no): movies = database.execute( "SELECT path FROM table_movies WHERE radarrId=? ORDER BY radarrId", (no, )) for movie in movies: store_subtitles_movie(movie['path'], path_mappings.path_replace_movie(movie['path']))
def post(self): # Manual Download radarrId = request.args.get('radarrid') movieInfo = TableMovies.select(TableMovies.title, TableMovies.path, TableMovies.sceneName, TableMovies.audio_language) \ .where(TableMovies.radarrId == radarrId) \ .dicts() \ .get() title = movieInfo['title'] moviePath = path_mappings.path_replace_movie(movieInfo['path']) sceneName = movieInfo['sceneName'] if sceneName is None: sceneName = "None" audio_language = movieInfo['audio_language'] 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(movie_id=radarrId) if len(audio_language_list) > 0: audio_language = audio_language_list[0]['name'] else: audio_language = 'None' try: result = manual_download_subtitle(moviePath, language, audio_language, hi, forced, subtitle, selected_provider, providers_auth, sceneName, title, 'movie', profile_id=get_profile_id(movie_id=radarrId)) 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_movie(2, radarrId, message, path, language_code, provider, score, subs_id, subs_path) if not settings.general.getboolean('dont_notify_manual_actions'): send_notifications_movie(radarrId, message) store_subtitles_movie(path, moviePath) except OSError: pass return '', 204
def movies_full_scan_subtitles(): use_ffprobe_cache = settings.radarr.getboolean('use_ffprobe_cache') movies = TableMovies.select(TableMovies.path).dicts() count_movies = len(movies) for i, movie in enumerate(movies): show_progress(id='movies_disk_scan', header='Full disk scan...', name='Movies subtitles', value=i, count=count_movies) store_subtitles_movie(movie['path'], path_mappings.path_replace_movie(movie['path']), use_cache=use_ffprobe_cache) hide_progress(id='movies_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 postprocessMovie(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(movie_id=item['radarrId']) # Parse alternate titles if 'alternativeTitles' in item: if item['alternativeTitles'] is None: item['alternativeTitles'] = [] else: item['alternativeTitles'] = ast.literal_eval(item['alternativeTitles']) # Parse failed attempts if 'failedAttempts' in item: if item['failedAttempts']: item['failedAttempts'] = ast.literal_eval(item['failedAttempts']) # Parse subtitles if 'subtitles' in item: if item['subtitles'] is None: item['subtitles'] = [] else: item['subtitles'] = ast.literal_eval(item['subtitles']) for i, subs in enumerate(item['subtitles']): language = subs[0].split(':') item['subtitles'][i] = {"path": path_mappings.path_replace_movie(subs[1]), "name": language_from_alpha2(language[0]), "code2": language[0], "code3": alpha3_from_alpha2(language[0]), "forced": False, "hi": False} if len(language) > 1: item['subtitles'][i].update({ "forced": True if language[1] == 'forced' else False, "hi": True if language[1] == 'hi' else False }) if settings.general.getboolean('embedded_subs_show_desired'): desired_lang_list = get_desired_languages(item['profileId']) item['subtitles'] = [x for x in item['subtitles'] if x['code2'] in desired_lang_list or x['path']] item['subtitles'] = sorted(item['subtitles'], key=itemgetter('name', 'forced')) # 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']): language = subs.split(':') item['missing_subtitles'][i] = {"name": language_from_alpha2(language[0]), "code2": language[0], "code3": alpha3_from_alpha2(language[0]), "forced": False, "hi": False} if len(language) > 1: item['missing_subtitles'][i].update({ "forced": True if language[1] == 'forced' else False, "hi": True if language[1] == 'hi' else False }) # Provide mapped path if 'path' in item: if item['path']: item['path'] = path_mappings.path_replace_movie(item['path']) if 'subtitles_path' in item: # Provide mapped subtitles path item['subtitles_path'] = path_mappings.path_replace_movie(item['subtitles_path']) # map poster and fanart to server proxy if 'poster' in item: poster = item['poster'] item['poster'] = f"{base_url}/images/movies{poster}" if poster else None if 'fanart' in item: fanart = item['fanart'] item['fanart'] = f"{base_url}/images/movies{fanart}" if fanart else None
def _wanted_movie(movie): audio_language_list = get_audio_profile_languages(movie_id=movie['radarrId']) if len(audio_language_list) > 0: audio_language = audio_language_list[0]['name'] else: audio_language = 'None' languages = [] for language in ast.literal_eval(movie['missing_subtitles']): # confirm if language is still missing or if cutoff have been reached confirmed_missing_subs = TableMovies.select(TableMovies.missing_subtitles) \ .where(TableMovies.radarrId == movie['radarrId']) \ .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=movie['failedAttempts']): TableMovies.update({TableMovies.failedAttempts: updateFailedAttempts(desired_language=language, attempt_string=movie['failedAttempts'])}) \ .where(TableMovies.radarrId == movie['radarrId']) \ .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.info(f"BAZARR Search is throttled by adaptive search for this movie {movie['path']} and " f"language: {language}") for result in generate_subtitles(path_mappings.path_replace_movie(movie['path']), languages, audio_language, str(movie['sceneName']), movie['title'], 'movie'): 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_movie(movie['path'], path_mappings.path_replace_movie(movie['path'])) history_log_movie(1, movie['radarrId'], message, path, language_code, provider, score, subs_id, subs_path) event_stream(type='movie-wanted', action='delete', payload=movie['radarrId']) send_notifications_movie(movie['radarrId'], message)
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 movies_download_subtitles(no): conditions = [(TableMovies.radarrId == no)] conditions += get_exclusion_clause('movie') movies = TableMovies.select(TableMovies.path, TableMovies.missing_subtitles, TableMovies.audio_language, TableMovies.radarrId, TableMovies.sceneName, TableMovies.title, TableMovies.tags, TableMovies.monitored)\ .where(reduce(operator.and_, conditions))\ .dicts() if not len(movies): logging.debug( "BAZARR no movie with that radarrId can be found in database:", str(no)) return else: movie = movies[0] if ast.literal_eval(movie['missing_subtitles']): count_movie = len(ast.literal_eval(movie['missing_subtitles'])) else: count_movie = 0 audio_language_list = get_audio_profile_languages( movie_id=movie['radarrId']) if len(audio_language_list) > 0: audio_language = audio_language_list[0]['name'] else: audio_language = 'None' languages = [] providers_list = None for i, language in enumerate(ast.literal_eval(movie['missing_subtitles'])): providers_list = get_providers() 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 providers_list: # confirm if language is still missing or if cutoff have been reached confirmed_missing_subs = TableMovies.select(TableMovies.missing_subtitles) \ .where(TableMovies.radarrId == movie['radarrId']) \ .dicts() \ .get_or_none() if not confirmed_missing_subs: continue if language not in ast.literal_eval( confirmed_missing_subs['missing_subtitles']): continue show_progress(id='movie_search_progress_{}'.format(no), header='Searching missing subtitles...', name=movie['title'], value=i, count=count_movie) if providers_list: for result in generate_subtitles( path_mappings.path_replace_movie(movie['path']), languages, audio_language, str(movie['sceneName']), movie['title'], 'movie'): 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_movie( movie['path'], path_mappings.path_replace_movie(movie['path'])) history_log_movie(1, no, message, path, language_code, provider, score, subs_id, subs_path) send_notifications_movie(no, message) else: logging.info("BAZARR All providers are throttled") hide_progress(id='movie_search_progress_{}'.format(no))
def get(self): start = request.args.get('start') or 0 length = request.args.get('length') or -1 radarrid = request.args.get('radarrid') upgradable_movies = [] upgradable_movies_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_movies_conditions = [ (TableHistoryMovie.action.in_(query_actions)), (TableHistoryMovie.timestamp > minimum_timestamp), (TableHistoryMovie.score.is_null(False)) ] upgradable_movies_conditions += get_exclusion_clause('movie') upgradable_movies = TableHistoryMovie.select(TableHistoryMovie.video_path, fn.MAX(TableHistoryMovie.timestamp).alias('timestamp'), TableHistoryMovie.score, TableMovies.tags, TableMovies.monitored)\ .join(TableMovies, on=(TableHistoryMovie.radarrId == TableMovies.radarrId))\ .where(reduce(operator.and_, upgradable_movies_conditions))\ .group_by(TableHistoryMovie.video_path)\ .dicts() upgradable_movies = list(upgradable_movies) for upgradable_movie in upgradable_movies: if upgradable_movie['timestamp'] > minimum_timestamp: try: int(upgradable_movie['score']) except ValueError: pass else: if int(upgradable_movie['score']) < 120: upgradable_movies_not_perfect.append( upgradable_movie) query_conditions = [(TableMovies.title.is_null(False))] if radarrid: query_conditions.append((TableMovies.radarrId == radarrid)) query_condition = reduce(operator.and_, query_conditions) movie_history = TableHistoryMovie.select(TableHistoryMovie.id, TableHistoryMovie.action, TableMovies.title, TableHistoryMovie.timestamp, TableHistoryMovie.description, TableHistoryMovie.radarrId, TableMovies.monitored, TableHistoryMovie.video_path.alias('path'), TableHistoryMovie.language, TableMovies.tags, TableHistoryMovie.score, TableHistoryMovie.subs_id, TableHistoryMovie.provider, TableHistoryMovie.subtitles_path)\ .join(TableMovies, on=(TableHistoryMovie.radarrId == TableMovies.radarrId))\ .where(query_condition)\ .order_by(TableHistoryMovie.timestamp.desc())\ .limit(length)\ .offset(start)\ .dicts() movie_history = list(movie_history) blacklist_db = TableBlacklistMovie.select( TableBlacklistMovie.provider, TableBlacklistMovie.subs_id).dicts() blacklist_db = list(blacklist_db) for item in movie_history: # Mark movies 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']) } in upgradable_movies_not_perfect: # noqa: E129 if os.path.isfile( path_mappings.path_replace_movie( item['subtitles_path'])): item.update({"upgradable": True}) del item['path'] postprocessMovie(item) if item['score']: item['score'] = str(round( (int(item['score']) * 100 / 120), 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']: # noqa: E125 item.update({"blacklisted": True}) break count = TableHistoryMovie.select()\ .join(TableMovies, on=(TableHistoryMovie.radarrId == TableMovies.radarrId))\ .where(TableMovies.title.is_null(False))\ .count() return jsonify(data=movie_history, total=count)
def post(self): # Upload # TODO: Support Multiply Upload radarrId = request.args.get('radarrid') movieInfo = TableMovies.select(TableMovies.title, TableMovies.path, TableMovies.sceneName, TableMovies.audio_language) \ .where(TableMovies.radarrId == radarrId) \ .dicts() \ .get_or_none() if not movieInfo: return 'Movie not found', 500 moviePath = path_mappings.path_replace_movie(movieInfo['path']) sceneName = movieInfo['sceneName'] or 'None' title = movieInfo['title'] audioLanguage = movieInfo['audio_language'] 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=moviePath, language=language, forced=forced, hi=hi, title=title, scene_name=sceneName, media_type='movie', subtitle=subFile, audio_language=audioLanguage) if not result: logging.debug( f"BAZARR unable to process subtitles for this movie: {moviePath}" ) else: 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 = 120 history_log_movie(4, radarrId, message, path, language_code, provider, score, subtitles_path=subs_path) if not settings.general.getboolean( 'dont_notify_manual_actions'): send_notifications_movie(radarrId, message) store_subtitles_movie(path, moviePath) except OSError: pass return '', 204
def patch(self): # Download radarrId = request.args.get('radarrid') movieInfo = TableMovies.select(TableMovies.title, TableMovies.path, TableMovies.sceneName, TableMovies.audio_language)\ .where(TableMovies.radarrId == radarrId)\ .dicts()\ .get_or_none() if not movieInfo: return 'Movie not found', 500 moviePath = path_mappings.path_replace_movie(movieInfo['path']) sceneName = movieInfo['sceneName'] or 'None' title = movieInfo['title'] audio_language = movieInfo['audio_language'] language = request.form.get('language') hi = request.form.get('hi').capitalize() forced = request.form.get('forced').capitalize() audio_language_list = get_audio_profile_languages(movie_id=radarrId) if len(audio_language_list) > 0: audio_language = audio_language_list[0]['name'] else: audio_language = None try: result = list( generate_subtitles( moviePath, [(language, hi, forced)], audio_language, sceneName, title, 'movie', profile_id=get_profile_id(movie_id=radarrId))) 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_movie(1, radarrId, message, path, language_code, provider, score, subs_id, subs_path) send_notifications_movie(radarrId, message) store_subtitles_movie(path, moviePath) else: event_stream(type='movie', payload=radarrId) except OSError: pass return '', 204
def update_one_movie(movie_id, action): logging.debug( 'BAZARR syncing this specific movie from Radarr: {}'.format(movie_id)) # Check if there's a row in database for this movie ID existing_movie = TableMovies.select(TableMovies.path)\ .where(TableMovies.radarrId == movie_id)\ .dicts()\ .get_or_none() # Remove movie from DB if action == 'deleted': if existing_movie: try: TableMovies.delete().where( TableMovies.radarrId == movie_id).execute() except Exception as e: logging.error( f"BAZARR cannot delete movie {existing_movie['path']} because of {e}" ) else: event_stream(type='movie', action='delete', payload=int(movie_id)) logging.debug( 'BAZARR deleted this movie from the database:{}'.format( path_mappings.path_replace_movie( existing_movie['path']))) return movie_default_enabled = settings.general.getboolean( 'movie_default_enabled') if movie_default_enabled is True: movie_default_profile = settings.general.movie_default_profile if movie_default_profile == '': movie_default_profile = None else: movie_default_profile = None audio_profiles = get_profile_list() tagsDict = get_tags() try: # Get movie data from radarr api movie = None movie_data = get_movies_from_radarr_api( url=url_radarr(), apikey_radarr=settings.radarr.apikey, radarr_id=movie_id) if not movie_data: return else: if action == 'updated' and existing_movie: movie = movieParser( movie_data, action='update', tags_dict=tagsDict, movie_default_profile=movie_default_profile, audio_profiles=audio_profiles) elif action == 'updated' and not existing_movie: movie = movieParser( movie_data, action='insert', tags_dict=tagsDict, movie_default_profile=movie_default_profile, audio_profiles=audio_profiles) except Exception: logging.debug( 'BAZARR cannot get movie returned by SignalR feed from Radarr API.' ) return # Drop useless events if not movie and not existing_movie: return # Remove movie from DB if not movie and existing_movie: try: TableMovies.delete().where( TableMovies.radarrId == movie_id).execute() except Exception as e: logging.error( f"BAZARR cannot insert episode {existing_movie['path']} because of {e}" ) else: event_stream(type='movie', action='delete', payload=int(movie_id)) logging.debug( 'BAZARR deleted this movie from the database:{}'.format( path_mappings.path_replace_movie(existing_movie['path']))) return # Update existing movie in DB elif movie and existing_movie: try: TableMovies.update(movie).where( TableMovies.radarrId == movie['radarrId']).execute() except IntegrityError as e: logging.error( f"BAZARR cannot insert episode {movie['path']} because of {e}") else: event_stream(type='movie', action='update', payload=int(movie_id)) logging.debug( 'BAZARR updated this movie into the database:{}'.format( path_mappings.path_replace_movie(movie['path']))) # Insert new movie in DB elif movie and not existing_movie: try: TableMovies.insert(movie).on_conflict(action='IGNORE').execute() except IntegrityError as e: logging.error( f"BAZARR cannot insert movie {movie['path']} because of {e}") else: event_stream(type='movie', action='update', payload=int(movie_id)) logging.debug( 'BAZARR inserted this movie into the database:{}'.format( path_mappings.path_replace_movie(movie['path']))) # Storing existing subtitles logging.debug('BAZARR storing subtitles for this movie: {}'.format( path_mappings.path_replace_movie(movie['path']))) store_subtitles_movie(movie['path'], path_mappings.path_replace_movie(movie['path'])) # Downloading missing subtitles logging.debug( 'BAZARR downloading missing subtitles for this movie: {}'.format( path_mappings.path_replace_movie(movie['path']))) movies_download_subtitles(movie_id)
def update_movies(): logging.debug('BAZARR Starting movie sync from Radarr.') apikey_radarr = settings.radarr.apikey radarr_version = get_radarr_version() movie_default_enabled = settings.general.getboolean( 'movie_default_enabled') if movie_default_enabled is True: movie_default_language = settings.general.movie_default_language movie_default_hi = settings.general.movie_default_hi movie_default_forced = settings.general.movie_default_forced else: movie_default_language = '[]' movie_default_hi = 'False' movie_default_forced = 'False' if apikey_radarr is None: pass else: audio_profiles = get_profile_list() tagsDict = get_tags() # Get movies data from radarr if radarr_version.startswith('0'): url_radarr_api_movies = url_radarr( ) + "/api/movie?apikey=" + apikey_radarr else: url_radarr_api_movies = url_radarr( ) + "/api/v3/movie?apikey=" + apikey_radarr try: r = requests.get(url_radarr_api_movies, timeout=60, verify=False) r.raise_for_status() except requests.exceptions.HTTPError as errh: logging.exception( "BAZARR Error trying to get movies from Radarr. Http error.") return except requests.exceptions.ConnectionError as errc: logging.exception( "BAZARR Error trying to get movies from Radarr. Connection Error." ) return except requests.exceptions.Timeout as errt: logging.exception( "BAZARR Error trying to get movies from Radarr. Timeout Error." ) return except requests.exceptions.RequestException as err: logging.exception("BAZARR Error trying to get movies from Radarr.") return else: # Get current movies in DB current_movies_db = database.execute( "SELECT tmdbId, path, radarrId FROM table_movies") current_movies_db_list = [x['tmdbId'] for x in current_movies_db] current_movies_radarr = [] movies_to_update = [] movies_to_add = [] altered_movies = [] moviesIdListLength = len(r.json()) for i, movie in enumerate(r.json(), 1): if movie['hasFile'] is True: if 'movieFile' in movie: # Detect file separator if movie['path'][0] == "/": separator = "/" else: separator = "\\" if movie["path"] != None and movie['movieFile'][ 'relativePath'] != None: try: overview = str(movie['overview']) except: overview = "" try: poster_big = movie['images'][0]['url'] poster = os.path.splitext( poster_big)[0] + '-500' + os.path.splitext( poster_big)[1] except: poster = "" try: fanart = movie['images'][1]['url'] except: fanart = "" if 'sceneName' in movie['movieFile']: sceneName = movie['movieFile']['sceneName'] else: sceneName = None alternativeTitles = None if radarr_version.startswith('0'): if 'alternativeTitles' in movie: alternativeTitles = str([ item['title'] for item in movie['alternativeTitles'] ]) else: if 'alternateTitles' in movie: alternativeTitles = str([ item['title'] for item in movie['alternateTitles'] ]) if 'imdbId' in movie: imdbId = movie['imdbId'] else: imdbId = None try: format, resolution = movie['movieFile'][ 'quality']['quality']['name'].split('-') except: format = movie['movieFile']['quality'][ 'quality']['name'] try: resolution = str( movie['movieFile']['quality'] ['quality']['resolution']) + 'p' except: resolution = None if 'mediaInfo' in movie['movieFile']: videoFormat = videoCodecID = videoProfile = videoCodecLibrary = None if radarr_version.startswith('0'): if 'videoFormat' in movie['movieFile'][ 'mediaInfo']: videoFormat = movie['movieFile'][ 'mediaInfo']['videoFormat'] else: if 'videoCodec' in movie['movieFile'][ 'mediaInfo']: videoFormat = movie['movieFile'][ 'mediaInfo']['videoCodec'] if 'videoCodecID' in movie['movieFile'][ 'mediaInfo']: videoCodecID = movie['movieFile'][ 'mediaInfo']['videoCodecID'] if 'videoProfile' in movie['movieFile'][ 'mediaInfo']: videoProfile = movie['movieFile'][ 'mediaInfo']['videoProfile'] if 'videoCodecLibrary' in movie['movieFile'][ 'mediaInfo']: videoCodecLibrary = movie['movieFile'][ 'mediaInfo']['videoCodecLibrary'] videoCodec = RadarrFormatVideoCodec( videoFormat, videoCodecID, videoCodecLibrary) audioFormat = audioCodecID = audioProfile = audioAdditionalFeatures = None if radarr_version.startswith('0'): if 'audioFormat' in movie['movieFile'][ 'mediaInfo']: audioFormat = movie['movieFile'][ 'mediaInfo']['audioFormat'] else: if 'audioCodec' in movie['movieFile'][ 'mediaInfo']: audioFormat = movie['movieFile'][ 'mediaInfo']['audioCodec'] if 'audioCodecID' in movie['movieFile'][ 'mediaInfo']: audioCodecID = movie['movieFile'][ 'mediaInfo']['audioCodecID'] if 'audioProfile' in movie['movieFile'][ 'mediaInfo']: audioProfile = movie['movieFile'][ 'mediaInfo']['audioProfile'] if 'audioAdditionalFeatures' in movie[ 'movieFile']['mediaInfo']: audioAdditionalFeatures = movie[ 'movieFile']['mediaInfo'][ 'audioAdditionalFeatures'] audioCodec = RadarrFormatAudioCodec( audioFormat, audioCodecID, audioProfile, audioAdditionalFeatures) else: videoCodec = None audioCodec = None audio_language = None if radarr_version.startswith('0'): if 'mediaInfo' in movie['movieFile']: if 'audioLanguages' in movie['movieFile'][ 'mediaInfo']: audio_language_list = movie[ 'movieFile']['mediaInfo'][ 'audioLanguages'].split('/') if len(audio_language_list): audio_language = audio_language_list[ 0].strip() if not audio_language: audio_language = profile_id_to_language( movie['qualityProfileId'], audio_profiles) else: if 'languages' in movie['movieFile'] and len( movie['movieFile']['languages']): for item in movie['movieFile'][ 'languages']: if isinstance(item, dict): if 'name' in item: audio_language = item['name'] break tags = [ d['label'] for d in tagsDict if d['id'] in movie['tags'] ] # Add movies in radarr to current movies list current_movies_radarr.append(str(movie['tmdbId'])) if str(movie['tmdbId']) in current_movies_db_list: movies_to_update.append({ 'radarrId': int(movie["id"]), 'title': movie["title"], 'path': movie["path"] + separator + movie['movieFile']['relativePath'], 'tmdbId': str(movie["tmdbId"]), 'poster': poster, 'fanart': fanart, 'audio_language': audio_language, 'sceneName': sceneName, 'monitored': str(bool(movie['monitored'])), 'year': str(movie['year']), 'sortTitle': movie['sortTitle'], 'alternativeTitles': alternativeTitles, 'format': format, 'resolution': resolution, 'video_codec': videoCodec, 'audio_codec': audioCodec, 'overview': overview, 'imdbId': imdbId, 'movie_file_id': int(movie['movieFile']['id']), 'tags': str(tags) }) else: movies_to_add.append({ 'radarrId': int(movie["id"]), 'title': movie["title"], 'path': movie["path"] + separator + movie['movieFile']['relativePath'], 'tmdbId': str(movie["tmdbId"]), 'languages': movie_default_language, 'subtitles': '[]', 'hearing_impaired': movie_default_hi, 'overview': overview, 'poster': poster, 'fanart': fanart, 'audio_language': audio_language, 'sceneName': sceneName, 'monitored': str(bool(movie['monitored'])), 'sortTitle': movie['sortTitle'], 'year': str(movie['year']), 'alternativeTitles': alternativeTitles, 'format': format, 'resolution': resolution, 'video_codec': videoCodec, 'audio_codec': audioCodec, 'imdbId': imdbId, 'forced': movie_default_forced, 'movie_file_id': int(movie['movieFile']['id']), 'tags': str(tags) }) else: logging.error( 'BAZARR Radarr returned a movie without a file path: ' + movie["path"] + separator + movie['movieFile']['relativePath']) # Remove old movies from DB removed_movies = list( set(current_movies_db_list) - set(current_movies_radarr)) for removed_movie in removed_movies: database.execute("DELETE FROM table_movies WHERE tmdbId=?", (removed_movie, )) # Update movies in DB movies_in_db_list = [] movies_in_db = database.execute( "SELECT radarrId, title, path, tmdbId, overview, poster, fanart, " "audio_language, sceneName, monitored, sortTitle, year, " "alternativeTitles, format, resolution, video_codec, audio_codec, imdbId," "movie_file_id, tags FROM table_movies") for item in movies_in_db: movies_in_db_list.append(item) movies_to_update_list = [ i for i in movies_to_update if i not in movies_in_db_list ] for updated_movie in movies_to_update_list: query = dict_converter.convert(updated_movie) database.execute( '''UPDATE table_movies SET ''' + query.keys_update + ''' WHERE tmdbId = ?''', query.values + (updated_movie['tmdbId'], )) altered_movies.append([ updated_movie['tmdbId'], updated_movie['path'], updated_movie['radarrId'], updated_movie['monitored'] ]) # Insert new movies in DB for added_movie in movies_to_add: query = dict_converter.convert(added_movie) result = database.execute( '''INSERT OR IGNORE INTO table_movies(''' + query.keys_insert + ''') VALUES(''' + query.question_marks + ''')''', query.values) if result > 0: altered_movies.append([ added_movie['tmdbId'], added_movie['path'], added_movie['radarrId'], added_movie['monitored'] ]) else: logging.debug( 'BAZARR unable to insert this movie into the database:', path_mappings.path_replace_movie(added_movie['path'])) # Store subtitles for added or modified movies for i, altered_movie in enumerate(altered_movies, 1): store_subtitles_movie( altered_movie[1], path_mappings.path_replace_movie(altered_movie[1])) logging.debug( 'BAZARR All movies synced from Radarr into database.') # Search for desired subtitles if no more than 5 movies have been added. if len(altered_movies) <= 5: logging.debug( "BAZARR No more than 5 movies were added during this sync then we'll search for subtitles." ) for altered_movie in altered_movies: data = database.execute( "SELECT * FROM table_movies WHERE radarrId = ?" + get_exclusion_clause('movie'), (altered_movie[2], ), only_one=True) if data: movies_download_subtitles(data['radarrId']) else: logging.debug( "BAZARR skipping download for this movie as it is excluded." ) else: logging.debug( "BAZARR More than 5 movies were added during this sync then we wont search for subtitles." )
def update_movies(send_event=True): check_radarr_rootfolder() logging.debug('BAZARR Starting movie sync from Radarr.') apikey_radarr = settings.radarr.apikey movie_default_enabled = settings.general.getboolean( 'movie_default_enabled') if movie_default_enabled is True: movie_default_profile = settings.general.movie_default_profile if movie_default_profile == '': movie_default_profile = None else: movie_default_profile = None if apikey_radarr is None: pass else: audio_profiles = get_profile_list() tagsDict = get_tags() # Get movies data from radarr movies = get_movies_from_radarr_api(url=url_radarr(), apikey_radarr=apikey_radarr) if not movies: return else: # Get current movies in DB current_movies_db = TableMovies.select( TableMovies.tmdbId, TableMovies.path, TableMovies.radarrId).dicts() current_movies_db_list = [x['tmdbId'] for x in current_movies_db] current_movies_radarr = [] movies_to_update = [] movies_to_add = [] altered_movies = [] # Build new and updated movies movies_count = len(movies) for i, movie in enumerate(movies): if send_event: show_progress(id='movies_progress', header='Syncing movies...', name=movie['title'], value=i, count=movies_count) if movie['hasFile'] is True: if 'movieFile' in movie: if movie['movieFile']['size'] > 20480: # Add movies in radarr to current movies list current_movies_radarr.append(str(movie['tmdbId'])) if str(movie['tmdbId']) in current_movies_db_list: movies_to_update.append( movieParser(movie, action='update', tags_dict=tagsDict, movie_default_profile= movie_default_profile, audio_profiles=audio_profiles)) else: movies_to_add.append( movieParser(movie, action='insert', tags_dict=tagsDict, movie_default_profile= movie_default_profile, audio_profiles=audio_profiles)) if send_event: hide_progress(id='movies_progress') # Remove old movies from DB removed_movies = list( set(current_movies_db_list) - set(current_movies_radarr)) for removed_movie in removed_movies: try: TableMovies.delete().where( TableMovies.tmdbId == removed_movie).execute() except Exception as e: logging.error( f"BAZARR cannot remove movie tmdbId {removed_movie} because of {e}" ) continue # Update movies in DB movies_in_db_list = [] movies_in_db = TableMovies.select( TableMovies.radarrId, TableMovies.title, TableMovies.path, TableMovies.tmdbId, TableMovies.overview, TableMovies.poster, TableMovies.fanart, TableMovies.audio_language, TableMovies.sceneName, TableMovies.monitored, TableMovies.sortTitle, TableMovies.year, TableMovies.alternativeTitles, TableMovies.format, TableMovies.resolution, TableMovies.video_codec, TableMovies.audio_codec, TableMovies.imdbId, TableMovies.movie_file_id, TableMovies.tags, TableMovies.file_size).dicts() for item in movies_in_db: movies_in_db_list.append(item) movies_to_update_list = [ i for i in movies_to_update if i not in movies_in_db_list ] for updated_movie in movies_to_update_list: try: TableMovies.update(updated_movie).where( TableMovies.tmdbId == updated_movie['tmdbId']).execute() except IntegrityError as e: logging.error( f"BAZARR cannot update movie {updated_movie['path']} because of {e}" ) continue else: altered_movies.append([ updated_movie['tmdbId'], updated_movie['path'], updated_movie['radarrId'], updated_movie['monitored'] ]) # Insert new movies in DB for added_movie in movies_to_add: try: result = TableMovies.insert(added_movie).on_conflict( action='IGNORE').execute() except IntegrityError as e: logging.error( f"BAZARR cannot insert movie {added_movie['path']} because of {e}" ) continue else: if result > 0: altered_movies.append([ added_movie['tmdbId'], added_movie['path'], added_movie['radarrId'], added_movie['monitored'] ]) if send_event: event_stream(type='movie', action='update', payload=int(added_movie['radarrId'])) else: logging.debug( 'BAZARR unable to insert this movie into the database:', path_mappings.path_replace_movie( added_movie['path'])) # Store subtitles for added or modified movies for i, altered_movie in enumerate(altered_movies, 1): store_subtitles_movie( altered_movie[1], path_mappings.path_replace_movie(altered_movie[1])) logging.debug( 'BAZARR All movies synced from Radarr into database.')