Merge branch 'development' into master

pull/1110/head v0.9.0.3
Louis Vézina 5 years ago
commit 4194aa25bb

@ -18,7 +18,7 @@ from config import settings, base_url, save_settings
from init import *
import logging
from database import database, filter_exclusions
from database import database, get_exclusion_clause
from helper import path_mappings
from get_languages import language_from_alpha3, language_from_alpha2, alpha2_from_alpha3, alpha2_from_language, \
alpha3_from_language, alpha3_from_alpha2
@ -54,8 +54,11 @@ def authenticate(actual_method):
apikey_settings = settings.auth.apikey
apikey_get = request.args.get('apikey')
apikey_post = request.form.get('apikey')
apikey_header = None
if 'X-Api-Key' in request.headers:
apikey_header = request.headers['X-Api-Key']
if apikey_settings in [apikey_get, apikey_post]:
if apikey_settings in [apikey_get, apikey_post, apikey_header]:
return actual_method(*args, **kwargs)
return abort(401, message="Unauthorized")
@ -83,8 +86,7 @@ class BadgesSeries(Resource):
missing_episodes = database.execute("SELECT table_shows.tags, table_episodes.monitored, table_shows.seriesType "
"FROM table_episodes INNER JOIN table_shows on table_shows.sonarrSeriesId ="
" table_episodes.sonarrSeriesId WHERE missing_subtitles is not null AND "
"missing_subtitles != '[]'")
missing_episodes = filter_exclusions(missing_episodes, 'series')
"missing_subtitles != '[]'" + get_exclusion_clause('series'))
missing_episodes = len(missing_episodes)
result = {
@ -97,8 +99,7 @@ class BadgesMovies(Resource):
@authenticate
def get(self):
missing_movies = database.execute("SELECT tags, monitored FROM table_movies WHERE missing_subtitles is not "
"null AND missing_subtitles != '[]'")
missing_movies = filter_exclusions(missing_movies, 'movie')
"null AND missing_subtitles != '[]'" + get_exclusion_clause('movie'))
missing_movies = len(missing_movies)
result = {
@ -327,8 +328,8 @@ class Series(Resource):
"table_shows.seriesType FROM table_episodes INNER JOIN table_shows "
"on table_shows.sonarrSeriesId = table_episodes.sonarrSeriesId "
"WHERE table_episodes.sonarrSeriesId=? AND missing_subtitles is not "
"null AND missing_subtitles != '[]'", (item['sonarrSeriesId'],))
episodeMissingCount = filter_exclusions(episodeMissingCount, 'series')
"null AND missing_subtitles != '[]'" +
get_exclusion_clause('series'), (item['sonarrSeriesId'],))
episodeMissingCount = len(episodeMissingCount)
item.update({"episodeMissingCount": episodeMissingCount})
@ -336,8 +337,8 @@ class Series(Resource):
episodeFileCount = database.execute("SELECT table_shows.tags, table_episodes.monitored, "
"table_shows.seriesType FROM table_episodes INNER JOIN table_shows on "
"table_shows.sonarrSeriesId = table_episodes.sonarrSeriesId WHERE "
"table_episodes.sonarrSeriesId=?", (item['sonarrSeriesId'],))
episodeFileCount = filter_exclusions(episodeFileCount, 'series')
"table_episodes.sonarrSeriesId=?" + get_exclusion_clause('series'),
(item['sonarrSeriesId'],))
episodeFileCount = len(episodeFileCount)
item.update({"episodeFileCount": episodeFileCount})
@ -386,6 +387,36 @@ class Series(Resource):
return '', 204
class SeriesEditor(Resource):
@authenticate
def get(self, **kwargs):
draw = request.args.get('draw')
result = database.execute("SELECT sonarrSeriesId, title, languages, hearing_impaired, forced, audio_language "
"FROM table_shows ORDER BY sortTitle")
row_count = len(result)
for item in result:
# Add Datatables rowId
item.update({"DT_RowId": 'row_' + str(item['sonarrSeriesId'])})
# Parse audio language
item.update({"audio_language": {"name": item['audio_language'],
"code2": alpha2_from_language(item['audio_language']) or None,
"code3": alpha3_from_language(item['audio_language']) or None}})
# Parse desired languages
if item['languages'] and item['languages'] != 'None':
item.update({"languages": ast.literal_eval(item['languages'])})
for i, subs in enumerate(item['languages']):
item['languages'][i] = {"name": language_from_alpha2(subs),
"code2": subs,
"code3": alpha3_from_alpha2(subs)}
return jsonify(draw=draw, recordsTotal=row_count, recordsFiltered=row_count, data=result)
class SeriesEditSave(Resource):
@authenticate
def post(self):
@ -427,7 +458,7 @@ class SeriesEditSave(Resource):
list_missing_subtitles(no=seriesId, send_event=False)
event_stream(type='series_editor', action='update')
event_stream(type='badges')
event_stream(type='badges_series')
return '', 204
@ -462,6 +493,11 @@ class Episodes(Resource):
# Add Datatables rowId
item.update({"DT_RowId": 'row_' + str(item['sonarrEpisodeId'])})
# Parse audio language
item.update({"audio_language": {"name": item['audio_language'],
"code2": alpha2_from_language(item['audio_language']) or None,
"code3": alpha3_from_language(item['audio_language']) or None}})
# Parse subtitles
if item['subtitles']:
item.update({"subtitles": ast.literal_eval(item['subtitles'])})
@ -540,8 +576,8 @@ class EpisodesSubtitlesDownload(Resource):
title = request.form.get('title')
providers_list = get_providers()
providers_auth = get_providers_auth()
audio_language = database.execute("SELECT audio_language FROM table_shows WHERE sonarrSeriesId=?",
(sonarrSeriesId,), only_one=True)['audio_language']
audio_language = database.execute("SELECT audio_language FROM table_episodes WHERE sonarrEpisodeId=?",
(sonarrEpisodeId,), only_one=True)['audio_language']
try:
result = download_subtitle(episodePath, language, audio_language, hi, forced, providers_list, providers_auth, sceneName,
@ -609,8 +645,8 @@ class EpisodesSubtitlesManualDownload(Resource):
sonarrEpisodeId = request.form.get('sonarrEpisodeId')
title = request.form.get('title')
providers_auth = get_providers_auth()
audio_language = database.execute("SELECT audio_language FROM table_shows WHERE sonarrSeriesId=?",
(sonarrSeriesId,), only_one=True)['audio_language']
audio_language = database.execute("SELECT audio_language FROM table_episodes WHERE sonarrEpisodeId=?",
(sonarrEpisodeId,), only_one=True)['audio_language']
try:
result = manual_download_subtitle(episodePath, language, audio_language, hi, forced, subtitle,
@ -709,6 +745,7 @@ class EpisodesHistory(Resource):
"sonarrEpisodeId, subs_id, video_path, subtitles_path FROM table_history "
"WHERE sonarrEpisodeId=? ORDER BY timestamp DESC", (episodeid,))
for item in episode_history:
item['raw_timestamp'] = item['timestamp']
item['timestamp'] = "<div title='" + \
time.strftime('%d/%m/%Y %H:%M:%S', time.localtime(item['timestamp'])) + \
"' data-toggle='tooltip' data-placement='left'>" + \
@ -916,6 +953,36 @@ class Movies(Resource):
return '', 204
class MoviesEditor(Resource):
@authenticate
def get(self):
draw = request.args.get('draw')
result = database.execute("SELECT radarrId, title, languages, hearing_impaired, forced, audio_language "
"FROM table_movies ORDER BY sortTitle")
row_count = len(result)
for item in result:
# Add Datatables rowId
item.update({"DT_RowId": 'row_' + str(item['radarrId'])})
# Parse audio language
item.update({"audio_language": {"name": item['audio_language'],
"code2": alpha2_from_language(item['audio_language']) or None,
"code3": alpha3_from_language(item['audio_language']) or None}})
# Parse desired languages
if item['languages'] and item['languages'] != 'None':
item.update({"languages": ast.literal_eval(item['languages'])})
for i, subs in enumerate(item['languages']):
item['languages'][i] = {"name": language_from_alpha2(subs),
"code2": subs,
"code3": alpha3_from_alpha2(subs)}
return jsonify(draw=draw, recordsTotal=row_count, recordsFiltered=row_count, data=result)
class MoviesEditSave(Resource):
@authenticate
def post(self):
@ -956,7 +1023,7 @@ class MoviesEditSave(Resource):
list_missing_subtitles_movies(no=radarrId, send_event=False)
event_stream(type='movies_editor', action='update')
event_stream(type='badges')
event_stream(type='badges_movies')
return '', 204
@ -1163,6 +1230,7 @@ class MovieHistory(Resource):
"video_path, subtitles_path FROM table_history_movie WHERE radarrId=? ORDER "
"BY timestamp DESC", (radarrid,))
for item in movie_history:
item['raw_timestamp'] = item['timestamp']
item['timestamp'] = "<div title='" + \
time.strftime('%d/%m/%Y %H:%M:%S', time.localtime(item['timestamp'])) + \
"' data-toggle='tooltip' data-placement='left'>" + \
@ -1261,9 +1329,9 @@ class HistorySeries(Resource):
"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')
','.join(map(str, query_actions)) + ") AND timestamp > ? AND score is not null" +
get_exclusion_clause('series') + " GROUP BY table_history.video_path, table_history.language",
(minimum_timestamp,))
for upgradable_episode in upgradable_episodes:
if upgradable_episode['timestamp'] > minimum_timestamp:
@ -1307,6 +1375,7 @@ class HistorySeries(Resource):
# Make timestamp pretty
if item['timestamp']:
item['raw_timestamp'] = item['timestamp']
item['timestamp'] = pretty.date(int(item['timestamp']))
if item['path']:
@ -1364,9 +1433,8 @@ class HistoryMovies(Resource):
upgradable_movies = database.execute(
"SELECT video_path, MAX(timestamp) as timestamp, score, tags, monitored FROM table_history_movie "
"INNER JOIN table_movies on table_movies.radarrId=table_history_movie.radarrId WHERE action IN (" +
','.join(map(str, query_actions)) + ") AND timestamp > ? AND score is not NULL GROUP BY video_path, "
"language", (minimum_timestamp,))
upgradable_movies = filter_exclusions(upgradable_movies, 'movie')
','.join(map(str, query_actions)) + ") AND timestamp > ? AND score is not NULL" +
get_exclusion_clause('movie') + " GROUP BY video_path, language", (minimum_timestamp,))
for upgradable_movie in upgradable_movies:
if upgradable_movie['timestamp'] > minimum_timestamp:
@ -1407,6 +1475,7 @@ class HistoryMovies(Resource):
# Make timestamp pretty
if item['timestamp']:
item['raw_timestamp'] = item['timestamp']
item['timestamp'] = pretty.date(int(item['timestamp']))
if item['video_path']:
@ -1503,8 +1572,8 @@ class WantedSeries(Resource):
data_count = database.execute("SELECT table_episodes.monitored, table_shows.tags, table_shows.seriesType FROM "
"table_episodes INNER JOIN table_shows on table_shows.sonarrSeriesId = "
"table_episodes.sonarrSeriesId WHERE table_episodes.missing_subtitles != '[]'")
data_count = filter_exclusions(data_count, 'series')
"table_episodes.sonarrSeriesId WHERE table_episodes.missing_subtitles != '[]'" +
get_exclusion_clause('series'))
row_count = len(data_count)
data = database.execute("SELECT table_shows.title as seriesTitle, table_episodes.monitored, "
"table_episodes.season || 'x' || table_episodes.episode as episode_number, "
@ -1513,8 +1582,8 @@ class WantedSeries(Resource):
"table_episodes.sonarrEpisodeId, table_episodes.scene_name, table_shows.tags, "
"table_episodes.failedAttempts, table_shows.seriesType 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")
data = filter_exclusions(data, 'series')[int(start):int(start)+int(length)]
"table_episodes.missing_subtitles != '[]'" + get_exclusion_clause('series') +
" ORDER BY table_episodes._rowid_ DESC LIMIT " + length + " OFFSET " + start)
for item in data:
# Parse missing subtitles
@ -1546,13 +1615,13 @@ class WantedMovies(Resource):
length = request.args.get('length') or -1
draw = request.args.get('draw')
data_count = database.execute("SELECT tags, monitored FROM table_movies WHERE missing_subtitles != '[]'")
data_count = filter_exclusions(data_count, 'movie')
data_count = database.execute("SELECT tags, monitored FROM table_movies WHERE missing_subtitles != '[]'" +
get_exclusion_clause('movie'))
row_count = len(data_count)
data = database.execute("SELECT title, missing_subtitles, radarrId, path, hearing_impaired, sceneName, "
"failedAttempts, tags, monitored FROM table_movies WHERE missing_subtitles != '[]' "
"ORDER BY _rowid_ DESC")
data = filter_exclusions(data, 'movie')[int(start):int(start)+int(length)]
"failedAttempts, tags, monitored FROM table_movies WHERE missing_subtitles != '[]'" +
get_exclusion_clause('movie') + " ORDER BY _rowid_ DESC LIMIT " + length + " OFFSET " +
start)
for item in data:
# Parse missing subtitles
@ -1609,6 +1678,7 @@ class BlacklistSeries(Resource):
"OFFSET ?", (length, start))
for item in data:
item['raw_timestamp'] = item['timestamp']
# Make timestamp pretty
item.update({'timestamp': pretty.date(datetime.datetime.fromtimestamp(item['timestamp']))})
@ -1686,6 +1756,7 @@ class BlacklistMovies(Resource):
"OFFSET ?", (length, start))
for item in data:
item['raw_timestamp'] = item['timestamp']
# Make timestamp pretty
item.update({'timestamp': pretty.date(datetime.datetime.fromtimestamp(item['timestamp']))})
@ -1835,6 +1906,7 @@ api.add_resource(SystemStatus, '/systemstatus')
api.add_resource(SystemReleases, '/systemreleases')
api.add_resource(Series, '/series')
api.add_resource(SeriesEditor, '/series_editor')
api.add_resource(SeriesEditSave, '/series_edit_save')
api.add_resource(Episodes, '/episodes')
api.add_resource(EpisodesSubtitlesDelete, '/episodes_subtitles_delete')
@ -1848,6 +1920,7 @@ api.add_resource(EpisodesHistory, '/episodes_history')
api.add_resource(EpisodesTools, '/episodes_tools')
api.add_resource(Movies, '/movies')
api.add_resource(MoviesEditor, '/movies_editor')
api.add_resource(MoviesEditSave, '/movies_edit_save')
api.add_resource(MovieSubtitlesDelete, '/movie_subtitles_delete')
api.add_resource(MovieSubtitlesDownload, '/movie_subtitles_download')

