From 2f208236acdb0d764f27bbd977697b12f659c1cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20V=C3=A9zina?= <5130500+morpheus65535@users.noreply.github.com> Date: Tue, 10 Dec 2019 22:20:42 -0500 Subject: [PATCH 1/9] First endpoint for the new API --- bazarr/api.py | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 bazarr/api.py diff --git a/bazarr/api.py b/bazarr/api.py new file mode 100644 index 000000000..f80d47fea --- /dev/null +++ b/bazarr/api.py @@ -0,0 +1,51 @@ +from flask import Flask, jsonify, request +from flask_restful import Resource, Api + +app = Flask(__name__) +api = Api(app) + +import os +import ast +import libs + +from get_args import args +from config import settings, url_sonarr, url_radarr, url_radarr_short, url_sonarr_short, base_url + +from init import * +import logging +from database import database, dict_mapper +from helper import path_replace, path_replace_reverse, path_replace_movie, path_replace_reverse_movie +from get_languages import alpha2_from_language, alpha3_from_language + +class Series(Resource): + def get(self): + seriesId = request.args.get('id') + if seriesId: + result = database.execute("SELECT * FROM table_shows WHERE sonarrSeriesId=?", (seriesId,)) + else: + result = database.execute("SELECT * FROM table_shows") + for item in result: + # Parse audio language + item.update({"audio_language": {"name": item['audio_language'], + "code2": alpha2_from_language(item['audio_language']), + "code3": alpha3_from_language(item['audio_language'])}}) + + # Parse desired languages + item.update({"languages": ast.literal_eval(item['languages'])}) + + # Parse alternate titles + item.update({"alternateTitles": ast.literal_eval(item['alternateTitles'])}) + + # Provide mapped path + mapped_path = path_replace(item['path']) + item.update({"mapped_path": mapped_path}) + + # Confirm if path exist + item.update({"exist": os.path.isdir(mapped_path)}) + return jsonify(result) + + +api.add_resource(Series, '/api/series') + +if __name__ == '__main__': + app.run(debug=True) From ce19a1723464697b57bb5e90ae648a9155136899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20V=C3=A9zina?= <5130500+morpheus65535@users.noreply.github.com> Date: Thu, 12 Dec 2019 21:59:48 -0500 Subject: [PATCH 2/9] Added missing languages function loading. --- bazarr/api.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bazarr/api.py b/bazarr/api.py index f80d47fea..e9743089b 100644 --- a/bazarr/api.py +++ b/bazarr/api.py @@ -15,7 +15,9 @@ from init import * import logging from database import database, dict_mapper from helper import path_replace, path_replace_reverse, path_replace_movie, path_replace_reverse_movie -from get_languages import alpha2_from_language, alpha3_from_language +from get_languages import load_language_in_db, alpha2_from_language, alpha3_from_language + +load_language_in_db() class Series(Resource): def get(self): From 5d150c509892c9df2620572fdac14c1c05700fd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20V=C3=A9zina?= <5130500+morpheus65535@users.noreply.github.com> Date: Thu, 12 Dec 2019 22:04:50 -0500 Subject: [PATCH 3/9] Created languages dictionary for faster conversion than calling database. --- bazarr/get_languages.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/bazarr/get_languages.py b/bazarr/get_languages.py index 4854a43c3..5e61e789e 100644 --- a/bazarr/get_languages.py +++ b/bazarr/get_languages.py @@ -29,35 +29,37 @@ def load_language_in_db(): # Update languages in database table database.execute("UPDATE table_settings_languages SET code3b=? WHERE code3=?", langs, execute_many=True) + # Create languages dictionary for faster conversion than calling database + create_languages_dict() + + +def create_languages_dict(): + global languages_dict + languages_dict = database.execute("SELECT name, code2, code3, code3b FROM table_settings_languages") + def language_from_alpha2(lang): - result = database.execute("SELECT name FROM table_settings_languages WHERE code2=?", (lang,)) - return result[0]['name'] or None + return next((item["name"] for item in languages_dict if item["code2"] == lang), None) def language_from_alpha3(lang): - result = database.execute("SELECT name FROM table_settings_languages WHERE code3=? or code3b=?", (lang, lang)) - return result[0]['name'] or None + return next((item["name"] for item in languages_dict if item["code3"] == lang or item["code3b"] == lang), None) def alpha2_from_alpha3(lang): - result = database.execute("SELECT code2 FROM table_settings_languages WHERE code3=? or code3b=?", (lang, lang)) - return result[0]['code2'] or None + return next((item["code2"] for item in languages_dict if item["code3"] == lang or item["code3b"] == lang), None) def alpha2_from_language(lang): - result = database.execute("SELECT code2 FROM table_settings_languages WHERE name=?", (lang,)) - return result[0]['code2'] or None + return next((item["code2"] for item in languages_dict if item["name"] == lang), None) def alpha3_from_alpha2(lang): - result = database.execute("SELECT code3 FROM table_settings_languages WHERE code2=?", (lang,)) - return result[0]['code3'] or None + return next((item["code3"] for item in languages_dict if item["code2"] == lang), None) def alpha3_from_language(lang): - result = database.execute("SELECT code3 FROM table_settings_languages WHERE name=?", (lang,)) - return result[0]['code3'] or None + return next((item["code3"] for item in languages_dict if item["name"] == lang), None) def get_language_set(): From 55bf67950f59c6b5647dfc24602e96c6afa04b7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20V=C3=A9zina?= <5130500+morpheus65535@users.noreply.github.com> Date: Fri, 13 Dec 2019 07:00:38 -0500 Subject: [PATCH 4/9] Created path mapping dictionaries for faster conversion than reading config.ini every time. --- bazarr/helper.py | 16 ++++++++++++---- bazarr/init.py | 2 ++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/bazarr/helper.py b/bazarr/helper.py index 55e765fd3..feef91067 100644 --- a/bazarr/helper.py +++ b/bazarr/helper.py @@ -11,11 +11,19 @@ from bs4 import UnicodeDammit from config import settings +def create_path_mapping_dict(): + global path_mapping_series + path_mapping_series = ast.literal_eval(settings.general.path_mappings) + + global path_mapping_movies + path_mapping_movies = ast.literal_eval(settings.general.path_mappings_movie) + + def path_replace(path): if path is None: return None - for path_mapping in ast.literal_eval(settings.general.path_mappings): + for path_mapping in path_mapping_series: if path_mapping[0] == path_mapping[1]: continue if '' in path_mapping: @@ -34,7 +42,7 @@ def path_replace_reverse(path): if path is None: return None - for path_mapping in ast.literal_eval(settings.general.path_mappings): + for path_mapping in path_mapping_series: if path_mapping[0] == path_mapping[1]: continue if '' in path_mapping: @@ -53,7 +61,7 @@ def path_replace_movie(path): if path is None: return None - for path_mapping in ast.literal_eval(settings.general.path_mappings_movie): + for path_mapping in path_mapping_movies: if path_mapping[0] == path_mapping[1]: continue if '' in path_mapping: @@ -72,7 +80,7 @@ def path_replace_reverse_movie(path): if path is None: return None - for path_mapping in ast.literal_eval(settings.general.path_mappings_movie): + for path_mapping in path_mapping_movies: if path_mapping[0] == path_mapping[1]: continue if '' in path_mapping: diff --git a/bazarr/init.py b/bazarr/init.py index e913de9e4..81968eb98 100644 --- a/bazarr/init.py +++ b/bazarr/init.py @@ -9,6 +9,7 @@ from cork import Cork from config import settings from get_args import args from logger import configure_logging +from helper import create_path_mapping_dict from dogpile.cache.region import register_backend as register_cache_backend import subliminal @@ -130,3 +131,4 @@ def init_binaries(): init_binaries() +create_path_mapping_dict() From e4b6020f63d8a7f6077df2bd64e64c2793bf3d19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20V=C3=A9zina?= <5130500+morpheus65535@users.noreply.github.com> Date: Sat, 14 Dec 2019 09:30:58 -0500 Subject: [PATCH 5/9] Added stdout flush even for Python 2.x. --- bazarr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bazarr.py b/bazarr.py index 83fa66ce5..ae5d88f13 100644 --- a/bazarr.py +++ b/bazarr.py @@ -51,9 +51,9 @@ def start_bazarr(): break if PY3: sys.stdout.buffer.write(line) - sys.stdout.flush() else: sys.stdout.write(line) + sys.stdout.flush() except KeyboardInterrupt: pass From 8b52c4d6aece0d918052a1286273d7ce3e7d4403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20V=C3=A9zina?= <5130500+morpheus65535@users.noreply.github.com> Date: Sat, 14 Dec 2019 12:34:14 -0500 Subject: [PATCH 6/9] WIP --- bazarr/api.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/bazarr/api.py b/bazarr/api.py index e9743089b..e51853fd3 100644 --- a/bazarr/api.py +++ b/bazarr/api.py @@ -47,7 +47,25 @@ class Series(Resource): return jsonify(result) +class Episodes(Resource): + def get(self): + seriesId = request.args.get('id') + if seriesId: + result = database.execute("SELECT * FROM table_episodes WHERE sonarrSeriesId=?", (seriesId,)) + else: + result = database.execute("SELECT * FROM table_episodes") + for item in result: + # Provide mapped path + mapped_path = path_replace(item['path']) + item.update({"mapped_path": mapped_path}) + + # Confirm if path exist + item.update({"exist": os.path.isfile(mapped_path)}) + return jsonify(result) + + api.add_resource(Series, '/api/series') +api.add_resource(Episodes, '/api/episodes') if __name__ == '__main__': app.run(debug=True) From 8d9023af80974ff09fef3fb8b5f2c51a4923de1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20V=C3=A9zina?= <5130500+morpheus65535@users.noreply.github.com> Date: Sat, 14 Dec 2019 12:37:10 -0500 Subject: [PATCH 7/9] Small change to get output from ffprobe with Python 3.x --- libs/pyprobe/pyprobe.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/pyprobe/pyprobe.py b/libs/pyprobe/pyprobe.py index b300965d5..59a6e5001 100644 --- a/libs/pyprobe/pyprobe.py +++ b/libs/pyprobe/pyprobe.py @@ -179,9 +179,9 @@ class VideoFileParser: if PY3: command = [parser] + commandArgs + [inputFile] completedProcess = subprocess.run( - command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf-8" + command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) - if completedProcess.returncode != 0: + if completedProcess.returncode: raise IOError( "Error occurred during execution - " + completedProcess.stderr ) From 1e99de61b3826444e37c698ea57906af39127a6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20V=C3=A9zina?= <5130500+morpheus65535@users.noreply.github.com> Date: Sat, 14 Dec 2019 23:58:51 -0500 Subject: [PATCH 8/9] WIP --- bazarr/api.py | 117 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 111 insertions(+), 6 deletions(-) diff --git a/bazarr/api.py b/bazarr/api.py index e51853fd3..bcaa72e0c 100644 --- a/bazarr/api.py +++ b/bazarr/api.py @@ -15,10 +15,24 @@ from init import * import logging from database import database, dict_mapper from helper import path_replace, path_replace_reverse, path_replace_movie, path_replace_reverse_movie -from get_languages import load_language_in_db, alpha2_from_language, alpha3_from_language +from get_languages import load_language_in_db, alpha2_from_language, alpha3_from_language, language_from_alpha2, \ + alpha3_from_alpha2 load_language_in_db() + +class Badges(Resource): + def get(self): + result = { + "missing_episodes": database.execute("SELECT COUNT(*) as count FROM table_episodes WHERE missing_subtitles " + "is not null AND missing_subtitles != '[]'", only_one=True)['count'], + "missing_movies": database.execute("SELECT COUNT(*) as count FROM table_movies WHERE missing_subtitles " + "is not null AND missing_subtitles != '[]'", only_one=True)['count'], + "throttled_providers": len(eval(str(settings.general.throtteled_providers))) + } + return jsonify(result) + + class Series(Resource): def get(self): seriesId = request.args.get('id') @@ -28,15 +42,22 @@ class Series(Resource): result = database.execute("SELECT * FROM table_shows") for item in result: # Parse audio language - item.update({"audio_language": {"name": item['audio_language'], - "code2": alpha2_from_language(item['audio_language']), - "code3": alpha3_from_language(item['audio_language'])}}) + if item['audio_language']: + item.update({"audio_language": {"name": item['audio_language'], + "code2": alpha2_from_language(item['audio_language']), + "code3": alpha3_from_language(item['audio_language'])}}) # Parse desired languages - item.update({"languages": ast.literal_eval(item['languages'])}) + if item['languages'] and item['languages'] != 'None': + item.update({"languages": ast.literal_eval(item['languages'])}) + for i, subs in enumerate(item['languages']): + item['languages'][i] = {"name": language_from_alpha2(subs), + "code2": subs, + "code3": alpha3_from_alpha2(subs)} # Parse alternate titles - item.update({"alternateTitles": ast.literal_eval(item['alternateTitles'])}) + if item['alternateTitles']: + item.update({"alternateTitles": ast.literal_eval(item['alternateTitles'])}) # Provide mapped path mapped_path = path_replace(item['path']) @@ -44,6 +65,17 @@ class Series(Resource): # Confirm if path exist item.update({"exist": os.path.isdir(mapped_path)}) + + # Add missing subtitles episode count + item.update({"episodeMissingCount": database.execute("SELECT COUNT(*) as count FROM table_episodes WHERE " + "sonarrSeriesId=? AND missing_subtitles is not null " + "AND missing_subtitles != '[]'", (seriesId,), + only_one=True)['count']}) + + # Add episode count + item.update({"episodeFileCount": database.execute("SELECT COUNT(*) as count FROM table_episodes WHERE " + "sonarrSeriesId=?", (seriesId,), + only_one=True)['count']}) return jsonify(result) @@ -55,6 +87,22 @@ class Episodes(Resource): else: result = database.execute("SELECT * FROM table_episodes") for item in result: + # Parse subtitles + if item['subtitles']: + item.update({"subtitles": ast.literal_eval(item['subtitles'])}) + for subs in item['subtitles']: + subs[0] = {"name": language_from_alpha2(subs[0]), + "code2": subs[0], + "code3": alpha3_from_alpha2(subs[0])} + + # Parse missing subtitles + if item['missing_subtitles']: + item.update({"missing_subtitles": ast.literal_eval(item['missing_subtitles'])}) + for i, subs in enumerate(item['missing_subtitles']): + item['missing_subtitles'][i] = {"name": language_from_alpha2(subs), + "code2": subs, + "code3": alpha3_from_alpha2(subs)} + # Provide mapped path mapped_path = path_replace(item['path']) item.update({"mapped_path": mapped_path}) @@ -64,8 +112,65 @@ class Episodes(Resource): return jsonify(result) +class Movies(Resource): + def get(self): + moviesId = request.args.get('id') + if moviesId: + result = database.execute("SELECT * FROM table_movies WHERE radarrId=?", (moviesId,)) + else: + result = database.execute("SELECT * FROM table_movies") + for item in result: + # Parse audio language + if item['audio_language']: + item.update({"audio_language": {"name": item['audio_language'], + "code2": alpha2_from_language(item['audio_language']), + "code3": alpha3_from_language(item['audio_language'])}}) + + # Parse desired languages + if item['languages'] and item['languages'] != 'None': + item.update({"languages": ast.literal_eval(item['languages'])}) + for i, subs in enumerate(item['languages']): + item['languages'][i] = {"name": language_from_alpha2(subs), + "code2": subs, + "code3": alpha3_from_alpha2(subs)} + + # Parse alternate titles + if item['alternativeTitles']: + item.update({"alternativeTitles": ast.literal_eval(item['alternativeTitles'])}) + + # Parse failed attempts + if item['failedAttempts']: + item.update({"failedAttempts": ast.literal_eval(item['failedAttempts'])}) + + # Parse subtitles + if item['subtitles']: + item.update({"subtitles": ast.literal_eval(item['subtitles'])}) + for subs in item['subtitles']: + subs[0] = {"name": language_from_alpha2(subs[0]), + "code2": subs[0], + "code3": alpha3_from_alpha2(subs[0])} + + # Parse missing subtitles + if item['missing_subtitles']: + item.update({"missing_subtitles": ast.literal_eval(item['missing_subtitles'])}) + for i, subs in enumerate(item['missing_subtitles']): + item['missing_subtitles'][i] = {"name": language_from_alpha2(subs), + "code2": subs, + "code3": alpha3_from_alpha2(subs)} + + # Provide mapped path + mapped_path = path_replace_movie(item['path']) + item.update({"mapped_path": mapped_path}) + + # Confirm if path exist + item.update({"exist": os.path.isfile(mapped_path)}) + return jsonify(result) + + +api.add_resource(Badges, '/api/badges') api.add_resource(Series, '/api/series') api.add_resource(Episodes, '/api/episodes') +api.add_resource(Movies, '/api/movies') if __name__ == '__main__': app.run(debug=True) From cafa6ded0b32b9a9afa5b44e3a23bc5cf82adae0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20V=C3=A9zina?= <5130500+morpheus65535@users.noreply.github.com> Date: Sun, 15 Dec 2019 23:44:30 -0500 Subject: [PATCH 9/9] This should be a good start of an API to get new UI on track. --- bazarr/api.py | 242 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 234 insertions(+), 8 deletions(-) diff --git a/bazarr/api.py b/bazarr/api.py index bcaa72e0c..4c45c06c1 100644 --- a/bazarr/api.py +++ b/bazarr/api.py @@ -1,23 +1,26 @@ -from flask import Flask, jsonify, request -from flask_restful import Resource, Api - -app = Flask(__name__) -api = Api(app) - import os import ast import libs +from datetime import timedelta +import datetime +import pretty from get_args import args -from config import settings, url_sonarr, url_radarr, url_radarr_short, url_sonarr_short, base_url +from config import settings from init import * import logging -from database import database, dict_mapper +from database import database from helper import path_replace, path_replace_reverse, path_replace_movie, path_replace_reverse_movie from get_languages import load_language_in_db, alpha2_from_language, alpha3_from_language, language_from_alpha2, \ alpha3_from_alpha2 +from flask import Flask, jsonify, request +from flask_restful import Resource, Api + +app = Flask(__name__) +api = Api(app) + load_language_in_db() @@ -167,10 +170,233 @@ class Movies(Resource): return jsonify(result) +class HistorySeries(Resource): + def get(self): + 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] + else: + query_actions = [1, 3] + + if settings.sonarr.getboolean('only_monitored'): + series_monitored_only_query_string = " AND monitored='True'" + else: + series_monitored_only_query_string = '' + + upgradable_episodes = database.execute( + "SELECT video_path, MAX(timestamp) as timestamp, score FROM table_history " + "INNER JOIN table_episodes on table_episodes.sonarrEpisodeId = " + "table_history.sonarrEpisodeId WHERE action IN (" + + ','.join(map(str, query_actions)) + ") AND timestamp > ? AND " + "score is not null" + series_monitored_only_query_string + " GROUP BY " + "table_history.video_path, table_history.language", + (minimum_timestamp,)) + + 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) + + data = database.execute("SELECT table_history.action, table_shows.title as seriesTitle, " + "table_episodes.season || 'x' || table_episodes.episode as episode_number, " + "table_episodes.title as episodeTitle, table_history.timestamp, " + "table_history.description, table_history.sonarrSeriesId, table_episodes.path, " + "table_history.language, table_history.score FROM table_history LEFT JOIN table_shows " + "on table_shows.sonarrSeriesId = table_history.sonarrSeriesId LEFT JOIN table_episodes " + "on table_episodes.sonarrEpisodeId = table_history.sonarrEpisodeId WHERE " + "table_episodes.title is not NULL ORDER BY timestamp DESC") + + for item in data: + # Mark episode as upgradable or not + if {"video_path": str(item['path']), "timestamp": float(item['timestamp']), "score": str(item['score'])} in upgradable_episodes_not_perfect: + item.update({"upgradable": True}) + else: + item.update({"upgradable": False}) + + # Parse language + if item['language'] and item['language'] != 'None': + splitted_language = item['language'].split(':') + item['language'] = {"name": language_from_alpha2(splitted_language[0]), + "code2": splitted_language[0], + "code3": alpha3_from_alpha2(splitted_language[0]), + "forced": True if len(splitted_language) > 1 else False} + + # Make timestamp pretty + if item['timestamp']: + item['timestamp'] = pretty.date(int(item['timestamp'])) + + # Provide mapped path + mapped_path = path_replace(item['path']) + item.update({"mapped_path": mapped_path}) + + # Confirm if path exist + item.update({"exist": os.path.isfile(mapped_path)}) + + return jsonify(data) + + +class HistoryMovies(Resource): + def get(self): + 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.radarr.getboolean('only_monitored'): + movies_monitored_only_query_string = ' AND table_movies.monitored = "True"' + else: + movies_monitored_only_query_string = "" + + if settings.general.getboolean('upgrade_manual'): + query_actions = [1, 2, 3] + else: + query_actions = [1, 3] + + upgradable_movies = database.execute( + "SELECT video_path, MAX(timestamp) as timestamp, score FROM table_history_movie " + "INNER JOIN table_movies on table_movies.radarrId=table_history_movie.radarrId WHERE action IN (" + + ','.join(map(str, query_actions)) + ") AND timestamp > ? AND score is not NULL" + + movies_monitored_only_query_string + " GROUP BY video_path, language", (minimum_timestamp,)) + + 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) + + data = database.execute("SELECT table_history_movie.action, table_movies.title, table_history_movie.timestamp, " + "table_history_movie.description, table_history_movie.radarrId, " + "table_history_movie.video_path, table_history_movie.language, " + "table_history_movie.score FROM table_history_movie LEFT JOIN table_movies on " + "table_movies.radarrId = table_history_movie.radarrId ORDER BY timestamp DESC") + + for item in data: + # Mark movies as upgradable or not + if {"video_path": str(item['video_path']), "timestamp": float(item['timestamp']), "score": str(item['score'])} in upgradable_movies_not_perfect: + item.update({"upgradable": True}) + else: + item.update({"upgradable": False}) + + # Parse language + if item['language'] and item['language'] != 'None': + splitted_language = item['language'].split(':') + item['language'] = {"name": language_from_alpha2(splitted_language[0]), + "code2": splitted_language[0], + "code3": alpha3_from_alpha2(splitted_language[0]), + "forced": True if len(splitted_language) > 1 else False} + + # Make timestamp pretty + if item['timestamp']: + item['timestamp'] = pretty.date(int(item['timestamp'])) + + if item['video_path']: + # Provide mapped path + mapped_path = path_replace_movie(item['video_path']) + item.update({"mapped_path": mapped_path}) + + # Confirm if path exist + item.update({"exist": os.path.isfile(mapped_path)}) + else: + item.update({"mapped_path": None}) + item.update({"exist": False}) + + return jsonify(data) + + +class WantedSeries(Resource): + def get(self): + if settings.sonarr.getboolean('only_monitored'): + monitored_only_query_string = " AND monitored='True'" + else: + monitored_only_query_string = '' + + data = database.execute("SELECT table_shows.title as seriesTitle, " + "table_episodes.season || 'x' || table_episodes.episode as episode_number, " + "table_episodes.title as episodeTitle, table_episodes.missing_subtitles, " + "table_episodes.sonarrSeriesId, table_episodes.path, table_shows.hearing_impaired, " + "table_episodes.sonarrEpisodeId, table_episodes.scene_name, " + "table_episodes.failedAttempts FROM table_episodes INNER JOIN table_shows on " + "table_shows.sonarrSeriesId = table_episodes.sonarrSeriesId WHERE " + "table_episodes.missing_subtitles != '[]'" + monitored_only_query_string + + " ORDER BY table_episodes._rowid_ DESC") + + for item in data: + # Parse missing subtitles + if item['missing_subtitles']: + item.update({"missing_subtitles": ast.literal_eval(item['missing_subtitles'])}) + for i, subs in enumerate(item['missing_subtitles']): + splitted_subs = subs.split(':') + item['missing_subtitles'][i] = {"name": language_from_alpha2(splitted_subs[0]), + "code2": splitted_subs[0], + "code3": alpha3_from_alpha2(splitted_subs[0]), + "forced": True if len(splitted_subs) > 1 else False} + + # Provide mapped path + mapped_path = path_replace(item['path']) + item.update({"mapped_path": mapped_path}) + + # Confirm if path exist + item.update({"exist": os.path.isfile(mapped_path)}) + + return jsonify(data) + + +class WantedMovies(Resource): + def get(self): + if settings.radarr.getboolean('only_monitored'): + monitored_only_query_string = " AND monitored='True'" + else: + monitored_only_query_string = '' + + data = database.execute("SELECT title, missing_subtitles, radarrId, path, hearing_impaired, sceneName, " + "failedAttempts FROM table_movies WHERE missing_subtitles != '[]'" + + monitored_only_query_string + " ORDER BY _rowid_ DESC") + + for item in data: + # Parse missing subtitles + if item['missing_subtitles']: + item.update({"missing_subtitles": ast.literal_eval(item['missing_subtitles'])}) + for i, subs in enumerate(item['missing_subtitles']): + splitted_subs = subs.split(':') + item['missing_subtitles'][i] = {"name": language_from_alpha2(splitted_subs[0]), + "code2": splitted_subs[0], + "code3": alpha3_from_alpha2(splitted_subs[0]), + "forced": True if len(splitted_subs) > 1 else False} + + # Provide mapped path + mapped_path = path_replace_movie(item['path']) + item.update({"mapped_path": mapped_path}) + + # Confirm if path exist + item.update({"exist": os.path.isfile(mapped_path)}) + + return jsonify(data) + + api.add_resource(Badges, '/api/badges') api.add_resource(Series, '/api/series') api.add_resource(Episodes, '/api/episodes') api.add_resource(Movies, '/api/movies') +api.add_resource(HistorySeries, '/api/history_series') +api.add_resource(HistoryMovies, '/api/history_movies') +api.add_resource(WantedSeries, '/api/wanted_series') +api.add_resource(WantedMovies, '/api/wanted_movies') if __name__ == '__main__': app.run(debug=True)