mirror of https://github.com/morpheus65535/bazarr
Merge remote-tracking branch 'origin/morpheus' into Flask
# Conflicts: # bazarr/api.py
This commit is contained in:
commit
54d437b4e3
|
@ -0,0 +1,35 @@
|
||||||
|
from __future__ import absolute_import
|
||||||
|
from collections import deque
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
class EventStream:
|
||||||
|
"""
|
||||||
|
This class is used to read or write items to the notifications deque.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.queue = deque(maxlen=10)
|
||||||
|
|
||||||
|
def write(self, msg):
|
||||||
|
"""
|
||||||
|
:param msg: The message to display.
|
||||||
|
:type msg: str
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.queue.append("data:" + msg + "\n\n")
|
||||||
|
|
||||||
|
def read(self):
|
||||||
|
"""
|
||||||
|
:return: Return the oldest notification available.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if self.queue:
|
||||||
|
return self.queue.popleft()
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
|
||||||
|
event_stream = EventStream()
|
100
bazarr/api.py
100
bazarr/api.py
|
@ -14,13 +14,27 @@ from database import database
|
||||||
from helper import path_replace, path_replace_reverse, path_replace_movie, path_replace_reverse_movie
|
from helper import path_replace, path_replace_reverse, path_replace_movie, path_replace_reverse_movie
|
||||||
from get_languages import load_language_in_db, alpha2_from_language, alpha3_from_language, language_from_alpha2, \
|
from get_languages import load_language_in_db, alpha2_from_language, alpha3_from_language, language_from_alpha2, \
|
||||||
alpha3_from_alpha2
|
alpha3_from_alpha2
|
||||||
|
from SSE import event_stream
|
||||||
|
|
||||||
|
from flask import Flask, jsonify, request, Response, Blueprint
|
||||||
|
|
||||||
from flask import Flask, jsonify, request, Blueprint
|
|
||||||
from flask_restful import Resource, Api
|
from flask_restful import Resource, Api
|
||||||
|
|
||||||
api_bp = Blueprint('api', __name__, url_prefix='/api')
|
api_bp = Blueprint('api', __name__, url_prefix='/api')
|
||||||
api = Api(api_bp)
|
api = Api(api_bp)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/event')
|
||||||
|
def event():
|
||||||
|
return Response(event_stream.read(), mimetype="text/event-stream")
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/write')
|
||||||
|
def write():
|
||||||
|
event_stream.write('fake message')
|
||||||
|
return "", 200
|
||||||
|
|
||||||
|
|
||||||
class Badges(Resource):
|
class Badges(Resource):
|
||||||
def get(self):
|
def get(self):
|
||||||
result = {
|
result = {
|
||||||
|
@ -35,11 +49,17 @@ class Badges(Resource):
|
||||||
|
|
||||||
class Series(Resource):
|
class Series(Resource):
|
||||||
def get(self):
|
def get(self):
|
||||||
|
start = request.args.get('start') or 0
|
||||||
|
length = request.args.get('length') or -1
|
||||||
|
draw = request.args.get('draw')
|
||||||
|
|
||||||
seriesId = request.args.get('id')
|
seriesId = request.args.get('id')
|
||||||
|
row_count = database.execute("SELECT COUNT(*) as count FROM table_shows", only_one=True)['count']
|
||||||
if seriesId:
|
if seriesId:
|
||||||
result = database.execute("SELECT * FROM table_shows WHERE sonarrSeriesId=?", (seriesId,))
|
result = database.execute("SELECT * FROM table_shows WHERE sonarrSeriesId=? LIMIT ? OFFSET ?",
|
||||||
|
(length, start), (seriesId,))
|
||||||
else:
|
else:
|
||||||
result = database.execute("SELECT * FROM table_shows")
|
result = database.execute("SELECT * FROM table_shows LIMIT ? OFFSET ?", (length, start))
|
||||||
for item in result:
|
for item in result:
|
||||||
# Parse audio language
|
# Parse audio language
|
||||||
if item['audio_language']:
|
if item['audio_language']:
|
||||||
|
@ -76,7 +96,7 @@ class Series(Resource):
|
||||||
item.update({"episodeFileCount": database.execute("SELECT COUNT(*) as count FROM table_episodes WHERE "
|
item.update({"episodeFileCount": database.execute("SELECT COUNT(*) as count FROM table_episodes WHERE "
|
||||||
"sonarrSeriesId=?", (seriesId,),
|
"sonarrSeriesId=?", (seriesId,),
|
||||||
only_one=True)['count']})
|
only_one=True)['count']})
|
||||||
return jsonify(result)
|
return jsonify(draw=draw, recordsTotal=row_count, recordsFiltered=row_count, data=result)
|
||||||
|
|
||||||
|
|
||||||
class Episodes(Resource):
|
class Episodes(Resource):
|
||||||
|
@ -85,7 +105,7 @@ class Episodes(Resource):
|
||||||
if seriesId:
|
if seriesId:
|
||||||
result = database.execute("SELECT * FROM table_episodes WHERE sonarrSeriesId=?", (seriesId,))
|
result = database.execute("SELECT * FROM table_episodes WHERE sonarrSeriesId=?", (seriesId,))
|
||||||
else:
|
else:
|
||||||
result = database.execute("SELECT * FROM table_episodes")
|
return "Series ID not provided", 400
|
||||||
for item in result:
|
for item in result:
|
||||||
# Parse subtitles
|
# Parse subtitles
|
||||||
if item['subtitles']:
|
if item['subtitles']:
|
||||||
|
@ -114,11 +134,17 @@ class Episodes(Resource):
|
||||||
|
|
||||||
class Movies(Resource):
|
class Movies(Resource):
|
||||||
def get(self):
|
def get(self):
|
||||||
|
start = request.args.get('start') or 0
|
||||||
|
length = request.args.get('length') or -1
|
||||||
|
draw = request.args.get('draw')
|
||||||
|
|
||||||
moviesId = request.args.get('id')
|
moviesId = request.args.get('id')
|
||||||
|
row_count = database.execute("SELECT COUNT(*) as count FROM table_movies", only_one=True)['count']
|
||||||
if moviesId:
|
if moviesId:
|
||||||
result = database.execute("SELECT * FROM table_movies WHERE radarrId=?", (moviesId,))
|
result = database.execute("SELECT * FROM table_movies WHERE radarrId=? LIMIT ? OFFSET ?", (length, start),
|
||||||
|
(moviesId,))
|
||||||
else:
|
else:
|
||||||
result = database.execute("SELECT * FROM table_movies")
|
result = database.execute("SELECT * FROM table_movies LIMIT ? OFFSET ?", (length, start))
|
||||||
for item in result:
|
for item in result:
|
||||||
# Parse audio language
|
# Parse audio language
|
||||||
if item['audio_language']:
|
if item['audio_language']:
|
||||||
|
@ -164,11 +190,15 @@ class Movies(Resource):
|
||||||
|
|
||||||
# Confirm if path exist
|
# Confirm if path exist
|
||||||
item.update({"exist": os.path.isfile(mapped_path)})
|
item.update({"exist": os.path.isfile(mapped_path)})
|
||||||
return jsonify(result)
|
return jsonify(draw=draw, recordsTotal=row_count, recordsFiltered=row_count, data=result)
|
||||||
|
|
||||||
|
|
||||||
class HistorySeries(Resource):
|
class HistorySeries(Resource):
|
||||||
def get(self):
|
def get(self):
|
||||||
|
start = request.args.get('start') or 0
|
||||||
|
length = request.args.get('length') or -1
|
||||||
|
draw = request.args.get('draw')
|
||||||
|
|
||||||
upgradable_episodes_not_perfect = []
|
upgradable_episodes_not_perfect = []
|
||||||
if settings.general.getboolean('upgrade_subs'):
|
if settings.general.getboolean('upgrade_subs'):
|
||||||
days_to_upgrade_subs = settings.general.days_to_upgrade_subs
|
days_to_upgrade_subs = settings.general.days_to_upgrade_subs
|
||||||
|
@ -204,6 +234,7 @@ class HistorySeries(Resource):
|
||||||
if int(upgradable_episode['score']) < 360:
|
if int(upgradable_episode['score']) < 360:
|
||||||
upgradable_episodes_not_perfect.append(upgradable_episode)
|
upgradable_episodes_not_perfect.append(upgradable_episode)
|
||||||
|
|
||||||
|
row_count = database.execute("SELECT COUNT(*) as count FROM table_history", only_one=True)['count']
|
||||||
data = database.execute("SELECT table_history.action, table_shows.title as seriesTitle, "
|
data = database.execute("SELECT table_history.action, table_shows.title as seriesTitle, "
|
||||||
"table_episodes.season || 'x' || table_episodes.episode as episode_number, "
|
"table_episodes.season || 'x' || table_episodes.episode as episode_number, "
|
||||||
"table_episodes.title as episodeTitle, table_history.timestamp, "
|
"table_episodes.title as episodeTitle, table_history.timestamp, "
|
||||||
|
@ -211,7 +242,8 @@ class HistorySeries(Resource):
|
||||||
"table_history.language, table_history.score FROM table_history LEFT JOIN table_shows "
|
"table_history.language, table_history.score FROM table_history LEFT JOIN table_shows "
|
||||||
"on table_shows.sonarrSeriesId = table_history.sonarrSeriesId LEFT JOIN table_episodes "
|
"on table_shows.sonarrSeriesId = table_history.sonarrSeriesId LEFT JOIN table_episodes "
|
||||||
"on table_episodes.sonarrEpisodeId = table_history.sonarrEpisodeId WHERE "
|
"on table_episodes.sonarrEpisodeId = table_history.sonarrEpisodeId WHERE "
|
||||||
"table_episodes.title is not NULL ORDER BY timestamp DESC")
|
"table_episodes.title is not NULL ORDER BY timestamp DESC LIMIT ? OFFSET ?",
|
||||||
|
(length, start))
|
||||||
|
|
||||||
for item in data:
|
for item in data:
|
||||||
# Mark episode as upgradable or not
|
# Mark episode as upgradable or not
|
||||||
|
@ -239,11 +271,15 @@ class HistorySeries(Resource):
|
||||||
# Confirm if path exist
|
# Confirm if path exist
|
||||||
item.update({"exist": os.path.isfile(mapped_path)})
|
item.update({"exist": os.path.isfile(mapped_path)})
|
||||||
|
|
||||||
return jsonify(data)
|
return jsonify(draw=draw, recordsTotal=row_count, recordsFiltered=row_count, data=data)
|
||||||
|
|
||||||
|
|
||||||
class HistoryMovies(Resource):
|
class HistoryMovies(Resource):
|
||||||
def get(self):
|
def get(self):
|
||||||
|
start = request.args.get('start') or 0
|
||||||
|
length = request.args.get('length') or -1
|
||||||
|
draw = request.args.get('draw')
|
||||||
|
|
||||||
upgradable_movies = []
|
upgradable_movies = []
|
||||||
upgradable_movies_not_perfect = []
|
upgradable_movies_not_perfect = []
|
||||||
if settings.general.getboolean('upgrade_subs'):
|
if settings.general.getboolean('upgrade_subs'):
|
||||||
|
@ -277,11 +313,13 @@ class HistoryMovies(Resource):
|
||||||
if int(upgradable_movie['score']) < 120:
|
if int(upgradable_movie['score']) < 120:
|
||||||
upgradable_movies_not_perfect.append(upgradable_movie)
|
upgradable_movies_not_perfect.append(upgradable_movie)
|
||||||
|
|
||||||
|
row_count = database.execute("SELECT COUNT(*) as count FROM table_history_movie", only_one=True)['count']
|
||||||
data = database.execute("SELECT table_history_movie.action, table_movies.title, table_history_movie.timestamp, "
|
data = database.execute("SELECT table_history_movie.action, table_movies.title, table_history_movie.timestamp, "
|
||||||
"table_history_movie.description, table_history_movie.radarrId, "
|
"table_history_movie.description, table_history_movie.radarrId, "
|
||||||
"table_history_movie.video_path, table_history_movie.language, "
|
"table_history_movie.video_path, table_history_movie.language, "
|
||||||
"table_history_movie.score FROM table_history_movie LEFT JOIN table_movies on "
|
"table_history_movie.score FROM table_history_movie LEFT JOIN table_movies on "
|
||||||
"table_movies.radarrId = table_history_movie.radarrId ORDER BY timestamp DESC")
|
"table_movies.radarrId = table_history_movie.radarrId ORDER BY timestamp DESC LIMIT ? "
|
||||||
|
"OFFSET ?", (length, start))
|
||||||
|
|
||||||
for item in data:
|
for item in data:
|
||||||
# Mark movies as upgradable or not
|
# Mark movies as upgradable or not
|
||||||
|
@ -313,16 +351,21 @@ class HistoryMovies(Resource):
|
||||||
item.update({"mapped_path": None})
|
item.update({"mapped_path": None})
|
||||||
item.update({"exist": False})
|
item.update({"exist": False})
|
||||||
|
|
||||||
return jsonify(data)
|
return jsonify(draw=draw, recordsTotal=row_count, recordsFiltered=row_count, data=data)
|
||||||
|
|
||||||
|
|
||||||
class WantedSeries(Resource):
|
class WantedSeries(Resource):
|
||||||
def get(self):
|
def get(self):
|
||||||
|
start = request.args.get('start') or 0
|
||||||
|
length = request.args.get('length') or -1
|
||||||
|
draw = request.args.get('draw')
|
||||||
|
|
||||||
if settings.sonarr.getboolean('only_monitored'):
|
if settings.sonarr.getboolean('only_monitored'):
|
||||||
monitored_only_query_string = " AND monitored='True'"
|
monitored_only_query_string = " AND monitored='True'"
|
||||||
else:
|
else:
|
||||||
monitored_only_query_string = ''
|
monitored_only_query_string = ''
|
||||||
|
|
||||||
|
row_count = database.execute("SELECT COUNT(*) as count FROM table_episodes", only_one=True)['count']
|
||||||
data = database.execute("SELECT table_shows.title as seriesTitle, "
|
data = database.execute("SELECT table_shows.title as seriesTitle, "
|
||||||
"table_episodes.season || 'x' || table_episodes.episode as episode_number, "
|
"table_episodes.season || 'x' || table_episodes.episode as episode_number, "
|
||||||
"table_episodes.title as episodeTitle, table_episodes.missing_subtitles, "
|
"table_episodes.title as episodeTitle, table_episodes.missing_subtitles, "
|
||||||
|
@ -331,7 +374,7 @@ class WantedSeries(Resource):
|
||||||
"table_episodes.failedAttempts FROM table_episodes INNER JOIN table_shows on "
|
"table_episodes.failedAttempts FROM table_episodes INNER JOIN table_shows on "
|
||||||
"table_shows.sonarrSeriesId = table_episodes.sonarrSeriesId WHERE "
|
"table_shows.sonarrSeriesId = table_episodes.sonarrSeriesId WHERE "
|
||||||
"table_episodes.missing_subtitles != '[]'" + monitored_only_query_string +
|
"table_episodes.missing_subtitles != '[]'" + monitored_only_query_string +
|
||||||
" ORDER BY table_episodes._rowid_ DESC")
|
" ORDER BY table_episodes._rowid_ DESC LIMIT ? OFFSET ?", (length, start))
|
||||||
|
|
||||||
for item in data:
|
for item in data:
|
||||||
# Parse missing subtitles
|
# Parse missing subtitles
|
||||||
|
@ -351,19 +394,25 @@ class WantedSeries(Resource):
|
||||||
# Confirm if path exist
|
# Confirm if path exist
|
||||||
item.update({"exist": os.path.isfile(mapped_path)})
|
item.update({"exist": os.path.isfile(mapped_path)})
|
||||||
|
|
||||||
return jsonify(data)
|
return jsonify(draw=draw, recordsTotal=row_count, recordsFiltered=row_count, data=data)
|
||||||
|
|
||||||
|
|
||||||
class WantedMovies(Resource):
|
class WantedMovies(Resource):
|
||||||
def get(self):
|
def get(self):
|
||||||
|
start = request.args.get('start') or 0
|
||||||
|
length = request.args.get('length') or -1
|
||||||
|
draw = request.args.get('draw')
|
||||||
|
|
||||||
if settings.radarr.getboolean('only_monitored'):
|
if settings.radarr.getboolean('only_monitored'):
|
||||||
monitored_only_query_string = " AND monitored='True'"
|
monitored_only_query_string = " AND monitored='True'"
|
||||||
else:
|
else:
|
||||||
monitored_only_query_string = ''
|
monitored_only_query_string = ''
|
||||||
|
|
||||||
|
row_count = database.execute("SELECT COUNT(*) as count FROM table_movies", only_one=True)['count']
|
||||||
data = database.execute("SELECT title, missing_subtitles, radarrId, path, hearing_impaired, sceneName, "
|
data = database.execute("SELECT title, missing_subtitles, radarrId, path, hearing_impaired, sceneName, "
|
||||||
"failedAttempts FROM table_movies WHERE missing_subtitles != '[]'" +
|
"failedAttempts FROM table_movies WHERE missing_subtitles != '[]'" +
|
||||||
monitored_only_query_string + " ORDER BY _rowid_ DESC")
|
monitored_only_query_string + " ORDER BY _rowid_ DESC LIMIT ? OFFSET ?",
|
||||||
|
(length, start))
|
||||||
|
|
||||||
for item in data:
|
for item in data:
|
||||||
# Parse missing subtitles
|
# Parse missing subtitles
|
||||||
|
@ -383,14 +432,17 @@ class WantedMovies(Resource):
|
||||||
# Confirm if path exist
|
# Confirm if path exist
|
||||||
item.update({"exist": os.path.isfile(mapped_path)})
|
item.update({"exist": os.path.isfile(mapped_path)})
|
||||||
|
|
||||||
return jsonify(data)
|
return jsonify(draw=draw, recordsTotal=row_count, recordsFiltered=row_count, data=data)
|
||||||
|
|
||||||
|
|
||||||
api.add_resource(Badges, '/badges')
|
api.add_resource(Badges, '/api/badges')
|
||||||
api.add_resource(Series, '/series')
|
api.add_resource(Series, '/api/series')
|
||||||
api.add_resource(Episodes, '/episodes')
|
api.add_resource(Episodes, '/api/episodes')
|
||||||
api.add_resource(Movies, '/movies')
|
api.add_resource(Movies, '/api/movies')
|
||||||
api.add_resource(HistorySeries, '/history_series')
|
api.add_resource(HistorySeries, '/api/history_series')
|
||||||
api.add_resource(HistoryMovies, '/history_movies')
|
api.add_resource(HistoryMovies, '/api/history_movies')
|
||||||
api.add_resource(WantedSeries, '/wanted_series')
|
api.add_resource(WantedSeries, '/api/wanted_series')
|
||||||
api.add_resource(WantedMovies, '/wanted_movies')
|
api.add_resource(WantedMovies, '/api/wanted_movies')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(debug=True)
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Server-side processing datatables test</title>
|
||||||
|
<script src="https://code.jquery.com/jquery-3.3.1.js" type="text/javascript"></script>
|
||||||
|
<script src="https://cdn.datatables.net/1.10.20/js/jquery.dataTables.min.js" type="text/javascript"></script>
|
||||||
|
<script src="https://cdn.datatables.net/1.10.20/js/dataTables.material.min.js" type="text/javascript"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/material-design-lite/1.1.0/material.min.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.20/css/dataTables.material.min.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<table id="example" class="mdl-data-table" style="width:100%">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>action</th>
|
||||||
|
<th>title</th>
|
||||||
|
<th>timestamp</th>
|
||||||
|
<th>description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(document).ready(function() {
|
||||||
|
var table = $('#example').DataTable( {
|
||||||
|
"processing": true,
|
||||||
|
"serverSide": true,
|
||||||
|
"ajax": "http://localhost:6767/api/history_movies",
|
||||||
|
"columns": [
|
||||||
|
{ "data": "action" },
|
||||||
|
{ "data": "title" },
|
||||||
|
{ "data": "timestamp" },
|
||||||
|
{ "data": "description" }
|
||||||
|
]
|
||||||
|
} );
|
||||||
|
|
||||||
|
var source = new EventSource('/event');
|
||||||
|
source.onmessage = function (event) {
|
||||||
|
table.ajax.reload(null, false);
|
||||||
|
};
|
||||||
|
} );
|
||||||
|
</script>
|
||||||
|
</html>
|
Loading…
Reference in New Issue