@ -162,7 +162,8 @@ defaults = {
'use_subsync_threshold': 'False',
'subsync_threshold': '90',
'use_subsync_movie_threshold': 'False',
'subsync_movie_threshold': '70'
'subsync_movie_threshold': '70',
'debug': 'False'
}
}
@ -181,6 +182,7 @@ def save_settings(settings_items):
update_schedule = False
update_path_map = False
configure_proxy = False
exclusion_updated = False
for key, value in settings_items:
# Intercept database stored settings
@ -227,6 +229,11 @@ def save_settings(settings_items):
'settings-proxy-password']:
configure_proxy = True
if key in ['settings-sonarr-excluded_tags', 'settings-sonarr-only_monitored',
'settings-sonarr-excluded_series_types', 'settings.radarr.excluded_tags',
'settings-radarr-only_monitored']:
exclusion_updated = True
if settings_keys[0] == 'settings':
settings[settings_keys[1]][settings_keys[2]] = str(value)
@ -252,6 +259,11 @@ def save_settings(settings_items):
if configure_proxy:
configure_proxy_func()
if exclusion_updated:
from event_handler import event_stream
event_stream(type='badges_series')
event_stream(type='badges_movies')
def url_sonarr():
if settings.sonarr.getboolean('ssl'):

@ -99,6 +99,7 @@ def db_upgrade():
['table_episodes', 'video_codec', 'text'],
['table_episodes', 'audio_codec', 'text'],
['table_episodes', 'episode_file_id', 'integer'],
['table_episodes', 'audio_language', 'text'],
['table_movies', 'sortTitle', 'text'],
['table_movies', 'year', 'text'],
['table_movies', 'alternativeTitles', 'text'],
@ -148,28 +149,29 @@ def db_upgrade():
"provider text, subs_id text, language text)")
def filter_exclusions(dicts_list, type):
def get_exclusion_clause(type):
where_clause = ''
if type == 'series':
tagsList = ast.literal_eval(settings.sonarr.excluded_tags)
monitoredOnly = settings.sonarr.getboolean('only_monitored')
for tag in tagsList:
where_clause += ' AND table_shows.tags NOT LIKE "%\'' + tag + '\'%"'
else:
tagsList = ast.literal_eval(settings.radarr.excluded_tags)
monitoredOnly = settings.radarr.getboolean('only_monitored')
for tag in tagsList:
where_clause += ' AND table_movies.tags NOT LIKE "%\'' + tag + '\'%"'
# Filter tags
dictsList_tags_filtered = [item for item in dicts_list if set(tagsList).isdisjoint(ast.literal_eval(item['tags']))]
# Filter monitored
if monitoredOnly:
dictsList_monitored_filtered = [item for item in dictsList_tags_filtered if item['monitored'] == 'True']
if type == 'series':
monitoredOnly = settings.sonarr.getboolean('only_monitored')
if monitoredOnly:
where_clause += ' AND table_episodes.monitored = "True"'
else:
dictsList_monitored_filtered = dictsList_tags_filtered
monitoredOnly = settings.radarr.getboolean('only_monitored')
if monitoredOnly:
where_clause += ' AND table_movies.monitored = "True"'
# Filter series type
if type == 'series':
dictsList_types_filtered = [item for item in dictsList_monitored_filtered if item['seriesType'] not in
ast.literal_eval(settings.sonarr.excluded_series_types)]
else:
dictsList_types_filtered = dictsList_monitored_filtered
typesList = ast.literal_eval(settings.sonarr.excluded_series_types)
for type in typesList:
where_clause += ' AND table_shows.seriesType != "' + type + '"'
return dictsList_types_filtered
return where_clause

