Added subtitles blacklisting.

pull/1038/head
Louis Vézina 4 years ago
parent e14d2f45de
commit 3b96b6b6ea

@ -24,11 +24,13 @@ from get_languages import language_from_alpha3, language_from_alpha2, alpha2_fro
alpha3_from_language, alpha3_from_alpha2
from get_subtitle import download_subtitle, series_download_subtitles, movies_download_subtitles, \
manual_search, manual_download_subtitle, manual_upload_subtitle, wanted_search_missing_subtitles_series, \
wanted_search_missing_subtitles_movies
wanted_search_missing_subtitles_movies, episode_download_subtitles, movies_download_subtitles
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
from utils import history_log, history_log_movie, get_sonarr_version, get_radarr_version
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, \
delete_subtitles
from get_providers import get_providers, get_providers_auth, list_throttled_providers, reset_throttled_providers
from event_handler import event_stream
from scheduler import scheduler
@ -471,22 +473,22 @@ class EpisodesSubtitlesDelete(Resource):
def delete(self):
episodePath = request.form.get('episodePath')
language = request.form.get('language')
forced = request.form.get('forced')
subtitlesPath = request.form.get('subtitlesPath')
sonarrSeriesId = request.form.get('sonarrSeriesId')
sonarrEpisodeId = request.form.get('sonarrEpisodeId')
try:
os.remove(path_mappings.path_replace(subtitlesPath))
result = language_from_alpha3(language) + " subtitles deleted from disk."
history_log(0, sonarrSeriesId, sonarrEpisodeId, result, language=alpha2_from_alpha3(language),
video_path=path_mappings.path_replace_reverse(episodePath))
store_subtitles(path_mappings.path_replace_reverse(episodePath), episodePath)
return result, 202
except OSError as e:
logging.exception('BAZARR cannot delete subtitles file: ' + subtitlesPath)
store_subtitles(path_mappings.path_replace_reverse(episodePath), episodePath)
return '', 204
result = delete_subtitles(media_type='series',
language=language,
forced=forced,
media_path=episodePath,
subtitles_path=subtitlesPath,
sonarr_series_id=sonarrSeriesId,
sonarr_episode_id=sonarrEpisodeId)
if result:
return '', 202
else:
return '', 204
class EpisodesSubtitlesDownload(Resource):
@ -518,7 +520,8 @@ class EpisodesSubtitlesDownload(Resource):
provider = result[3]
score = result[4]
subs_id = result[6]
history_log(1, sonarrSeriesId, sonarrEpisodeId, message, path, language_code, provider, score, subs_id)
subs_path = result[7]
history_log(1, sonarrSeriesId, sonarrEpisodeId, message, path, language_code, provider, score, subs_id, subs_path)
send_notifications(sonarrSeriesId, sonarrEpisodeId, message)
store_subtitles(path, episodePath)
else:
@ -586,7 +589,8 @@ class EpisodesSubtitlesManualDownload(Resource):
provider = result[3]
score = result[4]
subs_id = result[6]
history_log(2, sonarrSeriesId, sonarrEpisodeId, message, path, language_code, provider, score, subs_id)
subs_path = result[7]
history_log(2, sonarrSeriesId, sonarrEpisodeId, message, path, language_code, provider, score, subs_id, subs_path)
send_notifications(sonarrSeriesId, sonarrEpisodeId, message)
store_subtitles(path, episodePath)
return result, 201
@ -629,10 +633,11 @@ class EpisodesSubtitlesUpload(Resource):
if result is not None:
message = result[0]
path = result[1]
subs_path = result[2]
language_code = language + ":forced" if forced else language
provider = "manual"
score = 360
history_log(4, sonarrSeriesId, sonarrEpisodeId, message, path, language_code, provider, score)
history_log(4, sonarrSeriesId, sonarrEpisodeId, message, path, language_code, provider, score, subtitles_path=subs_path)
send_notifications(sonarrSeriesId, sonarrEpisodeId, message)
store_subtitles(path, episodePath)
@ -664,7 +669,8 @@ class EpisodesHistory(Resource):
def get(self):
episodeid = request.args.get('episodeid')
episode_history = database.execute("SELECT action, timestamp, language, provider, score FROM table_history "
episode_history = database.execute("SELECT action, timestamp, language, provider, score, sonarrSeriesId, "
"sonarrEpisodeId, subs_id, video_path, subtitles_path FROM table_history "
"WHERE sonarrEpisodeId=? ORDER BY timestamp DESC", (episodeid,))
for item in episode_history:
item['timestamp'] = "<div title='" + \
@ -672,12 +678,45 @@ class EpisodesHistory(Resource):
"' data-toggle='tooltip' data-placement='left'>" + \
pretty.date(datetime.datetime.fromtimestamp(item['timestamp'])) + "</div>"
if item['language']:
item['language'] = language_from_alpha2(item['language'])
else:
item['language'] = "<i>undefined</i>"
language = item['language'].split(':')
item['language'] = {"name": language_from_alpha2(language[0]),
"code2": language[0],
"code3": alpha3_from_alpha2(language[0]),
"forced": True if len(language) > 1 else False}
if item['score']:
item['score'] = str(round((int(item['score']) * 100 / 360), 2)) + "%"
if item['video_path']:
# Provide mapped path
mapped_path = path_mappings.path_replace(item['video_path'])
item.update({"mapped_path": mapped_path})
# Confirm if path exist
item.update({"exist": os.path.isfile(mapped_path)})
else:
item.update({"mapped_path": None})
item.update({"exist": False})
if item['subtitles_path']:
# Provide mapped subtitles path
mapped_subtitles_path = path_mappings.path_replace_movie(item['subtitles_path'])
item.update({"mapped_subtitles_path": mapped_subtitles_path})
else:
item.update({"mapped_subtitles_path": None})
# Check if subtitles is blacklisted
if item['action'] not in [0, 4, 5]:
blacklist_db = database.execute(
"SELECT provider, subs_id FROM table_blacklist WHERE provider=? AND "
"subs_id=?", (item['provider'], item['subs_id']))
else:
blacklist_db = []
if len(blacklist_db):
item.update({"blacklisted": True})
else:
item.update({"blacklisted": False})
return jsonify(data=episode_history)
@ -875,21 +914,20 @@ class MovieSubtitlesDelete(Resource):
def delete(self):
moviePath = request.form.get('moviePath')
language = request.form.get('language')
forced = request.form.get('forced')
subtitlesPath = request.form.get('subtitlesPath')
radarrId = request.form.get('radarrId')
try:
os.remove(path_mappings.path_replace_movie(subtitlesPath))
result = language_from_alpha3(language) + " subtitles deleted from disk."
history_log_movie(0, radarrId, result, language=alpha2_from_alpha3(language),
video_path=path_mappings.path_replace_reverse_movie(moviePath))
store_subtitles_movie(path_mappings.path_replace_reverse_movie(moviePath), moviePath)
return result, 202
except OSError as e:
logging.exception('BAZARR cannot delete subtitles file: ' + subtitlesPath)
store_subtitles_movie(path_mappings.path_replace_reverse_movie(moviePath), moviePath)
return '', 204
result = delete_subtitles(media_type='movie',
language=language,
forced=forced,
media_path=moviePath,
subtitles_path=subtitlesPath,
radarr_id=radarrId)
if result:
return '', 202
else:
return '', 204
class MovieSubtitlesDownload(Resource):
@ -920,7 +958,8 @@ class MovieSubtitlesDownload(Resource):
provider = result[3]
score = result[4]
subs_id = result[6]
history_log_movie(1, radarrId, message, path, language_code, provider, score, subs_id)
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:
@ -987,7 +1026,8 @@ class MovieSubtitlesManualDownload(Resource):
provider = result[3]
score = result[4]
subs_id = result[6]
history_log_movie(2, radarrId, message, path, language_code, provider, score, subs_id)
subs_path = result[7]
history_log_movie(2, radarrId, message, path, language_code, provider, score, subs_id, subs_path)
send_notifications_movie(radarrId, message)
store_subtitles_movie(path, moviePath)
return result, 201
@ -1029,10 +1069,11 @@ class MovieSubtitlesUpload(Resource):
if result is not None:
message = result[0]
path = result[1]
subs_path = result[2]
language_code = language + ":forced" if forced else language
provider = "manual"
score = 120
history_log_movie(4, radarrId, message, path, language_code, provider, score)
history_log_movie(4, radarrId, message, path, language_code, provider, score, subtitles_path=subs_path)
send_notifications_movie(radarrId, message)
store_subtitles_movie(path, moviePath)
@ -1064,21 +1105,54 @@ class MovieHistory(Resource):
def get(self):
radarrid = request.args.get('radarrid')
movie_history = database.execute("SELECT action, timestamp, language, provider, score "
"FROM table_history_movie WHERE radarrId=? ORDER BY timestamp DESC",
(radarrid,))
movie_history = database.execute("SELECT action, timestamp, language, provider, score, radarrId, subs_id, "
"video_path, subtitles_path FROM table_history_movie WHERE radarrId=? ORDER "
"BY timestamp DESC", (radarrid,))
for item in movie_history:
item['timestamp'] = "<div title='" + \
time.strftime('%d/%m/%Y %H:%M:%S', time.localtime(item['timestamp'])) + \
"' data-toggle='tooltip' data-placement='left'>" + \
pretty.date(datetime.datetime.fromtimestamp(item['timestamp'])) + "</div>"
if item['language']:
item['language'] = language_from_alpha2(item['language'])
else:
item['language'] = "<i>undefined</i>"
language = item['language'].split(':')
item['language'] = {"name": language_from_alpha2(language[0]),
"code2": language[0],
"code3": alpha3_from_alpha2(language[0]),
"forced": True if len(language) > 1 else False}
if item['score']:
item['score'] = str(round((int(item['score']) * 100 / 120), 2)) + "%"
if item['video_path']:
# Provide mapped path
mapped_path = path_mappings.path_replace(item['video_path'])
item.update({"mapped_path": mapped_path})
# Confirm if path exist
item.update({"exist": os.path.isfile(mapped_path)})
else:
item.update({"mapped_path": None})
item.update({"exist": False})
if item['subtitles_path']:
# Provide mapped subtitles path
mapped_subtitles_path = path_mappings.path_replace_movie(item['subtitles_path'])
item.update({"mapped_subtitles_path": mapped_subtitles_path})
else:
item.update({"mapped_subtitles_path": None})
# Check if subtitles is blacklisted
if item['action'] not in [0, 4, 5]:
blacklist_db = database.execute(
"SELECT provider, subs_id FROM table_blacklist_movie WHERE provider=? AND "
"subs_id=?", (item['provider'], item['subs_id']))
else:
blacklist_db = []
if len(blacklist_db):
item.update({"blacklisted": True})
else:
item.update({"blacklisted": False})
return jsonify(data=movie_history)
@ -1130,12 +1204,11 @@ class HistorySeries(Resource):
upgradable_episodes = database.execute(
"SELECT video_path, MAX(timestamp) as timestamp, score, table_shows.tags, table_episodes.monitored, "
"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 (" +
','.join(map(str, query_actions)) + ") AND timestamp > ? AND "
"score is not null GROUP BY table_history.video_path, table_history.language",
(minimum_timestamp,))
"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 (" +
','.join(map(str, query_actions)) + ") AND timestamp > ? AND score is not null GROUP BY "
"table_history.video_path, table_history.language", (minimum_timestamp,))
upgradable_episodes = filter_exclusions(upgradable_episodes, 'series')
for upgradable_episode in upgradable_episodes:
@ -1151,15 +1224,17 @@ class HistorySeries(Resource):
row_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']
data = database.execute("SELECT table_history.action, table_shows.title as seriesTitle, table_episodes.monitored, "
data = 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_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 FROM table_history "
"LEFT JOIN table_shows on table_shows.sonarrSeriesId = table_history.sonarrSeriesId "
"LEFT JOIN table_episodes on table_episodes.sonarrEpisodeId = "
"table_history.sonarrEpisodeId WHERE table_episodes.title is not NULL ORDER BY "
"timestamp DESC LIMIT ? OFFSET ?", (length, start))
"table_history.language, table_history.score, table_shows.tags, table_history.action, "
"table_history.subtitles_path, table_history.sonarrEpisodeId, table_history.provider "
"FROM table_history LEFT JOIN table_shows on table_shows.sonarrSeriesId = "
"table_history.sonarrSeriesId LEFT JOIN table_episodes on "
"table_episodes.sonarrEpisodeId = table_history.sonarrEpisodeId WHERE "
"table_episodes.title is not NULL ORDER BY timestamp DESC LIMIT ? OFFSET ?",
(length, start))
for item in data:
# Mark episode as upgradable or not
@ -1180,12 +1255,35 @@ class HistorySeries(Resource):
if item['timestamp']:
item['timestamp'] = pretty.date(int(item['timestamp']))
# Provide mapped path
mapped_path = path_mappings.path_replace(item['path'])
item.update({"mapped_path": mapped_path})
if item['path']:
# Provide mapped path
mapped_path = path_mappings.path_replace(item['path'])
item.update({"mapped_path": mapped_path})
# Confirm if path exist
item.update({"exist": os.path.isfile(mapped_path)})
# Confirm if path exist
item.update({"exist": os.path.isfile(mapped_path)})
else:
item.update({"mapped_path": None})
item.update({"exist": False})
if item['subtitles_path']:
# Provide mapped subtitles path
mapped_subtitles_path = path_mappings.path_replace_movie(item['subtitles_path'])
item.update({"mapped_subtitles_path": mapped_subtitles_path})
else:
item.update({"mapped_subtitles_path": None})
# Check if subtitles is blacklisted
if item['action'] not in [0, 4, 5]:
blacklist_db = database.execute("SELECT provider, subs_id FROM table_blacklist WHERE provider=? AND "
"subs_id=?", (item['provider'], item['subs_id']))
else:
blacklist_db = []
if len(blacklist_db):
item.update({"blacklisted": True})
else:
item.update({"blacklisted": False})
return jsonify(draw=draw, recordsTotal=row_count, recordsFiltered=row_count, data=data)
@ -1230,11 +1328,13 @@ class HistoryMovies(Resource):
"table_movies.radarrId = table_history_movie.radarrId WHERE table_movies.title "
"is not NULL", only_one=True)['count']
data = 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, table_history_movie.language, table_movies.tags, "
"table_history_movie.score FROM table_history_movie LEFT JOIN table_movies on "
"table_movies.radarrId = table_history_movie.radarrId WHERE table_movies.title "
"is not NULL ORDER BY timestamp DESC LIMIT ? OFFSET ?", (length, start))
"table_history_movie.description, table_history_movie.radarrId, table_movies.monitored,"
" table_history_movie.video_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 ORDER BY timestamp "
"DESC LIMIT ? OFFSET ?", (length, start))
for item in data:
# Mark movies as upgradable or not
@ -1266,6 +1366,25 @@ class HistoryMovies(Resource):
item.update({"mapped_path": None})
item.update({"exist": False})
if item['subtitles_path']:
# Provide mapped subtitles path
mapped_subtitles_path = path_mappings.path_replace_movie(item['subtitles_path'])
item.update({"mapped_subtitles_path": mapped_subtitles_path})
else:
item.update({"mapped_subtitles_path": None})
# Check if subtitles is blacklisted
if item['action'] not in [0, 4, 5]:
blacklist_db = database.execute("SELECT provider, subs_id FROM table_blacklist_movie WHERE provider=? "
"AND subs_id=?", (item['provider'], item['subs_id']))
else:
blacklist_db = []
if len(blacklist_db):
item.update({"blacklisted": True})
else:
item.update({"blacklisted": False})
return jsonify(draw=draw, recordsTotal=row_count, recordsFiltered=row_count, data=data)
@ -1419,6 +1538,159 @@ class SearchWantedMovies(Resource):
return '', 200
class BlacklistSeries(Resource):
@authenticate
def get(self):
start = request.args.get('start') or 0
length = request.args.get('length') or -1
draw = request.args.get('draw')
row_count = database.execute("SELECT COUNT(*) as count FROM table_blacklist", only_one=True)['count']
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
item.update({'timestamp': pretty.date(datetime.datetime.fromtimestamp(item['timestamp']))})
# Convert language code2 to name
if item['language']:
language = item['language'].split(':')
item['language'] = {"name": language_from_alpha2(language[0]),
"code2": language[0],
"code3": alpha3_from_alpha2(language[0]),
"forced": True if len(language) > 1 else False}
return jsonify(draw=draw, recordsTotal=row_count, recordsFiltered=row_count, data=data)
class BlacklistEpisodeSubtitlesAdd(Resource):
@authenticate
def post(self):
sonarr_series_id = int(request.form.get('sonarr_series_id'))
sonarr_episode_id = int(request.form.get('sonarr_episode_id'))
provider = request.form.get('provider')
subs_id = request.form.get('subs_id')
language = request.form.get('language')
forced = request.form.get('forced')
language_str = language + ':forced' if forced == 'true' else language
media_path = request.form.get('video_path')
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,
language=language_str)
delete_subtitles(media_type='series',
language=alpha3_from_alpha2(language),
forced=forced,
media_path=path_mappings.path_replace(media_path),
subtitles_path=path_mappings.path_replace(subtitles_path),
sonarr_series_id=sonarr_series_id,
sonarr_episode_id=sonarr_episode_id)
episode_download_subtitles(sonarr_episode_id)
event_stream(type='episodeHistory')
return '', 200
class BlacklistEpisodeSubtitlesRemove(Resource):
@authenticate
def delete(self):
provider = request.form.get('provider')
subs_id = request.form.get('subs_id')
blacklist_delete(provider=provider, subs_id=subs_id)
return '', 200
class BlacklistEpisodeSubtitlesRemoveAll(Resource):
@authenticate
def delete(self):
blacklist_delete_all()
return '', 200
class BlacklistMovies(Resource):
@authenticate
def get(self):
start = request.args.get('start') or 0
length = request.args.get('length') or -1
draw = request.args.get('draw')
row_count = database.execute("SELECT COUNT(*) as count FROM table_blacklist_movie", only_one=True)['count']
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:
# Make timestamp pretty
item.update({'timestamp': pretty.date(datetime.datetime.fromtimestamp(item['timestamp']))})
# Convert language code2 to name
if item['language']:
language = item['language'].split(':')
item['language'] = {"name": language_from_alpha2(language[0]),
"code2": language[0],
"code3": alpha3_from_alpha2(language[0]),
"forced": True if len(language) > 1 else False}
return jsonify(draw=draw, recordsTotal=row_count, recordsFiltered=row_count, data=data)
class BlacklistMovieSubtitlesAdd(Resource):
@authenticate
def post(self):
radarr_id = int(request.form.get('radarr_id'))
provider = request.form.get('provider')
subs_id = request.form.get('subs_id')
language = request.form.get('language')
forced = request.form.get('forced')
language_str = language + ':forced' if forced == 'true' else language
media_path = request.form.get('video_path')
subtitles_path = request.form.get('subtitles_path')
blacklist_log_movie(radarr_id=radarr_id,
provider=provider,
subs_id=subs_id,
language=language_str)
delete_subtitles(media_type='movie',
language=alpha3_from_alpha2(language),
forced=forced,
media_path=path_mappings.path_replace_movie(media_path),
subtitles_path=path_mappings.path_replace_movie(subtitles_path),
radarr_id=radarr_id)
movies_download_subtitles(radarr_id)
event_stream(type='movieHistory')
return '', 200
class BlacklistMovieSubtitlesRemove(Resource):
@authenticate
def delete(self):
provider = request.form.get('provider')
subs_id = request.form.get('subs_id')
blacklist_delete_movie(provider=provider, subs_id=subs_id)
return '', 200
class BlacklistMovieSubtitlesRemoveAll(Resource):
@authenticate
def delete(self):
blacklist_delete_all_movie()
return '', 200
class SyncSubtitles(Resource):
@authenticate
def post(self):
@ -1529,6 +1801,15 @@ api.add_resource(WantedMovies, '/wanted_movies')
api.add_resource(SearchWantedSeries, '/search_wanted_series')
api.add_resource(SearchWantedMovies, '/search_wanted_movies')
api.add_resource(BlacklistSeries, '/blacklist_series')
api.add_resource(BlacklistEpisodeSubtitlesAdd, '/blacklist_episode_subtitles_add')
api.add_resource(BlacklistEpisodeSubtitlesRemove, '/blacklist_episode_subtitles_remove')
api.add_resource(BlacklistEpisodeSubtitlesRemoveAll, '/blacklist_episode_subtitles_remove_all')
api.add_resource(BlacklistMovies, '/blacklist_movies')
api.add_resource(BlacklistMovieSubtitlesAdd, '/blacklist_movie_subtitles_add')
api.add_resource(BlacklistMovieSubtitlesRemove, '/blacklist_movie_subtitles_remove')
api.add_resource(BlacklistMovieSubtitlesRemoveAll, '/blacklist_movie_subtitles_remove_all')
api.add_resource(SyncSubtitles, '/sync_subtitles')
api.add_resource(BrowseBazarrFS, '/browse_bazarr_filesystem')

