2019-01-26 21:12:19 +00:00
|
|
|
# coding=utf-8
|
2018-10-03 10:53:22 +00:00
|
|
|
|
2020-01-29 11:53:29 +00:00
|
|
|
bazarr_version = '0.9'
|
2019-02-11 11:46:04 +00:00
|
|
|
|
2019-10-28 20:16:33 +00:00
|
|
|
import os
|
|
|
|
os.environ["SZ_USER_AGENT"] = "Bazarr/1"
|
|
|
|
os.environ["BAZARR_VERSION"] = bazarr_version
|
2019-09-18 15:30:46 +00:00
|
|
|
|
2019-09-28 16:38:26 +00:00
|
|
|
import gc
|
|
|
|
import sys
|
|
|
|
import libs
|
2020-01-30 01:30:30 +00:00
|
|
|
import io
|
2019-09-28 16:38:26 +00:00
|
|
|
|
2018-11-28 11:45:39 +00:00
|
|
|
import hashlib
|
|
|
|
import warnings
|
2019-01-01 20:31:01 +00:00
|
|
|
import apprise
|
2020-03-21 13:24:32 +00:00
|
|
|
import requests
|
2020-05-07 11:39:59 +00:00
|
|
|
import calendar
|
2018-10-31 19:34:40 +00:00
|
|
|
|
2019-11-28 11:34:37 +00:00
|
|
|
|
2018-10-31 19:34:40 +00:00
|
|
|
from get_args import args
|
2019-11-06 05:08:17 +00:00
|
|
|
from logger import empty_log
|
2020-05-08 04:22:14 +00:00
|
|
|
from config import settings, url_sonarr, url_radarr, url_radarr_short, url_sonarr_short, base_url, configure_proxy_func
|
2019-10-28 14:38:57 +00:00
|
|
|
|
2018-10-03 10:53:22 +00:00
|
|
|
from init import *
|
2019-11-06 05:08:17 +00:00
|
|
|
import logging
|
2019-10-26 18:52:22 +00:00
|
|
|
from database import database, dict_mapper
|
2019-02-24 03:21:40 +00:00
|
|
|
|
2018-10-22 16:39:11 +00:00
|
|
|
from notifier import update_notifier
|
2019-02-24 03:21:40 +00:00
|
|
|
|
2020-04-15 04:02:44 +00:00
|
|
|
from waitress.server import create_server
|
2019-02-24 03:21:40 +00:00
|
|
|
|
2020-02-13 04:16:22 +00:00
|
|
|
from urllib.parse import unquote
|
2019-11-20 03:02:59 +00:00
|
|
|
from get_languages import load_language_in_db, language_from_alpha3, language_from_alpha2, alpha2_from_alpha3
|
2020-01-21 00:35:55 +00:00
|
|
|
from flask import make_response, request, redirect, abort, render_template, Response, session, flash, url_for, \
|
2019-12-31 22:09:04 +00:00
|
|
|
send_file, stream_with_context
|
2019-11-20 03:02:59 +00:00
|
|
|
|
2018-11-28 11:45:39 +00:00
|
|
|
from get_series import *
|
|
|
|
from get_episodes import *
|
2019-09-03 03:22:38 +00:00
|
|
|
from get_movies import *
|
2018-11-28 11:45:39 +00:00
|
|
|
|
2020-02-01 00:54:17 +00:00
|
|
|
from scheduler import Scheduler
|
2020-03-21 13:24:32 +00:00
|
|
|
from check_update import check_and_apply_update
|
2019-12-29 21:17:39 +00:00
|
|
|
from functools import wraps
|
2018-10-03 10:53:22 +00:00
|
|
|
|
2020-05-11 17:46:34 +00:00
|
|
|
from app import create_app
|
2020-01-21 00:35:55 +00:00
|
|
|
app = create_app()
|
2019-12-16 13:58:10 +00:00
|
|
|
|
|
|
|
from api import api_bp
|
|
|
|
app.register_blueprint(api_bp)
|
2019-11-28 11:34:37 +00:00
|
|
|
|
2020-02-01 02:54:53 +00:00
|
|
|
scheduler = Scheduler()
|
2019-12-10 20:39:17 +00:00
|
|
|
|
2019-06-02 23:19:51 +00:00
|
|
|
# Check and install update on startup when running on Windows from installer
|
|
|
|
if args.release_update:
|
|
|
|
check_and_apply_update()
|
|
|
|
|
2020-05-08 04:22:14 +00:00
|
|
|
configure_proxy_func()
|
2018-10-03 10:53:22 +00:00
|
|
|
|
|
|
|
# Reset restart required warning on start
|
2019-10-22 02:13:37 +00:00
|
|
|
database.execute("UPDATE system SET configured='0', updated='0'")
|
2018-10-03 10:53:22 +00:00
|
|
|
|
|
|
|
# Load languages in database
|
|
|
|
load_language_in_db()
|
|
|
|
|
2018-12-15 00:36:28 +00:00
|
|
|
login_auth = settings.auth.type
|
2018-10-03 10:53:22 +00:00
|
|
|
|
2019-08-09 00:39:30 +00:00
|
|
|
update_notifier()
|
|
|
|
|
|
|
|
|
2018-10-03 10:53:22 +00:00
|
|
|
def check_credentials(user, pw):
|
2018-12-15 00:36:28 +00:00
|
|
|
username = settings.auth.username
|
|
|
|
password = settings.auth.password
|
2019-12-03 23:45:06 +00:00
|
|
|
if hashlib.md5(pw.encode('utf-8')).hexdigest() == password and user == username:
|
2018-10-03 10:53:22 +00:00
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2020-05-15 15:43:55 +00:00
|
|
|
|
2019-12-29 21:17:39 +00:00
|
|
|
def login_required(f):
|
|
|
|
@wraps(f)
|
|
|
|
def wrap(*args, **kwargs):
|
|
|
|
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"'
|
|
|
|
})
|
|
|
|
|
|
|
|
return f(*args, **kwargs)
|
2019-12-30 13:16:12 +00:00
|
|
|
elif settings.auth.type == 'form':
|
2019-12-29 21:17:39 +00:00
|
|
|
if 'logged_in' in session:
|
|
|
|
return f(*args, **kwargs)
|
|
|
|
else:
|
|
|
|
flash("You need to login first")
|
|
|
|
return redirect(url_for('login_page'))
|
|
|
|
else:
|
|
|
|
return f(*args, **kwargs)
|
|
|
|
return wrap
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/login/', methods=["GET", "POST"])
|
|
|
|
def login_page():
|
|
|
|
error = ''
|
2020-05-10 13:23:42 +00:00
|
|
|
password_reset = False
|
|
|
|
if settings.auth.password == hashlib.md5(settings.auth.username.encode('utf-8')).hexdigest():
|
|
|
|
password_reset = True
|
2019-12-29 21:17:39 +00:00
|
|
|
try:
|
|
|
|
if request.method == "POST":
|
|
|
|
if check_credentials(request.form['username'], request.form['password']):
|
|
|
|
session['logged_in'] = True
|
|
|
|
session['username'] = request.form['username']
|
|
|
|
|
|
|
|
flash("You are now logged in")
|
|
|
|
return redirect(url_for("redirect_root"))
|
|
|
|
else:
|
|
|
|
error = "Invalid credentials, try again."
|
|
|
|
gc.collect()
|
|
|
|
|
2020-05-10 13:23:42 +00:00
|
|
|
return render_template("login.html", error=error, password_reset=password_reset)
|
2019-12-29 21:17:39 +00:00
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
# flash(e)
|
|
|
|
error = "Invalid credentials, try again."
|
|
|
|
return render_template("login.html", error=error)
|
|
|
|
|
2018-10-03 10:53:22 +00:00
|
|
|
|
2019-12-13 14:46:24 +00:00
|
|
|
@app.context_processor
|
2020-05-15 15:43:55 +00:00
|
|
|
def template_variable_processor():
|
|
|
|
restart_required = database.execute("SELECT configured, updated FROM system", only_one=True)
|
|
|
|
|
|
|
|
return dict(restart_required=restart_required['configured'], update_required=restart_required['updated'],
|
|
|
|
settings=settings, args=args)
|
2019-12-13 14:46:24 +00:00
|
|
|
|
|
|
|
|
2019-11-27 03:12:16 +00:00
|
|
|
def api_authorize():
|
|
|
|
if 'apikey' in request.GET.dict:
|
|
|
|
if request.GET.dict['apikey'][0] == settings.auth.apikey:
|
|
|
|
return
|
|
|
|
else:
|
|
|
|
abort(401, 'Unauthorized')
|
|
|
|
else:
|
|
|
|
abort(401, 'Unauthorized')
|
|
|
|
|
|
|
|
|
2018-10-03 10:53:22 +00:00
|
|
|
def post_get(name, default=''):
|
|
|
|
return request.POST.get(name, default).strip()
|
|
|
|
|
|
|
|
|
2019-12-29 21:17:39 +00:00
|
|
|
@app.route("/logout/")
|
|
|
|
@login_required
|
2018-10-03 10:53:22 +00:00
|
|
|
def logout():
|
2019-12-29 21:17:39 +00:00
|
|
|
if settings.auth.type == 'basic':
|
|
|
|
return abort(401)
|
|
|
|
elif settings.auth.type == 'form':
|
|
|
|
session.clear()
|
|
|
|
flash("You have been logged out!")
|
|
|
|
gc.collect()
|
|
|
|
return redirect(url_for('redirect_root'))
|
2018-10-31 19:34:40 +00:00
|
|
|
|
2018-10-03 10:53:22 +00:00
|
|
|
|
2019-12-16 13:58:10 +00:00
|
|
|
@app.route('/emptylog')
|
2019-12-29 21:17:39 +00:00
|
|
|
@login_required
|
2018-10-03 10:53:22 +00:00
|
|
|
def emptylog():
|
2018-10-25 10:06:33 +00:00
|
|
|
empty_log()
|
2020-02-18 02:45:12 +00:00
|
|
|
return '', 200
|
2018-10-03 10:53:22 +00:00
|
|
|
|
2018-10-31 19:34:40 +00:00
|
|
|
|
2019-12-16 13:58:10 +00:00
|
|
|
@app.route('/bazarr.log')
|
2019-12-29 21:17:39 +00:00
|
|
|
@login_required
|
2018-10-03 10:53:22 +00:00
|
|
|
def download_log():
|
2020-02-18 02:45:12 +00:00
|
|
|
r = Response()
|
|
|
|
r.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
|
|
|
|
r.headers["Pragma"] = "no-cache"
|
|
|
|
r.headers["Expires"] = "0"
|
|
|
|
r.headers['Cache-Control'] = 'public, max-age=0'
|
|
|
|
return send_file(os.path.join(args.config_dir, 'log', 'bazarr.log'), cache_timeout=0)
|
2018-10-31 19:34:40 +00:00
|
|
|
|
2018-10-03 10:53:22 +00:00
|
|
|
|
2019-12-16 13:58:10 +00:00
|
|
|
@app.route('/image_proxy/<path:url>', methods=['GET'])
|
2019-12-29 21:17:39 +00:00
|
|
|
@login_required
|
2018-10-03 10:53:22 +00:00
|
|
|
def image_proxy(url):
|
2018-12-15 00:36:28 +00:00
|
|
|
apikey = settings.sonarr.apikey
|
2020-02-13 17:04:41 +00:00
|
|
|
url_image = (url_sonarr() + '/api/' + url + '?apikey=' + apikey).replace('poster-250', 'poster-500')
|
2018-10-03 10:53:22 +00:00
|
|
|
try:
|
2020-01-02 06:16:00 +00:00
|
|
|
req = requests.get(url_image, stream=True, timeout=15, verify=False)
|
2018-10-03 10:53:22 +00:00
|
|
|
except:
|
|
|
|
return None
|
|
|
|
else:
|
2019-12-31 22:09:04 +00:00
|
|
|
return Response(stream_with_context(req.iter_content(2048)), content_type=req.headers['content-type'])
|
2018-10-03 10:53:22 +00:00
|
|
|
|
2018-10-31 19:34:40 +00:00
|
|
|
|
2019-12-16 13:58:10 +00:00
|
|
|
@app.route('/image_proxy_movies/<path:url>', methods=['GET'])
|
2019-12-29 21:17:39 +00:00
|
|
|
@login_required
|
2018-10-03 10:53:22 +00:00
|
|
|
def image_proxy_movies(url):
|
2018-12-15 00:36:28 +00:00
|
|
|
apikey = settings.radarr.apikey
|
2020-02-13 17:04:41 +00:00
|
|
|
url_image = url_radarr() + '/api/' + url + '?apikey=' + apikey
|
2018-10-03 10:53:22 +00:00
|
|
|
try:
|
2020-02-06 11:40:39 +00:00
|
|
|
req = requests.get(url_image, stream=True, timeout=15, verify=False)
|
2018-10-03 10:53:22 +00:00
|
|
|
except:
|
2020-02-09 14:44:40 +00:00
|
|
|
return None
|
2018-10-03 10:53:22 +00:00
|
|
|
else:
|
2019-12-31 22:09:04 +00:00
|
|
|
return Response(stream_with_context(req.iter_content(2048)), content_type=req.headers['content-type'])
|
2018-10-03 10:53:22 +00:00
|
|
|
|
|
|
|
|
2019-12-29 21:17:39 +00:00
|
|
|
@app.route("/")
|
|
|
|
@login_required
|
2018-10-03 10:53:22 +00:00
|
|
|
def redirect_root():
|
2018-12-27 19:19:59 +00:00
|
|
|
if settings.general.getboolean('use_sonarr'):
|
2019-12-30 19:18:42 +00:00
|
|
|
return redirect(url_for('series'))
|
2018-12-27 19:19:59 +00:00
|
|
|
elif settings.general.getboolean('use_radarr'):
|
2019-12-30 19:18:42 +00:00
|
|
|
return redirect(url_for('movies'))
|
2018-10-03 10:53:22 +00:00
|
|
|
else:
|
2020-04-23 00:07:21 +00:00
|
|
|
return redirect(url_for('settingsgeneral'))
|
2018-10-03 10:53:22 +00:00
|
|
|
|
|
|
|
|
2019-12-16 13:58:10 +00:00
|
|
|
@app.route('/series/')
|
2019-12-29 21:17:39 +00:00
|
|
|
@login_required
|
2018-10-03 10:53:22 +00:00
|
|
|
def series():
|
2020-01-02 06:16:00 +00:00
|
|
|
return render_template('series.html')
|
2018-10-03 10:53:22 +00:00
|
|
|
|
2018-10-22 11:23:45 +00:00
|
|
|
|
2019-12-16 13:58:10 +00:00
|
|
|
@app.route('/serieseditor/')
|
2019-12-29 21:17:39 +00:00
|
|
|
@login_required
|
2018-10-03 10:53:22 +00:00
|
|
|
def serieseditor():
|
2020-01-27 05:07:35 +00:00
|
|
|
return render_template('serieseditor.html')
|
2018-10-03 10:53:22 +00:00
|
|
|
|
|
|
|
|
2020-02-05 03:50:35 +00:00
|
|
|
@app.route('/episodes/<no>')
|
2019-12-29 21:17:39 +00:00
|
|
|
@login_required
|
2018-10-03 10:53:22 +00:00
|
|
|
def episodes(no):
|
2020-01-02 06:16:00 +00:00
|
|
|
return render_template('episodes.html', id=str(no))
|
2018-10-31 19:34:40 +00:00
|
|
|
|
2019-01-15 16:25:13 +00:00
|
|
|
|
2019-12-16 13:58:10 +00:00
|
|
|
@app.route('/movies')
|
2019-12-29 21:17:39 +00:00
|
|
|
@login_required
|
2018-10-03 10:53:22 +00:00
|
|
|
def movies():
|
2019-12-28 05:52:00 +00:00
|
|
|
return render_template('movies.html')
|
2018-10-03 10:53:22 +00:00
|
|
|
|
2018-10-31 19:34:40 +00:00
|
|
|
|
2019-12-16 13:58:10 +00:00
|
|
|
@app.route('/movieseditor')
|
2019-12-29 21:17:39 +00:00
|
|
|
@login_required
|
2018-10-03 10:53:22 +00:00
|
|
|
def movieseditor():
|
2020-02-05 03:50:35 +00:00
|
|
|
return render_template('movieseditor.html')
|
2018-10-03 10:53:22 +00:00
|
|
|
|
2018-10-31 19:34:40 +00:00
|
|
|
|
2020-02-05 03:50:35 +00:00
|
|
|
@app.route('/movie/<no>')
|
2019-12-29 21:17:39 +00:00
|
|
|
@login_required
|
2018-10-03 10:53:22 +00:00
|
|
|
def movie(no):
|
2020-02-05 03:50:35 +00:00
|
|
|
return render_template('movie.html', id=str(no))
|
2018-10-31 19:34:40 +00:00
|
|
|
|
2019-01-15 16:25:13 +00:00
|
|
|
|
2020-03-26 19:37:17 +00:00
|
|
|
@app.route('/history/series/')
|
2019-12-29 21:17:39 +00:00
|
|
|
@login_required
|
2018-10-03 10:53:22 +00:00
|
|
|
def historyseries():
|
2019-12-28 16:43:48 +00:00
|
|
|
return render_template('historyseries.html')
|
2018-10-31 19:34:40 +00:00
|
|
|
|
2019-01-15 16:25:13 +00:00
|
|
|
|
2020-03-26 19:37:17 +00:00
|
|
|
@app.route('/history/movies/')
|
2019-12-29 21:17:39 +00:00
|
|
|
@login_required
|
2018-10-03 10:53:22 +00:00
|
|
|
def historymovies():
|
2019-12-28 16:43:48 +00:00
|
|
|
return render_template('historymovies.html')
|
2018-10-31 19:34:40 +00:00
|
|
|
|
2019-01-15 16:25:13 +00:00
|
|
|
|
2020-03-26 19:37:17 +00:00
|
|
|
@app.route('/wanted/series/')
|
2019-12-29 21:17:39 +00:00
|
|
|
@login_required
|
2018-10-03 10:53:22 +00:00
|
|
|
def wantedseries():
|
2020-02-12 17:41:40 +00:00
|
|
|
return render_template('wantedseries.html')
|
2019-12-13 14:46:24 +00:00
|
|
|
|
2019-08-08 10:33:00 +00:00
|
|
|
|
2020-03-26 19:37:17 +00:00
|
|
|
@app.route('/wanted/movies/')
|
2019-12-29 21:17:39 +00:00
|
|
|
@login_required
|
2018-10-03 10:53:22 +00:00
|
|
|
def wantedmovies():
|
2020-02-12 17:41:40 +00:00
|
|
|
return render_template('wantedmovies.html')
|
2018-10-31 19:34:40 +00:00
|
|
|
|
2019-01-15 16:25:13 +00:00
|
|
|
|
2020-03-26 19:37:17 +00:00
|
|
|
@app.route('/settings/general/')
|
2019-12-29 21:17:39 +00:00
|
|
|
@login_required
|
2020-02-27 02:08:52 +00:00
|
|
|
def settingsgeneral():
|
|
|
|
return render_template('settingsgeneral.html')
|
2018-10-03 10:53:22 +00:00
|
|
|
|
2018-10-31 19:34:40 +00:00
|
|
|
|
2020-03-26 19:37:17 +00:00
|
|
|
@app.route('/settings/sonarr/')
|
2020-03-18 12:22:22 +00:00
|
|
|
@login_required
|
|
|
|
def settingssonarr():
|
|
|
|
return render_template('settingssonarr.html')
|
|
|
|
|
|
|
|
|
2020-03-26 19:37:17 +00:00
|
|
|
@app.route('/settings/radarr/')
|
2019-12-29 21:17:39 +00:00
|
|
|
@login_required
|
2020-03-21 13:24:32 +00:00
|
|
|
def settingsradarr():
|
|
|
|
return render_template('settingsradarr.html')
|
2019-12-13 14:46:24 +00:00
|
|
|
|
2019-12-29 21:17:39 +00:00
|
|
|
|
2020-04-23 00:07:21 +00:00
|
|
|
@app.route('/settings/subtitles/')
|
|
|
|
@login_required
|
|
|
|
def settingssubtitles():
|
2020-04-28 12:18:30 +00:00
|
|
|
return render_template('settingssubtitles.html', os=sys.platform)
|
2020-04-23 00:07:21 +00:00
|
|
|
|
|
|
|
|
2020-04-24 10:56:03 +00:00
|
|
|
@app.route('/settings/languages/')
|
|
|
|
@login_required
|
|
|
|
def settingslanguages():
|
|
|
|
return render_template('settingslanguages.html')
|
|
|
|
|
|
|
|
|
2020-04-30 02:24:41 +00:00
|
|
|
@app.route('/settings/providers/')
|
|
|
|
@login_required
|
|
|
|
def settingsproviders():
|
|
|
|
return render_template('settingsproviders.html')
|
|
|
|
|
|
|
|
|
2020-05-06 00:09:25 +00:00
|
|
|
@app.route('/settings/notifications/')
|
|
|
|
@login_required
|
|
|
|
def settingsnotifications():
|
|
|
|
return render_template('settingsnotifications.html')
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/settings/scheduler/')
|
|
|
|
@login_required
|
|
|
|
def settingsscheduler():
|
2020-05-07 11:39:59 +00:00
|
|
|
days_of_the_week = list(enumerate(calendar.day_name))
|
|
|
|
return render_template('settingsscheduler.html', days=days_of_the_week)
|
2020-05-06 00:09:25 +00:00
|
|
|
|
|
|
|
|
2020-03-21 13:24:32 +00:00
|
|
|
@app.route('/check_update')
|
|
|
|
@login_required
|
|
|
|
def check_update():
|
2018-10-31 19:34:40 +00:00
|
|
|
if not args.no_update:
|
2018-10-03 10:53:22 +00:00
|
|
|
check_and_apply_update()
|
2019-12-29 21:17:39 +00:00
|
|
|
|
2020-03-21 13:24:32 +00:00
|
|
|
return '', 200
|
2018-10-03 10:53:22 +00:00
|
|
|
|
2018-10-31 19:34:40 +00:00
|
|
|
|
2020-03-26 19:37:17 +00:00
|
|
|
@app.route('/system/tasks')
|
2020-02-20 11:41:05 +00:00
|
|
|
@login_required
|
|
|
|
def systemtasks():
|
|
|
|
return render_template('systemtasks.html')
|
|
|
|
|
|
|
|
|
2020-03-26 19:37:17 +00:00
|
|
|
@app.route('/system/logs')
|
2020-02-17 17:54:20 +00:00
|
|
|
@login_required
|
|
|
|
def systemlogs():
|
|
|
|
return render_template('systemlogs.html')
|
|
|
|
|
|
|
|
|
2020-03-26 19:37:17 +00:00
|
|
|
@app.route('/system/providers')
|
2020-02-23 15:17:49 +00:00
|
|
|
@login_required
|
|
|
|
def systemproviders():
|
|
|
|
return render_template('systemproviders.html')
|
|
|
|
|
|
|
|
|
2020-03-26 19:37:17 +00:00
|
|
|
@app.route('/system/status')
|
2019-12-29 21:17:39 +00:00
|
|
|
@login_required
|
2020-02-17 00:38:10 +00:00
|
|
|
def systemstatus():
|
|
|
|
return render_template('systemstatus.html')
|
2019-12-13 14:46:24 +00:00
|
|
|
|
2019-09-11 10:50:33 +00:00
|
|
|
|
2020-03-26 19:37:17 +00:00
|
|
|
@app.route('/system/releases')
|
2020-02-17 00:38:10 +00:00
|
|
|
@login_required
|
|
|
|
def systemreleases():
|
|
|
|
return render_template('systemreleases.html')
|
2018-10-03 10:53:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
def configured():
|
2019-11-06 03:35:27 +00:00
|
|
|
database.execute("UPDATE system SET configured = 1")
|
2018-10-03 10:53:22 +00:00
|
|
|
|
2018-10-31 19:34:40 +00:00
|
|
|
|
2019-12-16 13:58:10 +00:00
|
|
|
@app.route('/api/series/wanted')
|
2018-10-03 10:53:22 +00:00
|
|
|
def api_wanted():
|
2020-05-15 15:43:55 +00:00
|
|
|
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 FROM table_episodes INNER JOIN table_shows on "
|
|
|
|
"table_shows.sonarrSeriesId = table_episodes.sonarrSeriesId WHERE "
|
|
|
|
"table_episodes.missing_subtitles != '[]' ORDER BY table_episodes._rowid_ DESC LIMIT 10")
|
2019-08-08 10:33:00 +00:00
|
|
|
|
|
|
|
wanted_subs = []
|
|
|
|
for item in data:
|
2020-05-15 15:43:55 +00:00
|
|
|
wanted_subs.append([item['seriesTitle'], item['episode_number'], item['episodeTitle'],
|
|
|
|
item['missing_subtitles']])
|
2019-08-08 10:33:00 +00:00
|
|
|
|
|
|
|
return dict(subtitles=wanted_subs)
|
2018-10-03 10:53:22 +00:00
|
|
|
|
2018-10-31 19:34:40 +00:00
|
|
|
|
2019-12-16 13:58:10 +00:00
|
|
|
@app.route('/api/series/history')
|
2018-10-03 10:53:22 +00:00
|
|
|
def api_history():
|
2019-10-25 02:35:04 +00:00
|
|
|
data = database.execute("SELECT table_shows.title as seriesTitle, "
|
|
|
|
"table_episodes.season || 'x' || table_episodes.episode as episode_number, "
|
|
|
|
"table_episodes.title as episodeTitle, "
|
|
|
|
"strftime('%Y-%m-%d', datetime(table_history.timestamp, 'unixepoch')) as date, "
|
|
|
|
"table_history.description FROM table_history "
|
2019-10-23 10:59:04 +00:00
|
|
|
"INNER JOIN table_shows on table_shows.sonarrSeriesId = table_history.sonarrSeriesId "
|
2020-05-15 15:43:55 +00:00
|
|
|
"INNER JOIN table_episodes on table_episodes.sonarrEpisodeId = "
|
|
|
|
"table_history.sonarrEpisodeId WHERE table_history.action != '0' ORDER BY id DESC LIMIT 10")
|
2019-08-08 10:33:00 +00:00
|
|
|
|
|
|
|
history_subs = []
|
|
|
|
for item in data:
|
2020-05-15 15:43:55 +00:00
|
|
|
history_subs.append([item['seriesTitle'], item['episode_number'], item['episodeTitle'], item['date'],
|
|
|
|
item['description']])
|
2019-08-08 10:33:00 +00:00
|
|
|
|
|
|
|
return dict(subtitles=history_subs)
|
2018-10-03 10:53:22 +00:00
|
|
|
|
2018-10-31 19:34:40 +00:00
|
|
|
|
2019-12-16 13:58:10 +00:00
|
|
|
@app.route('/api/movies/wanted/')
|
2019-11-28 11:34:37 +00:00
|
|
|
def api_movies_wanted():
|
2019-10-23 10:59:04 +00:00
|
|
|
data = database.execute("SELECT table_movies.title, table_movies.missing_subtitles FROM table_movies "
|
|
|
|
"WHERE table_movies.missing_subtitles != '[]' ORDER BY table_movies._rowid_ DESC LIMIT 10")
|
2019-08-08 10:33:00 +00:00
|
|
|
|
|
|
|
wanted_subs = []
|
|
|
|
for item in data:
|
2019-10-23 10:59:04 +00:00
|
|
|
wanted_subs.append([item['title'], item['missing_subtitles']])
|
2019-08-08 10:33:00 +00:00
|
|
|
|
|
|
|
return dict(subtitles=wanted_subs)
|
2018-10-03 10:53:22 +00:00
|
|
|
|
2018-10-31 19:34:40 +00:00
|
|
|
|
2019-12-16 13:58:10 +00:00
|
|
|
@app.route('/api/movies/history/')
|
2019-11-28 11:34:37 +00:00
|
|
|
def api_movies_history():
|
2019-10-23 10:59:04 +00:00
|
|
|
data = database.execute("SELECT table_movies.title, strftime('%Y-%m-%d', "
|
2019-10-25 02:35:04 +00:00
|
|
|
"datetime(table_history_movie.timestamp, 'unixepoch')) as date, "
|
|
|
|
"table_history_movie.description FROM table_history_movie "
|
2019-10-23 10:59:04 +00:00
|
|
|
"INNER JOIN table_movies on table_movies.radarrId = table_history_movie.radarrId "
|
|
|
|
"WHERE table_history_movie.action != '0' ORDER BY id DESC LIMIT 10")
|
2019-08-08 10:33:00 +00:00
|
|
|
|
|
|
|
history_subs = []
|
|
|
|
for item in data:
|
2019-10-23 10:59:04 +00:00
|
|
|
history_subs.append([item['title'], item['date'], item['description']])
|
2019-08-08 10:33:00 +00:00
|
|
|
|
|
|
|
return dict(subtitles=history_subs)
|
2018-10-03 10:53:22 +00:00
|
|
|
|
2018-10-31 19:34:40 +00:00
|
|
|
|
2020-03-21 13:24:32 +00:00
|
|
|
@app.route('/test_url', methods=['GET'])
|
2019-12-16 13:58:10 +00:00
|
|
|
@app.route('/test_url/<protocol>/<path:url>', methods=['GET'])
|
2019-12-29 21:17:39 +00:00
|
|
|
@login_required
|
2018-10-03 10:53:22 +00:00
|
|
|
def test_url(protocol, url):
|
2020-04-23 00:07:21 +00:00
|
|
|
url = protocol + '://' + unquote(url)
|
2018-10-03 10:53:22 +00:00
|
|
|
try:
|
2020-05-03 02:41:03 +00:00
|
|
|
result = requests.get(url, allow_redirects=False, verify=False, timeout=5)
|
2020-04-13 12:20:14 +00:00
|
|
|
except Exception as e:
|
2020-05-03 02:41:03 +00:00
|
|
|
return dict(status=False, error=repr(e))
|
2018-10-03 10:53:22 +00:00
|
|
|
else:
|
2020-05-03 02:41:03 +00:00
|
|
|
if result.status_code == 200:
|
|
|
|
return dict(status=True, version=result.json()['version'])
|
|
|
|
elif result.status_code == 401:
|
|
|
|
return dict(status=False, error='Access Denied. Check API key.')
|
|
|
|
elif 300 <= result.status_code <= 399:
|
|
|
|
return dict(status=False, error='Wrong URL Base.')
|
|
|
|
else:
|
|
|
|
return dict(status=False, error=result.raise_for_status())
|
2018-10-03 10:53:22 +00:00
|
|
|
|
2018-10-31 19:34:40 +00:00
|
|
|
|
2020-05-09 02:23:01 +00:00
|
|
|
@app.route('/test_notification', methods=['GET'])
|
2019-12-16 13:58:10 +00:00
|
|
|
@app.route('/test_notification/<protocol>/<path:provider>', methods=['GET'])
|
2019-12-29 21:17:39 +00:00
|
|
|
@login_required
|
2018-12-18 11:39:49 +00:00
|
|
|
def test_notification(protocol, provider):
|
2019-12-13 14:46:24 +00:00
|
|
|
|
2020-02-13 04:16:22 +00:00
|
|
|
provider = unquote(provider)
|
2018-11-27 21:34:09 +00:00
|
|
|
apobj = apprise.Apprise()
|
2018-12-18 11:39:49 +00:00
|
|
|
apobj.add(protocol + "://" + provider)
|
2019-12-29 21:17:39 +00:00
|
|
|
|
2018-11-27 21:34:09 +00:00
|
|
|
apobj.notify(
|
|
|
|
title='Bazarr test notification',
|
2020-03-21 13:24:32 +00:00
|
|
|
body='Test notification'
|
2018-11-27 21:34:09 +00:00
|
|
|
)
|
|
|
|
|
2020-05-09 02:23:01 +00:00
|
|
|
return '', 200
|
|
|
|
|
2018-11-27 21:34:09 +00:00
|
|
|
|
2020-05-15 18:40:56 +00:00
|
|
|
class Server:
|
2020-05-17 13:19:37 +00:00
|
|
|
# Mute DeprecationWarning
|
|
|
|
warnings.simplefilter("ignore", DeprecationWarning)
|
|
|
|
# Mute Insecure HTTPS requests made to Sonarr and Radarr
|
|
|
|
warnings.filterwarnings('ignore', message='Unverified HTTPS request')
|
|
|
|
# Mute Python3 BrokenPipeError
|
|
|
|
warnings.simplefilter("ignore", BrokenPipeError)
|
|
|
|
|
|
|
|
if args.dev:
|
|
|
|
server = app.run(host=str(settings.general.ip),
|
|
|
|
port=(int(args.port) if args.port else int(settings.general.port)))
|
|
|
|
else:
|
|
|
|
server = create_server(app,
|
|
|
|
host=str(settings.general.ip),
|
|
|
|
port=int(args.port) if args.port else int(settings.general.port),
|
|
|
|
threads=24)
|
2020-05-15 15:43:55 +00:00
|
|
|
|
2020-05-18 01:00:08 +00:00
|
|
|
# Make sure that server class instance isn't getting instantiated again but reused instead.
|
|
|
|
_instance = None
|
|
|
|
|
|
|
|
def __new__(cls, *args, **kwargs):
|
|
|
|
if not cls._instance:
|
|
|
|
cls._instance = super(Server, cls).__new__(
|
|
|
|
cls, *args, **kwargs)
|
|
|
|
return cls._instance
|
|
|
|
|
2020-05-15 18:40:56 +00:00
|
|
|
@classmethod
|
|
|
|
def start(cls):
|
2020-05-15 15:43:55 +00:00
|
|
|
try:
|
|
|
|
logging.info(
|
|
|
|
'BAZARR is started and waiting for request on http://' + str(settings.general.ip) + ':' + (str(
|
|
|
|
args.port) if args.port else str(settings.general.port)) + str(base_url))
|
|
|
|
if not args.dev:
|
2020-05-15 18:40:56 +00:00
|
|
|
cls.server.run()
|
2020-05-15 15:43:55 +00:00
|
|
|
except KeyboardInterrupt:
|
2020-05-15 18:40:56 +00:00
|
|
|
cls.shutdown()
|
2020-05-15 15:43:55 +00:00
|
|
|
|
2020-05-15 18:40:56 +00:00
|
|
|
@classmethod
|
|
|
|
def shutdown(cls):
|
2020-05-15 15:43:55 +00:00
|
|
|
try:
|
2020-05-15 18:40:56 +00:00
|
|
|
cls.server.close()
|
2020-05-15 19:29:07 +00:00
|
|
|
except Exception as e:
|
|
|
|
logging.error('BAZARR Cannot stop Waitress: ' + repr(e))
|
2020-05-15 15:43:55 +00:00
|
|
|
else:
|
|
|
|
database.close()
|
|
|
|
try:
|
|
|
|
stop_file = io.open(os.path.join(args.config_dir, "bazarr.stop"), "w", encoding='UTF-8')
|
|
|
|
except Exception as e:
|
2020-05-17 13:19:37 +00:00
|
|
|
logging.error('BAZARR Cannot create bazarr.stop file: ' + repr(e))
|
2020-05-15 15:43:55 +00:00
|
|
|
else:
|
|
|
|
logging.info('Bazarr is being shutdown...')
|
|
|
|
stop_file.write(str(''))
|
|
|
|
stop_file.close()
|
|
|
|
os._exit(0)
|
|
|
|
|
2020-05-15 18:40:56 +00:00
|
|
|
@classmethod
|
|
|
|
def restart(cls):
|
2020-05-15 15:43:55 +00:00
|
|
|
try:
|
2020-05-15 18:40:56 +00:00
|
|
|
cls.server.close()
|
2020-05-15 19:29:07 +00:00
|
|
|
except Exception as e:
|
|
|
|
logging.error('BAZARR Cannot stop Waitress: ' + repr(e))
|
2020-05-15 15:43:55 +00:00
|
|
|
else:
|
|
|
|
database.close()
|
|
|
|
try:
|
|
|
|
restart_file = io.open(os.path.join(args.config_dir, "bazarr.restart"), "w", encoding='UTF-8')
|
|
|
|
except Exception as e:
|
2020-05-17 13:19:37 +00:00
|
|
|
logging.error('BAZARR Cannot create bazarr.restart file: ' + repr(e))
|
2020-05-15 15:43:55 +00:00
|
|
|
else:
|
|
|
|
logging.info('Bazarr is being restarted...')
|
|
|
|
restart_file.write(str(''))
|
|
|
|
restart_file.close()
|
|
|
|
os._exit(0)
|
|
|
|
|
|
|
|
|
2020-04-16 11:52:35 +00:00
|
|
|
if __name__ == "__main__":
|
2020-05-15 18:40:56 +00:00
|
|
|
Server.start()
|