@ -1,7 +1,7 @@
# coding=utf-8
import requests
import logging
from database import database, dict_converter
from database import database, dict_converter, get_exclusion_clause
from config import settings, url_sonarr
from helper import path_mappings
@ -85,6 +85,13 @@ def sync_episodes():
videoCodec = None
audioCodec = None
audio_language = None
if 'language' in episode['episodeFile'] and len(episode['episodeFile']['language']):
item = episode['episodeFile']['language']
if isinstance(item, dict):
if 'name' in item:
audio_language = item['name']
# Add episodes in sonarr to current episode list
current_episodes_sonarr.append(episode['id'])
@ -101,7 +108,8 @@ def sync_episodes():
'resolution': resolution,
'video_codec': videoCodec,
'audio_codec': audioCodec,
'episode_file_id': episode['episodeFile']['id']})
'episode_file_id': episode['episodeFile']['id'],
'audio_language': audio_language})
else:
episodes_to_add.append({'sonarrSeriesId': episode['seriesId'],
'sonarrEpisodeId': episode['id'],
@ -115,7 +123,8 @@ def sync_episodes():
'resolution': resolution,
'video_codec': videoCodec,
'audio_codec': audioCodec,
'episode_file_id': episode['episodeFile']['id']})
'episode_file_id': episode['episodeFile']['id'],
'audio_language': audio_language})
# Remove old episodes from DB
removed_episodes = list(set(current_episodes_db_list) - set(current_episodes_sonarr))
@ -131,7 +140,7 @@ def sync_episodes():
episode_in_db_list = []
episodes_in_db = database.execute("SELECT sonarrSeriesId, sonarrEpisodeId, title, path, season, episode, "
"scene_name, monitored, format, resolution, video_codec, audio_codec, "
"episode_file_id FROM table_episodes")
"episode_file_id, audio_language FROM table_episodes")
for item in episodes_in_db:
episode_in_db_list.append(item)
@ -171,11 +180,13 @@ def sync_episodes():
if len(altered_episodes) <= 5:
logging.debug("BAZARR No more than 5 episodes were added during this sync then we'll search for subtitles.")
for altered_episode in altered_episodes:
if settings.sonarr.getboolean('only_monitored'):
if altered_episode[2] == 'True':
episode_download_subtitles(altered_episode[0])
else:
episode_download_subtitles(altered_episode[0])
data = database.execute("SELECT table_episodes.sonarrEpisodeId, table_episodes.monitored, table_shows.tags,"
" table_shows.seriesType FROM table_episodes LEFT JOIN table_shows on "
"table_episodes.sonarrSeriesId = table_shows.sonarrSeriesId WHERE "
"sonarrEpisodeId = ?" + get_exclusion_clause('series'), (altered_episode[0],),
only_one=True)
episode_download_subtitles(data['sonarrEpisodeId'])
else:
logging.debug("BAZARR More than 5 episodes were added during this sync then we wont search for subtitles right now.")

