2020-03-24 12:17:03 +00:00
|
|
|
# coding=utf-8
|
|
|
|
|
2019-12-11 03:20:42 +00:00
|
|
|
import ast
|
2019-12-16 04:44:30 +00:00
|
|
|
from datetime import timedelta
|
2020-06-19 13:18:48 +00:00
|
|
|
from dateutil import rrule
|
2019-12-16 04:44:30 +00:00
|
|
|
import pretty
|
2020-01-23 04:10:33 +00:00
|
|
|
import time
|
2020-02-07 17:40:43 +00:00
|
|
|
from operator import itemgetter
|
2020-02-17 00:38:10 +00:00
|
|
|
import platform
|
2020-03-29 13:58:32 +00:00
|
|
|
import re
|
2020-05-06 00:09:25 +00:00
|
|
|
import json
|
2021-03-25 14:22:43 +00:00
|
|
|
import hashlib
|
|
|
|
import apprise
|
|
|
|
import gc
|
2019-12-11 03:20:42 +00:00
|
|
|
|
2020-02-17 00:38:10 +00:00
|
|
|
from get_args import args
|
2021-03-25 14:22:43 +00:00
|
|
|
from config import settings, base_url, save_settings, get_settings
|
|
|
|
from logger import empty_log
|
2019-12-11 03:20:42 +00:00
|
|
|
|
|
|
|
from init import *
|
|
|
|
import logging
|
2021-01-19 04:49:51 +00:00
|
|
|
from database import database, get_exclusion_clause, get_profiles_list, get_desired_languages, get_profile_id_name, \
|
|
|
|
get_audio_profile_languages, update_profile_id_list
|
2020-05-19 13:27:13 +00:00
|
|
|
from helper import path_mappings
|
2021-02-03 21:01:43 +00:00
|
|
|
from get_languages import language_from_alpha2, language_from_alpha3, alpha2_from_alpha3, alpha3_from_alpha2
|
2021-01-19 04:49:51 +00:00
|
|
|
from get_subtitle import download_subtitle, series_download_subtitles, manual_search, manual_download_subtitle, \
|
|
|
|
manual_upload_subtitle, wanted_search_missing_subtitles_series, wanted_search_missing_subtitles_movies, \
|
|
|
|
episode_download_subtitles, movies_download_subtitles
|
2020-01-07 03:26:28 +00:00
|
|
|
from notifier import send_notifications, send_notifications_movie
|
|
|
|
from list_subtitles import store_subtitles, store_subtitles_movie, series_scan_subtitles, movies_scan_subtitles, \
|
|
|
|
list_missing_subtitles, list_missing_subtitles_movies
|
2020-07-19 20:02:38 +00:00
|
|
|
from utils import history_log, history_log_movie, blacklist_log, blacklist_delete, blacklist_delete_all, \
|
|
|
|
blacklist_log_movie, blacklist_delete_movie, blacklist_delete_all_movie, get_sonarr_version, get_radarr_version, \
|
2021-02-02 21:05:09 +00:00
|
|
|
delete_subtitles, subtitles_apply_mods, translate_subtitles_file
|
2021-01-31 13:39:10 +00:00
|
|
|
from get_providers import get_providers, get_providers_auth, list_throttled_providers, reset_throttled_providers, \
|
|
|
|
get_throttled_providers, set_throttled_providers
|
2020-05-12 12:25:03 +00:00
|
|
|
from event_handler import event_stream
|
2020-05-18 12:21:16 +00:00
|
|
|
from scheduler import scheduler
|
2020-06-12 19:08:44 +00:00
|
|
|
from subsyncer import subsync
|
2020-06-20 13:03:48 +00:00
|
|
|
from filesystem import browse_bazarr_filesystem, browse_sonarr_filesystem, browse_radarr_filesystem
|
2020-01-07 03:26:28 +00:00
|
|
|
|
2020-09-26 12:58:56 +00:00
|
|
|
from subliminal_patch.core import SUBTITLE_EXTENSIONS, guessit
|
2019-12-13 02:59:48 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
from flask import Flask, jsonify, request, Response, Blueprint, url_for, make_response, session
|
2019-12-13 02:59:48 +00:00
|
|
|
|
2020-04-07 16:15:04 +00:00
|
|
|
from flask_restful import Resource, Api, abort
|
|
|
|
from functools import wraps
|
2019-12-16 04:44:30 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
api_bp = Blueprint('api', __name__, url_prefix=base_url.rstrip('/') + '/api')
|
2019-12-16 13:58:10 +00:00
|
|
|
api = Api(api_bp)
|
2019-12-15 04:58:51 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
None_Keys = ['null', 'undefined', '']
|
|
|
|
|
|
|
|
|
|
|
|
def check_credentials(user, pw):
|
|
|
|
username = settings.auth.username
|
|
|
|
password = settings.auth.password
|
|
|
|
if hashlib.md5(pw.encode('utf-8')).hexdigest() == password and user == username:
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2019-12-15 04:58:51 +00:00
|
|
|
|
2020-04-07 16:15:04 +00:00
|
|
|
def authenticate(actual_method):
|
|
|
|
@wraps(actual_method)
|
|
|
|
def wrapper(*args, **kwargs):
|
2021-03-25 14:22:43 +00:00
|
|
|
if settings.auth.type == 'basic':
|
|
|
|
auth = request.authorization
|
|
|
|
if not (auth and check_credentials(request.authorization.username, request.authorization.password)):
|
|
|
|
return ('Unauthorized', 401, {
|
|
|
|
'WWW-Authenticate': 'Basic realm="Login Required"'
|
|
|
|
})
|
|
|
|
elif settings.auth.type == 'form':
|
|
|
|
if 'logged_in' not in session:
|
|
|
|
return abort(401, message="Unauthorized")
|
|
|
|
|
2020-04-07 16:15:04 +00:00
|
|
|
apikey_settings = settings.auth.apikey
|
|
|
|
apikey_get = request.args.get('apikey')
|
|
|
|
apikey_post = request.form.get('apikey')
|
2020-08-24 01:10:59 +00:00
|
|
|
apikey_header = None
|
2021-03-25 14:22:43 +00:00
|
|
|
if 'X-API-KEY' in request.headers:
|
|
|
|
apikey_header = request.headers['X-API-KEY']
|
2020-04-07 16:15:04 +00:00
|
|
|
|
2020-08-24 01:10:59 +00:00
|
|
|
if apikey_settings in [apikey_get, apikey_post, apikey_header]:
|
2020-04-07 16:15:04 +00:00
|
|
|
return actual_method(*args, **kwargs)
|
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
return abort(401)
|
2020-04-07 16:15:04 +00:00
|
|
|
|
|
|
|
return wrapper
|
|
|
|
|
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
def postprocess(item: dict):
|
|
|
|
# Parse tags
|
|
|
|
if 'tags' in item:
|
|
|
|
if item['tags'] is None:
|
|
|
|
item['tags'] = []
|
|
|
|
else:
|
|
|
|
item['tags'] = ast.literal_eval(item['tags'])
|
|
|
|
|
|
|
|
if 'monitored' in item:
|
|
|
|
if item['monitored'] is None:
|
|
|
|
item['monitored'] = False
|
|
|
|
else:
|
|
|
|
item['monitored'] = item['monitored'] == 'True'
|
|
|
|
|
|
|
|
if 'hearing_impaired' in item and item['hearing_impaired'] is not None:
|
|
|
|
if item['hearing_impaired'] is None:
|
|
|
|
item['hearing_impaired'] = False
|
|
|
|
else:
|
|
|
|
item['hearing_impaired'] = item['hearing_impaired'] == 'True'
|
|
|
|
|
|
|
|
if 'language' in item:
|
|
|
|
if item['language'] == 'None':
|
|
|
|
item['language'] = None
|
|
|
|
elif item['language'] is not 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 item['language'].endswith(':forced') else False,
|
|
|
|
"hi": True if item['language'].endswith(':hi') else False}
|
|
|
|
|
|
|
|
|
|
|
|
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'])
|
|
|
|
# Confirm if path exist
|
|
|
|
item['exist'] = os.path.isdir(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 'fanart' in item:
|
|
|
|
fanart = item['fanart']
|
|
|
|
item['fanart'] = f"{base_url}/images/series{fanart}"
|
|
|
|
|
|
|
|
|
|
|
|
def postprocessEpisode(item, desired=None):
|
|
|
|
if desired is None:
|
|
|
|
desired = []
|
|
|
|
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": 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})
|
|
|
|
|
|
|
|
if settings.general.getboolean('embedded_subs_show_desired'):
|
|
|
|
item['subtitles'] = [x for x in item['subtitles'] if
|
|
|
|
x['code2'] in desired or x['path']]
|
|
|
|
|
|
|
|
# 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:
|
|
|
|
if item['path']:
|
|
|
|
# Provide mapped path
|
|
|
|
item['path'] = path_mappings.path_replace(item['path'])
|
|
|
|
item['exist'] = os.path.isfile(item['path'])
|
|
|
|
|
2020-04-16 11:52:35 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
# TODO: Move
|
|
|
|
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'])
|
2020-04-16 11:52:35 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
# 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": 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'])
|
|
|
|
# Confirm if path exist
|
|
|
|
item['exist'] = os.path.isfile(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 'fanart' in item:
|
|
|
|
fanart = item['fanart']
|
|
|
|
item['fanart'] = f"{base_url}/images/movies{fanart}"
|
|
|
|
|
|
|
|
|
|
|
|
class SystemAccount(Resource):
|
|
|
|
def post(self):
|
|
|
|
if settings.auth.type != 'form':
|
|
|
|
return '', 405
|
|
|
|
|
|
|
|
action = request.args.get('action')
|
|
|
|
if action == 'login':
|
|
|
|
username = request.form.get('username')
|
|
|
|
password = request.form.get('password')
|
|
|
|
if check_credentials(username, password):
|
|
|
|
session['logged_in'] = True
|
|
|
|
return '', 204
|
|
|
|
elif action == 'logout':
|
|
|
|
if settings.auth.type == 'basic':
|
|
|
|
return abort(401)
|
|
|
|
elif settings.auth.type == 'form':
|
|
|
|
session.clear()
|
|
|
|
gc.collect()
|
|
|
|
return '', 204
|
|
|
|
|
|
|
|
return '', 401
|
|
|
|
|
|
|
|
|
|
|
|
class System(Resource):
|
2020-04-16 11:52:35 +00:00
|
|
|
@authenticate
|
2021-03-25 14:22:43 +00:00
|
|
|
def post(self):
|
2020-05-18 02:22:36 +00:00
|
|
|
from server import webserver
|
2021-03-25 14:22:43 +00:00
|
|
|
action = request.args.get('action')
|
|
|
|
if action == "shutdown":
|
|
|
|
webserver.shutdown()
|
|
|
|
elif action == "restart":
|
|
|
|
webserver.restart()
|
|
|
|
return '', 204
|
2020-04-16 11:52:35 +00:00
|
|
|
|
|
|
|
|
2020-08-11 14:36:14 +00:00
|
|
|
class BadgesSeries(Resource):
|
2020-04-07 16:15:04 +00:00
|
|
|
@authenticate
|
2019-12-15 04:58:51 +00:00
|
|
|
def get(self):
|
2020-07-13 14:57:43 +00:00
|
|
|
missing_episodes = database.execute("SELECT table_shows.tags, table_episodes.monitored, table_shows.seriesType "
|
|
|
|
"FROM table_episodes INNER JOIN table_shows on table_shows.sonarrSeriesId ="
|
|
|
|
" table_episodes.sonarrSeriesId WHERE missing_subtitles is not null AND "
|
2020-09-01 16:30:31 +00:00
|
|
|
"missing_subtitles != '[]'" + get_exclusion_clause('series'))
|
2020-07-12 20:37:20 +00:00
|
|
|
missing_episodes = len(missing_episodes)
|
|
|
|
|
|
|
|
missing_movies = database.execute("SELECT tags, monitored FROM table_movies WHERE missing_subtitles is not "
|
2020-09-01 16:30:31 +00:00
|
|
|
"null AND missing_subtitles != '[]'" + get_exclusion_clause('movie'))
|
2020-07-12 20:37:20 +00:00
|
|
|
missing_movies = len(missing_movies)
|
2020-05-20 03:20:35 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
throttled_providers = len(eval(str(get_throttled_providers())))
|
2020-08-11 14:36:14 +00:00
|
|
|
|
|
|
|
result = {
|
2021-03-25 14:22:43 +00:00
|
|
|
"episodes": missing_episodes,
|
|
|
|
"movies": missing_movies,
|
|
|
|
"providers": throttled_providers
|
2019-12-15 04:58:51 +00:00
|
|
|
}
|
|
|
|
return jsonify(result)
|
|
|
|
|
|
|
|
|
2020-01-22 04:54:32 +00:00
|
|
|
class Languages(Resource):
|
2020-04-07 16:15:04 +00:00
|
|
|
@authenticate
|
2020-01-22 04:54:32 +00:00
|
|
|
def get(self):
|
2021-03-25 14:22:43 +00:00
|
|
|
result = database.execute("SELECT name, code2, enabled FROM table_settings_languages ORDER BY name")
|
|
|
|
for item in result:
|
|
|
|
item['enabled'] = item['enabled'] == 1
|
2020-01-22 04:54:32 +00:00
|
|
|
return jsonify(result)
|
|
|
|
|
|
|
|
|
2021-01-19 04:49:51 +00:00
|
|
|
class LanguagesProfiles(Resource):
|
|
|
|
@authenticate
|
|
|
|
def get(self):
|
2021-03-25 14:22:43 +00:00
|
|
|
return jsonify(get_profiles_list())
|
2021-01-19 04:49:51 +00:00
|
|
|
|
|
|
|
|
2020-05-06 00:09:25 +00:00
|
|
|
class Notifications(Resource):
|
|
|
|
@authenticate
|
2021-03-25 14:22:43 +00:00
|
|
|
def patch(self):
|
|
|
|
protocol = request.form.get("protocol")
|
|
|
|
path = request.form.get("path")
|
2020-05-06 00:09:25 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
asset = apprise.AppriseAsset(async_mode=False)
|
|
|
|
|
|
|
|
apobj = apprise.Apprise(asset=asset)
|
|
|
|
|
|
|
|
apobj.add(f"{protocol}://{path}")
|
|
|
|
|
|
|
|
apobj.notify(
|
|
|
|
title='Bazarr test notification',
|
|
|
|
body='Test notification'
|
|
|
|
)
|
2020-05-06 00:09:25 +00:00
|
|
|
|
|
|
|
return '', 204
|
|
|
|
|
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
class Searches(Resource):
|
2020-04-07 16:15:04 +00:00
|
|
|
@authenticate
|
2020-03-29 13:58:32 +00:00
|
|
|
def get(self):
|
|
|
|
query = request.args.get('query')
|
|
|
|
search_list = []
|
|
|
|
|
|
|
|
if query:
|
|
|
|
if settings.general.getboolean('use_sonarr'):
|
|
|
|
# Get matching series
|
|
|
|
series = database.execute("SELECT title, sonarrSeriesId, year FROM table_shows WHERE title LIKE ? "
|
2021-03-25 14:22:43 +00:00
|
|
|
"ORDER BY title ASC", ("%" + query + "%",))
|
|
|
|
|
|
|
|
search_list += series
|
2020-03-29 13:58:32 +00:00
|
|
|
|
|
|
|
if settings.general.getboolean('use_radarr'):
|
|
|
|
# Get matching movies
|
|
|
|
movies = database.execute("SELECT title, radarrId, year FROM table_movies WHERE title LIKE ? ORDER BY "
|
2021-03-25 14:22:43 +00:00
|
|
|
"title ASC", ("%" + query + "%",))
|
|
|
|
|
|
|
|
search_list += movies
|
2020-03-29 13:58:32 +00:00
|
|
|
|
|
|
|
return jsonify(search_list)
|
|
|
|
|
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
class SystemSettings(Resource):
|
2020-04-30 12:38:05 +00:00
|
|
|
@authenticate
|
|
|
|
def get(self):
|
2021-03-25 14:22:43 +00:00
|
|
|
data = get_settings()
|
2020-04-30 12:38:05 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
notifications = database.execute("SELECT * FROM table_settings_notifier ORDER BY name")
|
|
|
|
for i, item in enumerate(notifications):
|
|
|
|
item["enabled"] = item["enabled"] == 1
|
|
|
|
notifications[i] = item
|
|
|
|
|
|
|
|
data['notifications'] = dict()
|
|
|
|
data['notifications']['providers'] = notifications
|
2020-04-30 12:38:05 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
return jsonify(data)
|
2020-04-30 12:38:05 +00:00
|
|
|
|
2020-04-07 16:15:04 +00:00
|
|
|
@authenticate
|
2020-03-11 10:58:45 +00:00
|
|
|
def post(self):
|
2021-03-25 14:22:43 +00:00
|
|
|
enabled_languages = request.form.getlist('languages-enabled')
|
|
|
|
if len(enabled_languages) != 0:
|
|
|
|
database.execute("UPDATE table_settings_languages SET enabled=0")
|
|
|
|
for code in enabled_languages:
|
|
|
|
database.execute("UPDATE table_settings_languages SET enabled=1 WHERE code2=?", (code,))
|
|
|
|
|
|
|
|
languages_profiles = request.form.get('languages-profiles')
|
2021-01-19 04:49:51 +00:00
|
|
|
if languages_profiles:
|
|
|
|
existing_ids = database.execute('SELECT profileId FROM table_languages_profiles')
|
|
|
|
existing = [x['profileId'] for x in existing_ids]
|
|
|
|
for item in json.loads(languages_profiles):
|
|
|
|
if item['profileId'] in existing:
|
|
|
|
# Update existing profiles
|
|
|
|
database.execute('UPDATE table_languages_profiles SET name = ?, cutoff = ?, items = ? '
|
|
|
|
'WHERE profileId = ?', (item['name'],
|
2021-03-25 14:22:43 +00:00
|
|
|
item['cutoff'] if item['cutoff'] != 'null' else None,
|
|
|
|
json.dumps(item['items']),
|
2021-01-19 04:49:51 +00:00
|
|
|
item['profileId']))
|
|
|
|
existing.remove(item['profileId'])
|
|
|
|
else:
|
|
|
|
# Add new profiles
|
|
|
|
database.execute('INSERT INTO table_languages_profiles (profileId, name, cutoff, items) '
|
|
|
|
'VALUES (?, ?, ?, ?)', (item['profileId'],
|
|
|
|
item['name'],
|
2021-03-25 14:22:43 +00:00
|
|
|
item['cutoff'] if item['cutoff'] != 'null' else None,
|
|
|
|
json.dumps(item['items'])))
|
2021-01-19 04:49:51 +00:00
|
|
|
for profileId in existing:
|
|
|
|
# Unassign this profileId from series and movies
|
|
|
|
database.execute('UPDATE table_shows SET profileId = null WHERE profileId = ?', (profileId,))
|
|
|
|
database.execute('UPDATE table_movies SET profileId = null WHERE profileId = ?', (profileId,))
|
|
|
|
# Remove deleted profiles
|
|
|
|
database.execute('DELETE FROM table_languages_profiles WHERE profileId = ?', (profileId,))
|
|
|
|
|
|
|
|
update_profile_id_list()
|
|
|
|
|
|
|
|
if settings.general.getboolean('use_sonarr'):
|
|
|
|
scheduler.add_job(list_missing_subtitles, kwargs={'send_event': False})
|
|
|
|
if settings.general.getboolean('use_radarr'):
|
|
|
|
scheduler.add_job(list_missing_subtitles_movies, kwargs={'send_event': False})
|
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
# Update Notification
|
|
|
|
notifications = request.form.getlist('notifications-providers')
|
|
|
|
for item in notifications:
|
|
|
|
item = json.loads(item)
|
|
|
|
database.execute("UPDATE table_settings_notifier SET enabled = ?, url = ? WHERE name = ?",
|
|
|
|
(item['enabled'], item['url'], item['name']))
|
2020-03-11 10:58:45 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
save_settings(zip(request.form.keys(), request.form.listvalues()))
|
|
|
|
return '', 204
|
2020-03-11 10:58:45 +00:00
|
|
|
|
|
|
|
|
2020-02-20 11:41:05 +00:00
|
|
|
class SystemTasks(Resource):
|
2020-04-07 16:15:04 +00:00
|
|
|
@authenticate
|
2020-02-20 11:41:05 +00:00
|
|
|
def get(self):
|
2020-02-23 14:25:24 +00:00
|
|
|
taskid = request.args.get('taskid')
|
|
|
|
|
2020-02-20 11:41:05 +00:00
|
|
|
task_list = scheduler.get_task_list()
|
|
|
|
|
2020-02-23 14:25:24 +00:00
|
|
|
if taskid:
|
|
|
|
for item in task_list:
|
|
|
|
if item['job_id'] == taskid:
|
|
|
|
task_list = [item]
|
|
|
|
continue
|
|
|
|
|
2020-02-20 11:41:05 +00:00
|
|
|
return jsonify(data=task_list)
|
|
|
|
|
2020-04-07 16:15:04 +00:00
|
|
|
@authenticate
|
2020-02-22 14:03:58 +00:00
|
|
|
def post(self):
|
2020-04-15 04:02:44 +00:00
|
|
|
taskid = request.form.get('taskid')
|
2020-02-22 14:03:58 +00:00
|
|
|
|
|
|
|
scheduler.execute_job_now(taskid)
|
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
return '', 204
|
2020-02-22 14:03:58 +00:00
|
|
|
|
|
|
|
|
2020-02-17 17:54:20 +00:00
|
|
|
class SystemLogs(Resource):
|
2020-04-07 16:15:04 +00:00
|
|
|
@authenticate
|
2020-02-17 17:54:20 +00:00
|
|
|
def get(self):
|
|
|
|
logs = []
|
|
|
|
with io.open(os.path.join(args.config_dir, 'log', 'bazarr.log'), encoding='UTF-8') as file:
|
2021-03-28 15:46:20 +00:00
|
|
|
raw_lines = file.read()
|
|
|
|
lines = raw_lines.split('|\n')
|
|
|
|
for line in lines:
|
|
|
|
raw_message = line.split('|')
|
|
|
|
raw_message_len = len(raw_message)
|
|
|
|
if raw_message_len > 3:
|
2021-03-27 12:59:12 +00:00
|
|
|
log = dict()
|
2021-03-28 15:46:20 +00:00
|
|
|
log["timestamp"] = raw_message[0]
|
|
|
|
log["type"] = raw_message[1].rstrip()
|
|
|
|
log["message"] = raw_message[3]
|
|
|
|
if raw_message_len > 4 and raw_message[4] != '\n':
|
|
|
|
log['exception'] = raw_message[4].strip('\'').replace(' ', '\u2003\u2003')
|
2021-03-25 14:22:43 +00:00
|
|
|
logs.append(log)
|
2021-03-28 15:46:20 +00:00
|
|
|
|
2020-02-17 17:54:20 +00:00
|
|
|
logs.reverse()
|
|
|
|
return jsonify(data=logs)
|
|
|
|
|
2020-04-07 16:15:04 +00:00
|
|
|
@authenticate
|
2021-03-25 14:22:43 +00:00
|
|
|
def delete(self):
|
|
|
|
empty_log()
|
|
|
|
return '', 204
|
2020-02-23 15:17:49 +00:00
|
|
|
|
|
|
|
|
2020-02-17 00:38:10 +00:00
|
|
|
class SystemStatus(Resource):
|
|
|
|
def get(self):
|
|
|
|
system_status = {}
|
|
|
|
system_status.update({'bazarr_version': os.environ["BAZARR_VERSION"]})
|
|
|
|
system_status.update({'sonarr_version': get_sonarr_version()})
|
|
|
|
system_status.update({'radarr_version': get_radarr_version()})
|
|
|
|
system_status.update({'operating_system': platform.platform()})
|
|
|
|
system_status.update({'python_version': platform.python_version()})
|
|
|
|
system_status.update({'bazarr_directory': os.path.dirname(os.path.dirname(__file__))})
|
|
|
|
system_status.update({'bazarr_config_directory': args.config_dir})
|
|
|
|
return jsonify(data=system_status)
|
|
|
|
|
|
|
|
|
|
|
|
class SystemReleases(Resource):
|
2020-04-07 16:15:04 +00:00
|
|
|
@authenticate
|
2020-02-17 00:38:10 +00:00
|
|
|
def get(self):
|
|
|
|
releases = []
|
|
|
|
try:
|
|
|
|
with io.open(os.path.join(args.config_dir, 'config', 'releases.txt'), 'r', encoding='UTF-8') as f:
|
2021-01-27 16:29:08 +00:00
|
|
|
releases = json.loads(f.read())
|
2021-03-24 17:43:47 +00:00
|
|
|
|
|
|
|
filtered_releases = []
|
|
|
|
for release in releases:
|
|
|
|
if settings.general.branch == 'master' and not release['prerelease']:
|
|
|
|
filtered_releases.append(release)
|
|
|
|
elif settings.general.branch != 'master' and any(not x['prerelease'] for x in filtered_releases):
|
|
|
|
continue
|
|
|
|
elif settings.general.branch != 'master':
|
|
|
|
filtered_releases.append(release)
|
|
|
|
if settings.general.branch == 'master':
|
|
|
|
filtered_releases = filtered_releases[:5]
|
|
|
|
|
|
|
|
for i, release in enumerate(filtered_releases):
|
2021-02-26 17:24:20 +00:00
|
|
|
body = release['body'].replace('- ', '').split('\n')[1:]
|
2021-03-24 17:43:47 +00:00
|
|
|
filtered_releases[i] = {"body": body,
|
|
|
|
"name": release['name'],
|
|
|
|
"date": release['date'][:10],
|
|
|
|
"prerelease": release['prerelease'],
|
|
|
|
"current": True if release['name'].lstrip('v') == os.environ["BAZARR_VERSION"]
|
|
|
|
else False}
|
2020-02-17 00:38:10 +00:00
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
logging.exception(
|
|
|
|
'BAZARR cannot parse releases caching file: ' + os.path.join(args.config_dir, 'config', 'releases.txt'))
|
2021-03-24 17:43:47 +00:00
|
|
|
return jsonify(data=filtered_releases)
|
2020-02-17 00:38:10 +00:00
|
|
|
|
|
|
|
|
2019-12-11 03:20:42 +00:00
|
|
|
class Series(Resource):
|
2020-04-07 16:15:04 +00:00
|
|
|
@authenticate
|
2021-03-25 14:22:43 +00:00
|
|
|
def get(self):
|
2019-12-17 00:41:50 +00:00
|
|
|
start = request.args.get('start') or 0
|
|
|
|
length = request.args.get('length') or -1
|
2021-03-25 14:22:43 +00:00
|
|
|
seriesId = request.args.getlist('seriesid[]')
|
2019-12-11 03:20:42 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
count = database.execute("SELECT COUNT(*) as count FROM table_shows", only_one=True)['count']
|
2019-12-11 03:20:42 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
if len(seriesId) != 0:
|
|
|
|
seriesIdList = ','.join(seriesId)
|
|
|
|
result = database.execute(
|
|
|
|
f"SELECT * FROM table_shows WHERE sonarrSeriesId in ({seriesIdList}) ORDER BY sortTitle ASC")
|
|
|
|
else:
|
|
|
|
result = database.execute("SELECT * FROM table_shows ORDER BY sortTitle ASC LIMIT ? OFFSET ?"
|
|
|
|
, (length, start))
|
2019-12-11 03:20:42 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
for item in result:
|
|
|
|
postprocessSeries(item)
|
2019-12-15 04:58:51 +00:00
|
|
|
|
|
|
|
# Add missing subtitles episode count
|
2020-07-13 14:57:43 +00:00
|
|
|
episodeMissingCount = database.execute("SELECT table_shows.tags, table_episodes.monitored, "
|
|
|
|
"table_shows.seriesType FROM table_episodes INNER JOIN table_shows "
|
|
|
|
"on table_shows.sonarrSeriesId = table_episodes.sonarrSeriesId "
|
2020-07-12 20:37:20 +00:00
|
|
|
"WHERE table_episodes.sonarrSeriesId=? AND missing_subtitles is not "
|
2021-03-25 14:22:43 +00:00
|
|
|
"null AND missing_subtitles != '[]'" +
|
2020-09-01 16:30:31 +00:00
|
|
|
get_exclusion_clause('series'), (item['sonarrSeriesId'],))
|
2020-07-12 20:37:20 +00:00
|
|
|
episodeMissingCount = len(episodeMissingCount)
|
|
|
|
item.update({"episodeMissingCount": episodeMissingCount})
|
2019-12-15 04:58:51 +00:00
|
|
|
|
|
|
|
# Add episode count
|
2020-07-13 14:57:43 +00:00
|
|
|
episodeFileCount = database.execute("SELECT table_shows.tags, table_episodes.monitored, "
|
|
|
|
"table_shows.seriesType FROM table_episodes INNER JOIN table_shows on "
|
|
|
|
"table_shows.sonarrSeriesId = table_episodes.sonarrSeriesId WHERE "
|
2021-03-25 14:22:43 +00:00
|
|
|
"table_episodes.sonarrSeriesId=?" + get_exclusion_clause('series'),
|
2020-09-01 16:30:31 +00:00
|
|
|
(item['sonarrSeriesId'],))
|
2020-07-12 20:37:20 +00:00
|
|
|
episodeFileCount = len(episodeFileCount)
|
|
|
|
item.update({"episodeFileCount": episodeFileCount})
|
2020-02-08 13:38:59 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
return jsonify(data=result, total=count)
|
2019-12-11 03:20:42 +00:00
|
|
|
|
2020-04-07 16:15:04 +00:00
|
|
|
@authenticate
|
2020-01-22 04:54:32 +00:00
|
|
|
def post(self):
|
2021-03-25 14:22:43 +00:00
|
|
|
seriesIdList = request.form.getlist('seriesid')
|
|
|
|
profileIdList = request.form.getlist('profileid')
|
2020-01-22 04:54:32 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
for idx in range(len(seriesIdList)):
|
|
|
|
seriesId = seriesIdList[idx]
|
|
|
|
profileId = profileIdList[idx]
|
2020-01-22 04:54:32 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
if profileId in None_Keys:
|
|
|
|
profileId = None
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
profileId = int(profileId)
|
|
|
|
except Exception:
|
|
|
|
return '', 400
|
2020-01-22 04:54:32 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
database.execute("UPDATE table_shows SET profileId=? WHERE sonarrSeriesId=?", (profileId, seriesId))
|
2020-01-22 04:54:32 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
list_missing_subtitles(no=seriesId)
|
2020-01-22 04:54:32 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
# event_stream(type='series', action='update', series=seriesId)
|
2020-08-13 13:33:12 +00:00
|
|
|
|
2020-01-22 04:54:32 +00:00
|
|
|
return '', 204
|
|
|
|
|
2020-04-07 16:15:04 +00:00
|
|
|
@authenticate
|
2021-03-25 14:22:43 +00:00
|
|
|
def patch(self):
|
|
|
|
seriesid = request.form.get('seriesid')
|
|
|
|
action = request.form.get('action')
|
|
|
|
if action == "scan-disk":
|
|
|
|
series_scan_subtitles(seriesid)
|
|
|
|
return '', 204
|
|
|
|
elif action == "search-missing":
|
|
|
|
series_download_subtitles(seriesid)
|
|
|
|
return '', 204
|
|
|
|
elif action == "search-wanted":
|
|
|
|
wanted_search_missing_subtitles_series()
|
|
|
|
return '', 204
|
2020-01-29 01:46:35 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
return '', 400
|
2020-01-29 01:46:35 +00:00
|
|
|
|
|
|
|
|
2019-12-14 17:34:14 +00:00
|
|
|
class Episodes(Resource):
|
2020-04-07 16:15:04 +00:00
|
|
|
@authenticate
|
2019-12-14 17:34:14 +00:00
|
|
|
def get(self):
|
2020-01-12 17:50:27 +00:00
|
|
|
seriesId = request.args.get('seriesid')
|
|
|
|
episodeId = request.args.get('episodeid')
|
|
|
|
if episodeId:
|
|
|
|
result = database.execute("SELECT * FROM table_episodes WHERE sonarrEpisodeId=?", (episodeId,))
|
|
|
|
elif seriesId:
|
2020-01-04 05:55:44 +00:00
|
|
|
result = database.execute("SELECT * FROM table_episodes WHERE sonarrSeriesId=? ORDER BY season DESC, "
|
|
|
|
"episode DESC", (seriesId,))
|
2019-12-14 17:34:14 +00:00
|
|
|
else:
|
2019-12-17 00:41:50 +00:00
|
|
|
return "Series ID not provided", 400
|
2021-01-19 04:49:51 +00:00
|
|
|
|
|
|
|
profileId = database.execute("SELECT profileId FROM table_shows WHERE sonarrSeriesId = ?", (seriesId,),
|
|
|
|
only_one=True)['profileId']
|
|
|
|
desired_languages = str(get_desired_languages(profileId))
|
2021-03-25 14:22:43 +00:00
|
|
|
desired = ast.literal_eval(desired_languages)
|
2021-01-19 04:49:51 +00:00
|
|
|
|
2019-12-14 17:34:14 +00:00
|
|
|
for item in result:
|
2021-03-25 14:22:43 +00:00
|
|
|
postprocessEpisode(item, desired)
|
2019-12-14 17:34:14 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
return jsonify(data=result)
|
2020-01-10 00:54:00 +00:00
|
|
|
|
2019-12-14 17:34:14 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
# PATCH: Download Subtitles
|
|
|
|
# POST: Upload Subtitles
|
|
|
|
# DELETE: Delete Subtitles
|
|
|
|
class EpisodesSubtitles(Resource):
|
2020-09-26 12:58:56 +00:00
|
|
|
@authenticate
|
2021-03-25 14:22:43 +00:00
|
|
|
def patch(self):
|
|
|
|
sonarrSeriesId = request.args.get('seriesid')
|
|
|
|
sonarrEpisodeId = request.args.get('episodeid')
|
|
|
|
episodeInfo = database.execute(
|
|
|
|
"SELECT title, path, scene_name, audio_language FROM table_episodes WHERE sonarrEpisodeId=?",
|
|
|
|
(sonarrEpisodeId,), only_one=True)
|
2020-11-07 04:18:09 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
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"
|
2020-04-07 16:15:04 +00:00
|
|
|
|
2020-01-07 03:26:28 +00:00
|
|
|
language = request.form.get('language')
|
|
|
|
hi = request.form.get('hi').capitalize()
|
|
|
|
forced = request.form.get('forced').capitalize()
|
2021-03-25 14:22:43 +00:00
|
|
|
|
2020-01-07 03:26:28 +00:00
|
|
|
providers_list = get_providers()
|
|
|
|
providers_auth = get_providers_auth()
|
2021-01-19 04:49:51 +00:00
|
|
|
|
|
|
|
audio_language_list = get_audio_profile_languages(episode_id=sonarrEpisodeId)
|
|
|
|
if len(audio_language_list) > 0:
|
|
|
|
audio_language = audio_language_list[0]['name']
|
|
|
|
else:
|
2021-03-25 14:22:43 +00:00
|
|
|
audio_language = None
|
2020-01-07 03:26:28 +00:00
|
|
|
|
|
|
|
try:
|
2021-03-25 14:22:43 +00:00
|
|
|
result = download_subtitle(episodePath, language, audio_language, hi, forced, providers_list,
|
|
|
|
providers_auth, sceneName, title, 'series')
|
2020-01-07 03:26:28 +00:00
|
|
|
if result is not None:
|
|
|
|
message = result[0]
|
|
|
|
path = result[1]
|
|
|
|
forced = result[5]
|
2020-09-10 18:26:37 +00:00
|
|
|
if result[8]:
|
|
|
|
language_code = result[2] + ":hi"
|
|
|
|
elif forced:
|
|
|
|
language_code = result[2] + ":forced"
|
|
|
|
else:
|
|
|
|
language_code = result[2]
|
2020-01-07 03:26:28 +00:00
|
|
|
provider = result[3]
|
|
|
|
score = result[4]
|
2020-07-14 19:16:20 +00:00
|
|
|
subs_id = result[6]
|
2020-07-19 20:02:38 +00:00
|
|
|
subs_path = result[7]
|
2021-03-25 14:22:43 +00:00
|
|
|
history_log(1, sonarrSeriesId, sonarrEpisodeId, message, path, language_code, provider, score, subs_id,
|
|
|
|
subs_path)
|
2020-01-07 03:26:28 +00:00
|
|
|
send_notifications(sonarrSeriesId, sonarrEpisodeId, message)
|
|
|
|
store_subtitles(path, episodePath)
|
2020-02-12 17:41:40 +00:00
|
|
|
else:
|
2020-05-12 12:25:03 +00:00
|
|
|
event_stream(type='episode', action='update', series=int(sonarrSeriesId), episode=int(sonarrEpisodeId))
|
2020-01-07 03:26:28 +00:00
|
|
|
|
|
|
|
except OSError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
return '', 204
|
|
|
|
|
2020-04-07 16:15:04 +00:00
|
|
|
@authenticate
|
2020-01-07 03:26:28 +00:00
|
|
|
def post(self):
|
2021-03-25 14:22:43 +00:00
|
|
|
sonarrSeriesId = request.args.get('seriesid')
|
|
|
|
sonarrEpisodeId = request.args.get('episodeid')
|
|
|
|
episodeInfo = database.execute(
|
|
|
|
"SELECT title, path, scene_name, audio_language FROM table_episodes WHERE sonarrEpisodeId=?",
|
|
|
|
(sonarrEpisodeId,), only_one=True)
|
|
|
|
|
|
|
|
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"
|
|
|
|
|
2020-01-07 03:26:28 +00:00
|
|
|
language = request.form.get('language')
|
2020-01-11 04:40:38 +00:00
|
|
|
forced = True if request.form.get('forced') == 'on' else False
|
2021-03-25 14:22:43 +00:00
|
|
|
subFile = request.files.get('file')
|
2020-01-07 03:26:28 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
_, ext = os.path.splitext(subFile.filename)
|
2020-01-07 03:26:28 +00:00
|
|
|
|
|
|
|
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,
|
|
|
|
title=title,
|
|
|
|
scene_name=sceneName,
|
|
|
|
media_type='series',
|
2021-03-25 14:22:43 +00:00
|
|
|
subtitle=subFile,
|
|
|
|
audio_language=audio_language)
|
2020-01-07 03:26:28 +00:00
|
|
|
|
|
|
|
if result is not None:
|
|
|
|
message = result[0]
|
|
|
|
path = result[1]
|
2020-07-19 20:02:38 +00:00
|
|
|
subs_path = result[2]
|
2020-09-10 18:26:37 +00:00
|
|
|
if forced:
|
2020-09-19 13:22:27 +00:00
|
|
|
language_code = language + ":forced"
|
2020-09-10 18:26:37 +00:00
|
|
|
else:
|
2020-09-19 13:22:27 +00:00
|
|
|
language_code = language
|
2020-01-07 03:26:28 +00:00
|
|
|
provider = "manual"
|
|
|
|
score = 360
|
2021-03-25 14:22:43 +00:00
|
|
|
history_log(4, sonarrSeriesId, sonarrEpisodeId, message, path, language_code, provider, score,
|
|
|
|
subtitles_path=subs_path)
|
2020-08-01 13:12:56 +00:00
|
|
|
if not settings.general.getboolean('dont_notify_manual_actions'):
|
|
|
|
send_notifications(sonarrSeriesId, sonarrEpisodeId, message)
|
2020-01-07 03:26:28 +00:00
|
|
|
store_subtitles(path, episodePath)
|
|
|
|
|
|
|
|
except OSError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
return '', 204
|
|
|
|
|
2020-04-07 16:15:04 +00:00
|
|
|
@authenticate
|
2021-03-25 14:22:43 +00:00
|
|
|
def delete(self):
|
|
|
|
sonarrSeriesId = request.args.get('seriesid')
|
|
|
|
sonarrEpisodeId = request.args.get('episodeid')
|
|
|
|
episodeInfo = database.execute(
|
|
|
|
"SELECT title, path, scene_name, audio_language FROM table_episodes WHERE sonarrEpisodeId=?",
|
|
|
|
(sonarrEpisodeId,), only_one=True)
|
2020-07-19 20:02:38 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
episodePath = path_mappings.path_replace(episodeInfo['path'])
|
2020-01-23 04:10:33 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
language = request.form.get('language')
|
|
|
|
forced = request.form.get('forced')
|
|
|
|
hi = request.form.get('hi')
|
|
|
|
subtitlesPath = request.form.get('path')
|
2020-01-23 04:10:33 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
result = 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)
|
2020-06-12 19:08:44 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
return '', 204
|
2020-06-12 19:08:44 +00:00
|
|
|
|
|
|
|
|
2019-12-15 04:58:51 +00:00
|
|
|
class Movies(Resource):
|
2020-04-07 16:15:04 +00:00
|
|
|
@authenticate
|
2019-12-15 04:58:51 +00:00
|
|
|
def get(self):
|
2019-12-17 00:41:50 +00:00
|
|
|
start = request.args.get('start') or 0
|
|
|
|
length = request.args.get('length') or -1
|
2021-03-25 14:22:43 +00:00
|
|
|
id = request.args.getlist('radarrid[]')
|
|
|
|
|
|
|
|
count = database.execute("SELECT COUNT(*) as count FROM table_movies", only_one=True)['count']
|
2019-12-17 00:41:50 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
if len(id) != 0:
|
|
|
|
movieIdList = ','.join(id)
|
|
|
|
result = database.execute(
|
|
|
|
f"SELECT * FROM table_movies WHERE radarrId in ({movieIdList}) ORDER BY sortTitle ASC")
|
2019-12-15 04:58:51 +00:00
|
|
|
else:
|
2019-12-28 16:43:48 +00:00
|
|
|
result = database.execute("SELECT * FROM table_movies ORDER BY sortTitle ASC LIMIT ? OFFSET ?",
|
|
|
|
(length, start))
|
2019-12-15 04:58:51 +00:00
|
|
|
for item in result:
|
2021-03-25 14:22:43 +00:00
|
|
|
postprocessMovie(item)
|
2019-12-15 04:58:51 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
return jsonify(data=result, total=count)
|
2020-07-12 20:37:20 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
@authenticate
|
|
|
|
def post(self):
|
|
|
|
radarrIdList = request.form.getlist('radarrid')
|
|
|
|
profileIdList = request.form.getlist('profileid')
|
2019-12-15 04:58:51 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
for idx in range(len(radarrIdList)):
|
|
|
|
radarrId = radarrIdList[idx]
|
|
|
|
profileId = profileIdList[idx]
|
2020-02-08 13:38:59 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
if profileId in None_Keys:
|
|
|
|
profileId = None
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
profileId = int(profileId)
|
|
|
|
except Exception:
|
|
|
|
return '', 400
|
2021-01-19 04:49:51 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
database.execute("UPDATE table_movies SET profileId=? WHERE radarrId=?", (profileId, radarrId))
|
2019-12-15 04:58:51 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
list_missing_subtitles_movies(no=radarrId)
|
2020-02-04 17:57:37 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
# event_stream(type='movies', action='update', movie=radarrId)
|
2020-02-04 17:57:37 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
return '', 204
|
2020-02-04 17:57:37 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
@authenticate
|
|
|
|
def patch(self):
|
|
|
|
radarrid = request.form.get('radarrid')
|
|
|
|
action = request.form.get('action')
|
|
|
|
if action == "scan-disk":
|
|
|
|
movies_scan_subtitles(radarrid)
|
|
|
|
return '', 204
|
|
|
|
elif action == "search-missing":
|
|
|
|
movies_download_subtitles(radarrid)
|
|
|
|
return '', 204
|
|
|
|
elif action == "search-wanted":
|
|
|
|
wanted_search_missing_subtitles_movies()
|
|
|
|
return '', 204
|
2020-02-04 17:57:37 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
return '', 400
|
2020-02-04 17:57:37 +00:00
|
|
|
|
2020-08-13 13:33:12 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
"""
|
|
|
|
:param language: Alpha2 language code
|
|
|
|
"""
|
2020-02-04 17:57:37 +00:00
|
|
|
|
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
class MoviesSubtitles(Resource):
|
2020-08-29 02:09:38 +00:00
|
|
|
@authenticate
|
2021-03-25 14:22:43 +00:00
|
|
|
def patch(self):
|
|
|
|
# Download
|
|
|
|
radarrId = request.args.get('radarrid')
|
2020-08-29 02:09:38 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
movieInfo = database.execute("SELECT title, path, sceneName, audio_language FROM table_movies WHERE radarrId=?",
|
|
|
|
(radarrId,), only_one=True)
|
2020-08-29 02:09:38 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
moviePath = path_mappings.path_replace_movie(movieInfo['path'])
|
|
|
|
sceneName = movieInfo['sceneName']
|
|
|
|
if sceneName is None: sceneName = 'None'
|
2020-08-29 02:09:38 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
title = movieInfo['title']
|
|
|
|
audio_language = movieInfo['audio_language']
|
2020-08-29 02:09:38 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
language = request.form.get('language')
|
|
|
|
hi = request.form.get('hi').capitalize()
|
|
|
|
forced = request.form.get('forced').capitalize()
|
2020-08-29 02:09:38 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
providers_list = get_providers()
|
|
|
|
providers_auth = get_providers_auth()
|
2020-08-29 02:09:38 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
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
|
2021-01-19 04:49:51 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
try:
|
|
|
|
result = download_subtitle(moviePath, language, audio_language, hi, forced, providers_list,
|
|
|
|
providers_auth, sceneName, title, 'movie')
|
|
|
|
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(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', action='update', movie=int(radarrId))
|
|
|
|
except OSError:
|
|
|
|
pass
|
2020-08-29 02:09:38 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
return '', 204
|
2020-08-29 02:09:38 +00:00
|
|
|
|
2020-04-07 16:15:04 +00:00
|
|
|
@authenticate
|
2020-02-05 03:50:35 +00:00
|
|
|
def post(self):
|
2021-03-25 14:22:43 +00:00
|
|
|
# Upload
|
|
|
|
# TODO: Support Multiply Upload
|
|
|
|
radarrId = request.args.get('radarrid')
|
|
|
|
movieInfo = database.execute("SELECT title, path, sceneName, audio_language FROM table_movies WHERE radarrId=?",
|
|
|
|
(radarrId,), only_one=True)
|
2020-02-05 03:50:35 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
moviePath = path_mappings.path_replace_movie(movieInfo['path'])
|
|
|
|
sceneName = movieInfo['sceneName']
|
|
|
|
if sceneName is None: sceneName = 'None'
|
|
|
|
|
|
|
|
title = movieInfo['title']
|
|
|
|
audioLanguage = movieInfo['audio_language']
|
|
|
|
|
|
|
|
language = request.form.get('language')
|
|
|
|
forced = True if request.form.get('forced') == '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.')
|
2020-02-05 03:50:35 +00:00
|
|
|
|
2020-07-23 22:20:58 +00:00
|
|
|
try:
|
2021-03-25 14:22:43 +00:00
|
|
|
result = manual_upload_subtitle(path=moviePath,
|
|
|
|
language=language,
|
|
|
|
forced=forced,
|
|
|
|
title=title,
|
|
|
|
scene_name=sceneName,
|
|
|
|
media_type='movie',
|
|
|
|
subtitle=subFile,
|
|
|
|
audio_language=audioLanguage)
|
2020-07-23 22:20:58 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
if result is not None:
|
|
|
|
message = result[0]
|
|
|
|
path = result[1]
|
|
|
|
subs_path = result[2]
|
|
|
|
if 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
|
2020-02-05 03:50:35 +00:00
|
|
|
|
|
|
|
return '', 204
|
|
|
|
|
2020-04-07 16:15:04 +00:00
|
|
|
@authenticate
|
2020-02-07 17:40:43 +00:00
|
|
|
def delete(self):
|
2021-03-25 14:22:43 +00:00
|
|
|
# Delete
|
|
|
|
radarrId = request.args.get('radarrid')
|
|
|
|
movieInfo = database.execute("SELECT path FROM table_movies WHERE radarrId=?", (radarrId,), only_one=True)
|
|
|
|
|
|
|
|
moviePath = path_mappings.path_replace_movie(movieInfo['path'])
|
|
|
|
|
2020-02-07 17:40:43 +00:00
|
|
|
language = request.form.get('language')
|
2020-07-19 20:02:38 +00:00
|
|
|
forced = request.form.get('forced')
|
2020-09-10 18:26:37 +00:00
|
|
|
hi = request.form.get('hi')
|
2021-03-25 14:22:43 +00:00
|
|
|
subtitlesPath = request.form.get('path')
|
2020-02-07 17:40:43 +00:00
|
|
|
|
2020-07-19 20:02:38 +00:00
|
|
|
result = delete_subtitles(media_type='movie',
|
|
|
|
language=language,
|
|
|
|
forced=forced,
|
2020-09-10 18:26:37 +00:00
|
|
|
hi=hi,
|
2020-07-19 20:02:38 +00:00
|
|
|
media_path=moviePath,
|
|
|
|
subtitles_path=subtitlesPath,
|
|
|
|
radarr_id=radarrId)
|
|
|
|
if result:
|
|
|
|
return '', 202
|
|
|
|
else:
|
|
|
|
return '', 204
|
2020-02-07 17:40:43 +00:00
|
|
|
|
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
class Providers(Resource):
|
|
|
|
@authenticate
|
|
|
|
def get(self):
|
|
|
|
throttled_providers = list_throttled_providers()
|
|
|
|
|
|
|
|
providers = list()
|
|
|
|
for provider in throttled_providers:
|
|
|
|
providers.append({
|
|
|
|
"name": provider[0],
|
|
|
|
"status": provider[1] if provider[1] is not None else "Good",
|
|
|
|
"retry": provider[2] if provider[2] != "now" else "-"
|
|
|
|
})
|
|
|
|
return jsonify(data=providers)
|
|
|
|
|
|
|
|
@authenticate
|
|
|
|
def post(self):
|
|
|
|
action = request.form.get('action')
|
|
|
|
|
|
|
|
if action == 'reset':
|
|
|
|
reset_throttled_providers()
|
|
|
|
return '', 204
|
|
|
|
|
|
|
|
return '', 400
|
|
|
|
|
|
|
|
|
|
|
|
class ProviderMovies(Resource):
|
|
|
|
@authenticate
|
|
|
|
def get(self):
|
|
|
|
# Manual Search
|
|
|
|
radarrId = request.args.get('radarrid')
|
|
|
|
movieInfo = database.execute("SELECT title, path, sceneName, profileId FROM table_movies WHERE radarrId=?",
|
|
|
|
(radarrId,), only_one=True)
|
|
|
|
|
|
|
|
title = movieInfo['title']
|
|
|
|
moviePath = path_mappings.path_replace_movie(movieInfo['path'])
|
|
|
|
sceneName = movieInfo['sceneName']
|
|
|
|
profileId = movieInfo['profileId']
|
|
|
|
if sceneName is None: sceneName = "None"
|
|
|
|
|
|
|
|
providers_list = get_providers()
|
|
|
|
providers_auth = get_providers_auth()
|
|
|
|
|
|
|
|
data = manual_search(moviePath, profileId, providers_list, providers_auth, sceneName, title,
|
|
|
|
'movie')
|
|
|
|
if not data:
|
|
|
|
data = []
|
|
|
|
return jsonify(data=data)
|
|
|
|
|
2020-04-07 16:15:04 +00:00
|
|
|
@authenticate
|
2020-02-07 17:40:43 +00:00
|
|
|
def post(self):
|
2021-03-25 14:22:43 +00:00
|
|
|
# Manual Download
|
|
|
|
radarrId = request.args.get('radarrid')
|
|
|
|
movieInfo = database.execute("SELECT title, path, sceneName, audio_language FROM table_movies WHERE radarrId=?",
|
|
|
|
(radarrId,), only_one=True)
|
|
|
|
|
|
|
|
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']
|
|
|
|
|
2020-02-07 17:40:43 +00:00
|
|
|
language = request.form.get('language')
|
|
|
|
hi = request.form.get('hi').capitalize()
|
|
|
|
forced = request.form.get('forced').capitalize()
|
2021-03-25 14:22:43 +00:00
|
|
|
selected_provider = request.form.get('provider')
|
|
|
|
subtitle = request.form.get('subtitle')
|
|
|
|
|
2020-02-07 17:40:43 +00:00
|
|
|
providers_auth = get_providers_auth()
|
2021-01-19 04:49:51 +00:00
|
|
|
|
|
|
|
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'
|
2020-02-07 17:40:43 +00:00
|
|
|
|
|
|
|
try:
|
2021-03-25 14:22:43 +00:00
|
|
|
result = manual_download_subtitle(moviePath, language, audio_language, hi, forced, subtitle,
|
|
|
|
selected_provider, providers_auth, sceneName, title, 'movie')
|
2020-02-07 17:40:43 +00:00
|
|
|
if result is not None:
|
|
|
|
message = result[0]
|
|
|
|
path = result[1]
|
|
|
|
forced = result[5]
|
2020-09-10 18:26:37 +00:00
|
|
|
if result[8]:
|
|
|
|
language_code = result[2] + ":hi"
|
|
|
|
elif forced:
|
|
|
|
language_code = result[2] + ":forced"
|
|
|
|
else:
|
|
|
|
language_code = result[2]
|
2020-02-07 17:40:43 +00:00
|
|
|
provider = result[3]
|
|
|
|
score = result[4]
|
2020-07-14 19:16:20 +00:00
|
|
|
subs_id = result[6]
|
2020-07-19 20:02:38 +00:00
|
|
|
subs_path = result[7]
|
2021-03-25 14:22:43 +00:00
|
|
|
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)
|
2020-02-07 17:40:43 +00:00
|
|
|
store_subtitles_movie(path, moviePath)
|
|
|
|
except OSError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
return '', 204
|
|
|
|
|
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
class ProviderEpisodes(Resource):
|
2020-04-07 16:15:04 +00:00
|
|
|
@authenticate
|
2021-03-25 14:22:43 +00:00
|
|
|
def get(self):
|
|
|
|
# Manual Search
|
|
|
|
sonarrEpisodeId = request.args.get('episodeid')
|
|
|
|
episodeInfo = database.execute(
|
|
|
|
"SELECT title, path, scene_name, audio_language, sonarrSeriesId FROM table_episodes WHERE sonarrEpisodeId=?",
|
|
|
|
(sonarrEpisodeId,), only_one=True)
|
|
|
|
|
|
|
|
title = episodeInfo['title']
|
|
|
|
episodePath = path_mappings.path_replace(episodeInfo['path'])
|
|
|
|
sceneName = episodeInfo['scene_name']
|
|
|
|
seriesId = episodeInfo['sonarrSeriesId']
|
|
|
|
|
|
|
|
seriesInfo = database.execute("SELECT profileId FROM table_shows WHERE sonarrSeriesId=?", (seriesId,),
|
|
|
|
only_one=True)
|
|
|
|
|
|
|
|
profileId = seriesInfo['profileId']
|
|
|
|
if sceneName is None: sceneName = "None"
|
|
|
|
|
2020-02-07 17:40:43 +00:00
|
|
|
providers_list = get_providers()
|
|
|
|
providers_auth = get_providers_auth()
|
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
data = manual_search(episodePath, profileId, providers_list, providers_auth, sceneName, title,
|
|
|
|
'series')
|
2020-07-04 13:59:26 +00:00
|
|
|
if not data:
|
|
|
|
data = []
|
2021-03-25 14:22:43 +00:00
|
|
|
return jsonify(data=data)
|
2020-02-07 17:40:43 +00:00
|
|
|
|
2020-04-07 16:15:04 +00:00
|
|
|
@authenticate
|
2020-02-07 17:40:43 +00:00
|
|
|
def post(self):
|
2021-03-25 14:22:43 +00:00
|
|
|
# Manual Download
|
|
|
|
sonarrSeriesId = request.args.get('seriesid')
|
|
|
|
sonarrEpisodeId = request.args.get('episodeid')
|
|
|
|
episodeInfo = database.execute("SELECT title, path, scene_name FROM table_episodes WHERE sonarrEpisodeId=?",
|
|
|
|
(sonarrEpisodeId,), only_one=True)
|
|
|
|
|
|
|
|
title = episodeInfo['title']
|
|
|
|
episodePath = path_mappings.path_replace(episodeInfo['path'])
|
|
|
|
sceneName = episodeInfo['scene_name']
|
|
|
|
if sceneName is None: sceneName = "None"
|
|
|
|
|
2020-02-07 17:40:43 +00:00
|
|
|
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()
|
2021-01-19 04:49:51 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
audio_language_list = get_audio_profile_languages(episode_id=sonarrEpisodeId)
|
2021-01-19 04:49:51 +00:00
|
|
|
if len(audio_language_list) > 0:
|
|
|
|
audio_language = audio_language_list[0]['name']
|
|
|
|
else:
|
|
|
|
audio_language = 'None'
|
2020-02-07 17:40:43 +00:00
|
|
|
|
|
|
|
try:
|
2021-03-25 14:22:43 +00:00
|
|
|
result = manual_download_subtitle(episodePath, language, audio_language, hi, forced, subtitle,
|
|
|
|
selected_provider, providers_auth, sceneName, title, 'series')
|
2020-02-07 17:40:43 +00:00
|
|
|
if result is not None:
|
|
|
|
message = result[0]
|
|
|
|
path = result[1]
|
|
|
|
forced = result[5]
|
2020-09-10 18:26:37 +00:00
|
|
|
if result[8]:
|
|
|
|
language_code = result[2] + ":hi"
|
|
|
|
elif forced:
|
|
|
|
language_code = result[2] + ":forced"
|
|
|
|
else:
|
|
|
|
language_code = result[2]
|
2020-02-07 17:40:43 +00:00
|
|
|
provider = result[3]
|
|
|
|
score = result[4]
|
2020-07-14 19:16:20 +00:00
|
|
|
subs_id = result[6]
|
2020-07-19 20:02:38 +00:00
|
|
|
subs_path = result[7]
|
2021-03-25 14:22:43 +00:00
|
|
|
history_log(2, sonarrSeriesId, sonarrEpisodeId, message, path, language_code, provider, score, subs_id,
|
|
|
|
subs_path)
|
2020-08-01 13:12:56 +00:00
|
|
|
if not settings.general.getboolean('dont_notify_manual_actions'):
|
2021-03-25 14:22:43 +00:00
|
|
|
send_notifications(sonarrSeriesId, sonarrEpisodeId, message)
|
|
|
|
store_subtitles(path, episodePath)
|
2020-02-07 17:40:43 +00:00
|
|
|
return result, 201
|
|
|
|
except OSError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
return '', 204
|
|
|
|
|
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
class EpisodesHistory(Resource):
|
2020-04-07 16:15:04 +00:00
|
|
|
@authenticate
|
2019-12-16 04:44:30 +00:00
|
|
|
def get(self):
|
2019-12-17 00:41:50 +00:00
|
|
|
start = request.args.get('start') or 0
|
|
|
|
length = request.args.get('length') or -1
|
2021-03-25 14:22:43 +00:00
|
|
|
episodeid = request.args.get('episodeid')
|
2019-12-17 00:41:50 +00:00
|
|
|
|
2019-12-16 04:44:30 +00:00
|
|
|
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'):
|
2021-02-03 21:01:43 +00:00
|
|
|
query_actions = [1, 2, 3, 6]
|
2019-12-16 04:44:30 +00:00
|
|
|
else:
|
|
|
|
query_actions = [1, 3]
|
|
|
|
|
|
|
|
upgradable_episodes = database.execute(
|
2020-07-13 14:57:43 +00:00
|
|
|
"SELECT video_path, MAX(timestamp) as timestamp, score, table_shows.tags, table_episodes.monitored, "
|
2020-07-19 20:02:38 +00:00
|
|
|
"table_shows.seriesType FROM table_history INNER JOIN table_episodes on "
|
|
|
|
"table_episodes.sonarrEpisodeId = table_history.sonarrEpisodeId INNER JOIN table_shows on "
|
|
|
|
"table_shows.sonarrSeriesId = table_episodes.sonarrSeriesId WHERE action IN (" +
|
2021-03-25 14:22:43 +00:00
|
|
|
','.join(map(str, query_actions)) + ") AND timestamp > ? AND score is not null" +
|
2021-01-19 04:49:51 +00:00
|
|
|
get_exclusion_clause('series') + " GROUP BY table_history.video_path", (minimum_timestamp,))
|
2019-12-16 04:44:30 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
# TODO: Find a better solution
|
|
|
|
query_limit = ""
|
|
|
|
if episodeid:
|
|
|
|
query_limit = f"AND table_episodes.sonarrEpisodeId={episodeid}"
|
|
|
|
|
|
|
|
episode_history = database.execute("SELECT table_shows.title as seriesTitle, table_episodes.monitored, "
|
|
|
|
"table_episodes.season || 'x' || table_episodes.episode as episode_number, "
|
|
|
|
"table_episodes.title as episodeTitle, table_history.timestamp, table_history.subs_id, "
|
|
|
|
"table_history.description, table_history.sonarrSeriesId, table_episodes.path, "
|
|
|
|
"table_history.language, table_history.score, table_shows.tags, table_history.action, "
|
|
|
|
"table_history.subtitles_path, table_history.sonarrEpisodeId, table_history.provider, "
|
|
|
|
"table_shows.seriesType 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 " + query_limit + " ORDER BY timestamp DESC LIMIT ? OFFSET ?",
|
|
|
|
(length, start))
|
|
|
|
|
|
|
|
blacklist_db = database.execute("SELECT provider, subs_id FROM table_blacklist ")
|
2019-12-16 04:44:30 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
for item in episode_history:
|
2019-12-16 04:44:30 +00:00
|
|
|
# Mark episode as upgradable or not
|
2021-02-05 22:40:19 +00:00
|
|
|
item.update({"upgradable": False})
|
2021-03-25 14:22:43 +00:00
|
|
|
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:
|
2021-02-05 22:40:19 +00:00
|
|
|
if os.path.isfile(path_mappings.path_replace(item['subtitles_path'])):
|
|
|
|
item.update({"upgradable": True})
|
2019-12-16 04:44:30 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
del item['path']
|
2019-12-16 04:44:30 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
postprocessEpisode(item)
|
2019-12-16 04:44:30 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
if item['score']:
|
|
|
|
item['score'] = str(round((int(item['score']) * 100 / 360), 2)) + "%"
|
2020-07-19 20:02:38 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
# Make timestamp pretty
|
|
|
|
if item['timestamp']:
|
2021-04-01 01:18:35 +00:00
|
|
|
item["raw_timestamp"] = int(item['timestamp'])
|
|
|
|
item["parsed_timestamp"] = datetime.datetime.fromtimestamp(int(item['timestamp'])).strftime('%x %X')
|
2021-03-25 14:22:43 +00:00
|
|
|
item['timestamp'] = pretty.date(item["raw_timestamp"])
|
2020-07-19 20:02:38 +00:00
|
|
|
|
|
|
|
# Check if subtitles is blacklisted
|
2021-03-25 14:22:43 +00:00
|
|
|
item.update({"blacklisted": False})
|
2020-07-19 20:02:38 +00:00
|
|
|
if item['action'] not in [0, 4, 5]:
|
2021-03-25 14:22:43 +00:00
|
|
|
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
|
2020-07-19 20:02:38 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
count = database.execute("SELECT COUNT(*) as count FROM table_history LEFT JOIN table_episodes "
|
|
|
|
"on table_episodes.sonarrEpisodeId = table_history.sonarrEpisodeId WHERE "
|
|
|
|
"table_episodes.title is not NULL", only_one=True)['count']
|
2019-12-16 04:44:30 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
return jsonify(data=episode_history, total=count)
|
2019-12-16 04:44:30 +00:00
|
|
|
|
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
class MoviesHistory(Resource):
|
2020-04-07 16:15:04 +00:00
|
|
|
@authenticate
|
2019-12-16 04:44:30 +00:00
|
|
|
def get(self):
|
2019-12-17 00:41:50 +00:00
|
|
|
start = request.args.get('start') or 0
|
|
|
|
length = request.args.get('length') or -1
|
2021-03-25 14:22:43 +00:00
|
|
|
radarrid = request.args.get('radarrid')
|
2019-12-16 17:46:03 +00:00
|
|
|
|
2019-12-16 04:44:30 +00:00
|
|
|
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'):
|
2021-02-03 21:01:43 +00:00
|
|
|
query_actions = [1, 2, 3, 6]
|
2019-12-16 04:44:30 +00:00
|
|
|
else:
|
|
|
|
query_actions = [1, 3]
|
|
|
|
|
|
|
|
upgradable_movies = database.execute(
|
2020-07-12 20:37:20 +00:00
|
|
|
"SELECT video_path, MAX(timestamp) as timestamp, score, tags, monitored FROM table_history_movie "
|
2019-12-16 04:44:30 +00:00
|
|
|
"INNER JOIN table_movies on table_movies.radarrId=table_history_movie.radarrId WHERE action IN (" +
|
2021-01-19 04:49:51 +00:00
|
|
|
','.join(map(str, query_actions)) + ") AND timestamp > ? AND score is not NULL" +
|
|
|
|
get_exclusion_clause('movie') + " GROUP BY video_path", (minimum_timestamp,))
|
2019-12-16 04:44:30 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
# TODO: Find a better solution
|
|
|
|
query_limit = ""
|
|
|
|
if radarrid:
|
|
|
|
query_limit = f"AND table_movies.radarrid={radarrid}"
|
2019-12-16 04:44:30 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
movie_history = database.execute(
|
|
|
|
"SELECT table_history_movie.action, table_movies.title, table_history_movie.timestamp, "
|
|
|
|
"table_history_movie.description, table_history_movie.radarrId, table_movies.monitored,"
|
|
|
|
"table_history_movie.video_path as path, table_history_movie.language, table_movies.tags, "
|
|
|
|
"table_history_movie.score, table_history_movie.subs_id, table_history_movie.provider, "
|
|
|
|
"table_history_movie.subtitles_path, table_history_movie.subtitles_path FROM "
|
|
|
|
"table_history_movie LEFT JOIN table_movies on table_movies.radarrId = "
|
|
|
|
"table_history_movie.radarrId WHERE table_movies.title is not NULL " + query_limit + " ORDER BY timestamp DESC LIMIT ? OFFSET ?",
|
|
|
|
(length, start))
|
|
|
|
|
|
|
|
blacklist_db = database.execute("SELECT provider, subs_id FROM table_blacklist_movie")
|
|
|
|
|
|
|
|
for item in movie_history:
|
2019-12-16 04:44:30 +00:00
|
|
|
# Mark movies as upgradable or not
|
2021-02-05 22:40:19 +00:00
|
|
|
item.update({"upgradable": False})
|
2021-03-25 14:22:43 +00:00
|
|
|
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:
|
2021-02-05 22:40:19 +00:00
|
|
|
if os.path.isfile(path_mappings.path_replace_movie(item['subtitles_path'])):
|
|
|
|
item.update({"upgradable": True})
|
2019-12-16 04:44:30 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
del item['path']
|
2019-12-16 04:44:30 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
postprocessMovie(item)
|
2019-12-16 04:44:30 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
if item['score']:
|
|
|
|
item['score'] = str(round((int(item['score']) * 100 / 120), 2)) + "%"
|
2019-12-16 04:44:30 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
# Make timestamp pretty
|
|
|
|
if item['timestamp']:
|
2021-04-01 01:18:35 +00:00
|
|
|
item["raw_timestamp"] = int(item['timestamp'])
|
|
|
|
item["parsed_timestamp"] = datetime.datetime.fromtimestamp(int(item['timestamp'])).strftime('%x %X')
|
2021-03-25 14:22:43 +00:00
|
|
|
item['timestamp'] = pretty.date(item["raw_timestamp"])
|
2020-07-19 20:02:38 +00:00
|
|
|
|
|
|
|
# Check if subtitles is blacklisted
|
2021-03-25 14:22:43 +00:00
|
|
|
item.update({"blacklisted": False})
|
2020-07-19 20:02:38 +00:00
|
|
|
if item['action'] not in [0, 4, 5]:
|
2021-03-25 14:22:43 +00:00
|
|
|
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
|
2020-07-19 20:02:38 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
count = database.execute("SELECT COUNT(*) as count FROM table_history_movie LEFT JOIN table_movies on "
|
|
|
|
"table_movies.radarrId = table_history_movie.radarrId WHERE table_movies.title "
|
|
|
|
"is not NULL", only_one=True)['count']
|
2020-07-19 20:02:38 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
return jsonify(data=movie_history, total=count)
|
2019-12-16 04:44:30 +00:00
|
|
|
|
|
|
|
|
2020-06-19 13:18:48 +00:00
|
|
|
class HistoryStats(Resource):
|
|
|
|
@authenticate
|
|
|
|
def get(self):
|
|
|
|
timeframe = request.args.get('timeframe') or 'month'
|
|
|
|
action = request.args.get('action') or 'All'
|
|
|
|
provider = request.args.get('provider') or 'All'
|
|
|
|
language = request.args.get('language') or 'All'
|
|
|
|
|
|
|
|
history_where_clause = " WHERE id"
|
|
|
|
|
|
|
|
# timeframe must be in ['week', 'month', 'trimester', 'year']
|
|
|
|
if timeframe == 'year':
|
|
|
|
days = 364
|
|
|
|
elif timeframe == 'trimester':
|
|
|
|
days = 90
|
|
|
|
elif timeframe == 'month':
|
|
|
|
days = 30
|
|
|
|
elif timeframe == 'week':
|
|
|
|
days = 6
|
|
|
|
|
|
|
|
history_where_clause += " AND datetime(timestamp, 'unixepoch') BETWEEN datetime('now', '-" + str(days) + \
|
|
|
|
" days') AND datetime('now', 'localtime')"
|
|
|
|
if action != 'All':
|
|
|
|
history_where_clause += " AND action = " + action
|
|
|
|
else:
|
|
|
|
history_where_clause += " AND action IN (1,2,3)"
|
|
|
|
if provider != 'All':
|
|
|
|
history_where_clause += " AND provider = '" + provider + "'"
|
|
|
|
if language != 'All':
|
|
|
|
history_where_clause += " AND language = '" + language + "'"
|
|
|
|
|
|
|
|
data_series = database.execute("SELECT strftime ('%Y-%m-%d',datetime(timestamp, 'unixepoch')) as date, "
|
|
|
|
"COUNT(id) as count FROM table_history" + history_where_clause +
|
|
|
|
" GROUP BY strftime ('%Y-%m-%d',datetime(timestamp, 'unixepoch'))")
|
|
|
|
data_movies = database.execute("SELECT strftime ('%Y-%m-%d',datetime(timestamp, 'unixepoch')) as date, "
|
|
|
|
"COUNT(id) as count FROM table_history_movie" + history_where_clause +
|
|
|
|
" GROUP BY strftime ('%Y-%m-%d',datetime(timestamp, 'unixepoch'))")
|
|
|
|
|
|
|
|
for dt in rrule.rrule(rrule.DAILY,
|
|
|
|
dtstart=datetime.datetime.now() - datetime.timedelta(days=days),
|
|
|
|
until=datetime.datetime.now()):
|
|
|
|
if not any(d['date'] == dt.strftime('%Y-%m-%d') for d in data_series):
|
|
|
|
data_series.append({'date': dt.strftime('%Y-%m-%d'), 'count': 0})
|
|
|
|
if not any(d['date'] == dt.strftime('%Y-%m-%d') for d in data_movies):
|
|
|
|
data_movies.append({'date': dt.strftime('%Y-%m-%d'), 'count': 0})
|
|
|
|
|
|
|
|
sorted_data_series = sorted(data_series, key=lambda i: i['date'])
|
|
|
|
sorted_data_movies = sorted(data_movies, key=lambda i: i['date'])
|
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
return jsonify(series=sorted_data_series, movies=sorted_data_movies)
|
2020-06-19 13:18:48 +00:00
|
|
|
|
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
# GET: Get Wanted Episodes
|
|
|
|
class EpisodesWanted(Resource):
|
2020-04-07 16:15:04 +00:00
|
|
|
@authenticate
|
2019-12-16 04:44:30 +00:00
|
|
|
def get(self):
|
2019-12-17 00:41:50 +00:00
|
|
|
start = request.args.get('start') or 0
|
|
|
|
length = request.args.get('length') or -1
|
2020-07-12 20:37:20 +00:00
|
|
|
data = database.execute("SELECT table_shows.title as seriesTitle, table_episodes.monitored, "
|
2019-12-16 04:44:30 +00:00
|
|
|
"table_episodes.season || 'x' || table_episodes.episode as episode_number, "
|
|
|
|
"table_episodes.title as episodeTitle, table_episodes.missing_subtitles, "
|
2021-03-25 14:22:43 +00:00
|
|
|
"table_episodes.sonarrSeriesId, "
|
|
|
|
"table_episodes.sonarrEpisodeId, table_episodes.scene_name as sceneName, table_shows.tags, "
|
2020-07-13 14:57:43 +00:00
|
|
|
"table_episodes.failedAttempts, table_shows.seriesType FROM table_episodes INNER JOIN "
|
|
|
|
"table_shows on table_shows.sonarrSeriesId = table_episodes.sonarrSeriesId WHERE "
|
2020-09-01 16:30:31 +00:00
|
|
|
"table_episodes.missing_subtitles != '[]'" + get_exclusion_clause('series') +
|
2021-01-22 15:36:23 +00:00
|
|
|
" ORDER BY table_episodes._rowid_ DESC LIMIT ? OFFSET ?", (length, start))
|
2019-12-16 04:44:30 +00:00
|
|
|
|
|
|
|
for item in data:
|
2021-03-25 14:22:43 +00:00
|
|
|
postprocessEpisode(item)
|
2019-12-16 04:44:30 +00:00
|
|
|
|
2021-03-25 18:43:06 +00:00
|
|
|
count = database.execute("SELECT COUNT(*) as count, table_shows.tags, table_shows.seriesType FROM "
|
|
|
|
"table_episodes INNER JOIN table_shows on table_shows.sonarrSeriesId = "
|
|
|
|
"table_episodes.sonarrSeriesId WHERE missing_subtitles != '[]'" +
|
|
|
|
get_exclusion_clause('series'), only_one=True)['count']
|
2019-12-16 04:44:30 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
return jsonify(data=data, total=count)
|
2019-12-16 04:44:30 +00:00
|
|
|
|
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
# GET: Get Wanted Movies
|
|
|
|
class MoviesWanted(Resource):
|
2020-04-07 16:15:04 +00:00
|
|
|
@authenticate
|
2019-12-16 04:44:30 +00:00
|
|
|
def get(self):
|
2019-12-17 00:41:50 +00:00
|
|
|
start = request.args.get('start') or 0
|
|
|
|
length = request.args.get('length') or -1
|
2021-03-25 14:22:43 +00:00
|
|
|
data = database.execute("SELECT title, missing_subtitles, radarrId, sceneName, "
|
2020-09-01 16:30:31 +00:00
|
|
|
"failedAttempts, tags, monitored FROM table_movies WHERE missing_subtitles != '[]'" +
|
2021-03-25 14:22:43 +00:00
|
|
|
get_exclusion_clause('movie') +
|
|
|
|
" ORDER BY _rowid_ DESC LIMIT ? OFFSET ?", (length, start))
|
2019-12-16 04:44:30 +00:00
|
|
|
|
|
|
|
for item in data:
|
2021-03-25 14:22:43 +00:00
|
|
|
postprocessMovie(item)
|
2019-12-16 04:44:30 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
count = database.execute("SELECT COUNT(*) as count FROM table_movies WHERE missing_subtitles != '[]'" +
|
2021-03-25 15:23:18 +00:00
|
|
|
get_exclusion_clause('movie'), only_one=True)['count']
|
2019-12-16 04:44:30 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
return jsonify(data=data, total=count)
|
2020-02-13 04:16:22 +00:00
|
|
|
|
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
# GET: get blacklist
|
|
|
|
# POST: add blacklist
|
|
|
|
# DELETE: remove blacklist
|
|
|
|
class EpisodesBlacklist(Resource):
|
2020-07-19 20:02:38 +00:00
|
|
|
@authenticate
|
|
|
|
def get(self):
|
|
|
|
start = request.args.get('start') or 0
|
|
|
|
length = request.args.get('length') or -1
|
|
|
|
|
|
|
|
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.sonarrSeriesId, table_blacklist.provider, table_blacklist.subs_id, "
|
|
|
|
"table_blacklist.language, table_blacklist.timestamp FROM table_blacklist INNER JOIN "
|
|
|
|
"table_episodes on table_episodes.sonarrEpisodeId = table_blacklist.sonarr_episode_id "
|
|
|
|
"INNER JOIN table_shows on table_shows.sonarrSeriesId = "
|
|
|
|
"table_blacklist.sonarr_series_id ORDER BY table_blacklist.timestamp DESC LIMIT ? "
|
|
|
|
"OFFSET ?", (length, start))
|
|
|
|
|
|
|
|
for item in data:
|
|
|
|
# Make timestamp pretty
|
2021-04-01 01:18:35 +00:00
|
|
|
item["parsed_timestamp"] = datetime.datetime.fromtimestamp(int(item['timestamp'])).strftime('%x %X')
|
2020-07-19 20:02:38 +00:00
|
|
|
item.update({'timestamp': pretty.date(datetime.datetime.fromtimestamp(item['timestamp']))})
|
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
postprocessEpisode(item)
|
2020-07-19 20:02:38 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
return jsonify(data=data)
|
2020-07-19 20:02:38 +00:00
|
|
|
|
|
|
|
@authenticate
|
|
|
|
def post(self):
|
2021-03-25 14:22:43 +00:00
|
|
|
sonarr_series_id = int(request.args.get('seriesid'))
|
|
|
|
sonarr_episode_id = int(request.args.get('episodeid'))
|
2020-07-19 20:02:38 +00:00
|
|
|
provider = request.form.get('provider')
|
|
|
|
subs_id = request.form.get('subs_id')
|
|
|
|
language = request.form.get('language')
|
2021-03-25 14:22:43 +00:00
|
|
|
|
|
|
|
episodeInfo = database.execute("SELECT path FROM table_episodes WHERE sonarrEpisodeId=?",
|
|
|
|
(sonarr_episode_id,), only_one=True)
|
|
|
|
|
|
|
|
media_path = episodeInfo['path']
|
2020-07-19 20:02:38 +00:00
|
|
|
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,
|
2021-03-25 14:22:43 +00:00
|
|
|
language=language)
|
2020-07-19 20:02:38 +00:00
|
|
|
delete_subtitles(media_type='series',
|
|
|
|
language=alpha3_from_alpha2(language),
|
2021-03-25 14:22:43 +00:00
|
|
|
forced=False,
|
|
|
|
hi=False,
|
|
|
|
media_path=media_path,
|
|
|
|
subtitles_path=subtitles_path,
|
2020-07-19 20:02:38 +00:00
|
|
|
sonarr_series_id=sonarr_series_id,
|
|
|
|
sonarr_episode_id=sonarr_episode_id)
|
|
|
|
episode_download_subtitles(sonarr_episode_id)
|
|
|
|
event_stream(type='episodeHistory')
|
|
|
|
return '', 200
|
|
|
|
|
|
|
|
@authenticate
|
|
|
|
def delete(self):
|
2021-03-25 14:22:43 +00:00
|
|
|
if request.args.get("all") == "true":
|
|
|
|
blacklist_delete_all()
|
|
|
|
else:
|
|
|
|
provider = request.form.get('provider')
|
|
|
|
subs_id = request.form.get('subs_id')
|
|
|
|
blacklist_delete(provider=provider, subs_id=subs_id)
|
|
|
|
return '', 204
|
2020-07-19 20:02:38 +00:00
|
|
|
|
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
# GET: get blacklist
|
|
|
|
# POST: add blacklist
|
|
|
|
# DELETE: remove blacklist
|
|
|
|
class MoviesBlacklist(Resource):
|
2020-07-19 20:02:38 +00:00
|
|
|
@authenticate
|
|
|
|
def get(self):
|
|
|
|
start = request.args.get('start') or 0
|
|
|
|
length = request.args.get('length') or -1
|
|
|
|
|
|
|
|
data = database.execute("SELECT table_movies.title, table_movies.radarrId, table_blacklist_movie.provider, "
|
|
|
|
"table_blacklist_movie.subs_id, table_blacklist_movie.language, "
|
|
|
|
"table_blacklist_movie.timestamp FROM table_blacklist_movie INNER JOIN "
|
|
|
|
"table_movies on table_movies.radarrId = table_blacklist_movie.radarr_id "
|
|
|
|
"ORDER BY table_blacklist_movie.timestamp DESC LIMIT ? "
|
|
|
|
"OFFSET ?", (length, start))
|
|
|
|
|
|
|
|
for item in data:
|
2021-03-25 14:22:43 +00:00
|
|
|
postprocessMovie(item)
|
|
|
|
|
2020-07-19 20:02:38 +00:00
|
|
|
# Make timestamp pretty
|
2021-04-01 01:18:35 +00:00
|
|
|
item["parsed_timestamp"] = datetime.datetime.fromtimestamp(int(item['timestamp'])).strftime('%x %X')
|
2020-07-19 20:02:38 +00:00
|
|
|
item.update({'timestamp': pretty.date(datetime.datetime.fromtimestamp(item['timestamp']))})
|
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
return jsonify(data=data)
|
2020-07-19 20:02:38 +00:00
|
|
|
|
|
|
|
@authenticate
|
|
|
|
def post(self):
|
2021-03-25 14:22:43 +00:00
|
|
|
radarr_id = int(request.args.get('radarrid'))
|
2020-07-19 20:02:38 +00:00
|
|
|
provider = request.form.get('provider')
|
|
|
|
subs_id = request.form.get('subs_id')
|
|
|
|
language = request.form.get('language')
|
2021-03-25 14:22:43 +00:00
|
|
|
|
|
|
|
data = database.execute("SELECT title, radarrId, provider, subs_id, path"
|
|
|
|
"timestamp FROM table_movies WHERE radarrId=?", (radarr_id), only_one=True)
|
|
|
|
|
|
|
|
media_path = data['path']
|
2020-07-19 20:02:38 +00:00
|
|
|
subtitles_path = request.form.get('subtitles_path')
|
|
|
|
|
|
|
|
blacklist_log_movie(radarr_id=radarr_id,
|
|
|
|
provider=provider,
|
|
|
|
subs_id=subs_id,
|
2021-03-25 14:22:43 +00:00
|
|
|
language=language)
|
2020-07-19 20:02:38 +00:00
|
|
|
delete_subtitles(media_type='movie',
|
|
|
|
language=alpha3_from_alpha2(language),
|
|
|
|
forced=forced,
|
2020-09-10 18:26:37 +00:00
|
|
|
hi=hi,
|
2021-03-25 14:22:43 +00:00
|
|
|
media_path=media_path,
|
|
|
|
subtitles_path=subtitles_path,
|
2020-07-19 20:02:38 +00:00
|
|
|
radarr_id=radarr_id)
|
|
|
|
movies_download_subtitles(radarr_id)
|
|
|
|
event_stream(type='movieHistory')
|
|
|
|
return '', 200
|
|
|
|
|
|
|
|
@authenticate
|
|
|
|
def delete(self):
|
2021-03-25 14:22:43 +00:00
|
|
|
if request.args.get("all") == "true":
|
|
|
|
blacklist_delete_all_movie()
|
|
|
|
else:
|
|
|
|
provider = request.form.get('provider')
|
|
|
|
subs_id = request.form.get('subs_id')
|
|
|
|
blacklist_delete_movie(provider=provider, subs_id=subs_id)
|
2020-07-19 20:02:38 +00:00
|
|
|
return '', 200
|
|
|
|
|
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
class Subtitles(Resource):
|
2020-07-19 20:02:38 +00:00
|
|
|
@authenticate
|
2021-03-25 14:22:43 +00:00
|
|
|
def patch(self):
|
|
|
|
action = request.args.get('action')
|
2020-07-19 20:02:38 +00:00
|
|
|
|
2020-06-12 19:08:44 +00:00
|
|
|
language = request.form.get('language')
|
2021-03-25 14:22:43 +00:00
|
|
|
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 = database.execute("SELECT path, sonarrSeriesId FROM table_episodes"
|
|
|
|
" WHERE sonarrEpisodeId = ?", (id,), only_one=True)
|
|
|
|
video_path = path_mappings.path_replace(metadata['path'])
|
2020-06-12 19:08:44 +00:00
|
|
|
else:
|
2021-03-25 14:22:43 +00:00
|
|
|
subtitles_path = path_mappings.path_replace_movie(subtitles_path)
|
|
|
|
metadata = database.execute("SELECT path FROM table_movies WHERE radarrId = ?",
|
|
|
|
(id,), only_one=True)
|
|
|
|
video_path = path_mappings.path_replace_movie(metadata['path'])
|
|
|
|
|
|
|
|
if action == 'sync':
|
|
|
|
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))
|
2021-03-11 15:25:46 +00:00
|
|
|
else:
|
2021-03-25 14:22:43 +00:00
|
|
|
subsync.sync(video_path=video_path, srt_path=subtitles_path,
|
|
|
|
srt_lang=language, media_type='movies', radarr_id=id)
|
|
|
|
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])
|
2020-08-05 12:43:53 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
return '', 204
|
2020-08-05 12:43:53 +00:00
|
|
|
|
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
class SubtitleNameInfo(Resource):
|
2021-02-02 21:05:09 +00:00
|
|
|
@authenticate
|
2021-03-25 14:22:43 +00:00
|
|
|
def get(self):
|
|
|
|
names = request.args.getlist('filenames[]')
|
|
|
|
results = []
|
|
|
|
for name in names:
|
|
|
|
opts = dict()
|
|
|
|
opts['type'] = 'episode'
|
|
|
|
result = guessit(name, options=opts)
|
|
|
|
result['filename'] = name
|
|
|
|
if 'subtitle_language' in result:
|
|
|
|
result['subtitle_language'] = str(result['subtitle_language'])
|
2021-02-02 21:05:09 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
if 'episode' in result:
|
|
|
|
result['episode'] = result['episode']
|
|
|
|
else:
|
|
|
|
result['episode'] = 0
|
2021-02-02 21:05:09 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
if 'season' in result:
|
|
|
|
result['season'] = result['season']
|
2021-02-02 21:05:09 +00:00
|
|
|
else:
|
2021-03-25 14:22:43 +00:00
|
|
|
result['season'] = 0
|
|
|
|
|
|
|
|
results.append(result)
|
|
|
|
|
|
|
|
return jsonify(data=results)
|
2021-02-02 21:05:09 +00:00
|
|
|
|
|
|
|
|
2020-06-20 13:03:48 +00:00
|
|
|
class BrowseBazarrFS(Resource):
|
|
|
|
@authenticate
|
|
|
|
def get(self):
|
2020-06-26 15:28:49 +00:00
|
|
|
path = request.args.get('path') or ''
|
2020-06-25 10:33:48 +00:00
|
|
|
data = []
|
2021-03-25 14:22:43 +00:00
|
|
|
try:
|
|
|
|
result = browse_bazarr_filesystem(path)
|
|
|
|
if result is None:
|
|
|
|
raise ValueError
|
|
|
|
except Exception:
|
|
|
|
return jsonify([])
|
2020-06-25 10:33:48 +00:00
|
|
|
for item in result['directories']:
|
2021-03-25 14:22:43 +00:00
|
|
|
data.append({'name': item['name'], 'children': True, 'path': item['path']})
|
2020-06-26 15:28:49 +00:00
|
|
|
return jsonify(data)
|
2020-06-20 13:03:48 +00:00
|
|
|
|
|
|
|
|
|
|
|
class BrowseSonarrFS(Resource):
|
|
|
|
@authenticate
|
|
|
|
def get(self):
|
2020-06-26 15:28:49 +00:00
|
|
|
path = request.args.get('path') or ''
|
2020-06-25 10:33:48 +00:00
|
|
|
data = []
|
2021-03-25 14:22:43 +00:00
|
|
|
try:
|
|
|
|
result = browse_sonarr_filesystem(path)
|
|
|
|
if result is None:
|
|
|
|
raise ValueError
|
|
|
|
except Exception:
|
|
|
|
return jsonify([])
|
2020-06-25 10:33:48 +00:00
|
|
|
for item in result['directories']:
|
2021-03-25 14:22:43 +00:00
|
|
|
data.append({'name': item['name'], 'children': True, 'path': item['path']})
|
2020-06-26 15:28:49 +00:00
|
|
|
return jsonify(data)
|
2020-06-20 13:03:48 +00:00
|
|
|
|
|
|
|
|
|
|
|
class BrowseRadarrFS(Resource):
|
|
|
|
@authenticate
|
|
|
|
def get(self):
|
2020-06-26 15:28:49 +00:00
|
|
|
path = request.args.get('path') or ''
|
2020-06-25 10:33:48 +00:00
|
|
|
data = []
|
2021-03-25 14:22:43 +00:00
|
|
|
try:
|
|
|
|
result = browse_radarr_filesystem(path)
|
|
|
|
if result is None:
|
|
|
|
raise ValueError
|
|
|
|
except Exception:
|
|
|
|
return jsonify([])
|
2020-06-25 10:33:48 +00:00
|
|
|
for item in result['directories']:
|
2021-03-25 14:22:43 +00:00
|
|
|
data.append({'name': item['name'], 'children': True, 'path': item['path']})
|
2020-06-26 15:28:49 +00:00
|
|
|
return jsonify(data)
|
2020-06-20 13:03:48 +00:00
|
|
|
|
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
api.add_resource(BadgesSeries, '/badges')
|
2020-02-04 17:57:37 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
api.add_resource(Providers, '/providers')
|
|
|
|
api.add_resource(ProviderMovies, '/providers/movies')
|
|
|
|
api.add_resource(ProviderEpisodes, '/providers/episodes')
|
2020-03-29 13:58:32 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
api.add_resource(System, '/system')
|
|
|
|
api.add_resource(Searches, "/system/searches")
|
|
|
|
api.add_resource(SystemAccount, '/system/account')
|
|
|
|
api.add_resource(SystemTasks, '/system/tasks')
|
|
|
|
api.add_resource(SystemLogs, '/system/logs')
|
|
|
|
api.add_resource(SystemStatus, '/system/status')
|
|
|
|
api.add_resource(SystemReleases, '/system/releases')
|
|
|
|
api.add_resource(SystemSettings, '/system/settings')
|
|
|
|
api.add_resource(Languages, '/system/languages')
|
|
|
|
api.add_resource(LanguagesProfiles, '/system/languages/profiles')
|
|
|
|
api.add_resource(Notifications, '/system/notifications')
|
2020-04-30 12:38:05 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
api.add_resource(Subtitles, '/subtitles')
|
|
|
|
api.add_resource(SubtitleNameInfo, '/subtitles/info')
|
2020-09-26 12:58:56 +00:00
|
|
|
|
2019-12-28 05:52:00 +00:00
|
|
|
api.add_resource(Series, '/series')
|
2021-03-25 14:22:43 +00:00
|
|
|
|
2019-12-28 05:52:00 +00:00
|
|
|
api.add_resource(Episodes, '/episodes')
|
2021-03-25 14:22:43 +00:00
|
|
|
api.add_resource(EpisodesWanted, '/episodes/wanted')
|
|
|
|
api.add_resource(EpisodesSubtitles, '/episodes/subtitles')
|
|
|
|
api.add_resource(EpisodesHistory, '/episodes/history')
|
|
|
|
api.add_resource(EpisodesBlacklist, '/episodes/blacklist')
|
2020-02-04 17:57:37 +00:00
|
|
|
|
2019-12-28 05:52:00 +00:00
|
|
|
api.add_resource(Movies, '/movies')
|
2021-03-25 14:22:43 +00:00
|
|
|
api.add_resource(MoviesWanted, '/movies/wanted')
|
|
|
|
api.add_resource(MoviesSubtitles, '/movies/subtitles')
|
|
|
|
api.add_resource(MoviesHistory, '/movies/history')
|
|
|
|
api.add_resource(MoviesBlacklist, '/movies/blacklist')
|
|
|
|
|
|
|
|
api.add_resource(HistoryStats, '/history/stats')
|
2021-03-11 15:25:46 +00:00
|
|
|
|
2021-03-25 14:22:43 +00:00
|
|
|
api.add_resource(BrowseBazarrFS, '/files')
|
|
|
|
api.add_resource(BrowseSonarrFS, '/files/sonarr')
|
|
|
|
api.add_resource(BrowseRadarrFS, '/files/radarr')
|