@ -115,11 +115,13 @@ def db_upgrade():
['table_history', 'provider', 'text'],
['table_history', 'score', 'text'],
['table_history', 'subs_id', 'text'],
['table_history', 'subtitles_path', 'text'],
['table_history_movie', 'video_path', 'text'],
['table_history_movie', 'language', 'text'],
['table_history_movie', 'provider', 'text'],
['table_history_movie', 'score', 'text'],
['table_history_movie', 'subs_id', 'text']
['table_history_movie', 'subs_id', 'text'],
['table_history_movie', 'subtitles_path', 'text']
]
for column in columnToAdd:
@ -139,6 +141,12 @@ def db_upgrade():
database.execute("UPDATE table_movies SET hearing_impaired = 'False' WHERE hearing_impaired is null")
database.execute("UPDATE table_movies SET forced = 'False' WHERE forced is null")
# Create blacklist tables
database.execute("CREATE TABLE IF NOT EXISTS table_blacklist (sonarr_series_id integer, sonarr_episode_id integer, "
"timestamp integer, provider text, subs_id text, language text)")
database.execute("CREATE TABLE IF NOT EXISTS table_blacklist_movie (radarr_id integer, timestamp integer, "
"provider text, subs_id text, language text)")
def filter_exclusions(dicts_list, type):
if type == 'series':