@ -10,7 +10,7 @@ from utils import get_radarr_version
from list_subtitles import store_subtitles_movie, list_missing_subtitles_movies, movies_full_scan_subtitles
from get_subtitle import movies_download_subtitles
from database import database, dict_converter
from database import database, dict_converter, get_exclusion_clause
def update_all_movies():
@ -150,8 +150,12 @@ def update_movies():
if radarr_version.startswith('0'):
audio_language = profile_id_to_language(movie['qualityProfileId'], audio_profiles)
else:
if len(movie['movieFile']['languages']):
audio_language = movie['movieFile']['languages'][0]['name']
if 'languages' in movie['movieFile'] and len(movie['movieFile']['languages']):
for item in movie['movieFile']['languages']:
if isinstance(item, dict):
if 'name' in item:
audio_language = item['name']
break
tags = [d['label'] for d in tagsDict if d['id'] in movie['tags']]
@ -261,11 +265,9 @@ def update_movies():
if len(altered_movies) <= 5:
logging.debug("BAZARR No more than 5 movies were added during this sync then we'll search for subtitles.")
for altered_movie in altered_movies:
if settings.radarr.getboolean('only_monitored'):
if altered_movie[3] == 'True':
movies_download_subtitles(altered_movie[2])
else:
movies_download_subtitles(altered_movie[2])
data = database.execute("SELECT * FROM table_movies WHERE radarrId = ?" +
get_exclusion_clause('movie'), (altered_movie[2],), only_one=True)
movies_download_subtitles(data['radarrId'])
else:
logging.debug("BAZARR More than 5 movies were added during this sync then we wont search for subtitles.")

@ -30,8 +30,8 @@ hours_until_end_of_day = time_until_end_of_day().seconds // 3600 + 1
VALID_THROTTLE_EXCEPTIONS = (TooManyRequests, DownloadLimitExceeded, ServiceUnavailable, APIThrottled,
ParseResponseError, IPAddressBlocked)
VALID_COUNT_EXCEPTIONS = ('TooManyRequests', 'ServiceUnavailable', 'APIThrottled', requests.Timeout,
requests.ReadTimeout, socket.timeout)
VALID_COUNT_EXCEPTIONS = ('TooManyRequests', 'ServiceUnavailable', 'APIThrottled', requests.exceptions.Timeout,
requests.exceptions.ConnectTimeout, requests.exceptions.ReadTimeout, socket.timeout)
PROVIDER_THROTTLE_MAP = {
"default": {
@ -40,9 +40,10 @@ PROVIDER_THROTTLE_MAP = {
ServiceUnavailable: (datetime.timedelta(minutes=20), "20 minutes"),
APIThrottled: (datetime.timedelta(minutes=10), "10 minutes"),
ParseResponseError: (datetime.timedelta(hours=6), "6 hours"),
requests.Timeout: (datetime.timedelta(hours=1), "1 hour"),
requests.exceptions.Timeout: (datetime.timedelta(hours=1), "1 hour"),
socket.timeout: (datetime.timedelta(hours=1), "1 hour"),
requests.ReadTimeout: (datetime.timedelta(hours=1), "1 hour"),
requests.exceptions.ConnectTimeout: (datetime.timedelta(hours=1), "1 hour"),
requests.exceptions.ReadTimeout: (datetime.timedelta(hours=1), "1 hour"),
},
"opensubtitles": {
TooManyRequests: (datetime.timedelta(hours=3), "3 hours"),

@ -29,11 +29,10 @@ 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
from subsyncer import subsync
from database import database, dict_mapper, filter_exclusions
from database import database, dict_mapper, get_exclusion_clause
from analytics import track_event
from locale import getpreferredencoding
import chardet
def get_video(path, title, sceneName, providers=None, media_type="movie"):
@ -641,16 +640,16 @@ def manual_upload_subtitle(path, language, forced, title, scene_name, media_type
def series_download_subtitles(no):
episodes_details = database.execute("SELECT table_episodes.path, table_episodes.missing_subtitles, monitored, "
"table_episodes.sonarrEpisodeId, table_episodes.scene_name, table_shows.tags, "
"table_shows.seriesType FROM table_episodes INNER JOIN table_shows on "
"table_shows.sonarrSeriesId = table_episodes.sonarrSeriesId WHERE "
"table_episodes.sonarrSeriesId=? and missing_subtitles!='[]'", (no,))
episodes_details = filter_exclusions(episodes_details, 'series')
"table_shows.seriesType, table_episodes.audio_language FROM table_episodes "
"INNER JOIN table_shows on table_shows.sonarrSeriesId = "
"table_episodes.sonarrSeriesId WHERE table_episodes.sonarrSeriesId=? and "
"missing_subtitles!='[]'" + get_exclusion_clause('series'), (no,))
if not episodes_details:
logging.debug("BAZARR no episode for that sonarrSeriesId can be found in database:", str(no))
return
series_details = database.execute(
"SELECT hearing_impaired, audio_language, title, forced FROM table_shows WHERE sonarrSeriesId=?",
"SELECT hearing_impaired, title, forced FROM table_shows WHERE sonarrSeriesId=?",
(no,), only_one=True)
if not series_details:
logging.debug("BAZARR no series with that sonarrSeriesId can be found in database:", str(no))
@ -667,7 +666,7 @@ def series_download_subtitles(no):
if language is not None:
result = download_subtitle(path_mappings.path_replace(episode['path']),
str(alpha3_from_alpha2(language.split(':')[0])),
series_details['audio_language'],
episode['audio_language'],
series_details['hearing_impaired'],
"True" if len(language.split(':')) > 1 else "False",
providers_list,
@ -697,10 +696,10 @@ def episode_download_subtitles(no):
episodes_details = database.execute("SELECT table_episodes.path, table_episodes.missing_subtitles, monitored, "
"table_episodes.sonarrEpisodeId, table_episodes.scene_name, table_shows.tags, "
"table_shows.hearing_impaired, table_shows.title, table_shows.sonarrSeriesId, "
"table_shows.forced, table_shows.audio_language, table_shows.seriesType FROM "
"table_shows.forced, table_episodes.audio_language, table_shows.seriesType FROM "
"table_episodes LEFT JOIN table_shows on table_episodes.sonarrSeriesId = "
"table_shows.sonarrSeriesId WHERE sonarrEpisodeId=?", (no,))
episodes_details = filter_exclusions(episodes_details, 'series')
"table_shows.sonarrSeriesId WHERE sonarrEpisodeId=?" +
get_exclusion_clause('series'), (no,))
if not episodes_details:
logging.debug("BAZARR no episode with that sonarrEpisodeId can be found in database:", str(no))
return
@ -743,8 +742,7 @@ def episode_download_subtitles(no):
def movies_download_subtitles(no):
movies = database.execute(
"SELECT path, missing_subtitles, audio_language, radarrId, sceneName, hearing_impaired, title, forced, tags, "
"monitored FROM table_movies WHERE radarrId=?", (no,))
movies = filter_exclusions(movies, 'movie')
"monitored FROM table_movies WHERE radarrId=?" + get_exclusion_clause('movie'), (no,))
if not len(movies):
logging.debug("BAZARR no movie with that radarrId can be found in database:", str(no))
return
@ -792,7 +790,7 @@ def movies_download_subtitles(no):
def wanted_download_subtitles(path, l, count_episodes):
episodes_details = database.execute("SELECT table_episodes.path, table_episodes.missing_subtitles, "
"table_episodes.sonarrEpisodeId, table_episodes.sonarrSeriesId, "
"table_shows.hearing_impaired, table_shows.audio_language, table_episodes.scene_name,"
"table_shows.hearing_impaired, table_episodes.audio_language, table_episodes.scene_name,"
"table_episodes.failedAttempts, table_shows.title, table_shows.forced "
"FROM table_episodes LEFT JOIN table_shows on "
"table_episodes.sonarrSeriesId = table_shows.sonarrSeriesId "
@ -911,8 +909,7 @@ def wanted_search_missing_subtitles_series():
episodes = database.execute("SELECT table_episodes.path, table_shows.tags, table_episodes.monitored, "
"table_shows.seriesType FROM table_episodes INNER JOIN table_shows on "
"table_shows.sonarrSeriesId = table_episodes.sonarrSeriesId WHERE missing_subtitles != "
"'[]'")
episodes = filter_exclusions(episodes, 'series')
"'[]'" + get_exclusion_clause('series'))
# path_replace
dict_mapper.path_replace(episodes)
@ -929,8 +926,8 @@ def wanted_search_missing_subtitles_series():
def wanted_search_missing_subtitles_movies():
movies = database.execute("SELECT path, tags, monitored FROM table_movies WHERE missing_subtitles != '[]'")
movies = filter_exclusions(movies, 'movie')
movies = database.execute("SELECT path, tags, monitored FROM table_movies WHERE missing_subtitles != '[]'" +
get_exclusion_clause('movie'))
# path_replace
dict_mapper.path_replace_movie(movies)
@ -1071,7 +1068,7 @@ def upgrade_subtitles():
if settings.general.getboolean('use_sonarr'):
upgradable_episodes = database.execute("SELECT table_history.video_path, table_history.language, "
"table_history.score, table_shows.hearing_impaired, "
"table_shows.audio_language, table_episodes.scene_name, table_episodes.title,"
"table_episodes.audio_language, table_episodes.scene_name, table_episodes.title,"
"table_episodes.sonarrSeriesId, table_episodes.sonarrEpisodeId,"
"MAX(table_history.timestamp) as timestamp, table_episodes.monitored, "
"table_shows.languages, table_shows.forced, table_shows.tags, "
@ -1079,11 +1076,10 @@ def upgrade_subtitles():
"table_shows.sonarrSeriesId = table_history.sonarrSeriesId INNER JOIN "
"table_episodes on table_episodes.sonarrEpisodeId = "
"table_history.sonarrEpisodeId WHERE action IN "
"(" + ','.join(map(str, query_actions)) +
") AND timestamp > ? AND score is not null GROUP BY "
"(" + ','.join(map(str, query_actions)) + ") AND timestamp > ? AND score"
" is not null" + get_exclusion_clause('series') + " GROUP BY "
"table_history.video_path, table_history.language",
(minimum_timestamp,))
upgradable_episodes = filter_exclusions(upgradable_episodes, 'series')
upgradable_episodes_not_perfect = []
for upgradable_episode in upgradable_episodes:
if upgradable_episode['timestamp'] > minimum_timestamp:
@ -1111,9 +1107,9 @@ def upgrade_subtitles():
"table_movies.monitored FROM table_history_movie INNER JOIN table_movies "
"on table_movies.radarrId = table_history_movie.radarrId WHERE action IN "
"(" + ','.join(map(str, query_actions)) + ") AND timestamp > ? AND score "
"is not null GROUP BY table_history_movie.video_path, "
"table_history_movie.language", (minimum_timestamp,))
upgradable_movies = filter_exclusions(upgradable_movies, 'movie')
"is not null" + get_exclusion_clause('movie') + " GROUP BY "
"table_history_movie.video_path, table_history_movie.language",
(minimum_timestamp,))
upgradable_movies_not_perfect = []
for upgradable_movie in upgradable_movies:
if upgradable_movie['timestamp'] > minimum_timestamp:

