mirror of
https://github.com/morpheus65535/bazarr
synced 2025-02-26 07:43:05 +00:00
Merge remote-tracking branch 'origin/development' into updater
This commit is contained in:
commit
4580e54052
17 changed files with 636 additions and 189 deletions
|
@ -32,7 +32,12 @@ defaults = {
|
|||
'minimum_score_movie': '70',
|
||||
'use_embedded_subs': 'True',
|
||||
'adaptive_searching': 'False',
|
||||
'enabled_providers': ''
|
||||
'enabled_providers': '',
|
||||
'throtteled_providers': '',
|
||||
'multithreading': 'True',
|
||||
'chmod': '0640',
|
||||
'subfolder': 'current',
|
||||
'subfolder_custom': ''
|
||||
},
|
||||
'auth': {
|
||||
'type': 'None',
|
||||
|
@ -77,8 +82,8 @@ defaults = {
|
|||
'addic7ed': {
|
||||
'username': '',
|
||||
'password': '',
|
||||
'random_agents': 'False'
|
||||
},
|
||||
'random_agents': 'True'
|
||||
},
|
||||
'legendastv': {
|
||||
'username': '',
|
||||
'password': ''
|
||||
|
|
|
@ -5,6 +5,7 @@ import sqlite3
|
|||
import pycountry
|
||||
|
||||
from get_args import args
|
||||
from subzero.language import Language
|
||||
|
||||
|
||||
def load_language_in_db():
|
||||
|
@ -108,5 +109,21 @@ def alpha3_from_language(lang):
|
|||
return result
|
||||
|
||||
|
||||
def get_language_set():
|
||||
db = sqlite3.connect(os.path.join(args.config_dir, 'db', 'bazarr.db'), timeout=30)
|
||||
c = db.cursor()
|
||||
languages = c.execute('''SELECT code3 FROM table_settings_languages WHERE enabled = 1''').fetchall()
|
||||
db.close()
|
||||
language_set = set()
|
||||
|
||||
for lang in languages:
|
||||
if lang[0] == 'pob':
|
||||
language_set.add(Language('por', 'BR'))
|
||||
else:
|
||||
language_set.add(Language(lang[0]))
|
||||
|
||||
return language_set
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
load_language_in_db()
|
||||
|
|
|
@ -1,17 +1,79 @@
|
|||
# coding=utf-8
|
||||
import os
|
||||
import datetime
|
||||
import logging
|
||||
import subliminal_patch
|
||||
|
||||
from get_args import args
|
||||
from config import settings
|
||||
from subliminal_patch.exceptions import TooManyRequests, APIThrottled
|
||||
from subliminal.exceptions import DownloadLimitExceeded, ServiceUnavailable
|
||||
|
||||
VALID_THROTTLE_EXCEPTIONS = (TooManyRequests, DownloadLimitExceeded, ServiceUnavailable, APIThrottled)
|
||||
|
||||
PROVIDER_THROTTLE_MAP = {
|
||||
"default": {
|
||||
TooManyRequests: (datetime.timedelta(hours=1), "1 hour"),
|
||||
DownloadLimitExceeded: (datetime.timedelta(hours=3), "3 hours"),
|
||||
ServiceUnavailable: (datetime.timedelta(minutes=20), "20 minutes"),
|
||||
APIThrottled: (datetime.timedelta(minutes=10), "10 minutes"),
|
||||
},
|
||||
"opensubtitles": {
|
||||
TooManyRequests: (datetime.timedelta(hours=3), "3 hours"),
|
||||
DownloadLimitExceeded: (datetime.timedelta(hours=6), "6 hours"),
|
||||
APIThrottled: (datetime.timedelta(seconds=15), "15 seconds"),
|
||||
},
|
||||
"addic7ed": {
|
||||
DownloadLimitExceeded: (datetime.timedelta(hours=3), "3 hours"),
|
||||
TooManyRequests: (datetime.timedelta(minutes=5), "5 minutes"),
|
||||
}
|
||||
}
|
||||
|
||||
PROVIDERS_FORCED_OFF = ["addic7ed", "tvsubtitles", "legendastv", "napiprojekt", "shooter", "hosszupuska",
|
||||
"supersubtitles", "titlovi", "argenteam", "assrt", "subscene"]
|
||||
|
||||
if not settings.general.throtteled_providers:
|
||||
tp = {}
|
||||
else:
|
||||
tp = eval(str(settings.general.throtteled_providers))
|
||||
|
||||
|
||||
def provider_pool():
|
||||
if settings.general.getboolean('multithreading'):
|
||||
return subliminal_patch.core.SZAsyncProviderPool
|
||||
return subliminal_patch.core.SZProviderPool
|
||||
|
||||
|
||||
def get_providers():
|
||||
changed = False
|
||||
providers_list = []
|
||||
if settings.general.enabled_providers:
|
||||
for provider in settings.general.enabled_providers.lower().split(','):
|
||||
reason, until, throttle_desc = tp.get(provider, (None, None, None))
|
||||
providers_list.append(provider)
|
||||
else:
|
||||
|
||||
if reason:
|
||||
now = datetime.datetime.now()
|
||||
if now < until:
|
||||
logging.info("Not using %s until %s, because of: %s", provider,
|
||||
until.strftime("%y/%m/%d %H:%M"), reason)
|
||||
providers_list.remove(provider)
|
||||
else:
|
||||
logging.info("Using %s again after %s, (disabled because: %s)", provider, throttle_desc, reason)
|
||||
del tp[provider]
|
||||
settings.general.throtteled_providers = str(tp)
|
||||
changed = True
|
||||
|
||||
if changed:
|
||||
with open(os.path.join(args.config_dir, 'config', 'config.ini'), 'w+') as handle:
|
||||
settings.write(handle)
|
||||
|
||||
# if forced only is enabled: # fixme: Prepared for forced only implementation to remove providers with don't support forced only subtitles
|
||||
# for provider in providers_list:
|
||||
# if provider in PROVIDERS_FORCED_OFF:
|
||||
# providers_list.remove(provider)
|
||||
|
||||
if not providers_list:
|
||||
providers_list = None
|
||||
|
||||
return providers_list
|
||||
|
@ -50,3 +112,30 @@ def get_providers_auth():
|
|||
}
|
||||
|
||||
return providers_auth
|
||||
|
||||
|
||||
def provider_throttle(name, exception):
|
||||
cls = getattr(exception, "__class__")
|
||||
cls_name = getattr(cls, "__name__")
|
||||
if cls not in VALID_THROTTLE_EXCEPTIONS:
|
||||
for valid_cls in VALID_THROTTLE_EXCEPTIONS:
|
||||
if isinstance(cls, valid_cls):
|
||||
cls = valid_cls
|
||||
|
||||
throttle_data = PROVIDER_THROTTLE_MAP.get(name, PROVIDER_THROTTLE_MAP["default"]).get(cls, None) or \
|
||||
PROVIDER_THROTTLE_MAP["default"].get(cls, None)
|
||||
|
||||
if not throttle_data:
|
||||
return
|
||||
|
||||
throttle_delta, throttle_description = throttle_data
|
||||
throttle_until = datetime.datetime.now() + throttle_delta
|
||||
|
||||
tp[name] = (cls_name, throttle_until, throttle_description)
|
||||
|
||||
settings.general.throtteled_providers = str(tp)
|
||||
with open(os.path.join(args.config_dir, 'config', 'config.ini'), 'w+') as handle:
|
||||
settings.write(handle)
|
||||
|
||||
logging.info("Throttling %s for %s, until %s, because of: %s. Exception info: %r", name, throttle_description,
|
||||
throttle_until.strftime("%y/%m/%d %H:%M"), cls_name, exception.message)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# coding=utf-8
|
||||
|
||||
import os
|
||||
import sys
|
||||
import sqlite3
|
||||
import ast
|
||||
import logging
|
||||
|
@ -26,11 +27,11 @@ from get_languages import language_from_alpha3, alpha2_from_alpha3, alpha3_from_
|
|||
from bs4 import UnicodeDammit
|
||||
from config import settings
|
||||
from helper import path_replace, path_replace_movie, path_replace_reverse, \
|
||||
path_replace_reverse_movie, pp_replace
|
||||
path_replace_reverse_movie, 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
|
||||
from notifier import send_notifications, send_notifications_movie
|
||||
from get_providers import get_providers, get_providers_auth
|
||||
from get_providers import get_providers, get_providers_auth, provider_throttle, provider_pool
|
||||
from get_args import args
|
||||
from queueconfig import notifications
|
||||
|
||||
|
@ -59,17 +60,20 @@ def get_video(path, title, sceneName, use_scenename, providers=None, media_type=
|
|||
# use the sceneName but keep the folder structure for better guessing
|
||||
path = os.path.join(os.path.dirname(path), sceneName + os.path.splitext(path)[1])
|
||||
dont_use_actual_file = True
|
||||
|
||||
try:
|
||||
video = parse_video(path, hints=hints, providers=providers, dry_run=dont_use_actual_file)
|
||||
video.used_scene_name = dont_use_actual_file
|
||||
video.original_name = original_name
|
||||
video.original_path = original_path
|
||||
refine_from_db(original_path, video)
|
||||
return video
|
||||
|
||||
except:
|
||||
logging.exception("BAZARR Error trying to get video information for this file: " + path)
|
||||
if providers:
|
||||
try:
|
||||
video = parse_video(path, hints=hints, providers=providers, dry_run=dont_use_actual_file)
|
||||
video.used_scene_name = dont_use_actual_file
|
||||
video.original_name = original_name
|
||||
video.original_path = original_path
|
||||
refine_from_db(original_path,video)
|
||||
return video
|
||||
|
||||
except:
|
||||
logging.exception("BAZARR Error trying to get video information for this file: " + path)
|
||||
else:
|
||||
logging.info("BAZARR All providers are throttled")
|
||||
return None
|
||||
|
||||
|
||||
def get_scores(video, media_type, min_score_movie_perc=60 * 100 / 120.0, min_score_series_perc=240 * 100 / 360.0,
|
||||
|
@ -96,25 +100,6 @@ def get_scores(video, media_type, min_score_movie_perc=60 * 100 / 120.0, min_sco
|
|||
return min_score, max_score, set(scores)
|
||||
|
||||
|
||||
def force_unicode(s):
|
||||
"""
|
||||
Ensure a string is unicode, not encoded; used for enforcing file paths to be unicode upon saving a subtitle,
|
||||
to prevent encoding issues when saving a subtitle to a non-ascii path.
|
||||
:param s: string
|
||||
:return: unicode string
|
||||
"""
|
||||
if not isinstance(s, types.UnicodeType):
|
||||
try:
|
||||
s = s.decode("utf-8")
|
||||
except UnicodeDecodeError:
|
||||
t = chardet.detect(s)
|
||||
try:
|
||||
s = s.decode(t["encoding"])
|
||||
except UnicodeDecodeError:
|
||||
s = UnicodeDammit(s).unicode_markup
|
||||
return s
|
||||
|
||||
|
||||
def download_subtitle(path, language, hi, providers, providers_auth, sceneName, title, media_type):
|
||||
# fixme: supply all missing languages, not only one, to hit providers only once who support multiple languages in
|
||||
# one query
|
||||
|
@ -147,102 +132,31 @@ def download_subtitle(path, language, hi, providers, providers_auth, sceneName,
|
|||
AsyncProviderPool:
|
||||
implement:
|
||||
blacklist=None,
|
||||
throttle_callback=None,
|
||||
pre_download_hook=None,
|
||||
post_download_hook=None,
|
||||
language_hook=None
|
||||
"""
|
||||
|
||||
"""
|
||||
throttle_callback:
|
||||
|
||||
VALID_THROTTLE_EXCEPTIONS = (TooManyRequests, DownloadLimitExceeded, ServiceUnavailable, APIThrottled)
|
||||
|
||||
PROVIDER_THROTTLE_MAP = {
|
||||
"default": {
|
||||
TooManyRequests: (datetime.timedelta(hours=1), "1 hour"),
|
||||
DownloadLimitExceeded: (datetime.timedelta(hours=3), "3 hours"),
|
||||
ServiceUnavailable: (datetime.timedelta(minutes=20), "20 minutes"),
|
||||
APIThrottled: (datetime.timedelta(minutes=10), "10 minutes"),
|
||||
},
|
||||
"opensubtitles": {
|
||||
TooManyRequests: (datetime.timedelta(hours=3), "3 hours"),
|
||||
DownloadLimitExceeded: (datetime.timedelta(hours=6), "6 hours"),
|
||||
APIThrottled: (datetime.timedelta(seconds=15), "15 seconds"),
|
||||
},
|
||||
"addic7ed": {
|
||||
DownloadLimitExceeded: (datetime.timedelta(hours=3), "3 hours"),
|
||||
TooManyRequests: (datetime.timedelta(minutes=5), "5 minutes"),
|
||||
}
|
||||
}
|
||||
|
||||
throttle_callback gist:
|
||||
def provider_throttle(self, name, exception):
|
||||
cls = getattr(exception, "__class__")
|
||||
cls_name = getattr(cls, "__name__")
|
||||
if cls not in VALID_THROTTLE_EXCEPTIONS:
|
||||
for valid_cls in VALID_THROTTLE_EXCEPTIONS:
|
||||
if isinstance(cls, valid_cls):
|
||||
cls = valid_cls
|
||||
|
||||
throttle_data = PROVIDER_THROTTLE_MAP.get(name, PROVIDER_THROTTLE_MAP["default"]).get(cls, None) or \
|
||||
PROVIDER_THROTTLE_MAP["default"].get(cls, None)
|
||||
|
||||
if not throttle_data:
|
||||
return
|
||||
|
||||
throttle_delta, throttle_description = throttle_data
|
||||
throttle_until = datetime.datetime.now() + throttle_delta
|
||||
|
||||
# save throttle_until together with provider name somewhere, then implement dynamic provider_list based on
|
||||
# that
|
||||
|
||||
provider_configs=
|
||||
{'addic7ed': {'username': Prefs['provider.addic7ed.username'],
|
||||
'password': Prefs['provider.addic7ed.password'],
|
||||
'use_random_agents': cast_bool(Prefs['provider.addic7ed.use_random_agents1']),
|
||||
},
|
||||
'opensubtitles': {'username': Prefs['provider.opensubtitles.username'],
|
||||
'password': Prefs['provider.opensubtitles.password'],
|
||||
'use_tag_search': self.exact_filenames,
|
||||
'only_foreign': self.forced_only,
|
||||
'also_foreign': self.forced_also,
|
||||
'is_vip': cast_bool(Prefs['provider.opensubtitles.is_vip']),
|
||||
'use_ssl': os_use_https,
|
||||
'timeout': self.advanced.providers.opensubtitles.timeout or 15,
|
||||
'skip_wrong_fps': os_skip_wrong_fps,
|
||||
},
|
||||
'podnapisi': {
|
||||
'only_foreign': self.forced_only,
|
||||
'also_foreign': self.forced_also,
|
||||
},
|
||||
'subscene': {
|
||||
'only_foreign': self.forced_only,
|
||||
},
|
||||
'legendastv': {'username': Prefs['provider.legendastv.username'],
|
||||
'password': Prefs['provider.legendastv.password'],
|
||||
},
|
||||
'assrt': {'token': Prefs['provider.assrt.token'], }
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
video = get_video(path, title, sceneName, use_scenename, providers=providers, media_type=media_type)
|
||||
if video:
|
||||
min_score, max_score, scores = get_scores(video, media_type, min_score_movie_perc=int(minimum_score_movie),
|
||||
min_score_series_perc=int(minimum_score))
|
||||
|
||||
downloaded_subtitles = download_best_subtitles({video}, language_set, int(min_score), hi,
|
||||
providers=providers,
|
||||
provider_configs=providers_auth,
|
||||
pool_class=SZAsyncProviderPool,
|
||||
compute_score=compute_score,
|
||||
throttle_time=None, # fixme
|
||||
blacklist=None, # fixme
|
||||
throttle_callback=None, # fixme
|
||||
pre_download_hook=None, # fixme
|
||||
post_download_hook=None, # fixme
|
||||
language_hook=None) # fixme
|
||||
|
||||
if providers:
|
||||
downloaded_subtitles = download_best_subtitles({video}, language_set, int(min_score), hi,
|
||||
providers=providers,
|
||||
provider_configs=providers_auth,
|
||||
pool_class=provider_pool(),
|
||||
compute_score=compute_score,
|
||||
throttle_time=None, # fixme
|
||||
blacklist=None, # fixme
|
||||
throttle_callback=provider_throttle,
|
||||
pre_download_hook=None, # fixme
|
||||
post_download_hook=None, # fixme
|
||||
language_hook=None) # fixme
|
||||
else:
|
||||
downloaded_subtitles = None
|
||||
logging.info("BAZARR All providers are throttled")
|
||||
|
||||
saved_any = False
|
||||
if downloaded_subtitles:
|
||||
|
@ -251,10 +165,12 @@ def download_subtitle(path, language, hi, providers, providers_auth, sceneName,
|
|||
continue
|
||||
|
||||
try:
|
||||
fld = get_target_folder(path)
|
||||
chmod = int(settings.general.chmod, 8) if not sys.platform.startswith('win') else None
|
||||
saved_subtitles = save_subtitles(video.original_path, subtitles, single=single,
|
||||
tags=None, # fixme
|
||||
directory=None, # fixme
|
||||
chmod=None, # fixme
|
||||
directory=fld,
|
||||
chmod=chmod,
|
||||
# formats=("srt", "vtt")
|
||||
path_decoder=force_unicode
|
||||
)
|
||||
|
@ -347,12 +263,16 @@ def manual_search(path, language, hi, providers, providers_auth, sceneName, titl
|
|||
min_score_series_perc=int(minimum_score))
|
||||
|
||||
try:
|
||||
subtitles = list_subtitles([video], language_set,
|
||||
providers=providers,
|
||||
provider_configs=providers_auth,
|
||||
pool_class=SZAsyncProviderPool, # fixme: make async optional
|
||||
throttle_callback=None, # fixme
|
||||
language_hook=None) # fixme
|
||||
if providers:
|
||||
subtitles = list_subtitles([video], language_set,
|
||||
providers=providers,
|
||||
provider_configs=providers_auth,
|
||||
pool_class=provider_pool(),
|
||||
throttle_callback=provider_throttle,
|
||||
language_hook=None) # fixme
|
||||
else:
|
||||
subtitles = []
|
||||
logging.info("BAZARR All providers are throttled")
|
||||
except Exception as e:
|
||||
logging.exception("BAZARR Error trying to get subtitle list from provider for this file: " + path)
|
||||
else:
|
||||
|
@ -405,9 +325,14 @@ def manual_download_subtitle(path, language, hi, subtitle, provider, providers_a
|
|||
if video:
|
||||
min_score, max_score, scores = get_scores(video, media_type)
|
||||
try:
|
||||
download_subtitles([subtitle], providers={provider}, provider_configs=providers_auth,
|
||||
pool_class=SZAsyncProviderPool,
|
||||
throttle_callback=None) # fixme
|
||||
if provider:
|
||||
download_subtitles([subtitle], providers={provider}, provider_configs=providers_auth,
|
||||
pool_class=provider_pool(),
|
||||
throttle_callback=provider_throttle)
|
||||
logging.debug('BAZARR Subtitles file downloaded for this file:' + path)
|
||||
else:
|
||||
logging.info("BAZARR All providers are throttled")
|
||||
return None
|
||||
except Exception as e:
|
||||
logging.exception('BAZARR Error downloading subtitles for this file ' + path)
|
||||
return None
|
||||
|
@ -419,7 +344,13 @@ def manual_download_subtitle(path, language, hi, subtitle, provider, providers_a
|
|||
logging.debug('BAZARR Subtitles file downloaded for this file:' + path)
|
||||
try:
|
||||
score = round(subtitle.score / max_score * 100, 2)
|
||||
fld = get_target_folder(path)
|
||||
chmod = int(settings.general.chmod, 8) if not sys.platform.startswith('win') else None
|
||||
saved_subtitles = save_subtitles(video.original_path, [subtitle], single=single,
|
||||
tags=None, # fixme
|
||||
directory=fld,
|
||||
chmod=chmod,
|
||||
# formats=("srt", "vtt")
|
||||
path_decoder=force_unicode)
|
||||
|
||||
except Exception as e:
|
||||
|
@ -472,7 +403,8 @@ def manual_download_subtitle(path, language, hi, subtitle, provider, providers_a
|
|||
return message
|
||||
else:
|
||||
logging.error(
|
||||
"BAZARR Tried to manually download a subtitles for file: " + path + " but we weren't able to do (probably throttled by " + str(subtitle.provider_name) + ". Please retry later or select a subtitles from another provider.")
|
||||
"BAZARR Tried to manually download a subtitles for file: " + path + " but we weren't able to do (probably throttled by " + str(
|
||||
subtitle.provider_name) + ". Please retry later or select a subtitles from another provider.")
|
||||
return None
|
||||
logging.debug('BAZARR Ended manually downloading subtitles for file: ' + path)
|
||||
|
||||
|
@ -569,7 +501,7 @@ def wanted_download_subtitles(path):
|
|||
|
||||
for i in range(len(attempt)):
|
||||
if attempt[i][0] == language:
|
||||
if search_active(attempt[i][1]) is True:
|
||||
if search_active(attempt[i][1]):
|
||||
notifications.write(
|
||||
msg='Searching ' + str(language_from_alpha2(language)) + ' subtitles for this episode: ' + path, queue='get_subtitle')
|
||||
message = download_subtitle(path_replace(episode[0]), str(alpha3_from_alpha2(language)),
|
||||
|
@ -658,14 +590,22 @@ def wanted_search_missing_subtitles():
|
|||
movies = c.fetchall()
|
||||
|
||||
c.close()
|
||||
|
||||
providers = get_providers()
|
||||
if settings.general.getboolean('use_sonarr'):
|
||||
for episode in episodes:
|
||||
wanted_download_subtitles(episode[0])
|
||||
if providers:
|
||||
for episode in episodes:
|
||||
wanted_download_subtitles(episode[0])
|
||||
else:
|
||||
q4ws.append('BAZARR All providers are throttled')
|
||||
logging.info("BAZARR All providers are throttled")
|
||||
|
||||
if settings.general.getboolean('use_radarr'):
|
||||
for movie in movies:
|
||||
wanted_download_subtitles_movie(movie[0])
|
||||
if providers:
|
||||
for movie in movies:
|
||||
wanted_download_subtitles_movie(movie[0])
|
||||
else:
|
||||
q4ws.append('BAZARR All providers are throttled')
|
||||
logging.info("BAZARR All providers are throttled")
|
||||
|
||||
logging.info('BAZARR Finished searching for missing subtitles. Check histories for more information.')
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
import ast
|
||||
import os
|
||||
import re
|
||||
import types
|
||||
import logging
|
||||
|
||||
from config import settings
|
||||
|
||||
|
@ -63,3 +65,59 @@ def pp_replace(pp_command, episode, subtitles, language, language_code2, languag
|
|||
pp_command = pp_command.replace('{{subtitles_language_code2}}', language_code2)
|
||||
pp_command = pp_command.replace('{{subtitles_language_code3}}', language_code3)
|
||||
return pp_command
|
||||
|
||||
|
||||
def get_subtitle_destination_folder():
|
||||
fld_custom = str(settings.general.subfolder_custom).strip() if settings.general.subfolder_custom else None
|
||||
return fld_custom or (
|
||||
settings.general.subfolder if settings.general.subfolder != "current" else None)
|
||||
|
||||
|
||||
def get_target_folder(file_path):
|
||||
subfolder = settings.general.subfolder
|
||||
fld_custom = str(settings.general.subfolder_custom).strip() \
|
||||
if settings.general.subfolder_custom else None
|
||||
|
||||
if subfolder != "current" and fld_custom:
|
||||
# specific subFolder requested, create it if it doesn't exist
|
||||
fld_base = os.path.split(file_path)[0]
|
||||
|
||||
if subfolder == "absolute":
|
||||
# absolute folder
|
||||
fld = fld_custom
|
||||
elif subfolder == "relative":
|
||||
fld = os.path.join(fld_base, fld_custom)
|
||||
else:
|
||||
fld = None
|
||||
|
||||
fld = force_unicode(fld)
|
||||
|
||||
if not os.path.isdir(fld):
|
||||
try:
|
||||
os.makedirs(fld)
|
||||
except Exception as e:
|
||||
logging.error('BAZARR is unable to create directory to save subtitles: ' + fld)
|
||||
fld = None
|
||||
else:
|
||||
fld = None
|
||||
|
||||
return fld
|
||||
|
||||
|
||||
def force_unicode(s):
|
||||
"""
|
||||
Ensure a string is unicode, not encoded; used for enforcing file paths to be unicode upon saving a subtitle,
|
||||
to prevent encoding issues when saving a subtitle to a non-ascii path.
|
||||
:param s: string
|
||||
:return: unicode string
|
||||
"""
|
||||
if not isinstance(s, types.UnicodeType):
|
||||
try:
|
||||
s = s.decode("utf-8")
|
||||
except UnicodeDecodeError:
|
||||
t = chardet.detect(s)
|
||||
try:
|
||||
s = s.decode(t["encoding"])
|
||||
except UnicodeDecodeError:
|
||||
s = UnicodeDammit(s).unicode_markup
|
||||
return s
|
||||
|
|
|
@ -16,10 +16,10 @@ from bs4 import UnicodeDammit
|
|||
from itertools import islice
|
||||
|
||||
from get_args import args
|
||||
from get_languages import alpha2_from_alpha3
|
||||
from get_languages import alpha2_from_alpha3, get_language_set
|
||||
from config import settings
|
||||
from helper import path_replace, path_replace_movie, path_replace_reverse, \
|
||||
path_replace_reverse_movie
|
||||
path_replace_reverse_movie, get_subtitle_destination_folder
|
||||
|
||||
from queueconfig import notifications
|
||||
|
||||
|
@ -54,22 +54,24 @@ def store_subtitles(file):
|
|||
|
||||
brazilian_portuguese = [".pt-br", ".pob", "pb"]
|
||||
try:
|
||||
# fixme: set subliminal_patch.core.CUSTOM_PATHS to a list of absolute folders or subfolders to support
|
||||
# subtitles outside the media file folder
|
||||
subtitles = search_external_subtitles(file)
|
||||
dest_folder = get_subtitle_destination_folder()
|
||||
subliminal_patch.core.CUSTOM_PATHS = [dest_folder] if dest_folder else []
|
||||
subtitles = search_external_subtitles(file, languages=get_language_set(),
|
||||
only_one=settings.general.getboolean('single_language'))
|
||||
except Exception as e:
|
||||
logging.exception("BAZARR unable to index external subtitles.")
|
||||
pass
|
||||
else:
|
||||
for subtitle, language in subtitles.iteritems():
|
||||
subtitle_path = get_external_subtitles_path(file, subtitle)
|
||||
if str(os.path.splitext(subtitle)[0]).lower().endswith(tuple(brazilian_portuguese)):
|
||||
logging.debug("BAZARR external subtitles detected: " + "pb")
|
||||
actual_subtitles.append(
|
||||
[str("pb"), path_replace_reverse(os.path.join(os.path.dirname(file), subtitle))])
|
||||
[str("pb"), path_replace_reverse(subtitle_path)])
|
||||
elif str(language) != 'und':
|
||||
logging.debug("BAZARR external subtitles detected: " + str(language))
|
||||
actual_subtitles.append(
|
||||
[str(language), path_replace_reverse(os.path.join(os.path.dirname(file), subtitle))])
|
||||
[str(language), path_replace_reverse(subtitle_path)])
|
||||
else:
|
||||
if os.path.splitext(subtitle)[1] != ".sub":
|
||||
logging.debug("BAZARR falling back to file content analysis to detect language.")
|
||||
|
@ -134,13 +136,13 @@ def store_subtitles_movie(file):
|
|||
pass
|
||||
else:
|
||||
logging.debug("BAZARR This file isn't an .mkv file.")
|
||||
|
||||
# fixme: set subliminal_patch.core.CUSTOM_PATHS to a list of absolute folders or subfolders to support
|
||||
# subtitles outside the media file folder
|
||||
subtitles = search_external_subtitles(file)
|
||||
|
||||
dest_folder = get_subtitle_destination_folder()
|
||||
subliminal_patch.core.CUSTOM_PATHS = [dest_folder] if dest_folder else []
|
||||
brazilian_portuguese = [".pt-br", ".pob", "pb"]
|
||||
try:
|
||||
subtitles = core.search_external_subtitles(file)
|
||||
subtitles = search_external_subtitles(file, languages=get_language_set(),
|
||||
only_one=settings.general.getboolean('single_language'))
|
||||
except Exception as e:
|
||||
logging.exception("BAZARR unable to index external subtitles.")
|
||||
pass
|
||||
|
@ -335,3 +337,30 @@ def movies_scan_subtitles(no):
|
|||
store_subtitles_movie(path_replace_movie(movie[0]))
|
||||
|
||||
list_missing_subtitles_movies(no)
|
||||
|
||||
|
||||
def get_external_subtitles_path(file, subtitle):
|
||||
fld = os.path.dirname(file)
|
||||
|
||||
if settings.general.subfolder == "current":
|
||||
path = os.path.join(fld, subtitle)
|
||||
elif settings.general.subfolder == "absolute":
|
||||
custom_fld = settings.general.subfolder_custom
|
||||
if os.path.exists(os.path.join(fld, subtitle)):
|
||||
path = os.path.join(fld, subtitle)
|
||||
elif os.path.exists(os.path.join(custom_fld, subtitle)):
|
||||
path = os.path.join(custom_fld, subtitle)
|
||||
else:
|
||||
path = None
|
||||
elif settings.general.subfolder == "relative":
|
||||
custom_fld = os.path.join(fld, settings.general.subfolder_custom)
|
||||
if os.path.exists(os.path.join(fld, subtitle)):
|
||||
path = os.path.join(fld, subtitle)
|
||||
elif os.path.exists(os.path.join(custom_fld, subtitle)):
|
||||
path = os.path.join(custom_fld, subtitle)
|
||||
else:
|
||||
path = None
|
||||
else:
|
||||
path = None
|
||||
|
||||
return path
|
||||
|
|
|
@ -67,7 +67,7 @@ from utils import history_log, history_log_movie
|
|||
from scheduler import *
|
||||
from notifier import send_notifications, send_notifications_movie
|
||||
from config import settings, url_sonarr, url_radarr, url_radarr_short, url_sonarr_short, base_url
|
||||
from helper import path_replace_movie
|
||||
from helper import path_replace_movie, get_subtitle_destination_folder
|
||||
from subliminal_patch.extensions import provider_registry as provider_manager
|
||||
|
||||
reload(sys)
|
||||
|
@ -276,6 +276,13 @@ def save_wizard():
|
|||
settings_general_use_radarr = 'False'
|
||||
else:
|
||||
settings_general_use_radarr = 'True'
|
||||
settings_general_embedded = request.forms.get('settings_general_embedded')
|
||||
if settings_general_embedded is None:
|
||||
settings_general_embedded = 'False'
|
||||
else:
|
||||
settings_general_embedded = 'True'
|
||||
settings_subfolder = request.forms.get('settings_subfolder')
|
||||
settings_subfolder_custom = request.forms.get('settings_subfolder_custom')
|
||||
|
||||
settings.general.ip = text_type(settings_general_ip)
|
||||
settings.general.port = text_type(settings_general_port)
|
||||
|
@ -285,6 +292,9 @@ def save_wizard():
|
|||
settings.general.use_sonarr = text_type(settings_general_use_sonarr)
|
||||
settings.general.use_radarr = text_type(settings_general_use_radarr)
|
||||
settings.general.path_mappings_movie = text_type(settings_general_pathmapping_movie)
|
||||
settings.general.subfolder = text_type(settings_subfolder)
|
||||
settings.general.subfolder_custom = text_type(settings_subfolder_custom)
|
||||
settings.general.use_embedded_subs = text_type(settings_general_embedded)
|
||||
|
||||
settings_sonarr_ip = request.forms.get('settings_sonarr_ip')
|
||||
settings_sonarr_port = request.forms.get('settings_sonarr_port')
|
||||
|
@ -1077,9 +1087,9 @@ def wantedmovies():
|
|||
def wanted_search_missing_subtitles_list():
|
||||
authorize()
|
||||
ref = request.environ['HTTP_REFERER']
|
||||
|
||||
|
||||
add_job(wanted_search_missing_subtitles, name='manual_wanted_search_missing_subtitles')
|
||||
|
||||
|
||||
redirect(ref)
|
||||
|
||||
|
||||
|
@ -1120,6 +1130,7 @@ def save_settings():
|
|||
settings_general_debug = 'False'
|
||||
else:
|
||||
settings_general_debug = 'True'
|
||||
settings_general_chmod = request.forms.get('settings_general_chmod')
|
||||
settings_general_sourcepath = request.forms.getall('settings_general_sourcepath')
|
||||
settings_general_destpath = request.forms.getall('settings_general_destpath')
|
||||
settings_general_pathmapping = []
|
||||
|
@ -1155,6 +1166,11 @@ def save_settings():
|
|||
settings_general_adaptive_searching = 'False'
|
||||
else:
|
||||
settings_general_adaptive_searching = 'True'
|
||||
settings_general_multithreading = request.forms.get('settings_general_multithreading')
|
||||
if settings_general_multithreading is None:
|
||||
settings_general_multithreading = 'False'
|
||||
else:
|
||||
settings_general_multithreading = 'True'
|
||||
settings_general_minimum_score = request.forms.get('settings_general_minimum_score')
|
||||
settings_general_minimum_score_movies = request.forms.get('settings_general_minimum_score_movies')
|
||||
settings_general_use_postprocessing = request.forms.get('settings_general_use_postprocessing')
|
||||
|
@ -1174,7 +1190,9 @@ def save_settings():
|
|||
else:
|
||||
settings_general_use_radarr = 'True'
|
||||
settings_page_size = request.forms.get('settings_page_size')
|
||||
|
||||
settings_subfolder = request.forms.get('settings_subfolder')
|
||||
settings_subfolder_custom = request.forms.get('settings_subfolder_custom')
|
||||
|
||||
before = (unicode(settings.general.ip), int(settings.general.port), unicode(settings.general.base_url),
|
||||
unicode(settings.general.path_mappings), unicode(settings.general.getboolean('use_sonarr')),
|
||||
unicode(settings.general.getboolean('use_radarr')), unicode(settings.general.path_mappings_movie))
|
||||
|
@ -1187,6 +1205,7 @@ def save_settings():
|
|||
settings.general.base_url = text_type(settings_general_baseurl)
|
||||
settings.general.path_mappings = text_type(settings_general_pathmapping)
|
||||
settings.general.debug = text_type(settings_general_debug)
|
||||
settings.general.chmod = text_type(settings_general_chmod)
|
||||
settings.general.branch = text_type(settings_general_branch)
|
||||
settings.general.auto_update = text_type(settings_general_automatic)
|
||||
settings.general.single_language = text_type(settings_general_single_language)
|
||||
|
@ -1198,10 +1217,13 @@ def save_settings():
|
|||
settings.general.use_radarr = text_type(settings_general_use_radarr)
|
||||
settings.general.path_mappings_movie = text_type(settings_general_pathmapping_movie)
|
||||
settings.general.page_size = text_type(settings_page_size)
|
||||
settings.general.subfolder = text_type(settings_subfolder)
|
||||
settings.general.subfolder_custom = text_type(settings_subfolder_custom)
|
||||
settings.general.minimum_score_movie = text_type(settings_general_minimum_score_movies)
|
||||
settings.general.use_embedded_subs = text_type(settings_general_embedded)
|
||||
settings.general.adaptive_searching = text_type(settings_general_adaptive_searching)
|
||||
|
||||
settings.general.multithreading = text_type(settings_general_multithreading)
|
||||
|
||||
if after != before:
|
||||
configured()
|
||||
|
||||
|
@ -1402,7 +1424,7 @@ def save_settings():
|
|||
with open(os.path.join(args.config_dir, 'config', 'config.ini'), 'w+') as handle:
|
||||
settings.write(handle)
|
||||
|
||||
configure_logging(settings.general.getboolean('debug'))
|
||||
configure_logging(settings.general.getboolean('debug') or args.debug)
|
||||
|
||||
notifiers = c.execute("SELECT * FROM table_settings_notifier ORDER BY name").fetchall()
|
||||
for notifier in notifiers:
|
||||
|
@ -1613,7 +1635,7 @@ def remove_subtitles():
|
|||
subtitlesPath = request.forms.get('subtitlesPath')
|
||||
sonarrSeriesId = request.forms.get('sonarrSeriesId')
|
||||
sonarrEpisodeId = request.forms.get('sonarrEpisodeId')
|
||||
|
||||
|
||||
try:
|
||||
os.remove(subtitlesPath)
|
||||
result = language_from_alpha3(language) + " subtitles deleted from disk."
|
||||
|
@ -1632,9 +1654,11 @@ def remove_subtitles_movie():
|
|||
language = request.forms.get('language')
|
||||
subtitlesPath = request.forms.get('subtitlesPath')
|
||||
radarrId = request.forms.get('radarrId')
|
||||
subfolder = ('/' + get_subtitle_destination_folder() + '/') if get_subtitle_destination_folder() else '/'
|
||||
subtitlesPath = os.path.split(subtitlesPath)
|
||||
|
||||
try:
|
||||
os.remove(subtitlesPath)
|
||||
os.remove(subtitlesPath[0] + subfolder + subtitlesPath[1])
|
||||
result = language_from_alpha3(language) + " subtitles deleted from disk."
|
||||
history_log_movie(0, radarrId, result)
|
||||
except OSError:
|
||||
|
|
|
@ -559,13 +559,16 @@ def _search_external_subtitles(path, languages=None, only_one=False, scandir_gen
|
|||
subtitles = {}
|
||||
_scandir = _scandir_generic if scandir_generic else scandir
|
||||
for entry in _scandir(dirpath):
|
||||
if not entry.name and not scandir_generic:
|
||||
logger.debug('Could not determine the name of the file, retrying with scandir_generic')
|
||||
return _search_external_subtitles(path, languages, only_one, True)
|
||||
if not entry.is_file(follow_symlinks=False):
|
||||
continue
|
||||
|
||||
p = entry.name
|
||||
|
||||
# keep only valid subtitle filenames
|
||||
if not p.lower().startswith(fileroot.lower()) or not p.endswith(SUBTITLE_EXTENSIONS):
|
||||
if not p.lower().startswith(fileroot.lower()) or not p.lower().endswith(SUBTITLE_EXTENSIONS):
|
||||
continue
|
||||
|
||||
p_root, p_ext = os.path.splitext(p)
|
||||
|
@ -600,7 +603,7 @@ def _search_external_subtitles(path, languages=None, only_one=False, scandir_gen
|
|||
logger.error('Cannot parse language code %r', language_code)
|
||||
language = None
|
||||
|
||||
if not language and only_one:
|
||||
elif not language_code and only_one:
|
||||
language = Language.rebuild(list(languages)[0], forced=forced)
|
||||
|
||||
subtitles[p] = language
|
||||
|
|
|
@ -63,3 +63,4 @@ subliminal.refiner_manager.register('drone = subliminal_patch.refiners.drone:ref
|
|||
subliminal.refiner_manager.register('filebot = subliminal_patch.refiners.filebot:refine')
|
||||
subliminal.refiner_manager.register('file_info_file = subliminal_patch.refiners.file_info_file:refine')
|
||||
subliminal.refiner_manager.register('symlinks = subliminal_patch.refiners.symlinks:refine')
|
||||
|
||||
|
|
|
@ -361,8 +361,11 @@
|
|||
});
|
||||
|
||||
$('#search_missing_subtitles').on('click', function(){
|
||||
$('#loader_text').text("Searching for missing subtitles...");
|
||||
window.location = '{{base_url}}search_missing_subtitles/{{no}}';
|
||||
$(this).addClass('disabled');
|
||||
$(this).find('i:first').addClass('loading');
|
||||
$.ajax({
|
||||
url: '{{base_url}}search_missing_subtitles/{{no}}'
|
||||
})
|
||||
});
|
||||
|
||||
$('.remove_subtitles').on('click', function(){
|
||||
|
@ -418,7 +421,7 @@
|
|||
});
|
||||
});
|
||||
|
||||
$('a:not(.manual_search), .menu .item, button:not(#config, .cancel)').on('click', function(){
|
||||
$('a:not(.manual_search), .menu .item, button:not(#config, .cancel, #search_missing_subtitles)').on('click', function(){
|
||||
$('#loader').addClass('active');
|
||||
});
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
.searchicon {
|
||||
color: white !important;
|
||||
}
|
||||
div.disabled { pointer-events: none; }
|
||||
button.disabled { pointer-events: none; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -222,13 +224,13 @@
|
|||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
var url = location.protocol +"//" + window.location.host + "{{base_url}}notifications";
|
||||
var notification;
|
||||
var url_notifications = location.protocol +"//" + window.location.host + "{{base_url}}notifications";
|
||||
var notificationTimeout;
|
||||
var timeout;
|
||||
var killer;
|
||||
function doAjax() {
|
||||
function doNotificationsAjax() {
|
||||
$.ajax({
|
||||
url: url,
|
||||
url: url_notifications,
|
||||
success: function (data) {
|
||||
if (data !== "") {
|
||||
data = JSON.parse(data);
|
||||
|
@ -275,17 +277,79 @@
|
|||
},
|
||||
complete: function (data) {
|
||||
// Schedule the next
|
||||
if (data != "") {
|
||||
notification = setTimeout(doAjax, 100);
|
||||
if (data !== "") {
|
||||
notificationTimeout = setTimeout(doNotificationsAjax, 100);
|
||||
} else {
|
||||
notification = setTimeout(doAjax, 1000);
|
||||
notificationTimeout = setTimeout(doNotificationsAjax, 1000);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
notification = setTimeout(doAjax, 1000);
|
||||
notificationTimeout = setTimeout(doNotificationsAjax, 1000);
|
||||
|
||||
$(window).bind('beforeunload', function(){
|
||||
clearTimeout(notification);
|
||||
clearTimeout(notificationTimeout);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
var url_tasks = location.protocol +"//" + window.location.host + "{{base_url}}running_tasks";
|
||||
var tasksTimeout;
|
||||
function doTasksAjax() {
|
||||
$.ajax({
|
||||
url: url_tasks,
|
||||
dataType: 'json',
|
||||
success: function (data) {
|
||||
$('#tasks > tbody > tr').each(function() {
|
||||
if ($.inArray($(this).attr('id'), data['tasks']) > -1) {
|
||||
$(this).find('td:last').find('div:first').addClass('disabled');
|
||||
$(this).find('td:last').find('div:first').find('i:first').addClass('loading');
|
||||
} else {
|
||||
$(this).find('td:last').find('div:first').removeClass('disabled');
|
||||
$(this).find('td:last').find('div:first').find('i:first').removeClass('loading');
|
||||
}
|
||||
});
|
||||
|
||||
if ($.inArray('wanted_search_missing_subtitles', data['tasks']) > -1) {
|
||||
$('#wanted_search_missing_subtitles').addClass('disabled');
|
||||
$('#wanted_search_missing_subtitles_movies').addClass('disabled');
|
||||
$('#wanted_search_missing_subtitles').find('i:first').addClass('loading');
|
||||
$('#wanted_search_missing_subtitles_movies').find('i:first').addClass('loading');
|
||||
} else {
|
||||
$('#wanted_search_missing_subtitles').removeClass('disabled');
|
||||
$('#wanted_search_missing_subtitles_movies').removeClass('disabled');
|
||||
$('#wanted_search_missing_subtitles').find('i:first').removeClass('loading');
|
||||
$('#wanted_search_missing_subtitles_movies').find('i:first').removeClass('loading');
|
||||
}
|
||||
|
||||
%if 'no' in locals():
|
||||
if ($.inArray('search_missing_subtitles_{{no}}', data['tasks']) > -1) {
|
||||
$('#search_missing_subtitles').addClass('disabled');
|
||||
$('#search_missing_subtitles').find('i:first').addClass('loading');
|
||||
} else {
|
||||
$('#search_missing_subtitles').removeClass('disabled');
|
||||
$('#search_missing_subtitles').find('i:first').removeClass('loading');
|
||||
}
|
||||
|
||||
if ($.inArray('search_missing_subtitles_movie_{{no}}', data['tasks']) > -1) {
|
||||
$('#search_missing_subtitles_movie').addClass('disabled');
|
||||
$('#search_missing_subtitles_movie').find('i:first').addClass('loading');
|
||||
} else {
|
||||
$('#search_missing_subtitles_movie').removeClass('disabled');
|
||||
$('#search_missing_subtitles_movie').find('i:first').removeClass('loading');
|
||||
}
|
||||
%end
|
||||
},
|
||||
complete: function (data) {
|
||||
// Schedule the next
|
||||
tasksTimeout = setTimeout(doTasksAjax, 5000);
|
||||
}
|
||||
});
|
||||
}
|
||||
tasksTimeout = setTimeout(doTasksAjax, 500);
|
||||
|
||||
$(window).bind('beforeunload', function(){
|
||||
clearTimeout(tasksTimeout);
|
||||
});
|
||||
</script>
|
|
@ -94,7 +94,7 @@
|
|||
<img class="left floated ui image" style="max-height:250px;" src="{{base_url}}image_proxy_movies{{details[2]}}">
|
||||
<div class="ui right floated basic icon buttons">
|
||||
<button id="scan_disk" class="ui button" data-tooltip="Scan disk for subtitles" data-inverted=""><i class="ui inverted large compact refresh icon"></i></button>
|
||||
<button id="search_missing_subtitles" class="ui button" data-tooltip="Download missing subtitles" data-inverted=""><i class="ui inverted huge compact search icon"></i></button>
|
||||
<button id="search_missing_subtitles_movie" class="ui button" data-tooltip="Download missing subtitles" data-inverted=""><i class="ui inverted huge compact search icon"></i></button>
|
||||
<%
|
||||
subs_languages = ast.literal_eval(str(details[7]))
|
||||
subs_languages_list = []
|
||||
|
@ -314,9 +314,12 @@
|
|||
window.location = '{{base_url}}scan_disk_movie/{{no}}';
|
||||
});
|
||||
|
||||
$('#search_missing_subtitles').on('click', function(){
|
||||
$('#loader_text').text("Searching for missing subtitles...");
|
||||
window.location = '{{base_url}}search_missing_subtitles_movie/{{no}}';
|
||||
$('#search_missing_subtitles_movie').on('click', function(){
|
||||
$(this).addClass('disabled');
|
||||
$(this).find('i:first').addClass('loading');
|
||||
$.ajax({
|
||||
url: '{{base_url}}search_missing_subtitles_movie/{{no}}'
|
||||
})
|
||||
});
|
||||
|
||||
$('.remove_subtitles').on('click', function(){
|
||||
|
@ -371,7 +374,7 @@
|
|||
});
|
||||
});
|
||||
|
||||
$('a, .menu .item, button:not(#config, .cancel, .manual_search)').on('click', function(){
|
||||
$('a, .menu .item, button:not(#config, .cancel, .manual_search, #search_missing_subtitles_movie)').on('click', function(){
|
||||
$('#loader').addClass('active');
|
||||
});
|
||||
|
||||
|
|
|
@ -152,6 +152,25 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="chmod" class="middle aligned row">
|
||||
<div class="right aligned four wide column">
|
||||
<label>Set subtitle file permissions to</label>
|
||||
</div>
|
||||
<div class="five wide column">
|
||||
<div class='field'>
|
||||
<div id="settings_chmod" class="ui fluid input">
|
||||
<input name="settings_general_chmod" type="text"
|
||||
value={{ settings.general.chmod }}>
|
||||
<label></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapsed center aligned column">
|
||||
<div class="ui basic icon" data-tooltip="Must be 4 digit octal, e.g.: 0775" data-inverted="">
|
||||
<i class="help circle large icon"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="middle aligned row">
|
||||
<div class="right aligned four wide column">
|
||||
|
@ -1039,6 +1058,49 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="middle aligned row">
|
||||
<div class="right aligned four wide column">
|
||||
<label>Subtitle folder</label>
|
||||
</div>
|
||||
<div class="five wide column">
|
||||
<select name="settings_subfolder" id="settings_subfolder"
|
||||
class="ui fluid selection dropdown">
|
||||
<option value="current">Alongside media file</option>
|
||||
<option value="relative">Relative path to media file</option>
|
||||
<option value="absolute">Absolute path</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="collapsed center aligned column">
|
||||
<div class="ui basic icon"
|
||||
data-tooltip='Choose folder where you want to store/read the subtitles'
|
||||
data-inverted="">
|
||||
<i class="help circle large icon"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="middle aligned row subfolder">
|
||||
<div class="right aligned four wide column">
|
||||
<label>Custom Subtitle folder</label>
|
||||
</div>
|
||||
<div class="five wide column">
|
||||
<div class='field'>
|
||||
<div class="ui fluid input">
|
||||
<input id="settings_subfolder_custom" name="settings_subfolder_custom"
|
||||
type="text" value="{{ settings.general.subfolder_custom }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="collapsed center aligned column">
|
||||
<div class="ui basic icon"
|
||||
data-tooltip='Choose your own folder for the subtitles' data-inverted="">
|
||||
<i class="help circle large icon"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="middle aligned row">
|
||||
<div class="right aligned four wide column">
|
||||
<label>Use embedded subtitles</label>
|
||||
|
@ -1076,6 +1138,26 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="middle aligned row">
|
||||
<div class="right aligned four wide column">
|
||||
<label>Search enabled providers simultaneously</label>
|
||||
</div>
|
||||
<div class="one wide column">
|
||||
<div id="settings_multithreading" class="ui toggle checkbox"
|
||||
data-multithreading={{ settings.general.getboolean('multithreading') }}>
|
||||
<input name="settings_general_multithreading" type="checkbox">
|
||||
<label></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapsed column">
|
||||
<div class="collapsed center aligned column">
|
||||
<div class="ui basic icon" data-tooltip="Search multi providers at once (Don't choose this on low powered devices)" data-inverted="">
|
||||
<i class="help circle large icon"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui dividing header">Subtitles providers</div>
|
||||
|
@ -1894,6 +1976,10 @@
|
|||
:
|
||||
$("#div_update").hide();
|
||||
% end
|
||||
% import sys
|
||||
% if sys.platform.startswith('win'):
|
||||
$("#chmod").hide();
|
||||
% end
|
||||
|
||||
$('.menu .item')
|
||||
.tab()
|
||||
|
@ -1967,6 +2053,12 @@
|
|||
$("#settings_adaptive_searching").checkbox('uncheck');
|
||||
}
|
||||
|
||||
if ($('#settings_multithreading').data("multithreading") === "True") {
|
||||
$("#settings_multithreading").checkbox('check');
|
||||
} else {
|
||||
$("#settings_multithreading").checkbox('uncheck');
|
||||
}
|
||||
|
||||
if ($('#settings_addic7ed_random_agents').data("randomagents") === "True") {
|
||||
$("#settings_addic7ed_random_agents").checkbox('check');
|
||||
} else {
|
||||
|
@ -2047,6 +2139,19 @@
|
|||
}
|
||||
});
|
||||
|
||||
if (($('#settings_subfolder').val() !== "relative") && ($('#settings_subfolder').val() !== "absolute")) {
|
||||
$('.subfolder').hide();
|
||||
}
|
||||
|
||||
$('#settings_subfolder').dropdown('setting', 'onChange', function(){
|
||||
if (($('#settings_subfolder').val() !== "relative") && ($('#settings_subfolder').val() !== "absolute")) {
|
||||
$('.subfolder').hide();
|
||||
}
|
||||
else {
|
||||
$('.subfolder').show();
|
||||
}
|
||||
});
|
||||
|
||||
if ($('#settings_auth_type').val() === "None") {
|
||||
$('.auth_option').hide();
|
||||
}
|
||||
|
@ -2210,6 +2315,8 @@
|
|||
$('#settings_loglevel').dropdown('set selected','{{!settings.general.getboolean('debug')}}');
|
||||
$('#settings_page_size').dropdown('clear');
|
||||
$('#settings_page_size').dropdown('set selected','{{!settings.general.page_size}}');
|
||||
$('#settings_subfolder').dropdown('clear');
|
||||
$('#settings_subfolder').dropdown('set selected', '{{!settings.general.subfolder}}');
|
||||
$('#settings_proxy_type').dropdown('clear');
|
||||
$('#settings_proxy_type').dropdown('set selected','{{!settings.proxy.type}}');
|
||||
$('#settings_providers').dropdown('clear');
|
||||
|
@ -2243,6 +2350,7 @@
|
|||
// form validation
|
||||
$('#settings_form')
|
||||
.form({
|
||||
on: 'blur',
|
||||
fields: {
|
||||
settings_general_ip : {
|
||||
rules : [
|
||||
|
@ -2264,6 +2372,16 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
% if not sys.platform.startswith('win'):
|
||||
settings_general_chmod: {
|
||||
rules: [
|
||||
{
|
||||
type: 'regExp[^([0-7]{4})$]',
|
||||
prompt: 'Please use only 4-digit octal (e.g.: 0775)'
|
||||
}
|
||||
]
|
||||
},
|
||||
% end
|
||||
settings_auth_password : {
|
||||
depends: 'settings_auth_username',
|
||||
rules : [
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
</div>
|
||||
<div class="ui bottom attached tab segment active" data-tab="tasks">
|
||||
<div class="content">
|
||||
<table class="ui very basic selectable table">
|
||||
<table class="ui very basic selectable table" id="tasks">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
|
@ -73,7 +73,7 @@
|
|||
</thead>
|
||||
<tbody>
|
||||
%for task in task_list:
|
||||
<tr>
|
||||
<tr id="{{task[3]}}">
|
||||
<td>{{task[0]}}</td>
|
||||
<td>{{task[1]}}</td>
|
||||
<td>{{task[2]}}</td>
|
||||
|
@ -346,7 +346,11 @@
|
|||
});
|
||||
|
||||
$('.execute').on('click', function(){
|
||||
window.location = '{{base_url}}execute/' + $(this).data("taskid");
|
||||
$(this).addClass('disabled');
|
||||
$(this).find('i:first').addClass('loading');
|
||||
$.ajax({
|
||||
url: '{{base_url}}execute/' + $(this).data("taskid")
|
||||
})
|
||||
});
|
||||
|
||||
$('a:not(.tabs), button:not(.cancel, #download_log), #restart').on('click', function(){
|
||||
|
|
|
@ -139,7 +139,7 @@
|
|||
|
||||
|
||||
<script>
|
||||
$('a, button').on('click', function(){
|
||||
$('a, button:not(#wanted_search_missing_subtitles_movies)').on('click', function(){
|
||||
$('#loader').addClass('active');
|
||||
});
|
||||
|
||||
|
@ -157,8 +157,11 @@
|
|||
});
|
||||
|
||||
$('#wanted_search_missing_subtitles_movies').on('click', function(){
|
||||
$('#loader_text').text("Searching for missing subtitles...");
|
||||
window.location = '{{base_url}}wanted_search_missing_subtitles';
|
||||
$(this).addClass('disabled');
|
||||
$(this).find('i:first').addClass('loading');
|
||||
$.ajax({
|
||||
url: '{{base_url}}wanted_search_missing_subtitles'
|
||||
})
|
||||
});
|
||||
|
||||
$('.get_subtitle').on('click', function(){
|
||||
|
|
|
@ -146,7 +146,7 @@
|
|||
|
||||
|
||||
<script>
|
||||
$('a, button').on('click', function(){
|
||||
$('a, button:not(#wanted_search_missing_subtitles)').on('click', function(){
|
||||
$('#loader').addClass('active');
|
||||
});
|
||||
|
||||
|
@ -164,8 +164,11 @@
|
|||
});
|
||||
|
||||
$('#wanted_search_missing_subtitles').on('click', function(){
|
||||
$('#loader_text').text("Searching for missing subtitles...");
|
||||
window.location = '{{base_url}}wanted_search_missing_subtitles';
|
||||
$(this).addClass('disabled');
|
||||
$(this).find('i:first').addClass('loading');
|
||||
$.ajax({
|
||||
url: '{{base_url}}wanted_search_missing_subtitles'
|
||||
})
|
||||
});
|
||||
|
||||
$('.get_subtitle').on('click', function(){
|
||||
|
|
|
@ -335,6 +335,82 @@
|
|||
Prev
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="ui dividing header">Subtitles options</div>
|
||||
<div class="twelve wide column">
|
||||
<div class="ui grid">
|
||||
|
||||
<div class="middle aligned row">
|
||||
<div class="right aligned four wide column">
|
||||
<label>Subtitle folder</label>
|
||||
</div>
|
||||
<div class="five wide column">
|
||||
<select name="settings_subfolder" id="settings_subfolder"
|
||||
class="ui fluid selection dropdown">
|
||||
<option value="current">Current</option>
|
||||
<option value="sub">sub</option>
|
||||
<option value="subs">subs</option>
|
||||
<option value="subtitle">subtitle</option>
|
||||
<option value="subtitles">subtitles</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="collapsed center aligned column">
|
||||
<div class="ui basic icon"
|
||||
data-tooltip='Choose folder where you want to store/read the subtitles'
|
||||
data-inverted="">
|
||||
<i class="help circle large icon"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="middle aligned row">
|
||||
<div class="right aligned four wide column">
|
||||
<label>Custom Subtitle folder</label>
|
||||
</div>
|
||||
<div class="five wide column">
|
||||
<div class='field'>
|
||||
<div class="ui fluid input">
|
||||
<input id="settings_subfolder_custom" name="settings_subfolder_custom"
|
||||
type="text" value="{{ settings.general.subfolder_custom }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="collapsed center aligned column">
|
||||
<div class="ui basic icon"
|
||||
data-tooltip='Choose your own folder for the subtitles'
|
||||
data-inverted="">
|
||||
<i class="help circle large icon"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="middle aligned row">
|
||||
<div class="right aligned four wide column">
|
||||
<label>Use embedded subtitles</label>
|
||||
</div>
|
||||
<div class="one wide column">
|
||||
<div id="settings_embedded" class="ui toggle checkbox"
|
||||
data-embedded={{ settings.general.getboolean('use_embedded_subs') }}>
|
||||
<input name="settings_general_embedded" type="checkbox">
|
||||
<label></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapsed column">
|
||||
<div class="collapsed center aligned column">
|
||||
<div class="ui basic icon"
|
||||
data-tooltip="Use embedded subtitles in media files when determining missing ones."
|
||||
data-inverted="">
|
||||
<i class="help circle large icon"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ui dividing header">Subtitles providers</div>
|
||||
<div class="twelve wide column">
|
||||
<div class="ui grid">
|
||||
|
@ -1502,6 +1578,11 @@ $(function() {
|
|||
} else {
|
||||
$("#radarr_ssl_div").checkbox('uncheck');
|
||||
}
|
||||
if ($('#settings_embedded').data("embedded") === "True") {
|
||||
$("#settings_embedded").checkbox('check');
|
||||
} else {
|
||||
$("#settings_embedded").checkbox('uncheck');
|
||||
}
|
||||
|
||||
if ($('#settings_addic7ed_random_agents').data("randomagents") === "True") {
|
||||
$("#settings_addic7ed_random_agents").checkbox('check');
|
||||
|
@ -1662,6 +1743,8 @@ $(function() {
|
|||
$('#settings_providers').dropdown('set selected',{{!enabled_providers}});
|
||||
$('#settings_languages').dropdown('clear');
|
||||
$('#settings_languages').dropdown('set selected',{{!enabled_languages}});
|
||||
$('#settings_subfolder').dropdown('clear');
|
||||
$('#settings_subfolder').dropdown('set selected', '{{!settings.general.subfolder}}');
|
||||
|
||||
$('#settings_providers').dropdown();
|
||||
$('#settings_languages').dropdown();
|
||||
|
|
Loading…
Reference in a new issue