@ -4,14 +4,15 @@ import logging
import os
from knowit import api
from utils import get_binary
class EmbeddedSubsReader:
def __init__(self):
self.ffprobe = get_binary("ffprobe")
self.ffprobe = None
def list_languages(self, file):
from utils import get_binary
self.ffprobe = get_binary("ffprobe")
subtitles_list = []
if self.ffprobe:

@ -23,7 +23,7 @@ from get_languages import language_from_alpha3, alpha2_from_alpha3, alpha3_from_
from config import settings
from helper import path_mappings, pp_replace, get_target_folder, force_unicode
from list_subtitles import store_subtitles, list_missing_subtitles, store_subtitles_movie, list_missing_subtitles_movies
from utils import history_log, history_log_movie, get_binary
from utils import history_log, history_log_movie, get_binary, get_blacklist
from notifier import send_notifications, send_notifications_movie
from get_providers import get_providers, get_providers_auth, provider_throttle, provider_pool
from knowit import api
@ -167,7 +167,7 @@ def download_subtitle(path, language, audio_language, hi, forced, providers, pro
pool_class=provider_pool(),
compute_score=compute_score,
throttle_time=None, # fixme
blacklist=None, # fixme
blacklist=get_blacklist(media_type=media_type),
throttle_callback=provider_throttle,
pre_download_hook=None, # fixme
post_download_hook=None, # fixme
@ -263,13 +263,16 @@ def download_subtitle(path, language, audio_language, hi, forced, providers, pro
# fixme: support multiple languages at once
if media_type == 'series':
reversed_path = path_mappings.path_replace_reverse(path)
reversed_subtitles_path = path_mappings.path_replace_reverse(downloaded_path)
else:
reversed_path = path_mappings.path_replace_reverse_movie(path)
reversed_subtitles_path = path_mappings.path_replace_reverse_movie(downloaded_path)
track_event(category=downloaded_provider, action=action, label=downloaded_language)
return message, reversed_path, downloaded_language_code2, downloaded_provider, subtitle.score, \
subtitle.language.forced, subtitle.id
subtitle.language.forced, subtitle.id, reversed_subtitles_path
if not saved_any:
logging.debug('BAZARR No Subtitles were found for this file: ' + path)
@ -332,6 +335,7 @@ def manual_search(path, language, hi, forced, providers, providers_auth, sceneNa
subtitles = list_all_subtitles([video], language_set,
providers=providers,
provider_configs=providers_auth,
blacklist=get_blacklist(media_type=media_type),
throttle_callback=provider_throttle,
language_hook=None) # fixme
else:
@ -420,8 +424,12 @@ def manual_download_subtitle(path, language, audio_language, hi, forced, subtitl
min_score, max_score, scores = get_scores(video, media_type)
try:
if provider:
download_subtitles([subtitle], providers={provider}, provider_configs=providers_auth,
pool_class=provider_pool(), throttle_callback=provider_throttle)
download_subtitles([subtitle],
providers={provider},
provider_configs=providers_auth,
pool_class=provider_pool(),
blacklist=get_blacklist(media_type=media_type),
throttle_callback=provider_throttle)
logging.debug('BAZARR Subtitles file downloaded for this file:' + path)
else:
logging.info("BAZARR All providers are throttled")
@ -507,14 +515,16 @@ def manual_download_subtitle(path, language, audio_language, hi, forced, subtitl
if media_type == 'series':
reversed_path = path_mappings.path_replace_reverse(path)
reversed_subtitles_path = path_mappings.path_replace_reverse(downloaded_path)
else:
reversed_path = path_mappings.path_replace_reverse_movie(path)
reversed_subtitles_path = path_mappings.path_replace_reverse_movie(downloaded_path)
track_event(category=downloaded_provider, action="manually_downloaded",
label=downloaded_language)
return message, reversed_path, downloaded_language_code2, downloaded_provider, subtitle.score, \
subtitle.language.forced, subtitle.id
subtitle.language.forced, subtitle.id, reversed_subtitles_path
else:
logging.error(
"BAZARR Tried to manually download a Subtitles for file: " + path + " but we weren't able to do (probably throttled by " + str(
@ -625,10 +635,12 @@ def manual_upload_subtitle(path, language, forced, title, scene_name, media_type
if media_type == 'series':
reversed_path = path_mappings.path_replace_reverse(path)
reversed_subtitles_path = path_mappings.path_replace_reverse(subtitle_path)
else:
reversed_path = path_mappings.path_replace_reverse_movie(path)
reversed_subtitles_path = path_mappings.path_replace_reverse_movie(subtitle_path)
return message, reversed_path
return message, reversed_path, reversed_subtitles_path
def series_download_subtitles(no):
@ -676,9 +688,10 @@ def series_download_subtitles(no):
provider = result[3]
score = result[4]
subs_id = result[6]
subs_path = result[7]
store_subtitles(episode['path'], path_mappings.path_replace(episode['path']))
history_log(1, no, episode['sonarrEpisodeId'], message, path, language_code, provider, score,
subs_id)
subs_id, subs_path)
send_notifications(no, episode['sonarrEpisodeId'], message)
else:
logging.info("BAZARR All providers are throttled")
@ -722,9 +735,10 @@ def episode_download_subtitles(no):
provider = result[3]
score = result[4]
subs_id = result[6]
subs_path = result[7]
store_subtitles(episode['path'], path_mappings.path_replace(episode['path']))
history_log(1, episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message, path,
language_code, provider, score, subs_id)
language_code, provider, score, subs_id, subs_path)
send_notifications(episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message)
else:
logging.info("BAZARR All providers are throttled")
@ -771,8 +785,9 @@ def movies_download_subtitles(no):
provider = result[3]
score = result[4]
subs_id = result[6]
subs_path = result[7]
store_subtitles_movie(movie['path'], path_mappings.path_replace_movie(movie['path']))
history_log_movie(1, no, message, path, language_code, provider, score, subs_id)
history_log_movie(1, no, message, path, language_code, provider, score, subs_id, subs_path)
send_notifications_movie(no, message)
else:
logging.info("BAZARR All providers are throttled")
@ -828,9 +843,11 @@ def wanted_download_subtitles(path, l, count_episodes):
language_code = result[2] + ":forced" if forced else result[2]
provider = result[3]
score = result[4]
subs_id = result[6]
subs_path = result[7]
store_subtitles(episode['path'], path_mappings.path_replace(episode['path']))
history_log(1, episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message, path,
language_code, provider, score)
language_code, provider, score, subs_id, subs_path)
send_notifications(episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message)
else:
logging.debug(
@ -884,9 +901,10 @@ def wanted_download_subtitles_movie(path, l, count_movies):
provider = result[3]
score = result[4]
subs_id = result[6]
subs_path = result[7]
store_subtitles_movie(movie['path'], path_mappings.path_replace_movie(movie['path']))
history_log_movie(1, movie['radarrId'], message, path, language_code, provider, score,
subs_id)
subs_id, subs_path)
send_notifications_movie(movie['radarrId'], message)
else:
logging.info(
@ -1167,9 +1185,10 @@ def upgrade_subtitles():
provider = result[3]
score = result[4]
subs_id = result[6]
subs_path = result[7]
store_subtitles(episode['video_path'], path_mappings.path_replace(episode['video_path']))
history_log(3, episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message, path,
language_code, provider, score, subs_id)
language_code, provider, score, subs_id, subs_path)
send_notifications(episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message)
if settings.general.getboolean('use_radarr'):
@ -1217,9 +1236,10 @@ def upgrade_subtitles():
provider = result[3]
score = result[4]
subs_id = result[6]
subs_path = result[7]
store_subtitles_movie(movie['video_path'],
path_mappings.path_replace_movie(movie['video_path']))
history_log_movie(3, movie['radarrId'], message, path, language_code, provider, score, subs_id)
history_log_movie(3, movie['radarrId'], message, path, language_code, provider, score, subs_id, subs_path)
send_notifications_movie(movie['radarrId'], message)

@ -274,6 +274,18 @@ def historystats():
data_languages=sorted(data_languages_list, key=lambda i: i['name']))
@app.route('/blacklist/series/')
@login_required
def blacklistseries():
return render_template('blacklistseries.html')
@app.route('/blacklist/movies/')
@login_required
def blacklistmovies():
return render_template('blacklistmovies.html')
@app.route('/wanted/series/')
@login_required
def wantedseries():

@ -105,11 +105,11 @@ class SubSyncer:
if media_type == 'series':
history_log(action=5, sonarr_series_id=sonarr_series_id, sonarr_episode_id=sonarr_episode_id,
description=message, video_path=path_mappings.path_replace_reverse(self.reference),
language=alpha2_from_alpha3(srt_lang))
language=alpha2_from_alpha3(srt_lang), subtitles_path=srt_path)
else:
history_log_movie(action=5, radarr_id=radarr_id, description=message,
video_path=path_mappings.path_replace_reverse_movie(self.reference),
language=alpha2_from_alpha3(srt_lang))
language=alpha2_from_alpha3(srt_lang), subtitles_path=srt_path)
else:
logging.error('BAZARR unable to sync subtitles: ' + self.srtin)

@ -11,6 +11,9 @@ from get_args import args
from config import settings, url_sonarr, url_radarr
from database import database
from event_handler import event_stream
from get_languages import alpha2_from_alpha3, language_from_alpha3
from helper import path_mappings
from list_subtitles import store_subtitles, store_subtitles_movie
from subliminal import region as subliminal_cache_region
import datetime
@ -22,22 +25,55 @@ class BinaryNotFound(Exception):
def history_log(action, sonarr_series_id, sonarr_episode_id, description, video_path=None, language=None, provider=None,
score=None, subs_id=None):
score=None, subs_id=None, subtitles_path=None):
database.execute("INSERT INTO table_history (action, sonarrSeriesId, sonarrEpisodeId, timestamp, description,"
"video_path, language, provider, score, subs_id) VALUES (?,?,?,?,?,?,?,?,?,?)",
"video_path, language, provider, score, subs_id, subtitles_path) VALUES (?,?,?,?,?,?,?,?,?,?,?)",
(action, sonarr_series_id, sonarr_episode_id, time.time(), description, video_path, language,
provider, score, subs_id))
provider, score, subs_id, subtitles_path))
event_stream(type='episodeHistory')
def blacklist_log(sonarr_series_id, sonarr_episode_id, provider, subs_id, language):
database.execute("INSERT INTO table_blacklist (sonarr_series_id, sonarr_episode_id, timestamp, provider, "
"subs_id, language) VALUES (?,?,?,?,?,?)",
(sonarr_series_id, sonarr_episode_id, time.time(), provider, subs_id, language))
event_stream(type='episodeBlacklist')
def blacklist_delete(provider, subs_id):
database.execute("DELETE FROM table_blacklist WHERE provider=? AND subs_id=?", (provider, subs_id))
event_stream(type='episodeBlacklist')
def blacklist_delete_all():
database.execute("DELETE FROM table_blacklist")
event_stream(type='episodeBlacklist')
def history_log_movie(action, radarr_id, description, video_path=None, language=None, provider=None, score=None,
subs_id=None):
subs_id=None, subtitles_path=None):
database.execute("INSERT INTO table_history_movie (action, radarrId, timestamp, description, video_path, language, "
"provider, score, subs_id) VALUES (?,?,?,?,?,?,?,?,?)",
(action, radarr_id, time.time(), description, video_path, language, provider, score, subs_id))
"provider, score, subs_id, subtitles_path) VALUES (?,?,?,?,?,?,?,?,?,?)",
(action, radarr_id, time.time(), description, video_path, language, provider, score, subs_id, subtitles_path))
event_stream(type='movieHistory')
def blacklist_log_movie(radarr_id, provider, subs_id, language):
database.execute("INSERT INTO table_blacklist_movie (radarr_id, timestamp, provider, subs_id, language) "
"VALUES (?,?,?,?,?)", (radarr_id, time.time(), provider, subs_id, language))
event_stream(type='movieBlacklist')
def blacklist_delete_movie(provider, subs_id):
database.execute("DELETE FROM table_blacklist_movie WHERE provider=? AND subs_id=?", (provider, subs_id))
event_stream(type='movieBlacklist')
def blacklist_delete_all_movie():
database.execute("DELETE FROM table_blacklist_movie")
event_stream(type='movieBlacklist')
def get_binary(name):
binaries_dir = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', 'bin'))
@ -65,6 +101,19 @@ def get_binary(name):
raise BinaryNotFound
def get_blacklist(media_type):
if media_type == 'series':
blacklist_db = database.execute("SELECT provider, subs_id FROM table_blacklist")
else:
blacklist_db = database.execute("SELECT provider, subs_id FROM table_blacklist_movie")
blacklist_list = []
for item in blacklist_db:
blacklist_list.append((item['provider'], item['subs_id']))
return blacklist_list
def cache_maintenance():
main_cache_validity = 14 # days
pack_cache_validity = 4 # days
@ -139,3 +188,39 @@ def get_radarr_platform():
except Exception:
logging.debug('BAZARR cannot get Radarr platform')
return radarr_platform
def delete_subtitles(media_type, language, forced, media_path, subtitles_path, sonarr_series_id=None,
sonarr_episode_id=None, radarr_id=None):
if not subtitles_path.endswith('.srt'):
logging.error('BAZARR can only delete .srt files.')
return False
language_log = alpha2_from_alpha3(language) + ':forced' if forced in [True, 'true'] else alpha2_from_alpha3(language)
language_string = language_from_alpha3(language) + ' forced' if forced in [True, 'true'] else language_from_alpha3(language)
result = language_string + " subtitles deleted from disk."
if media_type == 'series':
try:
os.remove(path_mappings.path_replace(subtitles_path))
except OSError:
logging.exception('BAZARR cannot delete subtitles file: ' + subtitles_path)
store_subtitles(path_mappings.path_replace_reverse(media_path), media_path)
return False
else:
history_log(0, sonarr_series_id, sonarr_episode_id, result, language=language_log,
video_path=path_mappings.path_replace_reverse(media_path),
subtitles_path=path_mappings.path_replace_reverse(subtitles_path))
store_subtitles(path_mappings.path_replace_reverse(media_path), media_path)
else:
try:
os.remove(path_mappings.path_replace_movie(subtitles_path))
except OSError:
logging.exception('BAZARR cannot delete subtitles file: ' + subtitles_path)
store_subtitles_movie(path_mappings.path_replace_reverse_movie(media_path), media_path)
return False
else:
history_log_movie(0, radarr_id, result, language=language_log,
video_path=path_mappings.path_replace_reverse_movie(media_path),
subtitles_path=path_mappings.path_replace_reverse_movie(subtitles_path))
store_subtitles_movie(path_mappings.path_replace_reverse_movie(media_path), media_path)
return True