@ -16,8 +16,8 @@ class PathMappings:
self.path_mapping_movies = []
def update(self):
self.path_mapping_series = ast.literal_eval(settings.general.path_mappings)
self.path_mapping_movies = ast.literal_eval(settings.general.path_mappings_movie)
self.path_mapping_series = [x for x in ast.literal_eval(settings.general.path_mappings) if x[0] != x[1]]
self.path_mapping_movies = [x for x in ast.literal_eval(settings.general.path_mappings_movie) if x[0] != x[1]]
def path_replace(self, path):
if path is None:

@ -1,6 +1,6 @@
# coding=utf-8
bazarr_version = '0.9.0.2'
bazarr_version = '0.9.0.3'
import os
os.environ["BAZARR_VERSION"] = bazarr_version

@ -5,6 +5,8 @@ from utils import get_binary
from utils import history_log, history_log_movie
from get_languages import alpha2_from_alpha3, language_from_alpha3
from helper import path_mappings
from config import settings
from get_args import args
class SubSyncer:
@ -15,12 +17,13 @@ class SubSyncer:
self.ffmpeg_path = None
self.args = None
self.vad = 'subs_then_auditok'
self.log_dir_path = os.path.join(args.config_dir, 'log')
def sync(self, video_path, srt_path, srt_lang, media_type, sonarr_series_id=None, sonarr_episode_id=None,
radarr_id=None):
self.reference = video_path
self.srtin = srt_path
self.srtout = None
self.srtout = '{}.synced.srt'.format(os.path.splitext(self.srtin)[0])
self.args = None
ffprobe_exe = get_binary('ffprobe')
@ -39,8 +42,10 @@ class SubSyncer:
self.ffmpeg_path = os.path.dirname(ffmpeg_exe)
try:
unparsed_args = [self.reference, '-i', self.srtin, '--overwrite-input', '--ffmpegpath', self.ffmpeg_path,
'--vad', self.vad]
unparsed_args = [self.reference, '-i', self.srtin, '-o', self.srtout, '--ffmpegpath', self.ffmpeg_path,
'--vad', self.vad, '--log-dir-path', self.log_dir_path]
if settings.subsync.getboolean('debug'):
unparsed_args.append('--make-test-case')
parser = make_parser()
self.args = parser.parse_args(args=unparsed_args)
result = run(self.args)
@ -49,18 +54,24 @@ class SubSyncer:
'{0}'.format(self.srtin))
else:
if result['sync_was_successful']:
message = "{0} subtitles synchronization ended with an offset of {1} seconds and a framerate scale " \
"factor of {2}.".format(language_from_alpha3(srt_lang), result['offset_seconds'],
"{:.2f}".format(result['framerate_scale_factor']))
if not settings.subsync.getboolean('debug'):
os.remove(self.srtin)
os.rename(self.srtout, self.srtin)
offset_seconds = result['offset_seconds'] or 0
framerate_scale_factor = result['framerate_scale_factor'] or 0
message = "{0} subtitles synchronization ended with an offset of {1} seconds and a framerate " \
"scale factor of {2}.".format(language_from_alpha3(srt_lang), offset_seconds,
"{:.2f}".format(framerate_scale_factor))
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), 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), subtitles_path=srt_path)
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), 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), subtitles_path=srt_path)
else:
logging.error('BAZARR unable to sync subtitles: {0}'.format(self.srtin))

@ -24,8 +24,8 @@ def get_keywords():
# each be defined on a line of their own. _version.py will just call
# get_keywords().
git_refnames = " (HEAD -> master)"
git_full = "997749de8aac74ec19137a2e641b97ef1bba81ea"
git_date = "2020-08-04 20:06:18 -0700"
git_full = "ce46d91fa2d325a13c2830f8030a316ed49b6cc9"
git_date = "2020-09-05 11:15:34 -0700"
keywords = {"refnames": git_refnames, "full": git_full, "date": git_date}
return keywords

@ -41,12 +41,15 @@ def make_test_case(args, npy_savename, sync_was_successful):
raise ValueError('need non-null npy_savename')
tar_dir = '{}.{}'.format(
args.reference,
datetime.now().strftime('%Y-%m-%d-%H:%M:%S')
datetime.now().strftime('%Y-%m-%d-%H-%M-%S')
)
logger.info('creating test archive {}.tar.gz...'.format(tar_dir))
os.mkdir(tar_dir)
try:
shutil.move('ffsubsync.log', tar_dir)
log_path = 'ffsubsync.log'
if args.log_dir_path and os.path.isdir(args.log_dir_path):
log_path = os.path.join(args.log_dir_path, log_path)
shutil.copy(log_path, tar_dir)
shutil.copy(args.srtin, tar_dir)
if sync_was_successful:
shutil.move(args.srtout, tar_dir)
@ -60,7 +63,7 @@ def make_test_case(args, npy_savename, sync_was_successful):
preferred_formats = ['gztar', 'bztar', 'xztar', 'zip', 'tar']
for archive_format in preferred_formats:
if archive_format in supported_formats:
shutil.make_archive(tar_dir, 'gztar', os.curdir, tar_dir)
shutil.make_archive(tar_dir, archive_format, os.curdir, tar_dir)
break
else:
logger.error('failed to create test archive; no formats supported '
@ -264,9 +267,14 @@ def run(args):
'when reference composed of subtitles')
result['retval'] = 1
return result
log_handler = None
log_path = None
if args.make_test_case:
handler = logging.FileHandler('ffsubsync.log')
logger.addHandler(handler)
log_path = 'ffsubsync.log'
if args.log_dir_path and os.path.isdir(args.log_dir_path):
log_path = os.path.join(args.log_dir_path, log_path)
log_handler = logging.FileHandler(log_path)
logger.addHandler(log_handler)
if args.extract_subs_from_stream is not None:
result['retval'] = extract_subtitles_from_reference(args)
return result
@ -285,8 +293,14 @@ def run(args):
return result
srt_pipes = make_srt_pipes(args)
sync_was_successful = try_sync(args, reference_pipe, srt_pipes, result)
if args.make_test_case:
result['retval'] += make_test_case(args, npy_savename, sync_was_successful)
if log_handler is not None and log_path is not None:
assert args.make_test_case
log_handler.close()
logger.removeHandler(log_handler)
try:
result['retval'] += make_test_case(args, npy_savename, sync_was_successful)
finally:
os.remove(log_path)
return result
@ -354,6 +368,8 @@ def add_cli_only_args(parser):
'--ffmpeg-path', '--ffmpegpath', default=None,
help='Where to look for ffmpeg and ffprobe. Uses the system PATH by default.'
)
parser.add_argument('--log-dir-path', default=None, help='Where to save ffsubsync.log file (must be an existing '
'directory).')
parser.add_argument('--vlc-mode', action='store_true', help=argparse.SUPPRESS)
parser.add_argument('--gui-mode', action='store_true', help=argparse.SUPPRESS)

@ -233,7 +233,7 @@ class VideoSpeechTransformer(TransformerMixin):
if not in_bytes:
break
newstuff = len(in_bytes) / float(bytes_per_frame) / self.frame_rate
if simple_progress + newstuff > total_duration:
if total_duration is not None and simple_progress + newstuff > total_duration:
newstuff = total_duration - simple_progress
simple_progress += newstuff
pbar.update(newstuff)

@ -168,7 +168,8 @@ class SubdivxSubtitlesProvider(Provider):
download_link = self._get_download_link(subtitle)
# download zip / rar file with the subtitle
response = self.session.get(download_link, headers={'Referer': subtitle.page_link}, timeout=30)
response = self.session.get(self.server_url + download_link, headers={'Referer': subtitle.page_link},
timeout=30)
self._check_response(response)
# open the compressed archive

@ -55,7 +55,12 @@ class SubsSabBzSubtitle(Subtitle):
self.video = video
self.fps = fps
self.num_cds = num_cds
self.release_info = os.path.splitext(filename)[0]
self.release_info = filename
if fps:
if video.fps and float(video.fps) == fps:
self.release_info += " <b>[{:.3f}]</b>".format(fps)
else:
self.release_info += " [{:.3f}]".format(fps)
@property
def id(self):
@ -168,7 +173,7 @@ class SubsSabBzProvider(Provider):
element = a_element_wrapper.find('a')
if element:
link = element.get('href')
notes = element.get('onmouseover')
notes = re.sub(r'ddrivetip\(\'<div.*/></div>(.*)\',\'#[0-9]+\'\)', r'\1', element.get('onmouseover'))
title = element.get_text()
try:
@ -248,12 +253,15 @@ class SubsSabBzProvider(Provider):
else:
logger.info('Cache file: %s', codecs.encode(cache_key, 'hex_codec').decode('utf-8'))
archive_stream = io.BytesIO(request.content)
if is_rarfile(archive_stream):
return self.process_archive_subtitle_files(RarFile(archive_stream), language, video, link, fps, num_cds)
elif is_zipfile(archive_stream):
return self.process_archive_subtitle_files(ZipFile(archive_stream), language, video, link, fps, num_cds)
else:
logger.error('Ignore unsupported archive %r', request.headers)
region.delete(cache_key)
return []
try:
archive_stream = io.BytesIO(request.content)
if is_rarfile(archive_stream):
return self.process_archive_subtitle_files(RarFile(archive_stream), language, video, link, fps, num_cds)
elif is_zipfile(archive_stream):
return self.process_archive_subtitle_files(ZipFile(archive_stream), language, video, link, fps, num_cds)
except:
pass
logger.error('Ignore unsupported archive %r', request.headers)
region.delete(cache_key)
return []