@ -232,6 +232,17 @@
</ul>
</li>
<li><a href="#"><i class="fas fa-file-excel"></i><span class="hide-menu"> Blacklist</span></a>
<ul aria-expanded="false" class="collapse">
{% if settings.general.getboolean('use_sonarr') %}
<li><a href="{{ url_for('blacklistseries') }}"> Series</a></li>
{% endif %}
{% if settings.general.getboolean('use_radarr') %}
<li><a href="{{ url_for('blacklistmovies') }}"> Movies</a></li>
{% endif %}
</ul>
</li>
<li><a href="#"><i class="fas fa-exclamation-triangle"></i><span
class="hide-menu"> Wanted</span></a>
<ul aria-expanded="false" class="collapse">

@ -0,0 +1,107 @@
{% extends '_main.html' %}
{% block title %}Blacklist (Movies) - Bazarr{% endblock %}
{% block bcleft %}
<button class="btn btn-outline" id="remove_all_button">
<div><i class="fas fa-trash-alt align-top text-themecolor text-center font-20" aria-hidden="true"></i></div>
<div class="align-bottom text-themecolor small text-center">Remove All</div>
</button>
{% endblock bcleft %}
{% block bcright %}
{% endblock bcright %}
{% block body %}
<table id="blacklist_movies" class="table table-striped" style="width:100%">
<thead>
<tr>
<th>Title</th>
<th>Language</th>
<th>Provider</th>
<th>Date</th>
<th>Actions</th>
</tr>
</thead>
</table>
{% endblock body %}
{% block tail %}
<script>
$(document).ready(function () {
var table = $('#blacklist_movies').DataTable({
processing: true,
serverSide: true,
language: {
zeroRecords: 'No Blacklisted Movies Subtitles',
processing: "Loading Blacklisted Movies Subtitles..."
},
searching: false,
ordering: false,
lengthChange: false,
responsive: true,
pageLength: {{ settings.general.page_size }},
ajax: "{{ url_for('api.blacklistmovies') }}",
columns: [
{
data: null,
render: function (data) {
return '<a href="' + "{{ url_for( 'movie', no='tempvalue' ) }}".replace("tempvalue", data.radarrId) + '">' + data.title + '</a>';
}
},
{
data: 'language',
render: function (value) {
return value.name + ((value.forced) ? ' forced' : '')
}
},
{data: "provider"},
{data: "timestamp"},
{data: null,
render: function (data) {
return '<a href="" class="blacklist_delete badge badge-secondary" data-toggle="tooltip" data-placement="right" title="Remove this subtitles from blacklist" data-provider="' + data.provider + '" data-subs_id=' + data.subs_id + '><i class="fas fa-trash-alt"></i></a>';
}
}
]
});
$('#remove_all_button').on('click', function (e) {
e.preventDefault();
$.ajax({
url: "{{ url_for('api.blacklistmoviesubtitlesremoveall') }}",
type: 'DELETE',
beforeSend: function () {
$('#remove_all_button').find("i").addClass('fa-spin');
},
complete: function () {
$('#remove_all_button').find("i").removeClass('fa-spin');
}
});
});
$('#blacklist_movies').on('click', '.blacklist_delete', function (e) {
$(this).tooltip('dispose');
e.preventDefault();
const values = {
provider: $(this).attr('data-provider'),
subs_id: $(this).attr('data-subs_id')
};
$.ajax({
url: "{{ url_for('api.blacklistmoviesubtitlesremove') }}",
type: "DELETE",
dataType: "json",
data: values
});
});
events.on('event', function (event) {
var event_json = JSON.parse(event);
if (event_json.type === 'movieBlacklist') {
$('#blacklist_movies').DataTable().ajax.reload(resetPaging = false);
$('[data-toggle="tooltip"]').tooltip({html: true});
}
});
});
</script>
{% endblock tail %}