@ -55,7 +55,12 @@ class SubsUnacsSubtitle(Subtitle):
self.video = video
self.fps = fps
self.num_cds = num_cds
self.release_info = os.path.splitext(filename)[0]
self.release_info = filename
if fps:
if video.fps and float(video.fps) == fps:
self.release_info += " <b>[{:.3f}]</b>".format(fps)
else:
self.release_info += " [{:.3f}]".format(fps)
@property
def id(self):
@ -168,7 +173,7 @@ class SubsUnacsProvider(Provider):
element = a_element_wrapper.find('a', {'class': 'tooltip'})
if element:
link = element.get('href')
notes = element.get('title')
notes = re.sub(r'(<img.*)(src=")(/)(.*.jpg">)', r"", element.get('title'))
title = element.get_text()
try:
@ -230,11 +235,8 @@ class SubsUnacsProvider(Provider):
is_7zip = isinstance(archiveStream, SevenZipFile)
if is_7zip:
try:
file_content = archiveStream.readall()
file_list = sorted(file_content)
except:
return []
file_content = archiveStream.readall()
file_list = sorted(file_content)
else:
file_list = sorted(archiveStream.namelist())
@ -268,14 +270,17 @@ class SubsUnacsProvider(Provider):
else:
logger.info('Cache file: %s', codecs.encode(cache_key, 'hex_codec').decode('utf-8'))
archive_stream = io.BytesIO(request.content)
if is_rarfile(archive_stream):
return self.process_archive_subtitle_files(RarFile(archive_stream), language, video, link, fps, num_cds)
elif is_zipfile(archive_stream):
return self.process_archive_subtitle_files(ZipFile(archive_stream), language, video, link, fps, num_cds)
elif archive_stream.seek(0) == 0 and is_7zfile(archive_stream):
return self.process_archive_subtitle_files(SevenZipFile(archive_stream), language, video, link, fps, num_cds)
else:
logger.error('Ignore unsupported archive %r', request.headers)
region.delete(cache_key)
return []
try:
archive_stream = io.BytesIO(request.content)
if is_rarfile(archive_stream):
return self.process_archive_subtitle_files(RarFile(archive_stream), language, video, link, fps, num_cds)
elif is_zipfile(archive_stream):
return self.process_archive_subtitle_files(ZipFile(archive_stream), language, video, link, fps, num_cds)
elif archive_stream.seek(0) == 0 and is_7zfile(archive_stream):
return self.process_archive_subtitle_files(SevenZipFile(archive_stream), language, video, link, fps, num_cds)
except:
pass
logger.error('Ignore unsupported archive %r', request.headers)
region.delete(cache_key)
return []

@ -37,7 +37,12 @@ class YavkaNetSubtitle(Subtitle):
self.type = type
self.video = video
self.fps = fps
self.release_info = os.path.splitext(filename)[0]
self.release_info = filename
if fps:
if video.fps and float(video.fps) == fps:
self.release_info += " <b>[{:.3f}]</b>".format(fps)
else:
self.release_info += " [{:.3f}]".format(fps)
@property
def id(self):
@ -141,7 +146,7 @@ class YavkaNetProvider(Provider):
element = row.find('a', {'class': 'selector'})
if element:
link = element.get('href')
notes = element.get('content')
notes = re.sub(r'(?s)<p.*><img [A-z0-9=\'/\. :;#]*>(.*)</p>', r"\1", element.get('content'))
title = element.get_text()
try:
@ -205,12 +210,15 @@ class YavkaNetProvider(Provider):
else:
logger.info('Cache file: %s', codecs.encode(cache_key, 'hex_codec').decode('utf-8'))
archive_stream = io.BytesIO(request.content)
if is_rarfile(archive_stream):
return self.process_archive_subtitle_files(RarFile(archive_stream), language, video, link, fps)
elif is_zipfile(archive_stream):
return self.process_archive_subtitle_files(ZipFile(archive_stream), language, video, link, fps)
else:
logger.error('Ignore unsupported archive %r', request.headers)
region.delete(cache_key)
return []
try:
archive_stream = io.BytesIO(request.content)
if is_rarfile(archive_stream):
return self.process_archive_subtitle_files(RarFile(archive_stream), language, video, link, fps)
elif is_zipfile(archive_stream):
return self.process_archive_subtitle_files(ZipFile(archive_stream), language, video, link, fps)
except:
pass
logger.error('Ignore unsupported archive %r', request.headers)
region.delete(cache_key)
return []

@ -95,7 +95,7 @@ class YifySubtitlesProvider(Provider):
]
languages = {Language(l, c) for (_, l, c) in YifyLanguages}
server_url = 'https://www.yifysubtitles.com'
server_urls = ['https://yifysubtitles.org', 'https://www.yifysubtitles.com']
video_types = (Movie,)
def initialize(self):
@ -112,20 +112,20 @@ class YifySubtitlesProvider(Provider):
def terminate(self):
self.session.close()
def _parse_row(self, row, languages):
def _parse_row(self, row, languages, server_url):
td = row.findAll('td')
rating = int(td[0].text)
sub_lang = td[1].text
release = re.sub(r'^subtitle ', '', td[2].text)
sub_link = td[2].find('a').get('href')
sub_link = re.sub(r'^/subtitles/', self.server_url + '/subtitle/', sub_link) + '.zip'
page_link = server_url + sub_link
sub_link = re.sub(r'^/subtitles/', server_url + '/subtitle/', sub_link) + '.zip'
hi = True if td[3].find('span', {'class': 'hi-subtitle'}) else False
uploader = td[4].text
page_link = self.server_url + td[5].find('a').get('href')
_, l, c = next(x for x in self.YifyLanguages if x[0] == sub_lang)
lang = Language(l, c)
if languages & set([lang]):
if languages & {lang}:
return [YifySubtitle(lang, page_link, release, uploader, sub_link, rating, hi)]
return []
@ -134,9 +134,13 @@ class YifySubtitlesProvider(Provider):
subtitles = []
logger.info('Searching subtitle %r', imdb_id)
response = self.session.get(self.server_url + '/movie-imdb/' + imdb_id,
allow_redirects=False, timeout=10,
headers={'Referer': self.server_url})
for server_url in self.server_urls:
response = self.session.get(server_url + '/movie-imdb/' + imdb_id,
allow_redirects=False, timeout=10,
headers={'Referer': server_url})
if response.status_code == 200:
break
response.raise_for_status()
if response.status_code != 200:
@ -150,7 +154,7 @@ class YifySubtitlesProvider(Provider):
for row in rows:
try:
subtitles = subtitles + self._parse_row(row, languages)
subtitles = subtitles + self._parse_row(row, languages, server_url)
except Exception as e:
pass