@ -0,0 +1,111 @@
{% extends '_main.html' %}
{% block title %}Blacklist (Series) - Bazarr{% endblock %}
{% block bcleft %}
<button class="btn btn-outline" id="remove_all_button">
<div><i class="fas fa-trash-alt align-top text-themecolor text-center font-20" aria-hidden="true"></i></div>
<div class="align-bottom text-themecolor small text-center">Remove All</div>
</button>
{% endblock bcleft %}
{% block bcright %}
{% endblock bcright %}
{% block body %}
<table id="blacklist_series" class="table table-striped" style="width:100%">
<thead>
<tr>
<th>Series</th>
<th>Episode</th>
<th>Episode Title</th>
<th>Language</th>
<th>Provider</th>
<th>Date</th>
<th>Actions</th>
</tr>
</thead>
</table>
{% endblock body %}
{% block tail %}
<script>
$(document).ready(function () {
var table = $('#blacklist_series').DataTable({
processing: true,
serverSide: true,
language: {
zeroRecords: 'No Blacklisted Series Subtitles',
processing: "Loading Blacklisted Series Subtitles..."
},
searching: false,
ordering: false,
lengthChange: false,
responsive: true,
pageLength: {{ settings.general.page_size }},
ajax: "{{ url_for('api.blacklistseries') }}",
columns: [
{
data: null,
render: function (data) {
return '<a href="' + "{{ url_for( 'episodes', no='tempvalue' ) }}".replace("tempvalue", data.sonarrSeriesId) + '">' + data.seriesTitle + '</a>';
}
},
{data: "episode_number"},
{data: "episodeTitle"},
{
data: 'language',
render: function (value) {
return value.name + ((value.forced) ? ' forced' : '')
}
},
{data: "provider"},
{data: "timestamp"},
{data: null,
render: function (data) {
return '<a href="" class="blacklist_delete badge badge-secondary" data-toggle="tooltip" data-placement="right" title="Remove this subtitles from blacklist" data-provider="' + data.provider + '" data-subs_id=' + data.subs_id + '><i class="fas fa-trash-alt"></i></a>';
}
}
]
});
$('#remove_all_button').on('click', function (e) {
e.preventDefault();
$.ajax({
url: "{{ url_for('api.blacklistepisodesubtitlesremoveall') }}",
type: 'DELETE',
beforeSend: function () {
$('#remove_all_button').find("i").addClass('fa-spin');
},
complete: function () {
$('#remove_all_button').find("i").removeClass('fa-spin');
}
});
});
$('#blacklist_series').on('click', '.blacklist_delete', function (e) {
$(this).tooltip('dispose');
e.preventDefault();
const values = {
provider: $(this).attr('data-provider'),
subs_id: $(this).attr('data-subs_id')
};
$.ajax({
url: "{{ url_for('api.blacklistepisodesubtitlesremove') }}",
type: "DELETE",
dataType: "json",
data: values
});
});
events.on('event', function (event) {
var event_json = JSON.parse(event);
if (event_json.type === 'episodeBlacklist') {
$('#blacklist_series').DataTable().ajax.reload(resetPaging = false);
$('[data-toggle="tooltip"]').tooltip({html: true});
}
});
});
</script>
{% endblock tail %}