@ -495,14 +495,7 @@
// Add apikey to all AJAX requests.
$.ajaxSetup({
data: {
apikey: "{{ settings.auth.apikey }}"
}
});
$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
if (originalOptions.data instanceof FormData) {
originalOptions.data.append("apikey", "{{ settings.auth.apikey }}");
}
headers: { 'X-Api-Key': "{{ settings.auth.apikey }}" }
});
$(window).on('beforeunload', function () {

@ -113,6 +113,7 @@
<th></th>
<th>Episode</th>
<th>Title</th>
<th>Audio Language</th>
<th>Existing Subtitles</th>
<th>Missing Subtitles</th>
<th>Manual Search</th>
@ -232,7 +233,7 @@
<div class="container-fluid">
<div class="row">
<div class="col-sm-3 text-right">
Audio Language
Audio Profile
</div>
<div class="form-group col-sm-8 pl-sm-0">
<span id="edit_audio_language_span"></span>
@ -579,6 +580,9 @@
}
}
},
{
data: 'audio_language.name'
},
{
data: null,
render: function (data) {
@ -629,7 +633,7 @@
data: null,
render: function (data) {
if (data.desired_languages !== '[]') {
return '<a href="" class="upload_subtitle badge badge-secondary" data-episodePath="' + data.mapped_path + '" data-sceneName"' + data.scene_name + '" data-sonarrSeriesId="' + seriesDetails['sonarrSeriesId'] + '" data-sonarrEpisodeId="' + data.sonarrEpisodeId + '" data-season="' + data.season + '" data-episode="' + data.episode + '" data-episode_title="' + data.title + '"><i class="fas fa-cloud-upload-alt"></i></a>';
return '<a href="" class="upload_subtitle badge badge-secondary" data-episodePath="' + data.mapped_path + '" data-sceneName"' + data.scene_name + '" data-sonarrSeriesId="' + seriesDetails['sonarrSeriesId'] + '" data-sonarrEpisodeId="' + data.sonarrEpisodeId + '" data-season="' + data.season + '" data-episode="' + data.episode + '" data-episode_title="' + data.title + '" data-audio_language="' + data.audio_language.name + '"><i class="fas fa-cloud-upload-alt"></i></a>';
} else {
return ''
}
@ -876,7 +880,7 @@
$('#upload_sonarrSeriesId').val($(this).data("sonarrseriesid"));
$('#upload_sonarrEpisodeId').val($(this).data("sonarrepisodeid"));
$('#upload_title').val($(this).data("episode_title"));
$('#upload_audioLanguage').val(seriesDetails['audio_language']['name']);
$('#upload_audioLanguage').val($(this).data("audio_language"));
$('#manual_language_select').empty();
$.each(enabledLanguages, function (i, item) {

@ -67,7 +67,7 @@
var event_json = JSON.parse(event);
if (event_json.type === 'movies_editor' && event_json.action === 'update') {
$.ajax({
url: "{{ url_for('api.movies') }}",
url: "{{ url_for('api.movieseditor') }}",
success: function (data) {
if (data.data.length) {
$('#movies').DataTable().ajax.reload(resetPaging = false);
@ -96,7 +96,7 @@
lengthChange: true,
responsive: true,
paging: false,
ajax: "{{ url_for('api.movies') }}",
ajax: "{{ url_for('api.movieseditor') }}",
columnDefs: [{
orderable: false,
className: 'select-checkbox',

@ -22,7 +22,7 @@
<tr>
<th>Name</th>
<th>Path Exist</th>
<th>Audio Language</th>
<th>Audio Profile</th>
<th>Subtitles Languages</th>
<th>Hearing-Impaired</th>
<th>Forced</th>
@ -46,7 +46,7 @@
<div class="container-fluid">
<div class="row">
<div class="col-sm-3 text-right">
Audio Language
Audio Profile
</div>
<div class="form-group col-sm-8 pl-sm-0">
<span id="edit_audio_language_span"></span>

@ -16,7 +16,7 @@
<tr>
<th></th>
<th>Name</th>
<th>Audio Language</th>
<th>Audio Profile</th>
<th>Subtitles Languages</th>
<th>Hearing-Impaired</th>
<th>Forced</th>
@ -67,7 +67,7 @@
var event_json = JSON.parse(event);
if (event_json.type === 'series_editor' && event_json.action === 'update') {
$.ajax({
url: "{{ url_for('api.series') }}",
url: "{{ url_for('api.serieseditor') }}",
success: function (data) {
if (data.data.length) {
$('#series').DataTable().ajax.reload(resetPaging = false);
@ -96,7 +96,7 @@
lengthChange: true,
responsive: true,
paging: false,
ajax: "{{ url_for('api.series') }}",
ajax: "{{ url_for('api.serieseditor') }}",
columnDefs: [{
orderable: false,
className: 'select-checkbox',

@ -412,14 +412,26 @@
{% endif %}
<div class="row">
<div class="col-sm-3 text-right">
<b>Subtitles synchronization</b>
<b>Automatic subtitles synchronization</b>
</div>
<div class="form-group col-sm-8">
<label class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="settings-subsync-use_subsync" name="settings-subsync-use_subsync">
<span class="custom-control-label" for="settings-subsync-use_subsync"></span>
</label>
<label>Enable the subtitles synchronization after downloading a subtitles.</label>
<label>Enable the automatic subtitles synchronization after downloading a subtitles.</label>
</div>
</div>
<div class="row">
<div class="col-sm-4 text-right">
<b>Subtitles synchronization debugging</b>
</div>
<div class="form-group col-sm-8">
<label class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="settings-subsync-debug" name="settings-subsync-debug">
<span class="custom-control-label" for="settings-subsync-debug"></span>
</label>
<label>Do not actually sync the subtitles but generate a .tar.gz file to be able to open an issue for ffsubsync. This file will reside alongside the media file.</label>
</div>
</div>
<div id="subsync_div">
@ -680,6 +692,7 @@
$('#settings-general-chmod_enabled').prop('checked', {{'true' if settings.general.getboolean('chmod_enabled') else 'false'}}).trigger('change');
$('#settings-subsync-use_subsync').prop('checked', {{'true' if settings.subsync.getboolean('use_subsync') else 'false'}}).trigger('change');
$('#settings-subsync-use_subsync_threshold').prop('checked', {{'true' if settings.subsync.getboolean('use_subsync_threshold') else 'false'}}).trigger('change');
$('#settings-subsync-debug').prop('checked', {{'true' if settings.subsync.getboolean('debug') else 'false'}}).trigger('change');
$('#settings-subsync-use_subsync_movie_threshold').prop('checked', {{'true' if settings.subsync.getboolean('use_subsync_movie_threshold') else 'false'}}).trigger('change');
$('#settings-general-use_postprocessing').prop('checked', {{'true' if settings.general.getboolean('use_postprocessing') else 'false'}}).trigger('change');
$('#settings-general-use_postprocessing_threshold').prop('checked', {{'true' if settings.general.getboolean('use_postprocessing_threshold') else 'false'}}).trigger('change');

Loading…
Cancel
Save