@ -298,13 +298,14 @@
<th style="text-align: left;">Provider:</th>
<th style="text-align: left;">Score:</th>
<th style="text-align: left;">Date:</th>
<th style="text-align: left;">Actions:</th>
</tr>
</thead>
</table>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
@ -434,7 +435,7 @@
if (value[1] === null) {
languages = languages + '<span class="badge badge-secondary" data-toggle="tooltip" data-placement="right" title="' + value[0].name + ((value[0].forced) ? ' forced' : '') + '">' + value[0].code2 + ((value[0].forced) ? ':forced' : '') + '</span> ';
} else {
languages = languages + '<a href="" class="remove_subtitles badge badge-secondary" data-toggle="tooltip" data-placement="right" title="' + value[0].name + ((value[0].forced) ? ' forced' : '') + '" data-episodePath="' + data.mapped_path + '" data-language="' + value[0].code3 + '" data-subtitlesPath="' + value[1] + '" data-sonarrEpisodeId=' + data.sonarrEpisodeId + '>' + value[0].code2 + ((value[0].forced) ? ':forced' : '') + ' <i class="far fa-trash-alt"></i></a> ';
languages = languages + '<a href="" class="remove_subtitles badge badge-secondary" data-toggle="tooltip" data-placement="right" title="' + value[0].name + ((value[0].forced) ? ' forced' : '') + '" data-episodePath="' + data.mapped_path + '" data-language="' + value[0].code3 + '" data-forced="' + value[0].forced + '" data-subtitlesPath="' + value[1] + '" data-sonarrEpisodeId=' + data.sonarrEpisodeId + '>' + value[0].code2 + ((value[0].forced) ? ':forced' : '') + ' <i class="far fa-trash-alt"></i></a> ';
}
}
}
@ -496,6 +497,7 @@
const values = {
episodePath: $(this).attr("data-episodePath"),
language: $(this).attr("data-language"),
forced: $(this).attr("data-forced"),
subtitlesPath: $(this).attr("data-subtitlesPath"),
sonarrSeriesId: seriesDetails['sonarrSeriesId'],
sonarrEpisodeId: $(this).attr("data-sonarrEpisodeId"),
@ -867,6 +869,13 @@
}
}
}
if (event_json.type === 'episodeBlacklist' || event_json.type === 'episodeHistory') {
if ($('#episode_history_result').DataTable().ajax.json()) {
$('#episode_history_result').DataTable().ajax.reload(resetPaging = false);
$('[data-toggle="tooltip"]').tooltip({html: true});
}
}
});
$('#episodes').on('click', '.episode_history', function (e) {
@ -888,7 +897,7 @@
searching: true,
ordering: false,
scrollX: true,
processing: false,
processing: true,
serverSide: false,
ajax: {
url: '{{ url_for( 'api.episodeshistory' )}}?episodeid=' + sonarrEpisodeId
@ -912,10 +921,29 @@
}
}
},
{data: 'language'},
{
data: 'language',
render: function (value) {
if (value) {
return value.name + ((value.forced) ? ' forced' : '')
} else {
return '<i>undefined</i>'
}
}
},
{data: 'provider'},
{data: 'score'},
{data: 'timestamp'}
{data: 'timestamp'},
{
data: null,
render: function (data) {
if (data.subs_id && data.subtitles_path && !data.blacklisted) {
return '<a href="" class="blacklist_subtitles badge badge-secondary" data-toggle="tooltip" data-placement="right" title="Blacklist this subtitles" data-sonarrSeriesId="' + data.sonarrSeriesId + '" data-sonarrEpisodeId="' + data.sonarrEpisodeId + '" data-language="' + data.language.code2 + '" data-forced="' + data.language.forced + '" data-provider="' + data.provider + '" data-subs_id="' + data.subs_id + '" data-video_path="' + data.video_path + '" data-subtitles_path="' + data.mapped_subtitles_path + '"><i class="far fa-file-excel"></i></a>';
} else {
return null;
}
}
}
]
});
@ -925,6 +953,31 @@
});
});
$('#episode_history_result').on('click', '.blacklist_subtitles', function (e) {
$(this).tooltip('dispose');
e.preventDefault();
const values = {
sonarr_series_id: $(this).attr('data-sonarrseriesid'),
sonarr_episode_id: $(this).attr('data-sonarrepisodeid'),
provider: $(this).attr('data-provider'),
subs_id: $(this).attr('data-subs_id'),
language: $(this).attr('data-language'),
forced: $(this).attr('data-forced'),
video_path: $(this).attr('data-video_path'),
subtitles_path: $(this).attr('data-subtitles_path')
};
var cell = $(this).parent();
$.ajax({
url: "{{ url_for('api.blacklistepisodesubtitlesadd') }}",
type: "POST",
dataType: "json",
data: values,
beforeSend: function () {
cell.html('<div class="spinner-border spinner-border-sm" role="status"><span class="sr-only">Loading...</span></div>');
}
});
});
$('#episodes').on('click', '.episode_tools', function (e) {
$(this).tooltip('dispose');
e.preventDefault();

@ -18,6 +18,7 @@
<th>title</th>
<th>timestamp</th>
<th style="max-width: 60%;">description</th>
<th>Actions</th>
</tr>
</thead>
</table>
@ -74,13 +75,47 @@
return data.description;
}
}
},
{
data: null,
render: function (data) {
if (data.subs_id && data.subtitles_path && !data.blacklisted) {
return '<a href="" class="blacklist_subtitles badge badge-secondary" data-toggle="tooltip" data-placement="right" title="Blacklist this subtitles" data-radarrid="' + data.radarrId + '" data-language="' + data.language.code2 + '" data-forced="' + data.language.forced + '" data-provider="' + data.provider + '" data-subs_id="' + data.subs_id + '" data-video_path="' + data.video_path + '" data-subtitles_path="' + data.subtitles_path + '"><i class="far fa-file-excel"></i></a>';
} else {
return null;
}
}
}
]
});
$('#history_movies').on('click', '.blacklist_subtitles', function (e) {
$(this).tooltip('dispose');
e.preventDefault();
const values = {
radarr_id: $(this).attr('data-radarrid'),
provider: $(this).attr('data-provider'),
subs_id: $(this).attr('data-subs_id'),
language: $(this).attr('data-language'),
forced: $(this).attr('data-forced'),
video_path: $(this).attr('data-video_path'),
subtitles_path: $(this).attr('data-subtitles_path')
};
var cell = $(this).parent();
$.ajax({
url: "{{ url_for('api.blacklistmoviesubtitlesadd') }}",
type: "POST",
dataType: "json",
data: values,
beforeSend: function () {
cell.html('<div class="spinner-border spinner-border-sm" role="status"><span class="sr-only">Loading...</span></div>');
}
});
});
events.on('event', function (event) {
var event_json = JSON.parse(event);
if (event_json.type === 'movieHistory') {
if (event_json.type === 'movieHistory' || event_json.type === 'movieBlacklist') {
$('#history_movies').DataTable().ajax.reload(resetPaging = false);
$('[data-toggle="tooltip"]').tooltip({html: true});
}

@ -20,6 +20,7 @@
<th>Episode Title</th>
<th>Date</th>
<th style="max-width: 40%;">Description</th>
<th>Actions</th>
</tr>
</thead>
</table>
@ -78,13 +79,48 @@
return data.description;
}
}
},
{
data: null,
render: function (data) {
if (data.subs_id && data.subtitles_path && !data.blacklisted) {
return '<a href="" class="blacklist_subtitles badge badge-secondary" data-toggle="tooltip" data-placement="right" title="Blacklist this subtitles" data-sonarrSeriesId="' + data.sonarrSeriesId + '" data-sonarrEpisodeId="' + data.sonarrEpisodeId + '" data-language="' + data.language.code2 + '" data-forced="' + data.language.forced + '" data-provider="' + data.provider + '" data-subs_id="' + data.subs_id + '" data-video_path="' + data.path + '" data-subtitles_path="' + data.mapped_subtitles_path + '"><i class="far fa-file-excel"></i></a>';
} else {
return null;
}
}
}
]
});
$('#history_series').on('click', '.blacklist_subtitles', function (e) {
$(this).tooltip('dispose');
e.preventDefault();
const values = {
sonarr_series_id: $(this).attr('data-sonarrseriesid'),
sonarr_episode_id: $(this).attr('data-sonarrepisodeid'),
provider: $(this).attr('data-provider'),
subs_id: $(this).attr('data-subs_id'),
language: $(this).attr('data-language'),
forced: $(this).attr('data-forced'),
video_path: $(this).attr('data-video_path'),
subtitles_path: $(this).attr('data-subtitles_path')
};
var cell = $(this).parent();
$.ajax({
url: "{{ url_for('api.blacklistepisodesubtitlesadd') }}",
type: "POST",
dataType: "json",
data: values,
beforeSend: function () {
cell.html('<div class="spinner-border spinner-border-sm" role="status"><span class="sr-only">Loading...</span></div>');
}
});
});
events.on('event', function (event) {
var event_json = JSON.parse(event);
if (event_json.type === 'episodeHistory') {
if (event_json.type === 'episodeHistory' || event_json.type === 'episodeBlacklist') {
$('#history_series').DataTable().ajax.reload(resetPaging = false);
$('[data-toggle="tooltip"]').tooltip({html: true});
}

@ -305,13 +305,14 @@
<th style="text-align: left;">Provider:</th>
<th style="text-align: left;">Score:</th>
<th style="text-align: left;">Date:</th>
<th style="text-align: left;">Actions:</th>
</tr>
</thead>
</table>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
@ -370,6 +371,7 @@
const values = {
moviePath: movieDetails['mapped_path'],
language: $(this).data("language"),
forced: $(this).data("forced"),
subtitlesPath: $(this).data("subtitlespath"),
radarrId: movieDetails['radarrId'],
tmdbid: movieDetails['tmdbId']
@ -694,6 +696,13 @@
movieDetailsRefresh();
}
}
if (event_json.type === 'movieBlacklist' || event_json.type === 'movieHistory') {
if ($('#movie_history_result').DataTable().ajax.json()) {
$('#movie_history_result').DataTable().ajax.reload(resetPaging = false);
$('[data-toggle="tooltip"]').tooltip({html: true});
}
}
});
$('#history_button').on('click', function(e){
@ -715,25 +724,46 @@
searching: true,
ordering: false,
scrollX: true,
processing: false,
processing: true,
serverSide: false,
ajax: {
url: '{{ url_for( 'api.moviehistory' )}}?radarrid=' + radarrId
},
columns: [
{ data: 'action',
"render": function(data) {
{
data: 'action',
render: function(data) {
if (data === 0) {return "<i class='fas fa-trash' title='Subtitle file has been erased.' data-toggle='tooltip' data-placement='right'></i>";}
else if (data === 1) {return "<i class='fas fa-download' title='Subtitle file has been downloaded.' data-toggle='tooltip' data-placement='right'></i>";}
else if (data === 2) {return "<i class='fas fa-user' title='Subtitle file has been manually downloaded.' data-toggle='tooltip' data-placement='right'></i>";}
else if (data === 3) {return "<i class='fas fa-recycle' title='Subtitle file has been upgraded.' data-toggle='tooltip' data-placement='right'></i>";}
else if (data === 4) {return "<i class='fas fa-cloud-upload-alt' title='Subtitle file has been manually uploaded.' data-toggle='tooltip' data-placement='right'></i>";}
else if (data === 5) {return "<i class='fas fa-clock' title='Subtitle file has been synced.' data-toggle='tooltip' data-placement='right'></i>";}
}},
{ data: 'language' },
}
},
{
data: 'language',
render: function (value) {
if (value) {
return value.name + ((value.forced) ? ' forced' : '')
} else {
return '<i>undefined</i>'
}
}
},
{ data: 'provider' },
{ data: 'score'},
{ data: 'timestamp' }
{ data: 'timestamp'},
{
data: null,
render: function (data) {
if (data.subs_id && data.subtitles_path && !data.blacklisted) {
return '<a href="" class="blacklist_subtitles badge badge-secondary" data-toggle="tooltip" data-placement="right" title="Blacklist this subtitles" data-radarrid="' + data.radarrId + '" data-language="' + data.language.code2 + '" data-forced="' + data.language.forced + '" data-provider="' + data.provider + '" data-subs_id="' + data.subs_id + '" data-video_path="' + data.video_path + '" data-subtitles_path="' + data.subtitles_path + '"><i class="far fa-file-excel"></i></a>';
} else {
return null;
}
}
}
]
} );
@ -743,6 +773,30 @@
});
});
$('#movie_history_result').on('click', '.blacklist_subtitles', function (e) {
$(this).tooltip('dispose');
e.preventDefault();
const values = {
radarr_id: $(this).attr('data-radarrid'),
provider: $(this).attr('data-provider'),
subs_id: $(this).attr('data-subs_id'),
language: $(this).attr('data-language'),
forced: $(this).attr('data-forced'),
video_path: $(this).attr('data-video_path'),
subtitles_path: $(this).attr('data-subtitles_path')
};
var cell = $(this).parent();
$.ajax({
url: "{{ url_for('api.blacklistmoviesubtitlesadd') }}",
type: "POST",
dataType: "json",
data: values,
beforeSend: function () {
cell.html('<div class="spinner-border spinner-border-sm" role="status"><span class="sr-only">Loading...</span></div>');
}
});
});
$('#tools_button').on('click', function (e) {
$(this).tooltip('dispose');
e.preventDefault();
@ -912,7 +966,7 @@
{ "data" : null,
"render": function(data) {
if (data['path']) {
return '<button type="button" class="remove_subtitles close" aria-label="Close" data-toggle="tooltip" data-placement="right" title="Delete Subtitles File" data-language='+data['code3']+' data-subtitlesPath="'+data['path']+'"><span aria-hidden="true">&times;</span></button>';
return '<button type="button" class="remove_subtitles close" aria-label="Close" data-toggle="tooltip" data-placement="right" title="Delete Subtitles File" data-language='+data['code3']+' data-forced='+data['forced']+' data-subtitlesPath="'+data['path']+'"><span aria-hidden="true">&times;</span></button>';
} else {
return '';
}

Loading…
Cancel
Save