mirror of
https://github.com/morpheus65535/bazarr.git
synced 2025-04-24 06:37:16 -04:00
Removed everything related to Sonarr, Radarr and path mapping.
This commit is contained in:
parent
3a944251da
commit
9b8f562078
74 changed files with 978 additions and 3510 deletions
|
@ -12,10 +12,6 @@ from pyga.entities import CustomVariable
|
|||
|
||||
from get_args import args
|
||||
from config import settings
|
||||
from utils import get_sonarr_info, get_radarr_info
|
||||
|
||||
sonarr_version = get_sonarr_info.version()
|
||||
radarr_version = get_radarr_info.version()
|
||||
|
||||
|
||||
def track_event(category=None, action=None, label=None):
|
||||
|
@ -49,15 +45,7 @@ def track_event(category=None, action=None, label=None):
|
|||
tracker.add_custom_variable(CustomVariable(index=1, name='BazarrVersion',
|
||||
value=os.environ["BAZARR_VERSION"].lstrip('v'), scope=1))
|
||||
tracker.add_custom_variable(CustomVariable(index=2, name='PythonVersion', value=platform.python_version(), scope=1))
|
||||
if settings.general.getboolean('use_sonarr'):
|
||||
tracker.add_custom_variable(CustomVariable(index=3, name='SonarrVersion', value=sonarr_version, scope=1))
|
||||
else:
|
||||
tracker.add_custom_variable(CustomVariable(index=3, name='SonarrVersion', value='unused', scope=1))
|
||||
if settings.general.getboolean('use_radarr'):
|
||||
tracker.add_custom_variable(CustomVariable(index=4, name='RadarrVersion', value=radarr_version, scope=1))
|
||||
else:
|
||||
tracker.add_custom_variable(CustomVariable(index=4, name='RadarrVersion', value='unused', scope=1))
|
||||
tracker.add_custom_variable(CustomVariable(index=5, name='OSVersion', value=platform.platform(), scope=1))
|
||||
tracker.add_custom_variable(CustomVariable(index=3, name='OSVersion', value=platform.platform(), scope=1))
|
||||
|
||||
try:
|
||||
tracker.track_event(event, session, visitor)
|
||||
|
|
401
bazarr/api.py
401
bazarr/api.py
File diff suppressed because it is too large
Load diff
|
@ -16,7 +16,7 @@ from config import settings
|
|||
|
||||
def check_releases():
|
||||
releases = []
|
||||
url_releases = 'https://api.github.com/repos/morpheus65535/Bazarr/releases?per_page=100'
|
||||
url_releases = 'https://api.github.com/repos/Bazarr/Bazarr2/releases?per_page=100'
|
||||
try:
|
||||
logging.debug('BAZARR getting releases from Github: {}'.format(url_releases))
|
||||
r = requests.get(url_releases, allow_redirects=True)
|
||||
|
|
263
bazarr/config.py
263
bazarr/config.py
|
@ -27,22 +27,19 @@ defaults = {
|
|||
'ip': '0.0.0.0',
|
||||
'port': '6767',
|
||||
'base_url': '',
|
||||
'path_mappings': '[]',
|
||||
'debug': 'False',
|
||||
'branch': 'master',
|
||||
'auto_update': 'True',
|
||||
'single_language': 'False',
|
||||
'minimum_score': '90',
|
||||
'use_scenename': 'True',
|
||||
'use_postprocessing': 'False',
|
||||
'postprocessing_cmd': '',
|
||||
'postprocessing_threshold': '90',
|
||||
'use_postprocessing_threshold': 'False',
|
||||
'postprocessing_threshold_movie': '70',
|
||||
'use_postprocessing_threshold_movie': 'False',
|
||||
'use_sonarr': 'False',
|
||||
'use_radarr': 'False',
|
||||
'path_mappings_movie': '[]',
|
||||
'use_series': 'False',
|
||||
'use_movies': 'False',
|
||||
'serie_default_enabled': 'False',
|
||||
'serie_default_profile': '',
|
||||
'movie_default_enabled': 'False',
|
||||
|
@ -78,33 +75,20 @@ defaults = {
|
|||
'username': '',
|
||||
'password': ''
|
||||
},
|
||||
'sonarr': {
|
||||
'ip': '127.0.0.1',
|
||||
'port': '8989',
|
||||
'base_url': '/',
|
||||
'ssl': 'False',
|
||||
'apikey': '',
|
||||
'series': {
|
||||
'full_update': 'Daily',
|
||||
'full_update_day': '6',
|
||||
'full_update_hour': '4',
|
||||
'only_monitored': 'False',
|
||||
'series_sync': '60',
|
||||
'episodes_sync': '60',
|
||||
'excluded_tags': '[]',
|
||||
'excluded_series_types': '[]',
|
||||
'use_ffprobe_cache': 'True'
|
||||
},
|
||||
'radarr': {
|
||||
'ip': '127.0.0.1',
|
||||
'port': '7878',
|
||||
'base_url': '/',
|
||||
'ssl': 'False',
|
||||
'apikey': '',
|
||||
'movies': {
|
||||
'full_update': 'Daily',
|
||||
'full_update_day': '6',
|
||||
'full_update_hour': '5',
|
||||
'only_monitored': 'False',
|
||||
'movies_sync': '60',
|
||||
'excluded_tags': '[]',
|
||||
'use_ffprobe_cache': 'True'
|
||||
},
|
||||
|
@ -116,6 +100,52 @@ defaults = {
|
|||
'password': '',
|
||||
'exclude': '["localhost","127.0.0.1"]'
|
||||
},
|
||||
'anticaptcha': {
|
||||
'anti_captcha_key': ''
|
||||
},
|
||||
'deathbycaptcha': {
|
||||
'username': '',
|
||||
'password': ''
|
||||
},
|
||||
'analytics': {
|
||||
'enabled': 'True'
|
||||
},
|
||||
'subsync': {
|
||||
'use_subsync': 'False',
|
||||
'use_subsync_threshold': 'False',
|
||||
'subsync_threshold': '90',
|
||||
'use_subsync_movie_threshold': 'False',
|
||||
'subsync_movie_threshold': '70',
|
||||
'debug': 'False'
|
||||
},
|
||||
'series_scores': {
|
||||
"hash": 359,
|
||||
"series": 180,
|
||||
"year": 90,
|
||||
"season": 30,
|
||||
"episode": 30,
|
||||
"release_group": 15,
|
||||
"source": 7,
|
||||
"audio_codec": 3,
|
||||
"resolution": 2,
|
||||
"video_codec": 2,
|
||||
"hearing_impaired": 1,
|
||||
"streaming_service": 0,
|
||||
"edition": 0,
|
||||
},
|
||||
'movie_scores': {
|
||||
"hash": 119,
|
||||
"title": 60,
|
||||
"year": 30,
|
||||
"release_group": 15,
|
||||
"source": 7,
|
||||
"audio_codec": 3,
|
||||
"resolution": 2,
|
||||
"video_codec": 2,
|
||||
"hearing_impaired": 1,
|
||||
"streaming_service": 0,
|
||||
"edition": 0,
|
||||
},
|
||||
'opensubtitles': {
|
||||
'username': '',
|
||||
'password': '',
|
||||
|
@ -155,13 +185,6 @@ defaults = {
|
|||
'assrt': {
|
||||
'token': ''
|
||||
},
|
||||
'anticaptcha': {
|
||||
'anti_captcha_key': ''
|
||||
},
|
||||
'deathbycaptcha': {
|
||||
'username': '',
|
||||
'password': ''
|
||||
},
|
||||
'napisy24': {
|
||||
'username': '',
|
||||
'password': ''
|
||||
|
@ -173,48 +196,9 @@ defaults = {
|
|||
'betaseries': {
|
||||
'token': ''
|
||||
},
|
||||
'analytics': {
|
||||
'enabled': 'True'
|
||||
},
|
||||
'titlovi': {
|
||||
'username': '',
|
||||
'password': ''
|
||||
},
|
||||
'subsync': {
|
||||
'use_subsync': 'False',
|
||||
'use_subsync_threshold': 'False',
|
||||
'subsync_threshold': '90',
|
||||
'use_subsync_movie_threshold': 'False',
|
||||
'subsync_movie_threshold': '70',
|
||||
'debug': 'False'
|
||||
},
|
||||
'series_scores': {
|
||||
"hash": 359,
|
||||
"series": 180,
|
||||
"year": 90,
|
||||
"season": 30,
|
||||
"episode": 30,
|
||||
"release_group": 15,
|
||||
"source": 7,
|
||||
"audio_codec": 3,
|
||||
"resolution": 2,
|
||||
"video_codec": 2,
|
||||
"hearing_impaired": 1,
|
||||
"streaming_service": 0,
|
||||
"edition": 0,
|
||||
},
|
||||
'movie_scores': {
|
||||
"hash": 119,
|
||||
"title": 60,
|
||||
"year": 30,
|
||||
"release_group": 15,
|
||||
"source": 7,
|
||||
"audio_codec": 3,
|
||||
"resolution": 2,
|
||||
"video_codec": 2,
|
||||
"hearing_impaired": 1,
|
||||
"streaming_service": 0,
|
||||
"edition": 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -235,22 +219,12 @@ array_keys = ['excluded_tags',
|
|||
'exclude',
|
||||
'subzero_mods',
|
||||
'excluded_series_types',
|
||||
'enabled_providers',
|
||||
'path_mappings',
|
||||
'path_mappings_movie']
|
||||
'enabled_providers']
|
||||
|
||||
str_keys = ['chmod']
|
||||
|
||||
empty_values = ['', 'None', 'null', 'undefined', None, []]
|
||||
|
||||
# Increase Sonarr and Radarr sync interval since we now use SignalR feed to update in real time
|
||||
if int(settings.sonarr.series_sync) < 15:
|
||||
settings.sonarr.series_sync = "60"
|
||||
if int(settings.sonarr.episodes_sync) < 15:
|
||||
settings.sonarr.episodes_sync = "60"
|
||||
if int(settings.radarr.movies_sync) < 15:
|
||||
settings.radarr.movies_sync = "60"
|
||||
|
||||
if os.path.exists(os.path.join(args.config_dir, 'config', 'config.ini')):
|
||||
with open(os.path.join(args.config_dir, 'config', 'config.ini'), 'w+') as handle:
|
||||
settings.write(handle)
|
||||
|
@ -304,13 +278,11 @@ def save_settings(settings_items):
|
|||
configure_debug = False
|
||||
configure_captcha = False
|
||||
update_schedule = False
|
||||
sonarr_changed = False
|
||||
radarr_changed = False
|
||||
update_path_map = False
|
||||
configure_proxy = False
|
||||
exclusion_updated = False
|
||||
sonarr_exclusion_updated = False
|
||||
radarr_exclusion_updated = False
|
||||
series_exclusion_updated = False
|
||||
movies_exclusion_updated = False
|
||||
|
||||
# Subzero Mods
|
||||
update_subzero = False
|
||||
|
@ -333,10 +305,6 @@ def save_settings(settings_items):
|
|||
if settings_keys[-1] in array_keys and value[0] in empty_values :
|
||||
value = []
|
||||
|
||||
# Handle path mappings settings since they are array in array
|
||||
if settings_keys[-1] in ['path_mappings', 'path_mappings_movie']:
|
||||
value = [v.split(',') for v in value]
|
||||
|
||||
if value == 'true':
|
||||
value = 'True'
|
||||
elif value == 'false':
|
||||
|
@ -353,41 +321,26 @@ def save_settings(settings_items):
|
|||
'settings-deathbycaptcha-username', 'settings-deathbycaptcha-password']:
|
||||
configure_captcha = True
|
||||
|
||||
if key in ['update_schedule', 'settings-general-use_sonarr', 'settings-general-use_radarr',
|
||||
'settings-general-auto_update', 'settings-general-upgrade_subs',
|
||||
'settings-sonarr-series_sync', 'settings-sonarr-episodes_sync', 'settings-radarr-movies_sync',
|
||||
'settings-sonarr-full_update', 'settings-sonarr-full_update_day', 'settings-sonarr-full_update_hour',
|
||||
'settings-radarr-full_update', 'settings-radarr-full_update_day', 'settings-radarr-full_update_hour',
|
||||
if key in ['update_schedule', 'settings-general-auto_update', 'settings-general-upgrade_subs',
|
||||
'settings-general-wanted_search_frequency', 'settings-general-wanted_search_frequency_movie',
|
||||
'settings-general-upgrade_frequency']:
|
||||
update_schedule = True
|
||||
|
||||
if key in ['settings-general-use_sonarr', 'settings-sonarr-ip', 'settings-sonarr-port',
|
||||
'settings-sonarr-base_url', 'settings-sonarr-ssl', 'settings-sonarr-apikey']:
|
||||
sonarr_changed = True
|
||||
|
||||
if key in ['settings-general-use_radarr', 'settings-radarr-ip', 'settings-radarr-port',
|
||||
'settings-radarr-base_url', 'settings-radarr-ssl', 'settings-radarr-apikey']:
|
||||
radarr_changed = True
|
||||
|
||||
if key in ['settings-general-path_mappings', 'settings-general-path_mappings_movie']:
|
||||
update_path_map = True
|
||||
|
||||
if key in ['settings-proxy-type', 'settings-proxy-url', 'settings-proxy-port', 'settings-proxy-username',
|
||||
'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']:
|
||||
if key in ['settings-series-excluded_tags', 'settings-series-only_monitored',
|
||||
'settings-series-excluded_series_types', 'settings.movies.excluded_tags',
|
||||
'settings-movies-only_monitored']:
|
||||
exclusion_updated = True
|
||||
|
||||
if key in ['settings-sonarr-excluded_tags', 'settings-sonarr-only_monitored',
|
||||
'settings-sonarr-excluded_series_types']:
|
||||
sonarr_exclusion_updated = True
|
||||
if key in ['settings-series-excluded_tags', 'settings-series-only_monitored',
|
||||
'settings-series-excluded_series_types']:
|
||||
series_exclusion_updated = True
|
||||
|
||||
if key in ['settings.radarr.excluded_tags', 'settings-radarr-only_monitored']:
|
||||
radarr_exclusion_updated = True
|
||||
if key in ['settings-movies-excluded_tags', 'settings-movies-only_monitored']:
|
||||
movies_exclusion_updated = True
|
||||
|
||||
if key == 'settings-addic7ed-username':
|
||||
if key != settings.addic7ed.username:
|
||||
|
@ -456,106 +409,18 @@ def save_settings(settings_items):
|
|||
from api import scheduler
|
||||
scheduler.update_configurable_tasks()
|
||||
|
||||
if sonarr_changed:
|
||||
from signalr_client import sonarr_signalr_client
|
||||
try:
|
||||
sonarr_signalr_client.restart()
|
||||
except:
|
||||
pass
|
||||
|
||||
if radarr_changed:
|
||||
from signalr_client import radarr_signalr_client
|
||||
try:
|
||||
radarr_signalr_client.restart()
|
||||
except:
|
||||
pass
|
||||
|
||||
if update_path_map:
|
||||
from helper import path_mappings
|
||||
path_mappings.update()
|
||||
|
||||
if configure_proxy:
|
||||
configure_proxy_func()
|
||||
|
||||
if exclusion_updated:
|
||||
from event_handler import event_stream
|
||||
event_stream(type='badges')
|
||||
if sonarr_exclusion_updated:
|
||||
if series_exclusion_updated:
|
||||
event_stream(type='reset-episode-wanted')
|
||||
if radarr_exclusion_updated:
|
||||
if movies_exclusion_updated:
|
||||
event_stream(type='reset-movie-wanted')
|
||||
|
||||
|
||||
def url_sonarr():
|
||||
if settings.sonarr.getboolean('ssl'):
|
||||
protocol_sonarr = "https"
|
||||
else:
|
||||
protocol_sonarr = "http"
|
||||
|
||||
if settings.sonarr.base_url == '':
|
||||
settings.sonarr.base_url = "/"
|
||||
if not settings.sonarr.base_url.startswith("/"):
|
||||
settings.sonarr.base_url = "/" + settings.sonarr.base_url
|
||||
if settings.sonarr.base_url.endswith("/"):
|
||||
settings.sonarr.base_url = settings.sonarr.base_url[:-1]
|
||||
|
||||
if settings.sonarr.port in empty_values:
|
||||
port = ""
|
||||
else:
|
||||
port = f":{settings.sonarr.port}"
|
||||
|
||||
return f"{protocol_sonarr}://{settings.sonarr.ip}{port}{settings.sonarr.base_url}"
|
||||
|
||||
|
||||
def url_sonarr_short():
|
||||
if settings.sonarr.getboolean('ssl'):
|
||||
protocol_sonarr = "https"
|
||||
else:
|
||||
protocol_sonarr = "http"
|
||||
|
||||
if settings.sonarr.port in empty_values:
|
||||
port = ""
|
||||
else:
|
||||
port = f":{settings.sonarr.port}"
|
||||
|
||||
return f"{protocol_sonarr}://{settings.sonarr.ip}{port}"
|
||||
|
||||
|
||||
def url_radarr():
|
||||
if settings.radarr.getboolean('ssl'):
|
||||
protocol_radarr = "https"
|
||||
else:
|
||||
protocol_radarr = "http"
|
||||
|
||||
if settings.radarr.base_url == '':
|
||||
settings.radarr.base_url = "/"
|
||||
if not settings.radarr.base_url.startswith("/"):
|
||||
settings.radarr.base_url = "/" + settings.radarr.base_url
|
||||
if settings.radarr.base_url.endswith("/"):
|
||||
settings.radarr.base_url = settings.radarr.base_url[:-1]
|
||||
|
||||
if settings.radarr.port in empty_values:
|
||||
port = ""
|
||||
else:
|
||||
port = f":{settings.radarr.port}"
|
||||
|
||||
return f"{protocol_radarr}://{settings.radarr.ip}{port}{settings.radarr.base_url}"
|
||||
|
||||
|
||||
def url_radarr_short():
|
||||
if settings.radarr.getboolean('ssl'):
|
||||
protocol_radarr = "https"
|
||||
else:
|
||||
protocol_radarr = "http"
|
||||
|
||||
if settings.radarr.port in empty_values:
|
||||
port = ""
|
||||
else:
|
||||
port = f":{settings.radarr.port}"
|
||||
|
||||
return f"{protocol_radarr}://{settings.radarr.ip}{port}"
|
||||
|
||||
|
||||
def get_array_from(property):
|
||||
if property:
|
||||
if '[' in property:
|
||||
|
|
|
@ -8,9 +8,7 @@ from peewee import *
|
|||
from playhouse.sqliteq import SqliteQueueDatabase
|
||||
from playhouse.shortcuts import model_to_dict
|
||||
from playhouse.migrate import *
|
||||
from playhouse.sqlite_ext import RowIDField
|
||||
|
||||
from helper import path_mappings
|
||||
from config import settings, get_array_from
|
||||
from get_args import args
|
||||
|
||||
|
@ -47,8 +45,8 @@ class System(BaseModel):
|
|||
class TableBlacklist(BaseModel):
|
||||
language = TextField(null=True)
|
||||
provider = TextField(null=True)
|
||||
sonarr_episode_id = IntegerField(null=True)
|
||||
sonarr_series_id = IntegerField(null=True)
|
||||
episode_id = IntegerField(null=True)
|
||||
series_id = IntegerField(null=True)
|
||||
subs_id = TextField(null=True)
|
||||
timestamp = IntegerField(null=True)
|
||||
|
||||
|
@ -60,7 +58,7 @@ class TableBlacklist(BaseModel):
|
|||
class TableBlacklistMovie(BaseModel):
|
||||
language = TextField(null=True)
|
||||
provider = TextField(null=True)
|
||||
radarr_id = IntegerField(null=True)
|
||||
movie_id = IntegerField(null=True)
|
||||
subs_id = TextField(null=True)
|
||||
timestamp = IntegerField(null=True)
|
||||
|
||||
|
@ -70,7 +68,6 @@ class TableBlacklistMovie(BaseModel):
|
|||
|
||||
|
||||
class TableEpisodes(BaseModel):
|
||||
rowid = RowIDField()
|
||||
audio_codec = TextField(null=True)
|
||||
audio_language = TextField(null=True)
|
||||
episode = IntegerField()
|
||||
|
@ -83,17 +80,15 @@ class TableEpisodes(BaseModel):
|
|||
monitored = TextField(null=True)
|
||||
path = TextField()
|
||||
resolution = TextField(null=True)
|
||||
scene_name = TextField(null=True)
|
||||
season = IntegerField()
|
||||
sonarrEpisodeId = IntegerField(unique=True)
|
||||
sonarrSeriesId = IntegerField()
|
||||
episodeId = AutoField()
|
||||
seriesId = IntegerField()
|
||||
subtitles = TextField(null=True)
|
||||
title = TextField()
|
||||
video_codec = TextField(null=True)
|
||||
|
||||
class Meta:
|
||||
table_name = 'table_episodes'
|
||||
primary_key = False
|
||||
|
||||
|
||||
class TableHistory(BaseModel):
|
||||
|
@ -103,8 +98,8 @@ class TableHistory(BaseModel):
|
|||
language = TextField(null=True)
|
||||
provider = TextField(null=True)
|
||||
score = TextField(null=True)
|
||||
sonarrEpisodeId = IntegerField()
|
||||
sonarrSeriesId = IntegerField()
|
||||
episodeId = IntegerField()
|
||||
seriesId = IntegerField()
|
||||
subs_id = TextField(null=True)
|
||||
subtitles_path = TextField(null=True)
|
||||
timestamp = IntegerField()
|
||||
|
@ -120,7 +115,7 @@ class TableHistoryMovie(BaseModel):
|
|||
id = AutoField()
|
||||
language = TextField(null=True)
|
||||
provider = TextField(null=True)
|
||||
radarrId = IntegerField()
|
||||
movieId = IntegerField()
|
||||
score = TextField(null=True)
|
||||
subs_id = TextField(null=True)
|
||||
subtitles_path = TextField(null=True)
|
||||
|
@ -142,7 +137,6 @@ class TableLanguagesProfiles(BaseModel):
|
|||
|
||||
|
||||
class TableMovies(BaseModel):
|
||||
rowid = RowIDField()
|
||||
alternativeTitles = TextField(null=True)
|
||||
audio_codec = TextField(null=True)
|
||||
audio_language = TextField(null=True)
|
||||
|
@ -159,9 +153,8 @@ class TableMovies(BaseModel):
|
|||
path = TextField(unique=True)
|
||||
poster = TextField(null=True)
|
||||
profileId = IntegerField(null=True)
|
||||
radarrId = IntegerField(unique=True)
|
||||
movieId = AutoField()
|
||||
resolution = TextField(null=True)
|
||||
sceneName = TextField(null=True)
|
||||
sortTitle = TextField(null=True)
|
||||
subtitles = TextField(null=True)
|
||||
tags = TextField(null=True)
|
||||
|
@ -215,11 +208,11 @@ class TableShows(BaseModel):
|
|||
poster = TextField(null=True)
|
||||
profileId = IntegerField(null=True)
|
||||
seriesType = TextField(null=True)
|
||||
sonarrSeriesId = IntegerField(unique=True)
|
||||
seriesId = AutoField()
|
||||
sortTitle = TextField(null=True)
|
||||
tags = TextField(null=True)
|
||||
title = TextField()
|
||||
tvdbId = AutoField()
|
||||
tvdbId = TextField(unique=True)
|
||||
year = TextField(null=True)
|
||||
|
||||
class Meta:
|
||||
|
@ -290,99 +283,34 @@ def init_db():
|
|||
|
||||
|
||||
def migrate_db():
|
||||
migrate(
|
||||
migrator.add_column('table_shows', 'year', TextField(null=True)),
|
||||
migrator.add_column('table_shows', 'alternateTitles', TextField(null=True)),
|
||||
migrator.add_column('table_shows', 'tags', TextField(default='[]', null=True)),
|
||||
migrator.add_column('table_shows', 'seriesType', TextField(default='""', null=True)),
|
||||
migrator.add_column('table_shows', 'imdbId', TextField(default='""', null=True)),
|
||||
migrator.add_column('table_shows', 'profileId', IntegerField(null=True)),
|
||||
migrator.add_column('table_episodes', 'format', TextField(null=True)),
|
||||
migrator.add_column('table_episodes', 'resolution', TextField(null=True)),
|
||||
migrator.add_column('table_episodes', 'video_codec', TextField(null=True)),
|
||||
migrator.add_column('table_episodes', 'audio_codec', TextField(null=True)),
|
||||
migrator.add_column('table_episodes', 'episode_file_id', IntegerField(null=True)),
|
||||
migrator.add_column('table_episodes', 'audio_language', TextField(null=True)),
|
||||
migrator.add_column('table_episodes', 'file_size', IntegerField(default=0, null=True)),
|
||||
migrator.add_column('table_episodes', 'ffprobe_cache', BlobField(null=True)),
|
||||
migrator.add_column('table_movies', 'sortTitle', TextField(null=True)),
|
||||
migrator.add_column('table_movies', 'year', TextField(null=True)),
|
||||
migrator.add_column('table_movies', 'alternativeTitles', TextField(null=True)),
|
||||
migrator.add_column('table_movies', 'format', TextField(null=True)),
|
||||
migrator.add_column('table_movies', 'resolution', TextField(null=True)),
|
||||
migrator.add_column('table_movies', 'video_codec', TextField(null=True)),
|
||||
migrator.add_column('table_movies', 'audio_codec', TextField(null=True)),
|
||||
migrator.add_column('table_movies', 'imdbId', TextField(null=True)),
|
||||
migrator.add_column('table_movies', 'movie_file_id', IntegerField(null=True)),
|
||||
migrator.add_column('table_movies', 'tags', TextField(default='[]', null=True)),
|
||||
migrator.add_column('table_movies', 'profileId', IntegerField(null=True)),
|
||||
migrator.add_column('table_movies', 'file_size', IntegerField(default=0, null=True)),
|
||||
migrator.add_column('table_movies', 'ffprobe_cache', BlobField(null=True)),
|
||||
migrator.add_column('table_history', 'video_path', TextField(null=True)),
|
||||
migrator.add_column('table_history', 'language', TextField(null=True)),
|
||||
migrator.add_column('table_history', 'provider', TextField(null=True)),
|
||||
migrator.add_column('table_history', 'score', TextField(null=True)),
|
||||
migrator.add_column('table_history', 'subs_id', TextField(null=True)),
|
||||
migrator.add_column('table_history', 'subtitles_path', TextField(null=True)),
|
||||
migrator.add_column('table_history_movie', 'video_path', TextField(null=True)),
|
||||
migrator.add_column('table_history_movie', 'language', TextField(null=True)),
|
||||
migrator.add_column('table_history_movie', 'provider', TextField(null=True)),
|
||||
migrator.add_column('table_history_movie', 'score', TextField(null=True)),
|
||||
migrator.add_column('table_history_movie', 'subs_id', TextField(null=True)),
|
||||
migrator.add_column('table_history_movie', 'subtitles_path', TextField(null=True))
|
||||
)
|
||||
|
||||
|
||||
class SqliteDictPathMapper:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def path_replace(values_dict):
|
||||
if type(values_dict) is list:
|
||||
for item in values_dict:
|
||||
item['path'] = path_mappings.path_replace(item['path'])
|
||||
elif type(values_dict) is dict:
|
||||
values_dict['path'] = path_mappings.path_replace(values_dict['path'])
|
||||
else:
|
||||
return path_mappings.path_replace(values_dict)
|
||||
|
||||
@staticmethod
|
||||
def path_replace_movie(values_dict):
|
||||
if type(values_dict) is list:
|
||||
for item in values_dict:
|
||||
item['path'] = path_mappings.path_replace_movie(item['path'])
|
||||
elif type(values_dict) is dict:
|
||||
values_dict['path'] = path_mappings.path_replace_movie(values_dict['path'])
|
||||
else:
|
||||
return path_mappings.path_replace_movie(values_dict)
|
||||
|
||||
|
||||
dict_mapper = SqliteDictPathMapper()
|
||||
pass
|
||||
# migrate(
|
||||
# migrator.add_column('table_shows', 'year', TextField(null=True))
|
||||
# )
|
||||
|
||||
|
||||
def get_exclusion_clause(exclusion_type):
|
||||
where_clause = []
|
||||
if exclusion_type == 'series':
|
||||
tagsList = ast.literal_eval(settings.sonarr.excluded_tags)
|
||||
tagsList = ast.literal_eval(settings.series.excluded_tags)
|
||||
for tag in tagsList:
|
||||
where_clause.append(~(TableShows.tags.contains("\'"+tag+"\'")))
|
||||
else:
|
||||
tagsList = ast.literal_eval(settings.radarr.excluded_tags)
|
||||
tagsList = ast.literal_eval(settings.movies.excluded_tags)
|
||||
for tag in tagsList:
|
||||
where_clause.append(~(TableMovies.tags.contains("\'"+tag+"\'")))
|
||||
|
||||
if exclusion_type == 'series':
|
||||
monitoredOnly = settings.sonarr.getboolean('only_monitored')
|
||||
monitoredOnly = settings.series.getboolean('only_monitored')
|
||||
if monitoredOnly:
|
||||
where_clause.append((TableEpisodes.monitored == 'True'))
|
||||
else:
|
||||
monitoredOnly = settings.radarr.getboolean('only_monitored')
|
||||
monitoredOnly = settings.movies.getboolean('only_monitored')
|
||||
if monitoredOnly:
|
||||
where_clause.append((TableMovies.monitored == 'True'))
|
||||
|
||||
if exclusion_type == 'series':
|
||||
typesList = get_array_from(settings.sonarr.excluded_series_types)
|
||||
typesList = get_array_from(settings.series.excluded_series_types)
|
||||
for item in typesList:
|
||||
where_clause.append((TableShows.seriesType != item))
|
||||
|
||||
|
@ -475,11 +403,11 @@ def get_audio_profile_languages(series_id=None, episode_id=None, movie_id=None):
|
|||
audio_languages = []
|
||||
|
||||
if series_id:
|
||||
audio_languages_list_str = TableShows.get(TableShows.sonarrSeriesId == series_id).audio_language
|
||||
audio_languages_list_str = TableShows.get(TableShows.seriesId == series_id).audio_language
|
||||
elif episode_id:
|
||||
audio_languages_list_str = TableEpisodes.get(TableEpisodes.sonarrEpisodeId == episode_id).audio_language
|
||||
audio_languages_list_str = TableEpisodes.get(TableEpisodes.episodeId == episode_id).audio_language
|
||||
elif movie_id:
|
||||
audio_languages_list_str = TableMovies.get(TableMovies.radarrId == movie_id).audio_language
|
||||
audio_languages_list_str = TableMovies.get(TableMovies.movieId == movie_id).audio_language
|
||||
else:
|
||||
return audio_languages
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ from enzyme.exceptions import MalformedMKVError
|
|||
from enzyme.exceptions import MalformedMKVError
|
||||
from custom_lang import CustomLanguage
|
||||
from database import TableEpisodes, TableMovies
|
||||
from helper import path_mappings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -78,12 +77,12 @@ def parse_video_metadata(file, file_size, episode_file_id=None, movie_file_id=No
|
|||
# Get the actual cache value form database
|
||||
if episode_file_id:
|
||||
cache_key = TableEpisodes.select(TableEpisodes.ffprobe_cache)\
|
||||
.where(TableEpisodes.path == path_mappings.path_replace_reverse(file))\
|
||||
.where(TableEpisodes.path == file)\
|
||||
.dicts()\
|
||||
.get()
|
||||
elif movie_file_id:
|
||||
cache_key = TableMovies.select(TableMovies.ffprobe_cache)\
|
||||
.where(TableMovies.path == path_mappings.path_replace_reverse_movie(file))\
|
||||
.where(TableMovies.path == file)\
|
||||
.dicts()\
|
||||
.get()
|
||||
else:
|
||||
|
@ -126,10 +125,10 @@ def parse_video_metadata(file, file_size, episode_file_id=None, movie_file_id=No
|
|||
# we write to db the result and return the newly cached ffprobe dict
|
||||
if episode_file_id:
|
||||
TableEpisodes.update({TableEpisodes.ffprobe_cache: pickle.dumps(data, pickle.HIGHEST_PROTOCOL)})\
|
||||
.where(TableEpisodes.path == path_mappings.path_replace_reverse(file))\
|
||||
.where(TableEpisodes.path == file)\
|
||||
.execute()
|
||||
elif movie_file_id:
|
||||
TableMovies.update({TableEpisodes.ffprobe_cache: pickle.dumps(data, pickle.HIGHEST_PROTOCOL)})\
|
||||
.where(TableMovies.path == path_mappings.path_replace_reverse_movie(file))\
|
||||
.where(TableMovies.path == file)\
|
||||
.execute()
|
||||
return data
|
||||
|
|
|
@ -5,8 +5,7 @@ import requests
|
|||
import logging
|
||||
import string
|
||||
|
||||
from config import settings, url_sonarr, url_radarr
|
||||
from utils import get_sonarr_info, get_radarr_info
|
||||
from config import settings
|
||||
|
||||
headers = {"User-Agent": os.environ["SZ_USER_AGENT"]}
|
||||
|
||||
|
@ -43,64 +42,3 @@ def browse_bazarr_filesystem(path='#'):
|
|||
result.update({'parent': parent})
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def browse_sonarr_filesystem(path='#'):
|
||||
if path == '#':
|
||||
path = ''
|
||||
if get_sonarr_info.is_legacy():
|
||||
url_sonarr_api_filesystem = url_sonarr() + "/api/filesystem?path=" + path + \
|
||||
"&allowFoldersWithoutTrailingSlashes=true&includeFiles=false&apikey=" + \
|
||||
settings.sonarr.apikey
|
||||
else:
|
||||
url_sonarr_api_filesystem = url_sonarr() + "/api/v3/filesystem?path=" + path + \
|
||||
"&allowFoldersWithoutTrailingSlashes=true&includeFiles=false&apikey=" + \
|
||||
settings.sonarr.apikey
|
||||
try:
|
||||
r = requests.get(url_sonarr_api_filesystem, timeout=60, verify=False, headers=headers)
|
||||
r.raise_for_status()
|
||||
except requests.exceptions.HTTPError:
|
||||
logging.exception("BAZARR Error trying to get series from Sonarr. Http error.")
|
||||
return
|
||||
except requests.exceptions.ConnectionError:
|
||||
logging.exception("BAZARR Error trying to get series from Sonarr. Connection Error.")
|
||||
return
|
||||
except requests.exceptions.Timeout:
|
||||
logging.exception("BAZARR Error trying to get series from Sonarr. Timeout Error.")
|
||||
return
|
||||
except requests.exceptions.RequestException:
|
||||
logging.exception("BAZARR Error trying to get series from Sonarr.")
|
||||
return
|
||||
|
||||
return r.json()
|
||||
|
||||
|
||||
def browse_radarr_filesystem(path='#'):
|
||||
if path == '#':
|
||||
path = ''
|
||||
|
||||
if get_radarr_info.is_legacy():
|
||||
url_radarr_api_filesystem = url_radarr() + "/api/filesystem?path=" + path + \
|
||||
"&allowFoldersWithoutTrailingSlashes=true&includeFiles=false&apikey=" + \
|
||||
settings.radarr.apikey
|
||||
else:
|
||||
url_radarr_api_filesystem = url_radarr() + "/api/v3/filesystem?path=" + path + \
|
||||
"&allowFoldersWithoutTrailingSlashes=true&includeFiles=false&apikey=" + \
|
||||
settings.radarr.apikey
|
||||
try:
|
||||
r = requests.get(url_radarr_api_filesystem, timeout=60, verify=False, headers=headers)
|
||||
r.raise_for_status()
|
||||
except requests.exceptions.HTTPError:
|
||||
logging.exception("BAZARR Error trying to get series from Radarr. Http error.")
|
||||
return
|
||||
except requests.exceptions.ConnectionError:
|
||||
logging.exception("BAZARR Error trying to get series from Radarr. Connection Error.")
|
||||
return
|
||||
except requests.exceptions.Timeout:
|
||||
logging.exception("BAZARR Error trying to get series from Radarr. Timeout Error.")
|
||||
return
|
||||
except requests.exceptions.RequestException:
|
||||
logging.exception("BAZARR Error trying to get series from Radarr.")
|
||||
return
|
||||
|
||||
return r.json()
|
||||
|
|
|
@ -1,417 +1,10 @@
|
|||
# coding=utf-8
|
||||
|
||||
import os
|
||||
import requests
|
||||
import logging
|
||||
from gevent import sleep
|
||||
from peewee import DoesNotExist
|
||||
|
||||
from database import get_exclusion_clause, TableEpisodes, TableShows
|
||||
from config import settings, url_sonarr
|
||||
from helper import path_mappings
|
||||
from list_subtitles import store_subtitles, series_full_scan_subtitles
|
||||
from get_subtitle import episode_download_subtitles
|
||||
from event_handler import event_stream, show_progress, hide_progress
|
||||
from utils import get_sonarr_info
|
||||
|
||||
headers = {"User-Agent": os.environ["SZ_USER_AGENT"]}
|
||||
from list_subtitles import series_full_scan_subtitles
|
||||
|
||||
|
||||
def update_all_episodes():
|
||||
series_full_scan_subtitles()
|
||||
logging.info('BAZARR All existing episode subtitles indexed from disk.')
|
||||
|
||||
|
||||
def sync_episodes(series_id=None, send_event=True):
|
||||
logging.debug('BAZARR Starting episodes sync from Sonarr.')
|
||||
apikey_sonarr = settings.sonarr.apikey
|
||||
|
||||
# Get current episodes id in DB
|
||||
current_episodes_db = TableEpisodes.select(TableEpisodes.sonarrEpisodeId,
|
||||
TableEpisodes.path,
|
||||
TableEpisodes.sonarrSeriesId)\
|
||||
.where((TableEpisodes.sonarrSeriesId == series_id) if series_id else None)\
|
||||
.dicts()
|
||||
|
||||
current_episodes_db_list = [x['sonarrEpisodeId'] for x in current_episodes_db]
|
||||
|
||||
current_episodes_sonarr = []
|
||||
episodes_to_update = []
|
||||
episodes_to_add = []
|
||||
altered_episodes = []
|
||||
|
||||
# Get sonarrId for each series from database
|
||||
seriesIdList = get_series_from_sonarr_api(series_id=series_id, url=url_sonarr(), apikey_sonarr=apikey_sonarr,)
|
||||
|
||||
series_count = len(seriesIdList)
|
||||
for i, seriesId in enumerate(seriesIdList):
|
||||
sleep()
|
||||
if send_event:
|
||||
show_progress(id='episodes_progress',
|
||||
header='Syncing episodes...',
|
||||
name=seriesId['title'],
|
||||
value=i,
|
||||
count=series_count)
|
||||
|
||||
# Get episodes data for a series from Sonarr
|
||||
episodes = get_episodes_from_sonarr_api(url=url_sonarr(), apikey_sonarr=apikey_sonarr,
|
||||
series_id=seriesId['sonarrSeriesId'])
|
||||
if not episodes:
|
||||
continue
|
||||
else:
|
||||
# For Sonarr v3, we need to update episodes to integrate the episodeFile API endpoint results
|
||||
if not get_sonarr_info.is_legacy():
|
||||
episodeFiles = get_episodesFiles_from_sonarr_api(url=url_sonarr(), apikey_sonarr=apikey_sonarr,
|
||||
series_id=seriesId['sonarrSeriesId'])
|
||||
for episode in episodes:
|
||||
if episode['hasFile']:
|
||||
item = [x for x in episodeFiles if x['id'] == episode['episodeFileId']]
|
||||
if item:
|
||||
episode['episodeFile'] = item[0]
|
||||
|
||||
for episode in episodes:
|
||||
sleep()
|
||||
if 'hasFile' in episode:
|
||||
if episode['hasFile'] is True:
|
||||
if 'episodeFile' in episode:
|
||||
if episode['episodeFile']['size'] > 20480:
|
||||
# Add episodes in sonarr to current episode list
|
||||
current_episodes_sonarr.append(episode['id'])
|
||||
|
||||
# Parse episode data
|
||||
if episode['id'] in current_episodes_db_list:
|
||||
episodes_to_update.append(episodeParser(episode))
|
||||
else:
|
||||
episodes_to_add.append(episodeParser(episode))
|
||||
|
||||
if send_event:
|
||||
hide_progress(id='episodes_progress')
|
||||
|
||||
# Remove old episodes from DB
|
||||
removed_episodes = list(set(current_episodes_db_list) - set(current_episodes_sonarr))
|
||||
|
||||
for removed_episode in removed_episodes:
|
||||
sleep()
|
||||
episode_to_delete = TableEpisodes.select(TableEpisodes.sonarrSeriesId, TableEpisodes.sonarrEpisodeId)\
|
||||
.where(TableEpisodes.sonarrEpisodeId == removed_episode)\
|
||||
.dicts()\
|
||||
.get()
|
||||
TableEpisodes.delete().where(TableEpisodes.sonarrEpisodeId == removed_episode).execute()
|
||||
if send_event:
|
||||
event_stream(type='episode', action='delete', payload=episode_to_delete['sonarrEpisodeId'])
|
||||
|
||||
# Update existing episodes in DB
|
||||
episode_in_db_list = []
|
||||
episodes_in_db = TableEpisodes.select(TableEpisodes.sonarrSeriesId,
|
||||
TableEpisodes.sonarrEpisodeId,
|
||||
TableEpisodes.title,
|
||||
TableEpisodes.path,
|
||||
TableEpisodes.season,
|
||||
TableEpisodes.episode,
|
||||
TableEpisodes.scene_name,
|
||||
TableEpisodes.monitored,
|
||||
TableEpisodes.format,
|
||||
TableEpisodes.resolution,
|
||||
TableEpisodes.video_codec,
|
||||
TableEpisodes.audio_codec,
|
||||
TableEpisodes.episode_file_id,
|
||||
TableEpisodes.audio_language,
|
||||
TableEpisodes.file_size).dicts()
|
||||
|
||||
for item in episodes_in_db:
|
||||
episode_in_db_list.append(item)
|
||||
|
||||
episodes_to_update_list = [i for i in episodes_to_update if i not in episode_in_db_list]
|
||||
|
||||
for updated_episode in episodes_to_update_list:
|
||||
sleep()
|
||||
TableEpisodes.update(updated_episode).where(TableEpisodes.sonarrEpisodeId ==
|
||||
updated_episode['sonarrEpisodeId']).execute()
|
||||
altered_episodes.append([updated_episode['sonarrEpisodeId'],
|
||||
updated_episode['path'],
|
||||
updated_episode['sonarrSeriesId']])
|
||||
|
||||
# Insert new episodes in DB
|
||||
for added_episode in episodes_to_add:
|
||||
sleep()
|
||||
result = TableEpisodes.insert(added_episode).on_conflict(action='IGNORE').execute()
|
||||
if result > 0:
|
||||
altered_episodes.append([added_episode['sonarrEpisodeId'],
|
||||
added_episode['path'],
|
||||
added_episode['monitored']])
|
||||
if send_event:
|
||||
event_stream(type='episode', payload=added_episode['sonarrEpisodeId'])
|
||||
else:
|
||||
logging.debug('BAZARR unable to insert this episode into the database:{}'.format(
|
||||
path_mappings.path_replace(added_episode['path'])))
|
||||
|
||||
# Store subtitles for added or modified episodes
|
||||
for i, altered_episode in enumerate(altered_episodes, 1):
|
||||
sleep()
|
||||
store_subtitles(altered_episode[1], path_mappings.path_replace(altered_episode[1]))
|
||||
|
||||
logging.debug('BAZARR All episodes synced from Sonarr into database.')
|
||||
|
||||
|
||||
def sync_one_episode(episode_id):
|
||||
logging.debug('BAZARR syncing this specific episode from Sonarr: {}'.format(episode_id))
|
||||
url = url_sonarr()
|
||||
apikey_sonarr = settings.sonarr.apikey
|
||||
|
||||
# Check if there's a row in database for this episode ID
|
||||
try:
|
||||
existing_episode = TableEpisodes.select(TableEpisodes.path, TableEpisodes.episode_file_id)\
|
||||
.where(TableEpisodes.sonarrEpisodeId == episode_id)\
|
||||
.dicts()\
|
||||
.get()
|
||||
except DoesNotExist:
|
||||
existing_episode = None
|
||||
|
||||
try:
|
||||
# Get episode data from sonarr api
|
||||
episode = None
|
||||
episode_data = get_episodes_from_sonarr_api(url=url, apikey_sonarr=apikey_sonarr,
|
||||
episode_id=episode_id)
|
||||
if not episode_data:
|
||||
return
|
||||
|
||||
else:
|
||||
# For Sonarr v3, we need to update episodes to integrate the episodeFile API endpoint results
|
||||
if not get_sonarr_info.is_legacy() and existing_episode and episode_data['hasFile']:
|
||||
episode_data['episodeFile'] = \
|
||||
get_episodesFiles_from_sonarr_api(url=url, apikey_sonarr=apikey_sonarr,
|
||||
episode_file_id=existing_episode['episode_file_id'])
|
||||
episode = episodeParser(episode_data)
|
||||
except Exception:
|
||||
logging.debug('BAZARR cannot get episode returned by SignalR feed from Sonarr API.')
|
||||
return
|
||||
|
||||
# Drop useless events
|
||||
if not episode and not existing_episode:
|
||||
return
|
||||
|
||||
# Remove episode from DB
|
||||
if not episode and existing_episode:
|
||||
TableEpisodes.delete().where(TableEpisodes.sonarrEpisodeId == episode_id).execute()
|
||||
event_stream(type='episode', action='delete', payload=int(episode_id))
|
||||
logging.debug('BAZARR deleted this episode from the database:{}'.format(path_mappings.path_replace(
|
||||
existing_episode['path'])))
|
||||
return
|
||||
|
||||
# Update existing episodes in DB
|
||||
elif episode and existing_episode:
|
||||
TableEpisodes.update(episode).where(TableEpisodes.sonarrEpisodeId == episode_id).execute()
|
||||
event_stream(type='episode', action='update', payload=int(episode_id))
|
||||
logging.debug('BAZARR updated this episode into the database:{}'.format(path_mappings.path_replace(
|
||||
episode['path'])))
|
||||
|
||||
# Insert new episodes in DB
|
||||
elif episode and not existing_episode:
|
||||
TableEpisodes.insert(episode).on_conflict(action='IGNORE').execute()
|
||||
event_stream(type='episode', action='update', payload=int(episode_id))
|
||||
logging.debug('BAZARR inserted this episode into the database:{}'.format(path_mappings.path_replace(
|
||||
episode['path'])))
|
||||
|
||||
# Storing existing subtitles
|
||||
logging.debug('BAZARR storing subtitles for this episode: {}'.format(path_mappings.path_replace(
|
||||
episode['path'])))
|
||||
store_subtitles(episode['path'], path_mappings.path_replace(episode['path']))
|
||||
|
||||
# Downloading missing subtitles
|
||||
logging.debug('BAZARR downloading missing subtitles for this episode: {}'.format(path_mappings.path_replace(
|
||||
episode['path'])))
|
||||
episode_download_subtitles(episode_id)
|
||||
|
||||
|
||||
def SonarrFormatAudioCodec(audio_codec):
|
||||
if audio_codec == 'AC-3':
|
||||
return 'AC3'
|
||||
if audio_codec == 'E-AC-3':
|
||||
return 'EAC3'
|
||||
if audio_codec == 'MPEG Audio':
|
||||
return 'MP3'
|
||||
|
||||
return audio_codec
|
||||
|
||||
|
||||
def SonarrFormatVideoCodec(video_codec):
|
||||
if video_codec == 'x264' or video_codec == 'AVC':
|
||||
return 'h264'
|
||||
elif video_codec == 'x265' or video_codec == 'HEVC':
|
||||
return 'h265'
|
||||
elif video_codec.startswith('XviD'):
|
||||
return 'XviD'
|
||||
elif video_codec.startswith('DivX'):
|
||||
return 'DivX'
|
||||
elif video_codec == 'MPEG-1 Video':
|
||||
return 'Mpeg'
|
||||
elif video_codec == 'MPEG-2 Video':
|
||||
return 'Mpeg2'
|
||||
elif video_codec == 'MPEG-4 Video':
|
||||
return 'Mpeg4'
|
||||
elif video_codec == 'VC-1':
|
||||
return 'VC1'
|
||||
elif video_codec.endswith('VP6'):
|
||||
return 'VP6'
|
||||
elif video_codec.endswith('VP7'):
|
||||
return 'VP7'
|
||||
elif video_codec.endswith('VP8'):
|
||||
return 'VP8'
|
||||
elif video_codec.endswith('VP9'):
|
||||
return 'VP9'
|
||||
else:
|
||||
return video_codec
|
||||
|
||||
|
||||
def episodeParser(episode):
|
||||
if 'hasFile' in episode:
|
||||
if episode['hasFile'] is True:
|
||||
if 'episodeFile' in episode:
|
||||
if episode['episodeFile']['size'] > 20480:
|
||||
if 'sceneName' in episode['episodeFile']:
|
||||
sceneName = episode['episodeFile']['sceneName']
|
||||
else:
|
||||
sceneName = None
|
||||
|
||||
audio_language = []
|
||||
if 'language' in episode['episodeFile'] and len(episode['episodeFile']['language']):
|
||||
item = episode['episodeFile']['language']
|
||||
if isinstance(item, dict):
|
||||
if 'name' in item:
|
||||
audio_language.append(item['name'])
|
||||
else:
|
||||
audio_language = TableShows.get(TableShows.sonarrSeriesId == episode['seriesId']).audio_language
|
||||
|
||||
if 'mediaInfo' in episode['episodeFile']:
|
||||
if 'videoCodec' in episode['episodeFile']['mediaInfo']:
|
||||
videoCodec = episode['episodeFile']['mediaInfo']['videoCodec']
|
||||
videoCodec = SonarrFormatVideoCodec(videoCodec)
|
||||
else:
|
||||
videoCodec = None
|
||||
|
||||
if 'audioCodec' in episode['episodeFile']['mediaInfo']:
|
||||
audioCodec = episode['episodeFile']['mediaInfo']['audioCodec']
|
||||
audioCodec = SonarrFormatAudioCodec(audioCodec)
|
||||
else:
|
||||
audioCodec = None
|
||||
else:
|
||||
videoCodec = None
|
||||
audioCodec = None
|
||||
|
||||
try:
|
||||
video_format, video_resolution = episode['episodeFile']['quality']['quality']['name'].split('-')
|
||||
except:
|
||||
video_format = episode['episodeFile']['quality']['quality']['name']
|
||||
try:
|
||||
video_resolution = str(episode['episodeFile']['quality']['quality']['resolution']) + 'p'
|
||||
except:
|
||||
video_resolution = None
|
||||
|
||||
return {'sonarrSeriesId': episode['seriesId'],
|
||||
'sonarrEpisodeId': episode['id'],
|
||||
'title': episode['title'],
|
||||
'path': episode['episodeFile']['path'],
|
||||
'season': episode['seasonNumber'],
|
||||
'episode': episode['episodeNumber'],
|
||||
'scene_name': sceneName,
|
||||
'monitored': str(bool(episode['monitored'])),
|
||||
'format': video_format,
|
||||
'resolution': video_resolution,
|
||||
'video_codec': videoCodec,
|
||||
'audio_codec': audioCodec,
|
||||
'episode_file_id': episode['episodeFile']['id'],
|
||||
'audio_language': str(audio_language),
|
||||
'file_size': episode['episodeFile']['size']}
|
||||
|
||||
|
||||
def get_series_from_sonarr_api(series_id, url, apikey_sonarr):
|
||||
if series_id:
|
||||
url_sonarr_api_series = url + "/api/{0}series/{1}?apikey={2}".format(
|
||||
'' if get_sonarr_info.is_legacy() else 'v3/', series_id, apikey_sonarr)
|
||||
else:
|
||||
url_sonarr_api_series = url + "/api/{0}series?apikey={1}".format(
|
||||
'' if get_sonarr_info.is_legacy() else 'v3/', apikey_sonarr)
|
||||
try:
|
||||
r = requests.get(url_sonarr_api_series, timeout=60, verify=False, headers=headers)
|
||||
r.raise_for_status()
|
||||
except requests.exceptions.HTTPError as e:
|
||||
if e.response.status_code:
|
||||
raise requests.exceptions.HTTPError
|
||||
logging.exception("BAZARR Error trying to get series from Sonarr. Http error.")
|
||||
return
|
||||
except requests.exceptions.ConnectionError:
|
||||
logging.exception("BAZARR Error trying to get series from Sonarr. Connection Error.")
|
||||
return
|
||||
except requests.exceptions.Timeout:
|
||||
logging.exception("BAZARR Error trying to get series from Sonarr. Timeout Error.")
|
||||
return
|
||||
except requests.exceptions.RequestException:
|
||||
logging.exception("BAZARR Error trying to get series from Sonarr.")
|
||||
return
|
||||
else:
|
||||
series_json = []
|
||||
if series_id:
|
||||
series_json.append(r.json())
|
||||
else:
|
||||
series_json = r.json()
|
||||
series_list = []
|
||||
for series in series_json:
|
||||
series_list.append({'sonarrSeriesId': series['id'], 'title': series['title']})
|
||||
return series_list
|
||||
|
||||
|
||||
def get_episodes_from_sonarr_api(url, apikey_sonarr, series_id=None, episode_id=None):
|
||||
if series_id:
|
||||
url_sonarr_api_episode = url + "/api/{0}episode?seriesId={1}&apikey={2}".format(
|
||||
'' if get_sonarr_info.is_legacy() else 'v3/', series_id, apikey_sonarr)
|
||||
elif episode_id:
|
||||
url_sonarr_api_episode = url + "/api/{0}episode/{1}?apikey={2}".format(
|
||||
'' if get_sonarr_info.is_legacy() else 'v3/', episode_id, apikey_sonarr)
|
||||
else:
|
||||
return
|
||||
|
||||
try:
|
||||
r = requests.get(url_sonarr_api_episode, timeout=60, verify=False, headers=headers)
|
||||
r.raise_for_status()
|
||||
except requests.exceptions.HTTPError:
|
||||
logging.exception("BAZARR Error trying to get episodes from Sonarr. Http error.")
|
||||
return
|
||||
except requests.exceptions.ConnectionError:
|
||||
logging.exception("BAZARR Error trying to get episodes from Sonarr. Connection Error.")
|
||||
return
|
||||
except requests.exceptions.Timeout:
|
||||
logging.exception("BAZARR Error trying to get episodes from Sonarr. Timeout Error.")
|
||||
return
|
||||
except requests.exceptions.RequestException:
|
||||
logging.exception("BAZARR Error trying to get episodes from Sonarr.")
|
||||
return
|
||||
else:
|
||||
return r.json()
|
||||
|
||||
|
||||
def get_episodesFiles_from_sonarr_api(url, apikey_sonarr, series_id=None, episode_file_id=None):
|
||||
if series_id:
|
||||
url_sonarr_api_episodeFiles = url + "/api/v3/episodeFile?seriesId={0}&apikey={1}".format(series_id,
|
||||
apikey_sonarr)
|
||||
elif episode_file_id:
|
||||
url_sonarr_api_episodeFiles = url + "/api/v3/episodeFile/{0}?apikey={1}".format(episode_file_id, apikey_sonarr)
|
||||
else:
|
||||
return
|
||||
|
||||
try:
|
||||
r = requests.get(url_sonarr_api_episodeFiles, timeout=60, verify=False, headers=headers)
|
||||
r.raise_for_status()
|
||||
except requests.exceptions.HTTPError:
|
||||
logging.exception("BAZARR Error trying to get episodeFiles from Sonarr. Http error.")
|
||||
return
|
||||
except requests.exceptions.ConnectionError:
|
||||
logging.exception("BAZARR Error trying to get episodeFiles from Sonarr. Connection Error.")
|
||||
return
|
||||
except requests.exceptions.Timeout:
|
||||
logging.exception("BAZARR Error trying to get episodeFiles from Sonarr. Timeout Error.")
|
||||
return
|
||||
except requests.exceptions.RequestException:
|
||||
logging.exception("BAZARR Error trying to get episodeFiles from Sonarr.")
|
||||
return
|
||||
else:
|
||||
return r.json()
|
||||
|
|
|
@ -1,537 +1,10 @@
|
|||
# coding=utf-8
|
||||
|
||||
import os
|
||||
import requests
|
||||
import logging
|
||||
import operator
|
||||
from functools import reduce
|
||||
from gevent import sleep
|
||||
from peewee import DoesNotExist
|
||||
|
||||
from config import settings, url_radarr
|
||||
from helper import path_mappings
|
||||
from utils import get_radarr_info
|
||||
from list_subtitles import store_subtitles_movie, movies_full_scan_subtitles
|
||||
from get_rootfolder import check_radarr_rootfolder
|
||||
|
||||
from get_subtitle import movies_download_subtitles
|
||||
from database import get_exclusion_clause, TableMovies
|
||||
from event_handler import event_stream, show_progress, hide_progress
|
||||
|
||||
headers = {"User-Agent": os.environ["SZ_USER_AGENT"]}
|
||||
from list_subtitles import movies_full_scan_subtitles
|
||||
|
||||
|
||||
def update_all_movies():
|
||||
movies_full_scan_subtitles()
|
||||
logging.info('BAZARR All existing movie subtitles indexed from disk.')
|
||||
|
||||
|
||||
def update_movies(send_event=True):
|
||||
check_radarr_rootfolder()
|
||||
logging.debug('BAZARR Starting movie sync from Radarr.')
|
||||
apikey_radarr = settings.radarr.apikey
|
||||
|
||||
movie_default_enabled = settings.general.getboolean('movie_default_enabled')
|
||||
|
||||
if movie_default_enabled is True:
|
||||
movie_default_profile = settings.general.movie_default_profile
|
||||
if movie_default_profile == '':
|
||||
movie_default_profile = None
|
||||
else:
|
||||
movie_default_profile = None
|
||||
|
||||
if apikey_radarr is None:
|
||||
pass
|
||||
else:
|
||||
audio_profiles = get_profile_list()
|
||||
tagsDict = get_tags()
|
||||
|
||||
# Get movies data from radarr
|
||||
movies = get_movies_from_radarr_api(url=url_radarr(), apikey_radarr=apikey_radarr)
|
||||
if not movies:
|
||||
return
|
||||
else:
|
||||
# Get current movies in DB
|
||||
current_movies_db = TableMovies.select(TableMovies.tmdbId, TableMovies.path, TableMovies.radarrId).dicts()
|
||||
|
||||
current_movies_db_list = [x['tmdbId'] for x in current_movies_db]
|
||||
|
||||
current_movies_radarr = []
|
||||
movies_to_update = []
|
||||
movies_to_add = []
|
||||
altered_movies = []
|
||||
|
||||
# Build new and updated movies
|
||||
movies_count = len(movies)
|
||||
for i, movie in enumerate(movies):
|
||||
sleep()
|
||||
if send_event:
|
||||
show_progress(id='movies_progress',
|
||||
header='Syncing movies...',
|
||||
name=movie['title'],
|
||||
value=i,
|
||||
count=movies_count)
|
||||
|
||||
if movie['hasFile'] is True:
|
||||
if 'movieFile' in movie:
|
||||
if movie['movieFile']['size'] > 20480:
|
||||
# Add movies in radarr to current movies list
|
||||
current_movies_radarr.append(str(movie['tmdbId']))
|
||||
|
||||
if str(movie['tmdbId']) in current_movies_db_list:
|
||||
movies_to_update.append(movieParser(movie, action='update',
|
||||
tags_dict=tagsDict,
|
||||
movie_default_profile=movie_default_profile,
|
||||
audio_profiles=audio_profiles))
|
||||
else:
|
||||
movies_to_add.append(movieParser(movie, action='insert',
|
||||
tags_dict=tagsDict,
|
||||
movie_default_profile=movie_default_profile,
|
||||
audio_profiles=audio_profiles))
|
||||
|
||||
if send_event:
|
||||
hide_progress(id='movies_progress')
|
||||
|
||||
# Remove old movies from DB
|
||||
removed_movies = list(set(current_movies_db_list) - set(current_movies_radarr))
|
||||
|
||||
for removed_movie in removed_movies:
|
||||
sleep()
|
||||
TableMovies.delete().where(TableMovies.tmdbId == removed_movie).execute()
|
||||
|
||||
# Update movies in DB
|
||||
movies_in_db_list = []
|
||||
movies_in_db = TableMovies.select(TableMovies.radarrId,
|
||||
TableMovies.title,
|
||||
TableMovies.path,
|
||||
TableMovies.tmdbId,
|
||||
TableMovies.overview,
|
||||
TableMovies.poster,
|
||||
TableMovies.fanart,
|
||||
TableMovies.audio_language,
|
||||
TableMovies.sceneName,
|
||||
TableMovies.monitored,
|
||||
TableMovies.sortTitle,
|
||||
TableMovies.year,
|
||||
TableMovies.alternativeTitles,
|
||||
TableMovies.format,
|
||||
TableMovies.resolution,
|
||||
TableMovies.video_codec,
|
||||
TableMovies.audio_codec,
|
||||
TableMovies.imdbId,
|
||||
TableMovies.movie_file_id,
|
||||
TableMovies.tags,
|
||||
TableMovies.file_size).dicts()
|
||||
|
||||
for item in movies_in_db:
|
||||
movies_in_db_list.append(item)
|
||||
|
||||
movies_to_update_list = [i for i in movies_to_update if i not in movies_in_db_list]
|
||||
|
||||
for updated_movie in movies_to_update_list:
|
||||
sleep()
|
||||
TableMovies.update(updated_movie).where(TableMovies.tmdbId == updated_movie['tmdbId']).execute()
|
||||
altered_movies.append([updated_movie['tmdbId'],
|
||||
updated_movie['path'],
|
||||
updated_movie['radarrId'],
|
||||
updated_movie['monitored']])
|
||||
|
||||
# Insert new movies in DB
|
||||
for added_movie in movies_to_add:
|
||||
sleep()
|
||||
result = TableMovies.insert(added_movie).on_conflict(action='IGNORE').execute()
|
||||
if result > 0:
|
||||
altered_movies.append([added_movie['tmdbId'],
|
||||
added_movie['path'],
|
||||
added_movie['radarrId'],
|
||||
added_movie['monitored']])
|
||||
if send_event:
|
||||
event_stream(type='movie', action='update', payload=int(added_movie['radarrId']))
|
||||
else:
|
||||
logging.debug('BAZARR unable to insert this movie into the database:',
|
||||
path_mappings.path_replace_movie(added_movie['path']))
|
||||
|
||||
# Store subtitles for added or modified movies
|
||||
for i, altered_movie in enumerate(altered_movies, 1):
|
||||
sleep()
|
||||
store_subtitles_movie(altered_movie[1], path_mappings.path_replace_movie(altered_movie[1]))
|
||||
|
||||
logging.debug('BAZARR All movies synced from Radarr into database.')
|
||||
|
||||
|
||||
def update_one_movie(movie_id, action):
|
||||
logging.debug('BAZARR syncing this specific movie from Radarr: {}'.format(movie_id))
|
||||
|
||||
# Check if there's a row in database for this movie ID
|
||||
try:
|
||||
existing_movie = TableMovies.select(TableMovies.path)\
|
||||
.where(TableMovies.radarrId == movie_id)\
|
||||
.dicts()\
|
||||
.get()
|
||||
except DoesNotExist:
|
||||
existing_movie = None
|
||||
|
||||
# Remove movie from DB
|
||||
if action == 'deleted':
|
||||
if existing_movie:
|
||||
TableMovies.delete().where(TableMovies.radarrId == movie_id).execute()
|
||||
event_stream(type='movie', action='delete', payload=int(movie_id))
|
||||
logging.debug('BAZARR deleted this movie from the database:{}'.format(path_mappings.path_replace_movie(
|
||||
existing_movie['path'])))
|
||||
return
|
||||
|
||||
movie_default_enabled = settings.general.getboolean('movie_default_enabled')
|
||||
|
||||
if movie_default_enabled is True:
|
||||
movie_default_profile = settings.general.movie_default_profile
|
||||
if movie_default_profile == '':
|
||||
movie_default_profile = None
|
||||
else:
|
||||
movie_default_profile = None
|
||||
|
||||
audio_profiles = get_profile_list()
|
||||
tagsDict = get_tags()
|
||||
|
||||
try:
|
||||
# Get movie data from radarr api
|
||||
movie = None
|
||||
movie_data = get_movies_from_radarr_api(url=url_radarr(), apikey_radarr=settings.radarr.apikey,
|
||||
radarr_id=movie_id)
|
||||
if not movie_data:
|
||||
return
|
||||
else:
|
||||
if action == 'updated' and existing_movie:
|
||||
movie = movieParser(movie_data, action='update', tags_dict=tagsDict,
|
||||
movie_default_profile=movie_default_profile, audio_profiles=audio_profiles)
|
||||
elif action == 'updated' and not existing_movie:
|
||||
movie = movieParser(movie_data, action='insert', tags_dict=tagsDict,
|
||||
movie_default_profile=movie_default_profile, audio_profiles=audio_profiles)
|
||||
except Exception:
|
||||
logging.debug('BAZARR cannot get movie returned by SignalR feed from Radarr API.')
|
||||
return
|
||||
|
||||
# Drop useless events
|
||||
if not movie and not existing_movie:
|
||||
return
|
||||
|
||||
# Remove movie from DB
|
||||
if not movie and existing_movie:
|
||||
TableMovies.delete().where(TableMovies.radarrId == movie_id).execute()
|
||||
event_stream(type='movie', action='delete', payload=int(movie_id))
|
||||
logging.debug('BAZARR deleted this movie from the database:{}'.format(path_mappings.path_replace_movie(
|
||||
existing_movie['path'])))
|
||||
return
|
||||
|
||||
# Update existing movie in DB
|
||||
elif movie and existing_movie:
|
||||
TableMovies.update(movie).where(TableMovies.radarrId == movie['radarrId']).execute()
|
||||
event_stream(type='movie', action='update', payload=int(movie_id))
|
||||
logging.debug('BAZARR updated this movie into the database:{}'.format(path_mappings.path_replace_movie(
|
||||
movie['path'])))
|
||||
|
||||
# Insert new movie in DB
|
||||
elif movie and not existing_movie:
|
||||
TableMovies.insert(movie).on_conflict(action='IGNORE').execute()
|
||||
event_stream(type='movie', action='update', payload=int(movie_id))
|
||||
logging.debug('BAZARR inserted this movie into the database:{}'.format(path_mappings.path_replace_movie(
|
||||
movie['path'])))
|
||||
|
||||
# Storing existing subtitles
|
||||
logging.debug('BAZARR storing subtitles for this movie: {}'.format(path_mappings.path_replace_movie(
|
||||
movie['path'])))
|
||||
store_subtitles_movie(movie['path'], path_mappings.path_replace_movie(movie['path']))
|
||||
|
||||
# Downloading missing subtitles
|
||||
logging.debug('BAZARR downloading missing subtitles for this movie: {}'.format(path_mappings.path_replace_movie(
|
||||
movie['path'])))
|
||||
movies_download_subtitles(movie_id)
|
||||
|
||||
|
||||
def get_profile_list():
|
||||
apikey_radarr = settings.radarr.apikey
|
||||
profiles_list = []
|
||||
# Get profiles data from radarr
|
||||
if get_radarr_info.is_legacy():
|
||||
url_radarr_api_movies = url_radarr() + "/api/profile?apikey=" + apikey_radarr
|
||||
else:
|
||||
url_radarr_api_movies = url_radarr() + "/api/v3/qualityprofile?apikey=" + apikey_radarr
|
||||
|
||||
try:
|
||||
profiles_json = requests.get(url_radarr_api_movies, timeout=60, verify=False, headers=headers)
|
||||
except requests.exceptions.ConnectionError as errc:
|
||||
logging.exception("BAZARR Error trying to get profiles from Radarr. Connection Error.")
|
||||
except requests.exceptions.Timeout as errt:
|
||||
logging.exception("BAZARR Error trying to get profiles from Radarr. Timeout Error.")
|
||||
except requests.exceptions.RequestException as err:
|
||||
logging.exception("BAZARR Error trying to get profiles from Radarr.")
|
||||
else:
|
||||
# Parsing data returned from radarr
|
||||
if get_radarr_info.is_legacy():
|
||||
for profile in profiles_json.json():
|
||||
profiles_list.append([profile['id'], profile['language'].capitalize()])
|
||||
else:
|
||||
for profile in profiles_json.json():
|
||||
profiles_list.append([profile['id'], profile['language']['name'].capitalize()])
|
||||
|
||||
return profiles_list
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def profile_id_to_language(id, profiles):
|
||||
for profile in profiles:
|
||||
profiles_to_return = []
|
||||
if id == profile[0]:
|
||||
profiles_to_return.append(profile[1])
|
||||
return profiles_to_return
|
||||
|
||||
|
||||
def RadarrFormatAudioCodec(audioFormat, audioCodecID, audioProfile, audioAdditionalFeatures):
|
||||
if audioFormat == "AC-3": return "AC3"
|
||||
if audioFormat == "E-AC-3": return "EAC3"
|
||||
if audioFormat == "AAC":
|
||||
if audioCodecID == "A_AAC/MPEG4/LC/SBR":
|
||||
return "HE-AAC"
|
||||
else:
|
||||
return "AAC"
|
||||
if audioFormat.strip() == "mp3": return "MP3"
|
||||
if audioFormat == "MPEG Audio":
|
||||
if audioCodecID == "55" or audioCodecID == "A_MPEG/L3" or audioProfile == "Layer 3": return "MP3"
|
||||
if audioCodecID == "A_MPEG/L2" or audioProfile == "Layer 2": return "MP2"
|
||||
if audioFormat == "MLP FBA":
|
||||
if audioAdditionalFeatures == "16-ch":
|
||||
return "TrueHD Atmos"
|
||||
else:
|
||||
return "TrueHD"
|
||||
|
||||
return audioFormat
|
||||
|
||||
|
||||
def RadarrFormatVideoCodec(videoFormat, videoCodecID, videoCodecLibrary):
|
||||
if videoFormat == "x264": return "h264"
|
||||
if videoFormat == "AVC" or videoFormat == "V.MPEG4/ISO/AVC": return "h264"
|
||||
if videoCodecLibrary and (videoFormat == "HEVC" or videoFormat == "V_MPEGH/ISO/HEVC"):
|
||||
if videoCodecLibrary.startswith("x265"): return "h265"
|
||||
if videoCodecID and videoFormat == "MPEG Video":
|
||||
if videoCodecID == "2" or videoCodecID == "V_MPEG2":
|
||||
return "Mpeg2"
|
||||
else:
|
||||
return "Mpeg"
|
||||
if videoFormat == "MPEG-1 Video": return "Mpeg"
|
||||
if videoFormat == "MPEG-2 Video": return "Mpeg2"
|
||||
if videoCodecLibrary and videoCodecID and videoFormat == "MPEG-4 Visual":
|
||||
if videoCodecID.endswith("XVID") or videoCodecLibrary.startswith("XviD"): return "XviD"
|
||||
if videoCodecID.endswith("DIV3") or videoCodecID.endswith("DIVX") or videoCodecID.endswith(
|
||||
"DX50") or videoCodecLibrary.startswith("DivX"): return "DivX"
|
||||
if videoFormat == "VC-1": return "VC1"
|
||||
if videoFormat == "WMV2":
|
||||
return "WMV"
|
||||
if videoFormat == "DivX" or videoFormat == "div3":
|
||||
return "DivX"
|
||||
|
||||
return videoFormat
|
||||
|
||||
|
||||
def get_tags():
|
||||
apikey_radarr = settings.radarr.apikey
|
||||
tagsDict = []
|
||||
|
||||
# Get tags data from Radarr
|
||||
if get_radarr_info.is_legacy():
|
||||
url_radarr_api_series = url_radarr() + "/api/tag?apikey=" + apikey_radarr
|
||||
else:
|
||||
url_radarr_api_series = url_radarr() + "/api/v3/tag?apikey=" + apikey_radarr
|
||||
|
||||
try:
|
||||
tagsDict = requests.get(url_radarr_api_series, timeout=60, verify=False, headers=headers)
|
||||
except requests.exceptions.ConnectionError:
|
||||
logging.exception("BAZARR Error trying to get tags from Radarr. Connection Error.")
|
||||
return []
|
||||
except requests.exceptions.Timeout:
|
||||
logging.exception("BAZARR Error trying to get tags from Radarr. Timeout Error.")
|
||||
return []
|
||||
except requests.exceptions.RequestException:
|
||||
logging.exception("BAZARR Error trying to get tags from Radarr.")
|
||||
return []
|
||||
else:
|
||||
return tagsDict.json()
|
||||
|
||||
|
||||
def movieParser(movie, action, tags_dict, movie_default_profile, audio_profiles):
|
||||
if 'movieFile' in movie:
|
||||
# Detect file separator
|
||||
if movie['path'][0] == "/":
|
||||
separator = "/"
|
||||
else:
|
||||
separator = "\\"
|
||||
|
||||
try:
|
||||
overview = str(movie['overview'])
|
||||
except:
|
||||
overview = ""
|
||||
try:
|
||||
poster_big = movie['images'][0]['url']
|
||||
poster = os.path.splitext(poster_big)[0] + '-500' + os.path.splitext(poster_big)[1]
|
||||
except:
|
||||
poster = ""
|
||||
try:
|
||||
fanart = movie['images'][1]['url']
|
||||
except:
|
||||
fanart = ""
|
||||
|
||||
if 'sceneName' in movie['movieFile']:
|
||||
sceneName = movie['movieFile']['sceneName']
|
||||
else:
|
||||
sceneName = None
|
||||
|
||||
alternativeTitles = None
|
||||
if get_radarr_info.is_legacy():
|
||||
if 'alternativeTitles' in movie:
|
||||
alternativeTitles = str([item['title'] for item in movie['alternativeTitles']])
|
||||
else:
|
||||
if 'alternateTitles' in movie:
|
||||
alternativeTitles = str([item['title'] for item in movie['alternateTitles']])
|
||||
|
||||
if 'imdbId' in movie:
|
||||
imdbId = movie['imdbId']
|
||||
else:
|
||||
imdbId = None
|
||||
|
||||
try:
|
||||
format, resolution = movie['movieFile']['quality']['quality']['name'].split('-')
|
||||
except:
|
||||
format = movie['movieFile']['quality']['quality']['name']
|
||||
try:
|
||||
resolution = str(movie['movieFile']['quality']['quality']['resolution']) + 'p'
|
||||
except:
|
||||
resolution = None
|
||||
|
||||
if 'mediaInfo' in movie['movieFile']:
|
||||
videoFormat = videoCodecID = videoProfile = videoCodecLibrary = None
|
||||
if get_radarr_info.is_legacy():
|
||||
if 'videoFormat' in movie['movieFile']['mediaInfo']: videoFormat = \
|
||||
movie['movieFile']['mediaInfo']['videoFormat']
|
||||
else:
|
||||
if 'videoCodec' in movie['movieFile']['mediaInfo']: videoFormat = \
|
||||
movie['movieFile']['mediaInfo']['videoCodec']
|
||||
if 'videoCodecID' in movie['movieFile']['mediaInfo']: videoCodecID = \
|
||||
movie['movieFile']['mediaInfo']['videoCodecID']
|
||||
if 'videoProfile' in movie['movieFile']['mediaInfo']: videoProfile = \
|
||||
movie['movieFile']['mediaInfo']['videoProfile']
|
||||
if 'videoCodecLibrary' in movie['movieFile']['mediaInfo']: videoCodecLibrary = \
|
||||
movie['movieFile']['mediaInfo']['videoCodecLibrary']
|
||||
videoCodec = RadarrFormatVideoCodec(videoFormat, videoCodecID, videoCodecLibrary)
|
||||
|
||||
audioFormat = audioCodecID = audioProfile = audioAdditionalFeatures = None
|
||||
if get_radarr_info.is_legacy():
|
||||
if 'audioFormat' in movie['movieFile']['mediaInfo']: audioFormat = \
|
||||
movie['movieFile']['mediaInfo']['audioFormat']
|
||||
else:
|
||||
if 'audioCodec' in movie['movieFile']['mediaInfo']: audioFormat = \
|
||||
movie['movieFile']['mediaInfo']['audioCodec']
|
||||
if 'audioCodecID' in movie['movieFile']['mediaInfo']: audioCodecID = \
|
||||
movie['movieFile']['mediaInfo']['audioCodecID']
|
||||
if 'audioProfile' in movie['movieFile']['mediaInfo']: audioProfile = \
|
||||
movie['movieFile']['mediaInfo']['audioProfile']
|
||||
if 'audioAdditionalFeatures' in movie['movieFile']['mediaInfo']: audioAdditionalFeatures = \
|
||||
movie['movieFile']['mediaInfo']['audioAdditionalFeatures']
|
||||
audioCodec = RadarrFormatAudioCodec(audioFormat, audioCodecID, audioProfile,
|
||||
audioAdditionalFeatures)
|
||||
else:
|
||||
videoCodec = None
|
||||
audioCodec = None
|
||||
|
||||
audio_language = []
|
||||
if get_radarr_info.is_legacy():
|
||||
if 'mediaInfo' in movie['movieFile']:
|
||||
if 'audioLanguages' in movie['movieFile']['mediaInfo']:
|
||||
audio_languages_list = movie['movieFile']['mediaInfo']['audioLanguages'].split('/')
|
||||
if len(audio_languages_list):
|
||||
for audio_language_list in audio_languages_list:
|
||||
audio_language.append(audio_language_list.strip())
|
||||
if not audio_language:
|
||||
audio_language = profile_id_to_language(movie['qualityProfileId'], audio_profiles)
|
||||
else:
|
||||
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.append(item['name'])
|
||||
|
||||
tags = [d['label'] for d in tags_dict if d['id'] in movie['tags']]
|
||||
|
||||
if action == 'update':
|
||||
return {'radarrId': int(movie["id"]),
|
||||
'title': movie["title"],
|
||||
'path': movie["path"] + separator + movie['movieFile']['relativePath'],
|
||||
'tmdbId': str(movie["tmdbId"]),
|
||||
'poster': poster,
|
||||
'fanart': fanart,
|
||||
'audio_language': str(audio_language),
|
||||
'sceneName': sceneName,
|
||||
'monitored': str(bool(movie['monitored'])),
|
||||
'year': str(movie['year']),
|
||||
'sortTitle': movie['sortTitle'],
|
||||
'alternativeTitles': alternativeTitles,
|
||||
'format': format,
|
||||
'resolution': resolution,
|
||||
'video_codec': videoCodec,
|
||||
'audio_codec': audioCodec,
|
||||
'overview': overview,
|
||||
'imdbId': imdbId,
|
||||
'movie_file_id': int(movie['movieFile']['id']),
|
||||
'tags': str(tags),
|
||||
'file_size': movie['movieFile']['size']}
|
||||
else:
|
||||
return {'radarrId': int(movie["id"]),
|
||||
'title': movie["title"],
|
||||
'path': movie["path"] + separator + movie['movieFile']['relativePath'],
|
||||
'tmdbId': str(movie["tmdbId"]),
|
||||
'subtitles': '[]',
|
||||
'overview': overview,
|
||||
'poster': poster,
|
||||
'fanart': fanart,
|
||||
'audio_language': str(audio_language),
|
||||
'sceneName': sceneName,
|
||||
'monitored': str(bool(movie['monitored'])),
|
||||
'sortTitle': movie['sortTitle'],
|
||||
'year': str(movie['year']),
|
||||
'alternativeTitles': alternativeTitles,
|
||||
'format': format,
|
||||
'resolution': resolution,
|
||||
'video_codec': videoCodec,
|
||||
'audio_codec': audioCodec,
|
||||
'imdbId': imdbId,
|
||||
'movie_file_id': int(movie['movieFile']['id']),
|
||||
'tags': str(tags),
|
||||
'profileId': movie_default_profile,
|
||||
'file_size': movie['movieFile']['size']}
|
||||
|
||||
|
||||
def get_movies_from_radarr_api(url, apikey_radarr, radarr_id=None):
|
||||
if get_radarr_info.is_legacy():
|
||||
url_radarr_api_movies = url + "/api/movie" + ("/{}".format(radarr_id) if radarr_id else "") + "?apikey=" + \
|
||||
apikey_radarr
|
||||
else:
|
||||
url_radarr_api_movies = url + "/api/v3/movie" + ("/{}".format(radarr_id) if radarr_id else "") + "?apikey=" + \
|
||||
apikey_radarr
|
||||
|
||||
try:
|
||||
r = requests.get(url_radarr_api_movies, timeout=60, verify=False, headers=headers)
|
||||
if r.status_code == 404:
|
||||
return
|
||||
r.raise_for_status()
|
||||
except requests.exceptions.HTTPError as errh:
|
||||
logging.exception("BAZARR Error trying to get movies from Radarr. Http error.")
|
||||
return
|
||||
except requests.exceptions.ConnectionError as errc:
|
||||
logging.exception("BAZARR Error trying to get movies from Radarr. Connection Error.")
|
||||
return
|
||||
except requests.exceptions.Timeout as errt:
|
||||
logging.exception("BAZARR Error trying to get movies from Radarr. Timeout Error.")
|
||||
return
|
||||
except requests.exceptions.RequestException as err:
|
||||
logging.exception("BAZARR Error trying to get movies from Radarr.")
|
||||
return
|
||||
else:
|
||||
return r.json()
|
||||
|
|
|
@ -1,159 +1,8 @@
|
|||
# coding=utf-8
|
||||
|
||||
import os
|
||||
import requests
|
||||
import logging
|
||||
|
||||
from config import settings, url_sonarr, url_radarr
|
||||
from helper import path_mappings
|
||||
from database import TableShowsRootfolder, TableMoviesRootfolder, TableShows, TableMovies
|
||||
from utils import get_sonarr_info, get_radarr_info
|
||||
|
||||
headers = {"User-Agent": os.environ["SZ_USER_AGENT"]}
|
||||
def get_series_rootfolder():
|
||||
pass
|
||||
|
||||
|
||||
def get_sonarr_rootfolder():
|
||||
apikey_sonarr = settings.sonarr.apikey
|
||||
sonarr_rootfolder = []
|
||||
|
||||
# Get root folder data from Sonarr
|
||||
if get_sonarr_info.is_legacy():
|
||||
url_sonarr_api_rootfolder = url_sonarr() + "/api/rootfolder?apikey=" + apikey_sonarr
|
||||
else:
|
||||
url_sonarr_api_rootfolder = url_sonarr() + "/api/v3/rootfolder?apikey=" + apikey_sonarr
|
||||
|
||||
try:
|
||||
rootfolder = requests.get(url_sonarr_api_rootfolder, timeout=60, verify=False, headers=headers)
|
||||
except requests.exceptions.ConnectionError:
|
||||
logging.exception("BAZARR Error trying to get rootfolder from Sonarr. Connection Error.")
|
||||
return []
|
||||
except requests.exceptions.Timeout:
|
||||
logging.exception("BAZARR Error trying to get rootfolder from Sonarr. Timeout Error.")
|
||||
return []
|
||||
except requests.exceptions.RequestException:
|
||||
logging.exception("BAZARR Error trying to get rootfolder from Sonarr.")
|
||||
return []
|
||||
else:
|
||||
sonarr_movies_paths = list(TableShows.select(TableShows.path).dicts())
|
||||
for folder in rootfolder.json():
|
||||
if any(item['path'].startswith(folder['path']) for item in sonarr_movies_paths):
|
||||
sonarr_rootfolder.append({'id': folder['id'], 'path': folder['path']})
|
||||
db_rootfolder = TableShowsRootfolder.select(TableShowsRootfolder.id, TableShowsRootfolder.path).dicts()
|
||||
rootfolder_to_remove = [x for x in db_rootfolder if not
|
||||
next((item for item in sonarr_rootfolder if item['id'] == x['id']), False)]
|
||||
rootfolder_to_update = [x for x in sonarr_rootfolder if
|
||||
next((item for item in db_rootfolder if item['id'] == x['id']), False)]
|
||||
rootfolder_to_insert = [x for x in sonarr_rootfolder if not
|
||||
next((item for item in db_rootfolder if item['id'] == x['id']), False)]
|
||||
|
||||
for item in rootfolder_to_remove:
|
||||
TableShowsRootfolder.delete().where(TableShowsRootfolder.id == item['id']).execute()
|
||||
for item in rootfolder_to_update:
|
||||
TableShowsRootfolder.update({TableShowsRootfolder.path: item['path']})\
|
||||
.where(TableShowsRootfolder.id == item['id'])\
|
||||
.execute()
|
||||
for item in rootfolder_to_insert:
|
||||
TableShowsRootfolder.insert({TableShowsRootfolder.id: item['id'], TableShowsRootfolder.path: item['path']})\
|
||||
.execute()
|
||||
|
||||
|
||||
def check_sonarr_rootfolder():
|
||||
get_sonarr_rootfolder()
|
||||
rootfolder = TableShowsRootfolder.select(TableShowsRootfolder.id, TableShowsRootfolder.path).dicts()
|
||||
for item in rootfolder:
|
||||
root_path = item['path']
|
||||
if not root_path.endswith(('/', '\\')):
|
||||
if root_path.startswith('/'):
|
||||
root_path += '/'
|
||||
else:
|
||||
root_path += '\\'
|
||||
if not os.path.isdir(path_mappings.path_replace(root_path)):
|
||||
TableShowsRootfolder.update({TableShowsRootfolder.accessible: 0,
|
||||
TableShowsRootfolder.error: 'This Sonarr root directory does not seems to '
|
||||
'be accessible by Bazarr. Please check path '
|
||||
'mapping.'})\
|
||||
.where(TableShowsRootfolder.id == item['id'])\
|
||||
.execute()
|
||||
elif not os.access(path_mappings.path_replace(root_path), os.W_OK):
|
||||
TableShowsRootfolder.update({TableShowsRootfolder.accessible: 0,
|
||||
TableShowsRootfolder.error: 'Bazarr cannot write to this directory.'}) \
|
||||
.where(TableShowsRootfolder.id == item['id']) \
|
||||
.execute()
|
||||
else:
|
||||
TableShowsRootfolder.update({TableShowsRootfolder.accessible: 1,
|
||||
TableShowsRootfolder.error: ''}) \
|
||||
.where(TableShowsRootfolder.id == item['id']) \
|
||||
.execute()
|
||||
|
||||
|
||||
def get_radarr_rootfolder():
|
||||
apikey_radarr = settings.radarr.apikey
|
||||
radarr_rootfolder = []
|
||||
|
||||
# Get root folder data from Radarr
|
||||
if get_radarr_info.is_legacy():
|
||||
url_radarr_api_rootfolder = url_radarr() + "/api/rootfolder?apikey=" + apikey_radarr
|
||||
else:
|
||||
url_radarr_api_rootfolder = url_radarr() + "/api/v3/rootfolder?apikey=" + apikey_radarr
|
||||
|
||||
try:
|
||||
rootfolder = requests.get(url_radarr_api_rootfolder, timeout=60, verify=False, headers=headers)
|
||||
except requests.exceptions.ConnectionError:
|
||||
logging.exception("BAZARR Error trying to get rootfolder from Radarr. Connection Error.")
|
||||
return []
|
||||
except requests.exceptions.Timeout:
|
||||
logging.exception("BAZARR Error trying to get rootfolder from Radarr. Timeout Error.")
|
||||
return []
|
||||
except requests.exceptions.RequestException:
|
||||
logging.exception("BAZARR Error trying to get rootfolder from Radarr.")
|
||||
return []
|
||||
else:
|
||||
radarr_movies_paths = list(TableMovies.select(TableMovies.path).dicts())
|
||||
for folder in rootfolder.json():
|
||||
if any(item['path'].startswith(folder['path']) for item in radarr_movies_paths):
|
||||
radarr_rootfolder.append({'id': folder['id'], 'path': folder['path']})
|
||||
db_rootfolder = TableMoviesRootfolder.select(TableMoviesRootfolder.id, TableMoviesRootfolder.path).dicts()
|
||||
rootfolder_to_remove = [x for x in db_rootfolder if not
|
||||
next((item for item in radarr_rootfolder if item['id'] == x['id']), False)]
|
||||
rootfolder_to_update = [x for x in radarr_rootfolder if
|
||||
next((item for item in db_rootfolder if item['id'] == x['id']), False)]
|
||||
rootfolder_to_insert = [x for x in radarr_rootfolder if not
|
||||
next((item for item in db_rootfolder if item['id'] == x['id']), False)]
|
||||
|
||||
for item in rootfolder_to_remove:
|
||||
TableMoviesRootfolder.delete().where(TableMoviesRootfolder.id == item['id']).execute()
|
||||
for item in rootfolder_to_update:
|
||||
TableMoviesRootfolder.update({TableMoviesRootfolder.path: item['path']})\
|
||||
.where(TableMoviesRootfolder.id == item['id']).execute()
|
||||
for item in rootfolder_to_insert:
|
||||
TableMoviesRootfolder.insert({TableMoviesRootfolder.id: item['id'],
|
||||
TableMoviesRootfolder.path: item['path']}).execute()
|
||||
|
||||
|
||||
def check_radarr_rootfolder():
|
||||
get_radarr_rootfolder()
|
||||
rootfolder = TableMoviesRootfolder.select(TableMoviesRootfolder.id, TableMoviesRootfolder.path).dicts()
|
||||
for item in rootfolder:
|
||||
root_path = item['path']
|
||||
if not root_path.endswith(('/', '\\')):
|
||||
if root_path.startswith('/'):
|
||||
root_path += '/'
|
||||
else:
|
||||
root_path += '\\'
|
||||
if not os.path.isdir(path_mappings.path_replace_movie(root_path)):
|
||||
TableMoviesRootfolder.update({TableMoviesRootfolder.accessible: 0,
|
||||
TableMoviesRootfolder.error: 'This Radarr root directory does not seems to '
|
||||
'be accessible by Bazarr. Please check path '
|
||||
'mapping.'}) \
|
||||
.where(TableMoviesRootfolder.id == item['id']) \
|
||||
.execute()
|
||||
elif not os.access(path_mappings.path_replace_movie(root_path), os.W_OK):
|
||||
TableMoviesRootfolder.update({TableMoviesRootfolder.accessible: 0,
|
||||
TableMoviesRootfolder.error: 'Bazarr cannot write to this directory'}) \
|
||||
.where(TableMoviesRootfolder.id == item['id']) \
|
||||
.execute()
|
||||
else:
|
||||
TableMoviesRootfolder.update({TableMoviesRootfolder.accessible: 1,
|
||||
TableMoviesRootfolder.error: ''}) \
|
||||
.where(TableMoviesRootfolder.id == item['id']) \
|
||||
.execute()
|
||||
def get_movies_rootfolder():
|
||||
pass
|
||||
|
|
|
@ -1,346 +1 @@
|
|||
# coding=utf-8
|
||||
|
||||
import os
|
||||
import requests
|
||||
import logging
|
||||
from gevent import sleep
|
||||
from peewee import DoesNotExist
|
||||
|
||||
from config import settings, url_sonarr
|
||||
from list_subtitles import list_missing_subtitles
|
||||
from get_rootfolder import check_sonarr_rootfolder
|
||||
from database import TableShows, TableEpisodes
|
||||
from get_episodes import sync_episodes
|
||||
from utils import get_sonarr_info
|
||||
from helper import path_mappings
|
||||
from event_handler import event_stream, show_progress, hide_progress
|
||||
|
||||
headers = {"User-Agent": os.environ["SZ_USER_AGENT"]}
|
||||
|
||||
|
||||
def update_series(send_event=True):
|
||||
check_sonarr_rootfolder()
|
||||
apikey_sonarr = settings.sonarr.apikey
|
||||
if apikey_sonarr is None:
|
||||
return
|
||||
|
||||
serie_default_enabled = settings.general.getboolean('serie_default_enabled')
|
||||
|
||||
if serie_default_enabled is True:
|
||||
serie_default_profile = settings.general.serie_default_profile
|
||||
if serie_default_profile == '':
|
||||
serie_default_profile = None
|
||||
else:
|
||||
serie_default_profile = None
|
||||
|
||||
audio_profiles = get_profile_list()
|
||||
tagsDict = get_tags()
|
||||
|
||||
# Get shows data from Sonarr
|
||||
series = get_series_from_sonarr_api(url=url_sonarr(), apikey_sonarr=apikey_sonarr)
|
||||
if not series:
|
||||
return
|
||||
else:
|
||||
# Get current shows in DB
|
||||
current_shows_db = TableShows.select(TableShows.sonarrSeriesId).dicts()
|
||||
|
||||
current_shows_db_list = [x['sonarrSeriesId'] for x in current_shows_db]
|
||||
current_shows_sonarr = []
|
||||
series_to_update = []
|
||||
series_to_add = []
|
||||
|
||||
series_count = len(series)
|
||||
for i, show in enumerate(series):
|
||||
sleep()
|
||||
if send_event:
|
||||
show_progress(id='series_progress',
|
||||
header='Syncing series...',
|
||||
name=show['title'],
|
||||
value=i,
|
||||
count=series_count)
|
||||
|
||||
# Add shows in Sonarr to current shows list
|
||||
current_shows_sonarr.append(show['id'])
|
||||
|
||||
if show['id'] in current_shows_db_list:
|
||||
series_to_update.append(seriesParser(show, action='update', tags_dict=tagsDict,
|
||||
serie_default_profile=serie_default_profile,
|
||||
audio_profiles=audio_profiles))
|
||||
else:
|
||||
series_to_add.append(seriesParser(show, action='insert', tags_dict=tagsDict,
|
||||
serie_default_profile=serie_default_profile,
|
||||
audio_profiles=audio_profiles))
|
||||
|
||||
if send_event:
|
||||
hide_progress(id='series_progress')
|
||||
|
||||
# Remove old series from DB
|
||||
removed_series = list(set(current_shows_db_list) - set(current_shows_sonarr))
|
||||
|
||||
for series in removed_series:
|
||||
sleep()
|
||||
TableShows.delete().where(TableShows.sonarrSeriesId == series).execute()
|
||||
if send_event:
|
||||
event_stream(type='series', action='delete', payload=series)
|
||||
|
||||
# Update existing series in DB
|
||||
series_in_db_list = []
|
||||
series_in_db = TableShows.select(TableShows.title,
|
||||
TableShows.path,
|
||||
TableShows.tvdbId,
|
||||
TableShows.sonarrSeriesId,
|
||||
TableShows.overview,
|
||||
TableShows.poster,
|
||||
TableShows.fanart,
|
||||
TableShows.audio_language,
|
||||
TableShows.sortTitle,
|
||||
TableShows.year,
|
||||
TableShows.alternateTitles,
|
||||
TableShows.tags,
|
||||
TableShows.seriesType,
|
||||
TableShows.imdbId).dicts()
|
||||
|
||||
for item in series_in_db:
|
||||
series_in_db_list.append(item)
|
||||
|
||||
series_to_update_list = [i for i in series_to_update if i not in series_in_db_list]
|
||||
|
||||
for updated_series in series_to_update_list:
|
||||
sleep()
|
||||
TableShows.update(updated_series).where(TableShows.sonarrSeriesId ==
|
||||
updated_series['sonarrSeriesId']).execute()
|
||||
if send_event:
|
||||
event_stream(type='series', payload=updated_series['sonarrSeriesId'])
|
||||
|
||||
# Insert new series in DB
|
||||
for added_series in series_to_add:
|
||||
sleep()
|
||||
result = TableShows.insert(added_series).on_conflict(action='IGNORE').execute()
|
||||
if result:
|
||||
list_missing_subtitles(no=added_series['sonarrSeriesId'])
|
||||
else:
|
||||
logging.debug('BAZARR unable to insert this series into the database:',
|
||||
path_mappings.path_replace(added_series['path']))
|
||||
|
||||
if send_event:
|
||||
event_stream(type='series', action='update', payload=added_series['sonarrSeriesId'])
|
||||
|
||||
logging.debug('BAZARR All series synced from Sonarr into database.')
|
||||
|
||||
|
||||
def update_one_series(series_id, action):
|
||||
logging.debug('BAZARR syncing this specific series from Sonarr: {}'.format(series_id))
|
||||
|
||||
# Check if there's a row in database for this series ID
|
||||
try:
|
||||
existing_series = TableShows.select(TableShows.path)\
|
||||
.where(TableShows.sonarrSeriesId == series_id)\
|
||||
.dicts()\
|
||||
.get()
|
||||
except DoesNotExist:
|
||||
existing_series = None
|
||||
|
||||
# Delete series from DB
|
||||
if action == 'deleted' and existing_series:
|
||||
TableShows.delete().where(TableShows.sonarrSeriesId == int(series_id)).execute()
|
||||
TableEpisodes.delete().where(TableEpisodes.sonarrSeriesId == int(series_id)).execute()
|
||||
event_stream(type='series', action='delete', payload=int(series_id))
|
||||
return
|
||||
|
||||
serie_default_enabled = settings.general.getboolean('serie_default_enabled')
|
||||
|
||||
if serie_default_enabled is True:
|
||||
serie_default_profile = settings.general.serie_default_profile
|
||||
if serie_default_profile == '':
|
||||
serie_default_profile = None
|
||||
else:
|
||||
serie_default_profile = None
|
||||
|
||||
audio_profiles = get_profile_list()
|
||||
tagsDict = get_tags()
|
||||
|
||||
try:
|
||||
# Get series data from sonarr api
|
||||
series = None
|
||||
|
||||
series_data = get_series_from_sonarr_api(url=url_sonarr(), apikey_sonarr=settings.sonarr.apikey,
|
||||
sonarr_series_id=int(series_id))
|
||||
|
||||
if not series_data:
|
||||
return
|
||||
else:
|
||||
if action == 'updated' and existing_series:
|
||||
series = seriesParser(series_data, action='update', tags_dict=tagsDict,
|
||||
serie_default_profile=serie_default_profile,
|
||||
audio_profiles=audio_profiles)
|
||||
elif action == 'updated' and not existing_series:
|
||||
series = seriesParser(series_data, action='insert', tags_dict=tagsDict,
|
||||
serie_default_profile=serie_default_profile,
|
||||
audio_profiles=audio_profiles)
|
||||
except Exception:
|
||||
logging.debug('BAZARR cannot parse series returned by SignalR feed.')
|
||||
return
|
||||
|
||||
# Update existing series in DB
|
||||
if action == 'updated' and existing_series:
|
||||
TableShows.update(series).where(TableShows.sonarrSeriesId == series['sonarrSeriesId']).execute()
|
||||
sync_episodes(series_id=int(series_id), send_event=True)
|
||||
event_stream(type='series', action='update', payload=int(series_id))
|
||||
logging.debug('BAZARR updated this series into the database:{}'.format(path_mappings.path_replace(
|
||||
series['path'])))
|
||||
|
||||
# Insert new series in DB
|
||||
elif action == 'updated' and not existing_series:
|
||||
TableShows.insert(series).on_conflict(action='IGNORE').execute()
|
||||
event_stream(type='series', action='update', payload=int(series_id))
|
||||
logging.debug('BAZARR inserted this series into the database:{}'.format(path_mappings.path_replace(
|
||||
series['path'])))
|
||||
|
||||
|
||||
def get_profile_list():
|
||||
apikey_sonarr = settings.sonarr.apikey
|
||||
profiles_list = []
|
||||
|
||||
# Get profiles data from Sonarr
|
||||
if get_sonarr_info.is_legacy():
|
||||
url_sonarr_api_series = url_sonarr() + "/api/profile?apikey=" + apikey_sonarr
|
||||
else:
|
||||
url_sonarr_api_series = url_sonarr() + "/api/v3/languageprofile?apikey=" + apikey_sonarr
|
||||
|
||||
try:
|
||||
profiles_json = requests.get(url_sonarr_api_series, timeout=60, verify=False, headers=headers)
|
||||
except requests.exceptions.ConnectionError:
|
||||
logging.exception("BAZARR Error trying to get profiles from Sonarr. Connection Error.")
|
||||
return None
|
||||
except requests.exceptions.Timeout:
|
||||
logging.exception("BAZARR Error trying to get profiles from Sonarr. Timeout Error.")
|
||||
return None
|
||||
except requests.exceptions.RequestException:
|
||||
logging.exception("BAZARR Error trying to get profiles from Sonarr.")
|
||||
return None
|
||||
|
||||
# Parsing data returned from Sonarr
|
||||
if get_sonarr_info.is_legacy():
|
||||
for profile in profiles_json.json():
|
||||
profiles_list.append([profile['id'], profile['language'].capitalize()])
|
||||
else:
|
||||
for profile in profiles_json.json():
|
||||
profiles_list.append([profile['id'], profile['name'].capitalize()])
|
||||
|
||||
return profiles_list
|
||||
|
||||
|
||||
def profile_id_to_language(id_, profiles):
|
||||
profiles_to_return = []
|
||||
for profile in profiles:
|
||||
if id_ == profile[0]:
|
||||
profiles_to_return.append(profile[1])
|
||||
return profiles_to_return
|
||||
|
||||
|
||||
def get_tags():
|
||||
apikey_sonarr = settings.sonarr.apikey
|
||||
tagsDict = []
|
||||
|
||||
# Get tags data from Sonarr
|
||||
if get_sonarr_info.is_legacy():
|
||||
url_sonarr_api_series = url_sonarr() + "/api/tag?apikey=" + apikey_sonarr
|
||||
else:
|
||||
url_sonarr_api_series = url_sonarr() + "/api/v3/tag?apikey=" + apikey_sonarr
|
||||
|
||||
try:
|
||||
tagsDict = requests.get(url_sonarr_api_series, timeout=60, verify=False, headers=headers)
|
||||
except requests.exceptions.ConnectionError:
|
||||
logging.exception("BAZARR Error trying to get tags from Sonarr. Connection Error.")
|
||||
return []
|
||||
except requests.exceptions.Timeout:
|
||||
logging.exception("BAZARR Error trying to get tags from Sonarr. Timeout Error.")
|
||||
return []
|
||||
except requests.exceptions.RequestException:
|
||||
logging.exception("BAZARR Error trying to get tags from Sonarr.")
|
||||
return []
|
||||
else:
|
||||
return tagsDict.json()
|
||||
|
||||
|
||||
def seriesParser(show, action, tags_dict, serie_default_profile, audio_profiles):
|
||||
overview = show['overview'] if 'overview' in show else ''
|
||||
poster = ''
|
||||
fanart = ''
|
||||
for image in show['images']:
|
||||
if image['coverType'] == 'poster':
|
||||
poster_big = image['url'].split('?')[0]
|
||||
poster = os.path.splitext(poster_big)[0] + '-250' + os.path.splitext(poster_big)[1]
|
||||
|
||||
if image['coverType'] == 'fanart':
|
||||
fanart = image['url'].split('?')[0]
|
||||
|
||||
alternate_titles = None
|
||||
if show['alternateTitles'] is not None:
|
||||
alternate_titles = str([item['title'] for item in show['alternateTitles']])
|
||||
|
||||
audio_language = []
|
||||
if get_sonarr_info.is_legacy():
|
||||
audio_language = profile_id_to_language(show['qualityProfileId'], audio_profiles)
|
||||
else:
|
||||
audio_language = profile_id_to_language(show['languageProfileId'], audio_profiles)
|
||||
|
||||
tags = [d['label'] for d in tags_dict if d['id'] in show['tags']]
|
||||
|
||||
imdbId = show['imdbId'] if 'imdbId' in show else None
|
||||
|
||||
if action == 'update':
|
||||
return {'title': show["title"],
|
||||
'path': show["path"],
|
||||
'tvdbId': int(show["tvdbId"]),
|
||||
'sonarrSeriesId': int(show["id"]),
|
||||
'overview': overview,
|
||||
'poster': poster,
|
||||
'fanart': fanart,
|
||||
'audio_language': str(audio_language),
|
||||
'sortTitle': show['sortTitle'],
|
||||
'year': str(show['year']),
|
||||
'alternateTitles': alternate_titles,
|
||||
'tags': str(tags),
|
||||
'seriesType': show['seriesType'],
|
||||
'imdbId': imdbId}
|
||||
else:
|
||||
return {'title': show["title"],
|
||||
'path': show["path"],
|
||||
'tvdbId': show["tvdbId"],
|
||||
'sonarrSeriesId': show["id"],
|
||||
'overview': overview,
|
||||
'poster': poster,
|
||||
'fanart': fanart,
|
||||
'audio_language': str(audio_language),
|
||||
'sortTitle': show['sortTitle'],
|
||||
'year': str(show['year']),
|
||||
'alternateTitles': alternate_titles,
|
||||
'tags': str(tags),
|
||||
'seriesType': show['seriesType'],
|
||||
'imdbId': imdbId,
|
||||
'profileId': serie_default_profile}
|
||||
|
||||
|
||||
def get_series_from_sonarr_api(url, apikey_sonarr, sonarr_series_id=None):
|
||||
url_sonarr_api_series = url + "/api/{0}series/{1}?apikey={2}".format(
|
||||
'' if get_sonarr_info.is_legacy() else 'v3/', sonarr_series_id if sonarr_series_id else "", apikey_sonarr)
|
||||
try:
|
||||
r = requests.get(url_sonarr_api_series, timeout=60, verify=False, headers=headers)
|
||||
r.raise_for_status()
|
||||
except requests.exceptions.HTTPError as e:
|
||||
if e.response.status_code:
|
||||
raise requests.exceptions.HTTPError
|
||||
logging.exception("BAZARR Error trying to get series from Sonarr. Http error.")
|
||||
return
|
||||
except requests.exceptions.ConnectionError:
|
||||
logging.exception("BAZARR Error trying to get series from Sonarr. Connection Error.")
|
||||
return
|
||||
except requests.exceptions.Timeout:
|
||||
logging.exception("BAZARR Error trying to get series from Sonarr. Timeout Error.")
|
||||
return
|
||||
except requests.exceptions.RequestException:
|
||||
logging.exception("BAZARR Error trying to get series from Sonarr.")
|
||||
return
|
||||
else:
|
||||
return r.json()
|
||||
|
|
|
@ -26,16 +26,16 @@ from subliminal_patch.subtitle import Subtitle
|
|||
from get_languages import language_from_alpha3, alpha2_from_alpha3, alpha3_from_alpha2, language_from_alpha2, \
|
||||
alpha2_from_language, alpha3_from_language
|
||||
from config import settings, get_array_from
|
||||
from helper import path_mappings, pp_replace, get_target_folder, force_unicode
|
||||
from helper import pp_replace, get_target_folder, force_unicode
|
||||
from list_subtitles import store_subtitles, list_missing_subtitles, store_subtitles_movie, list_missing_subtitles_movies
|
||||
from utils import history_log, history_log_movie, get_binary, get_blacklist, notify_sonarr, notify_radarr
|
||||
from utils import history_log, history_log_movie, get_binary, get_blacklist
|
||||
from notifier import send_notifications, send_notifications_movie
|
||||
from get_providers import get_providers, get_providers_auth, provider_throttle, provider_pool
|
||||
from knowit import api
|
||||
from subsyncer import subsync
|
||||
from guessit import guessit
|
||||
from custom_lang import CustomLanguage
|
||||
from database import dict_mapper, get_exclusion_clause, get_profiles_list, get_audio_profile_languages, \
|
||||
from database import get_exclusion_clause, get_profiles_list, get_audio_profile_languages, \
|
||||
get_desired_languages, TableShows, TableEpisodes, TableMovies, TableHistory, TableHistoryMovie
|
||||
from event_handler import event_stream, show_progress, hide_progress
|
||||
from embedded_subs_reader import parse_video_metadata
|
||||
|
@ -221,29 +221,29 @@ def download_subtitle(path, language, audio_language, hi, forced, providers, pro
|
|||
downloaded_provider + " with a score of " + str(percent_score) + "%."
|
||||
|
||||
if media_type == 'series':
|
||||
episode_metadata = TableEpisodes.select(TableEpisodes.sonarrSeriesId,
|
||||
TableEpisodes.sonarrEpisodeId)\
|
||||
.where(TableEpisodes.path == path_mappings.path_replace_reverse(path))\
|
||||
episode_metadata = TableEpisodes.select(TableEpisodes.seriesId,
|
||||
TableEpisodes.episodeId)\
|
||||
.where(TableEpisodes.path == path)\
|
||||
.dicts()\
|
||||
.get()
|
||||
series_id = episode_metadata['sonarrSeriesId']
|
||||
episode_id = episode_metadata['sonarrEpisodeId']
|
||||
series_id = episode_metadata['seriesId']
|
||||
episode_id = episode_metadata['episodeId']
|
||||
sync_subtitles(video_path=path, srt_path=downloaded_path,
|
||||
srt_lang=downloaded_language_code2, media_type=media_type,
|
||||
percent_score=percent_score,
|
||||
sonarr_series_id=episode_metadata['sonarrSeriesId'],
|
||||
sonarr_episode_id=episode_metadata['sonarrEpisodeId'])
|
||||
series_id=episode_metadata['seriesId'],
|
||||
episode_id=episode_metadata['episodeId'])
|
||||
else:
|
||||
movie_metadata = TableMovies.select(TableMovies.radarrId)\
|
||||
.where(TableMovies.path == path_mappings.path_replace_reverse_movie(path))\
|
||||
movie_metadata = TableMovies.select(TableMovies.movieId)\
|
||||
.where(TableMovies.path == path)\
|
||||
.dicts()\
|
||||
.get()
|
||||
series_id = ""
|
||||
episode_id = movie_metadata['radarrId']
|
||||
episode_id = movie_metadata['movieId']
|
||||
sync_subtitles(video_path=path, srt_path=downloaded_path,
|
||||
srt_lang=downloaded_language_code2, media_type=media_type,
|
||||
percent_score=percent_score,
|
||||
radarr_id=movie_metadata['radarrId'])
|
||||
movie_id=movie_metadata['movieId'])
|
||||
|
||||
if use_postprocessing is True:
|
||||
command = pp_replace(postprocessing_cmd, path, downloaded_path, downloaded_language,
|
||||
|
@ -268,21 +268,15 @@ def download_subtitle(path, language, audio_language, hi, forced, providers, pro
|
|||
|
||||
# fixme: support multiple languages at once
|
||||
if media_type == 'series':
|
||||
reversed_path = path_mappings.path_replace_reverse(path)
|
||||
reversed_subtitles_path = path_mappings.path_replace_reverse(downloaded_path)
|
||||
notify_sonarr(episode_metadata['sonarrSeriesId'])
|
||||
event_stream(type='episode-wanted', action='delete', payload=episode_metadata['sonarrEpisodeId'])
|
||||
event_stream(type='episode-wanted', action='delete', payload=episode_metadata['episodeId'])
|
||||
|
||||
else:
|
||||
reversed_path = path_mappings.path_replace_reverse_movie(path)
|
||||
reversed_subtitles_path = path_mappings.path_replace_reverse_movie(downloaded_path)
|
||||
notify_radarr(movie_metadata['radarrId'])
|
||||
event_stream(type='movie-wanted', action='delete', payload=movie_metadata['radarrId'])
|
||||
event_stream(type='movie-wanted', action='delete', payload=movie_metadata['movieId'])
|
||||
|
||||
track_event(category=downloaded_provider, action=action, label=downloaded_language)
|
||||
|
||||
return message, reversed_path, downloaded_language_code2, downloaded_provider, subtitle.score, \
|
||||
subtitle.language.forced, subtitle.id, reversed_subtitles_path, subtitle.language.hi
|
||||
return message, path, downloaded_language_code2, downloaded_provider, subtitle.score, \
|
||||
subtitle.language.forced, subtitle.id, downloaded_path, subtitle.language.hi
|
||||
|
||||
if not saved_any:
|
||||
logging.debug('BAZARR No Subtitles were found for this file: ' + path)
|
||||
|
@ -542,28 +536,28 @@ def manual_download_subtitle(path, language, audio_language, hi, forced, subtitl
|
|||
downloaded_provider + " with a score of " + str(score) + "% using manual search."
|
||||
|
||||
if media_type == 'series':
|
||||
episode_metadata = TableEpisodes.select(TableEpisodes.sonarrSeriesId,
|
||||
TableEpisodes.sonarrEpisodeId)\
|
||||
.where(TableEpisodes.path == path_mappings.path_replace_reverse(path))\
|
||||
episode_metadata = TableEpisodes.select(TableEpisodes.seriesId,
|
||||
TableEpisodes.episodeId)\
|
||||
.where(TableEpisodes.path == path)\
|
||||
.dicts()\
|
||||
.get()
|
||||
series_id = episode_metadata['sonarrSeriesId']
|
||||
episode_id = episode_metadata['sonarrEpisodeId']
|
||||
series_id = episode_metadata['seriesId']
|
||||
episode_id = episode_metadata['episodeId']
|
||||
sync_subtitles(video_path=path, srt_path=downloaded_path,
|
||||
srt_lang=downloaded_language_code2, media_type=media_type,
|
||||
percent_score=score,
|
||||
sonarr_series_id=episode_metadata['sonarrSeriesId'],
|
||||
sonarr_episode_id=episode_metadata['sonarrEpisodeId'])
|
||||
series_id=episode_metadata['seriesId'],
|
||||
episode_id=episode_metadata['episodeId'])
|
||||
else:
|
||||
movie_metadata = TableMovies.select(TableMovies.radarrId)\
|
||||
.where(TableMovies.path == path_mappings.path_replace_reverse_movie(path))\
|
||||
movie_metadata = TableMovies.select(TableMovies.movieId)\
|
||||
.where(TableMovies.path == path)\
|
||||
.dicts()\
|
||||
.get()
|
||||
series_id = ""
|
||||
episode_id = movie_metadata['radarrId']
|
||||
episode_id = movie_metadata['movieId']
|
||||
sync_subtitles(video_path=path, srt_path=downloaded_path,
|
||||
srt_lang=downloaded_language_code2, media_type=media_type,
|
||||
percent_score=score, radarr_id=movie_metadata['radarrId'])
|
||||
percent_score=score, movie_id=movie_metadata['movieId'])
|
||||
|
||||
if use_postprocessing:
|
||||
percent_score = round(subtitle.score * 100 / max_score, 2)
|
||||
|
@ -587,20 +581,11 @@ def manual_download_subtitle(path, language, audio_language, hi, forced, subtitl
|
|||
logging.debug("BAZARR post-processing skipped because subtitles score isn't below this "
|
||||
"threshold value: " + pp_threshold + "%")
|
||||
|
||||
if media_type == 'series':
|
||||
reversed_path = path_mappings.path_replace_reverse(path)
|
||||
reversed_subtitles_path = path_mappings.path_replace_reverse(downloaded_path)
|
||||
notify_sonarr(episode_metadata['sonarrSeriesId'])
|
||||
else:
|
||||
reversed_path = path_mappings.path_replace_reverse_movie(path)
|
||||
reversed_subtitles_path = path_mappings.path_replace_reverse_movie(downloaded_path)
|
||||
notify_radarr(movie_metadata['radarrId'])
|
||||
|
||||
track_event(category=downloaded_provider, action="manually_downloaded",
|
||||
label=downloaded_language)
|
||||
|
||||
return message, reversed_path, downloaded_language_code2, downloaded_provider, subtitle.score, \
|
||||
subtitle.language.forced, subtitle.id, reversed_subtitles_path, subtitle.language.hi
|
||||
return message, path, downloaded_language_code2, downloaded_provider, subtitle.score, \
|
||||
subtitle.language.forced, subtitle.id, downloaded_path, subtitle.language.hi
|
||||
else:
|
||||
logging.error(
|
||||
"BAZARR Tried to manually download a Subtitles for file: " + path + " but we weren't able to do (probably throttled by " + str(
|
||||
|
@ -687,24 +672,24 @@ def manual_upload_subtitle(path, language, forced, hi, title, scene_name, media_
|
|||
audio_language_code3 = alpha3_from_language(audio_language)
|
||||
|
||||
if media_type == 'series':
|
||||
episode_metadata = TableEpisodes.select(TableEpisodes.sonarrSeriesId, TableEpisodes.sonarrEpisodeId)\
|
||||
.where(TableEpisodes.path == path_mappings.path_replace_reverse(path))\
|
||||
episode_metadata = TableEpisodes.select(TableEpisodes.seriesId, TableEpisodes.episodeId)\
|
||||
.where(TableEpisodes.path == path)\
|
||||
.dicts()\
|
||||
.get()
|
||||
series_id = episode_metadata['sonarrSeriesId']
|
||||
episode_id = episode_metadata['sonarrEpisodeId']
|
||||
series_id = episode_metadata['seriesId']
|
||||
episode_id = episode_metadata['episodeId']
|
||||
sync_subtitles(video_path=path, srt_path=subtitle_path, srt_lang=uploaded_language_code2, media_type=media_type,
|
||||
percent_score=100, sonarr_series_id=episode_metadata['sonarrSeriesId'],
|
||||
sonarr_episode_id=episode_metadata['sonarrEpisodeId'])
|
||||
percent_score=100, series_id=episode_metadata['seriesId'],
|
||||
episode_id=episode_metadata['episodeId'])
|
||||
else:
|
||||
movie_metadata = TableMovies.select(TableMovies.radarrId)\
|
||||
.where(TableMovies.path == path_mappings.path_replace_reverse_movie(path))\
|
||||
movie_metadata = TableMovies.select(TableMovies.movieId)\
|
||||
.where(TableMovies.path == path)\
|
||||
.dicts()\
|
||||
.get()
|
||||
series_id = ""
|
||||
episode_id = movie_metadata['radarrId']
|
||||
episode_id = movie_metadata['movieId']
|
||||
sync_subtitles(video_path=path, srt_path=subtitle_path, srt_lang=uploaded_language_code2, media_type=media_type,
|
||||
percent_score=100, radarr_id=movie_metadata['radarrId'])
|
||||
percent_score=100, movie_id=movie_metadata['movieId'])
|
||||
|
||||
if use_postprocessing :
|
||||
command = pp_replace(postprocessing_cmd, path, subtitle_path, uploaded_language,
|
||||
|
@ -713,26 +698,17 @@ def manual_upload_subtitle(path, language, forced, hi, title, scene_name, media_
|
|||
episode_id, hi=hi)
|
||||
postprocessing(command, path)
|
||||
|
||||
if media_type == 'series':
|
||||
reversed_path = path_mappings.path_replace_reverse(path)
|
||||
reversed_subtitles_path = path_mappings.path_replace_reverse(subtitle_path)
|
||||
notify_sonarr(episode_metadata['sonarrSeriesId'])
|
||||
else:
|
||||
reversed_path = path_mappings.path_replace_reverse_movie(path)
|
||||
reversed_subtitles_path = path_mappings.path_replace_reverse_movie(subtitle_path)
|
||||
notify_radarr(movie_metadata['radarrId'])
|
||||
|
||||
return message, reversed_path, reversed_subtitles_path
|
||||
return message, path, subtitles_path
|
||||
|
||||
|
||||
def series_download_subtitles(no):
|
||||
conditions = [(TableEpisodes.sonarrSeriesId == no),
|
||||
conditions = [(TableEpisodes.seriesId == no),
|
||||
(TableEpisodes.missing_subtitles != '[]')]
|
||||
conditions += get_exclusion_clause('series')
|
||||
episodes_details = TableEpisodes.select(TableEpisodes.path,
|
||||
TableEpisodes.missing_subtitles,
|
||||
TableEpisodes.monitored,
|
||||
TableEpisodes.sonarrEpisodeId,
|
||||
TableEpisodes.episodeId,
|
||||
TableEpisodes.scene_name,
|
||||
TableShows.tags,
|
||||
TableShows.seriesType,
|
||||
|
@ -741,11 +717,11 @@ def series_download_subtitles(no):
|
|||
TableEpisodes.season,
|
||||
TableEpisodes.episode,
|
||||
TableEpisodes.title.alias('episodeTitle'))\
|
||||
.join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId))\
|
||||
.join(TableShows, on=(TableEpisodes.seriesId == TableShows.seriesId))\
|
||||
.where(reduce(operator.and_, conditions))\
|
||||
.dicts()
|
||||
if not episodes_details:
|
||||
logging.debug("BAZARR no episode for that sonarrSeriesId have been found in database or they have all been "
|
||||
logging.debug("BAZARR no episode for that seriesId have been found in database or they have all been "
|
||||
"ignored because of monitored status, series type or series tags: {}".format(no))
|
||||
return
|
||||
|
||||
|
@ -768,20 +744,20 @@ def series_download_subtitles(no):
|
|||
for language in ast.literal_eval(episode['missing_subtitles']):
|
||||
# confirm if language is still missing or if cutoff have been reached
|
||||
confirmed_missing_subs = TableEpisodes.select(TableEpisodes.missing_subtitles) \
|
||||
.where(TableEpisodes.sonarrEpisodeId == episode['sonarrEpisodeId']) \
|
||||
.where(TableEpisodes.episodeId == episode['episodeId']) \
|
||||
.dicts() \
|
||||
.get()
|
||||
if language not in ast.literal_eval(confirmed_missing_subs['missing_subtitles']):
|
||||
continue
|
||||
|
||||
if language is not None:
|
||||
audio_language_list = get_audio_profile_languages(episode_id=episode['sonarrEpisodeId'])
|
||||
audio_language_list = get_audio_profile_languages(episode_id=episode['episodeId'])
|
||||
if len(audio_language_list) > 0:
|
||||
audio_language = audio_language_list[0]['name']
|
||||
else:
|
||||
audio_language = 'None'
|
||||
|
||||
result = download_subtitle(path_mappings.path_replace(episode['path']),
|
||||
result = download_subtitle(episode['path'],
|
||||
language.split(':')[0],
|
||||
audio_language,
|
||||
"True" if language.endswith(':hi') else "False",
|
||||
|
@ -805,10 +781,10 @@ def series_download_subtitles(no):
|
|||
score = result[4]
|
||||
subs_id = result[6]
|
||||
subs_path = result[7]
|
||||
store_subtitles(episode['path'], path_mappings.path_replace(episode['path']))
|
||||
history_log(1, no, episode['sonarrEpisodeId'], message, path, language_code, provider, score,
|
||||
store_subtitles(episode['path'])
|
||||
history_log(1, no, episode['episodeId'], message, path, language_code, provider, score,
|
||||
subs_id, subs_path)
|
||||
send_notifications(no, episode['sonarrEpisodeId'], message)
|
||||
send_notifications(no, episode['episodeId'], message)
|
||||
else:
|
||||
logging.info("BAZARR All providers are throttled")
|
||||
break
|
||||
|
@ -817,26 +793,26 @@ def series_download_subtitles(no):
|
|||
|
||||
|
||||
def episode_download_subtitles(no, send_progress=False):
|
||||
conditions = [(TableEpisodes.sonarrEpisodeId == no)]
|
||||
conditions = [(TableEpisodes.episodeId == no)]
|
||||
conditions += get_exclusion_clause('series')
|
||||
episodes_details = TableEpisodes.select(TableEpisodes.path,
|
||||
TableEpisodes.missing_subtitles,
|
||||
TableEpisodes.monitored,
|
||||
TableEpisodes.sonarrEpisodeId,
|
||||
TableEpisodes.episodeId,
|
||||
TableEpisodes.scene_name,
|
||||
TableShows.tags,
|
||||
TableShows.title,
|
||||
TableShows.sonarrSeriesId,
|
||||
TableShows.seriesId,
|
||||
TableEpisodes.audio_language,
|
||||
TableShows.seriesType,
|
||||
TableEpisodes.title.alias('episodeTitle'),
|
||||
TableEpisodes.season,
|
||||
TableEpisodes.episode)\
|
||||
.join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId))\
|
||||
.join(TableShows, on=(TableEpisodes.seriesId == TableShows.seriesId))\
|
||||
.where(reduce(operator.and_, conditions))\
|
||||
.dicts()
|
||||
if not episodes_details:
|
||||
logging.debug("BAZARR no episode with that sonarrEpisodeId can be found in database:", str(no))
|
||||
logging.debug("BAZARR no episode with that episodeId can be found in database:", str(no))
|
||||
return
|
||||
|
||||
providers_list = get_providers()
|
||||
|
@ -856,20 +832,20 @@ def episode_download_subtitles(no, send_progress=False):
|
|||
for language in ast.literal_eval(episode['missing_subtitles']):
|
||||
# confirm if language is still missing or if cutoff have been reached
|
||||
confirmed_missing_subs = TableEpisodes.select(TableEpisodes.missing_subtitles) \
|
||||
.where(TableEpisodes.sonarrEpisodeId == episode['sonarrEpisodeId']) \
|
||||
.where(TableEpisodes.episodeId == episode['episodeId']) \
|
||||
.dicts() \
|
||||
.get()
|
||||
if language not in ast.literal_eval(confirmed_missing_subs['missing_subtitles']):
|
||||
continue
|
||||
|
||||
if language is not None:
|
||||
audio_language_list = get_audio_profile_languages(episode_id=episode['sonarrEpisodeId'])
|
||||
audio_language_list = get_audio_profile_languages(episode_id=episode['episodeId'])
|
||||
if len(audio_language_list) > 0:
|
||||
audio_language = audio_language_list[0]['name']
|
||||
else:
|
||||
audio_language = 'None'
|
||||
|
||||
result = download_subtitle(path_mappings.path_replace(episode['path']),
|
||||
result = download_subtitle(episode['path'],
|
||||
language.split(':')[0],
|
||||
audio_language,
|
||||
"True" if language.endswith(':hi') else "False",
|
||||
|
@ -893,10 +869,10 @@ def episode_download_subtitles(no, send_progress=False):
|
|||
score = result[4]
|
||||
subs_id = result[6]
|
||||
subs_path = result[7]
|
||||
store_subtitles(episode['path'], path_mappings.path_replace(episode['path']))
|
||||
history_log(1, episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message, path,
|
||||
store_subtitles(episode['path'])
|
||||
history_log(1, episode['seriesId'], episode['episodeId'], message, path,
|
||||
language_code, provider, score, subs_id, subs_path)
|
||||
send_notifications(episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message)
|
||||
send_notifications(episode['seriesId'], episode['episodeId'], message)
|
||||
if send_progress:
|
||||
hide_progress(id='episode_search_progress_{}'.format(no))
|
||||
else:
|
||||
|
@ -905,12 +881,12 @@ def episode_download_subtitles(no, send_progress=False):
|
|||
|
||||
|
||||
def movies_download_subtitles(no):
|
||||
conditions = [(TableMovies.radarrId == no)]
|
||||
conditions = [(TableMovies.movieId == no)]
|
||||
conditions += get_exclusion_clause('movie')
|
||||
movies = TableMovies.select(TableMovies.path,
|
||||
TableMovies.missing_subtitles,
|
||||
TableMovies.audio_language,
|
||||
TableMovies.radarrId,
|
||||
TableMovies.movieId,
|
||||
TableMovies.sceneName,
|
||||
TableMovies.title,
|
||||
TableMovies.tags,
|
||||
|
@ -918,7 +894,7 @@ def movies_download_subtitles(no):
|
|||
.where(reduce(operator.and_, conditions))\
|
||||
.dicts()
|
||||
if not len(movies):
|
||||
logging.debug("BAZARR no movie with that radarrId can be found in database:", str(no))
|
||||
logging.debug("BAZARR no movie with that movieId can be found in database:", str(no))
|
||||
return
|
||||
else:
|
||||
movie = movies[0]
|
||||
|
@ -934,7 +910,7 @@ def movies_download_subtitles(no):
|
|||
for i, language in enumerate(ast.literal_eval(movie['missing_subtitles'])):
|
||||
# confirm if language is still missing or if cutoff have been reached
|
||||
confirmed_missing_subs = TableMovies.select(TableMovies.missing_subtitles)\
|
||||
.where(TableMovies.radarrId == movie['radarrId'])\
|
||||
.where(TableMovies.movieId == movie['movieId'])\
|
||||
.dicts()\
|
||||
.get()
|
||||
if language not in ast.literal_eval(confirmed_missing_subs['missing_subtitles']):
|
||||
|
@ -948,13 +924,13 @@ def movies_download_subtitles(no):
|
|||
count=count_movie)
|
||||
|
||||
if language is not None:
|
||||
audio_language_list = get_audio_profile_languages(movie_id=movie['radarrId'])
|
||||
audio_language_list = get_audio_profile_languages(movie_id=movie['movieId'])
|
||||
if len(audio_language_list) > 0:
|
||||
audio_language = audio_language_list[0]['name']
|
||||
else:
|
||||
audio_language = 'None'
|
||||
|
||||
result = download_subtitle(path_mappings.path_replace_movie(movie['path']),
|
||||
result = download_subtitle(movie['path'],
|
||||
language.split(':')[0],
|
||||
audio_language,
|
||||
"True" if language.endswith(':hi') else "False",
|
||||
|
@ -978,7 +954,7 @@ def movies_download_subtitles(no):
|
|||
score = result[4]
|
||||
subs_id = result[6]
|
||||
subs_path = result[7]
|
||||
store_subtitles_movie(movie['path'], path_mappings.path_replace_movie(movie['path']))
|
||||
store_subtitles_movie(movie['path'])
|
||||
history_log_movie(1, no, message, path, language_code, provider, score, subs_id, subs_path)
|
||||
send_notifications_movie(no, message)
|
||||
else:
|
||||
|
@ -988,17 +964,17 @@ def movies_download_subtitles(no):
|
|||
hide_progress(id='movie_search_progress_{}'.format(no))
|
||||
|
||||
|
||||
def wanted_download_subtitles(sonarr_episode_id):
|
||||
def wanted_download_subtitles(episode_id):
|
||||
episodes_details = TableEpisodes.select(TableEpisodes.path,
|
||||
TableEpisodes.missing_subtitles,
|
||||
TableEpisodes.sonarrEpisodeId,
|
||||
TableEpisodes.sonarrSeriesId,
|
||||
TableEpisodes.episodeId,
|
||||
TableEpisodes.seriesId,
|
||||
TableEpisodes.audio_language,
|
||||
TableEpisodes.scene_name,
|
||||
TableEpisodes.failedAttempts,
|
||||
TableShows.title)\
|
||||
.join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId))\
|
||||
.where((TableEpisodes.sonarrEpisodeId == sonarr_episode_id))\
|
||||
.join(TableShows, on=(TableEpisodes.seriesId == TableShows.seriesId))\
|
||||
.where((TableEpisodes.episodeId == episode_id))\
|
||||
.dicts()
|
||||
episodes_details = list(episodes_details)
|
||||
|
||||
|
@ -1012,7 +988,7 @@ def wanted_download_subtitles(sonarr_episode_id):
|
|||
for language in ast.literal_eval(episode['missing_subtitles']):
|
||||
# confirm if language is still missing or if cutoff have been reached
|
||||
confirmed_missing_subs = TableEpisodes.select(TableEpisodes.missing_subtitles) \
|
||||
.where(TableEpisodes.sonarrEpisodeId == episode['sonarrEpisodeId']) \
|
||||
.where(TableEpisodes.episodeId == episode['episodeId']) \
|
||||
.dicts() \
|
||||
.get()
|
||||
if language not in ast.literal_eval(confirmed_missing_subs['missing_subtitles']):
|
||||
|
@ -1027,19 +1003,19 @@ def wanted_download_subtitles(sonarr_episode_id):
|
|||
attempt.append([language, time.time()])
|
||||
|
||||
TableEpisodes.update({TableEpisodes.failedAttempts: str(attempt)})\
|
||||
.where(TableEpisodes.sonarrEpisodeId == episode['sonarrEpisodeId'])\
|
||||
.where(TableEpisodes.episodeId == episode['episodeId'])\
|
||||
.execute()
|
||||
|
||||
for i in range(len(attempt)):
|
||||
if attempt[i][0] == language:
|
||||
if search_active(attempt[i][1]):
|
||||
audio_language_list = get_audio_profile_languages(episode_id=episode['sonarrEpisodeId'])
|
||||
audio_language_list = get_audio_profile_languages(episode_id=episode['episodeId'])
|
||||
if len(audio_language_list) > 0:
|
||||
audio_language = audio_language_list[0]['name']
|
||||
else:
|
||||
audio_language = 'None'
|
||||
|
||||
result = download_subtitle(path_mappings.path_replace(episode['path']),
|
||||
result = download_subtitle(episode['path'],
|
||||
language.split(':')[0],
|
||||
audio_language,
|
||||
"True" if language.endswith(':hi') else "False",
|
||||
|
@ -1063,26 +1039,26 @@ def wanted_download_subtitles(sonarr_episode_id):
|
|||
score = result[4]
|
||||
subs_id = result[6]
|
||||
subs_path = result[7]
|
||||
store_subtitles(episode['path'], path_mappings.path_replace(episode['path']))
|
||||
history_log(1, episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message, path,
|
||||
store_subtitles(episode['path'])
|
||||
history_log(1, episode['seriesId'], episode['episodeId'], message, path,
|
||||
language_code, provider, score, subs_id, subs_path)
|
||||
event_stream(type='episode-wanted', action='delete', payload=episode['sonarrEpisodeId'])
|
||||
send_notifications(episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message)
|
||||
event_stream(type='episode-wanted', action='delete', payload=episode['episodeId'])
|
||||
send_notifications(episode['seriesId'], episode['episodeId'], message)
|
||||
else:
|
||||
logging.debug(
|
||||
'BAZARR Search is not active for episode ' + episode['path'] + ' Language: ' + attempt[i][
|
||||
0])
|
||||
|
||||
|
||||
def wanted_download_subtitles_movie(radarr_id):
|
||||
def wanted_download_subtitles_movie(movie_id):
|
||||
movies_details = TableMovies.select(TableMovies.path,
|
||||
TableMovies.missing_subtitles,
|
||||
TableMovies.radarrId,
|
||||
TableMovies.movieId,
|
||||
TableMovies.audio_language,
|
||||
TableMovies.sceneName,
|
||||
TableMovies.failedAttempts,
|
||||
TableMovies.title)\
|
||||
.where((TableMovies.radarrId == radarr_id))\
|
||||
.where((TableMovies.movieId == movie_id))\
|
||||
.dicts()
|
||||
movies_details = list(movies_details)
|
||||
|
||||
|
@ -1096,7 +1072,7 @@ def wanted_download_subtitles_movie(radarr_id):
|
|||
for language in ast.literal_eval(movie['missing_subtitles']):
|
||||
# confirm if language is still missing or if cutoff have been reached
|
||||
confirmed_missing_subs = TableMovies.select(TableMovies.missing_subtitles) \
|
||||
.where(TableMovies.radarrId == movie['radarrId']) \
|
||||
.where(TableMovies.movieId == movie['movieId']) \
|
||||
.dicts() \
|
||||
.get()
|
||||
if language not in ast.literal_eval(confirmed_missing_subs['missing_subtitles']):
|
||||
|
@ -1111,19 +1087,19 @@ def wanted_download_subtitles_movie(radarr_id):
|
|||
attempt.append([language, time.time()])
|
||||
|
||||
TableMovies.update({TableMovies.failedAttempts: str(attempt)})\
|
||||
.where(TableMovies.radarrId == movie['radarrId'])\
|
||||
.where(TableMovies.movieId == movie['movieId'])\
|
||||
.execute()
|
||||
|
||||
for i in range(len(attempt)):
|
||||
if attempt[i][0] == language:
|
||||
if search_active(attempt[i][1]) is True:
|
||||
audio_language_list = get_audio_profile_languages(movie_id=movie['radarrId'])
|
||||
audio_language_list = get_audio_profile_languages(movie_id=movie['movieId'])
|
||||
if len(audio_language_list) > 0:
|
||||
audio_language = audio_language_list[0]['name']
|
||||
else:
|
||||
audio_language = 'None'
|
||||
|
||||
result = download_subtitle(path_mappings.path_replace_movie(movie['path']),
|
||||
result = download_subtitle(movie['path'],
|
||||
language.split(':')[0],
|
||||
audio_language,
|
||||
"True" if language.endswith(':hi') else "False",
|
||||
|
@ -1147,11 +1123,11 @@ def wanted_download_subtitles_movie(radarr_id):
|
|||
score = result[4]
|
||||
subs_id = result[6]
|
||||
subs_path = result[7]
|
||||
store_subtitles_movie(movie['path'], path_mappings.path_replace_movie(movie['path']))
|
||||
history_log_movie(1, movie['radarrId'], message, path, language_code, provider, score,
|
||||
store_subtitles_movie(movie['path'])
|
||||
history_log_movie(1, movie['movieId'], message, path, language_code, provider, score,
|
||||
subs_id, subs_path)
|
||||
event_stream(type='movie-wanted', action='delete', payload=movie['radarrId'])
|
||||
send_notifications_movie(movie['radarrId'], message)
|
||||
event_stream(type='movie-wanted', action='delete', payload=movie['movieId'])
|
||||
send_notifications_movie(movie['movieId'], message)
|
||||
else:
|
||||
logging.info(
|
||||
'BAZARR Search is not active for this Movie ' + movie['path'] + ' Language: ' + attempt[i][
|
||||
|
@ -1161,8 +1137,8 @@ def wanted_download_subtitles_movie(radarr_id):
|
|||
def wanted_search_missing_subtitles_series():
|
||||
conditions = [(TableEpisodes.missing_subtitles != '[]')]
|
||||
conditions += get_exclusion_clause('series')
|
||||
episodes = TableEpisodes.select(TableEpisodes.sonarrSeriesId,
|
||||
TableEpisodes.sonarrEpisodeId,
|
||||
episodes = TableEpisodes.select(TableEpisodes.seriesId,
|
||||
TableEpisodes.episodeId,
|
||||
TableShows.tags,
|
||||
TableEpisodes.monitored,
|
||||
TableShows.title,
|
||||
|
@ -1170,7 +1146,7 @@ def wanted_search_missing_subtitles_series():
|
|||
TableEpisodes.episode,
|
||||
TableEpisodes.title.alias('episodeTitle'),
|
||||
TableShows.seriesType)\
|
||||
.join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId))\
|
||||
.join(TableShows, on=(TableEpisodes.seriesId == TableShows.seriesId))\
|
||||
.where(reduce(operator.and_, conditions))\
|
||||
.dicts()
|
||||
episodes = list(episodes)
|
||||
|
@ -1188,7 +1164,7 @@ def wanted_search_missing_subtitles_series():
|
|||
|
||||
providers = get_providers()
|
||||
if providers:
|
||||
wanted_download_subtitles(episode['sonarrEpisodeId'])
|
||||
wanted_download_subtitles(episode['episodeId'])
|
||||
else:
|
||||
logging.info("BAZARR All providers are throttled")
|
||||
return
|
||||
|
@ -1201,7 +1177,7 @@ def wanted_search_missing_subtitles_series():
|
|||
def wanted_search_missing_subtitles_movies():
|
||||
conditions = [(TableMovies.missing_subtitles != '[]')]
|
||||
conditions += get_exclusion_clause('movie')
|
||||
movies = TableMovies.select(TableMovies.radarrId,
|
||||
movies = TableMovies.select(TableMovies.movieId,
|
||||
TableMovies.tags,
|
||||
TableMovies.monitored,
|
||||
TableMovies.title)\
|
||||
|
@ -1219,7 +1195,7 @@ def wanted_search_missing_subtitles_movies():
|
|||
|
||||
providers = get_providers()
|
||||
if providers:
|
||||
wanted_download_subtitles_movie(movie['radarrId'])
|
||||
wanted_download_subtitles_movie(movie['movieId'])
|
||||
else:
|
||||
logging.info("BAZARR All providers are throttled")
|
||||
return
|
||||
|
@ -1270,8 +1246,8 @@ def refine_from_db(path, video):
|
|||
TableEpisodes.audio_codec,
|
||||
TableEpisodes.path,
|
||||
TableShows.imdbId)\
|
||||
.join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId))\
|
||||
.where((TableEpisodes.path == path_mappings.path_replace_reverse(path)))\
|
||||
.join(TableShows, on=(TableEpisodes.seriesId == TableShows.seriesId))\
|
||||
.where((TableEpisodes.path == path))\
|
||||
.dicts()
|
||||
|
||||
if len(data):
|
||||
|
@ -1280,9 +1256,8 @@ def refine_from_db(path, video):
|
|||
video.season = int(data['season'])
|
||||
video.episode = int(data['episode'])
|
||||
video.title = data['episodeTitle']
|
||||
# Commented out because Sonarr provided so much bad year
|
||||
# if data['year']:
|
||||
# if int(data['year']) > 0: video.year = int(data['year'])
|
||||
if data['year']:
|
||||
if int(data['year']) > 0: video.year = int(data['year'])
|
||||
video.series_tvdb_id = int(data['tvdbId'])
|
||||
video.alternative_series = ast.literal_eval(data['alternateTitles'])
|
||||
if data['imdbId'] and not video.series_imdb_id:
|
||||
|
@ -1304,15 +1279,14 @@ def refine_from_db(path, video):
|
|||
TableMovies.video_codec,
|
||||
TableMovies.audio_codec,
|
||||
TableMovies.imdbId)\
|
||||
.where(TableMovies.path == path_mappings.path_replace_reverse_movie(path))\
|
||||
.where(TableMovies.path == path)\
|
||||
.dicts()
|
||||
|
||||
if len(data):
|
||||
data = data[0]
|
||||
video.title = re.sub(r'\s(\(\d\d\d\d\))', '', data['title'])
|
||||
# Commented out because Radarr provided so much bad year
|
||||
# if data['year']:
|
||||
# if int(data['year']) > 0: video.year = int(data['year'])
|
||||
if data['year']:
|
||||
if int(data['year']) > 0: video.year = int(data['year'])
|
||||
if data['imdbId'] and not video.imdb_id:
|
||||
video.imdb_id = data['imdbId']
|
||||
video.alternative_titles = ast.literal_eval(data['alternativeTitles'])
|
||||
|
@ -1331,12 +1305,12 @@ def refine_from_db(path, video):
|
|||
def refine_from_ffprobe(path, video):
|
||||
if isinstance(video, Movie):
|
||||
file_id = TableMovies.select(TableMovies.movie_file_id, TableMovies.file_size)\
|
||||
.where(TableMovies.path == path_mappings.path_replace_reverse_movie(path))\
|
||||
.where(TableMovies.path == path)\
|
||||
.dicts()\
|
||||
.get()
|
||||
else:
|
||||
file_id = TableEpisodes.select(TableEpisodes.episode_file_id, TableEpisodes.file_size)\
|
||||
.where(TableEpisodes.path == path_mappings.path_replace_reverse(path))\
|
||||
.where(TableEpisodes.path == path)\
|
||||
.dicts()\
|
||||
.get()
|
||||
|
||||
|
@ -1395,7 +1369,7 @@ def upgrade_subtitles():
|
|||
else:
|
||||
query_actions = [1, 3]
|
||||
|
||||
if settings.general.getboolean('use_sonarr'):
|
||||
if settings.general.getboolean('use_series'):
|
||||
upgradable_episodes_conditions = [(TableHistory.action << query_actions),
|
||||
(TableHistory.timestamp > minimum_timestamp),
|
||||
(TableHistory.score is not None)]
|
||||
|
@ -1408,18 +1382,18 @@ def upgrade_subtitles():
|
|||
TableEpisodes.audio_language,
|
||||
TableEpisodes.scene_name,
|
||||
TableEpisodes.title,
|
||||
TableEpisodes.sonarrSeriesId,
|
||||
TableEpisodes.seriesId,
|
||||
TableHistory.action,
|
||||
TableHistory.subtitles_path,
|
||||
TableEpisodes.sonarrEpisodeId,
|
||||
TableEpisodes.episodeId,
|
||||
fn.MAX(TableHistory.timestamp).alias('timestamp'),
|
||||
TableEpisodes.monitored,
|
||||
TableEpisodes.season,
|
||||
TableEpisodes.episode,
|
||||
TableShows.title.alias('seriesTitle'),
|
||||
TableShows.seriesType)\
|
||||
.join(TableShows, on=(TableHistory.sonarrSeriesId == TableShows.sonarrSeriesId))\
|
||||
.join(TableEpisodes, on=(TableHistory.sonarrEpisodeId == TableEpisodes.sonarrEpisodeId))\
|
||||
.join(TableShows, on=(TableHistory.seriesId == TableShows.seriesId))\
|
||||
.join(TableEpisodes, on=(TableHistory.episodeId == TableEpisodes.episodeId))\
|
||||
.where(reduce(operator.and_, upgradable_episodes_conditions))\
|
||||
.group_by(TableHistory.video_path, TableHistory.language)\
|
||||
.dicts()
|
||||
|
@ -1437,12 +1411,12 @@ def upgrade_subtitles():
|
|||
|
||||
episodes_to_upgrade = []
|
||||
for episode in upgradable_episodes_not_perfect:
|
||||
if os.path.exists(path_mappings.path_replace(episode['subtitles_path'])) and int(episode['score']) < 357:
|
||||
if os.path.exists(episode['subtitles_path']) and int(episode['score']) < 357:
|
||||
episodes_to_upgrade.append(episode)
|
||||
|
||||
count_episode_to_upgrade = len(episodes_to_upgrade)
|
||||
|
||||
if settings.general.getboolean('use_radarr'):
|
||||
if settings.general.getboolean('use_movies'):
|
||||
upgradable_movies_conditions = [(TableHistoryMovie.action << query_actions),
|
||||
(TableHistoryMovie.timestamp > minimum_timestamp),
|
||||
(TableHistoryMovie.score is not None)]
|
||||
|
@ -1458,9 +1432,9 @@ def upgrade_subtitles():
|
|||
fn.MAX(TableHistoryMovie.timestamp).alias('timestamp'),
|
||||
TableMovies.monitored,
|
||||
TableMovies.tags,
|
||||
TableMovies.radarrId,
|
||||
TableMovies.movieId,
|
||||
TableMovies.title)\
|
||||
.join(TableMovies, on=(TableHistoryMovie.radarrId == TableMovies.radarrId))\
|
||||
.join(TableMovies, on=(TableHistoryMovie.movieId == TableMovies.movieId))\
|
||||
.where(reduce(operator.and_, upgradable_movies_conditions))\
|
||||
.group_by(TableHistoryMovie.video_path, TableHistoryMovie.language)\
|
||||
.dicts()
|
||||
|
@ -1478,7 +1452,7 @@ def upgrade_subtitles():
|
|||
|
||||
movies_to_upgrade = []
|
||||
for movie in upgradable_movies_not_perfect:
|
||||
if os.path.exists(path_mappings.path_replace_movie(movie['subtitles_path'])) and int(movie['score']) < 117:
|
||||
if os.path.exists(movie['subtitles_path']) and int(movie['score']) < 117:
|
||||
movies_to_upgrade.append(movie)
|
||||
|
||||
count_movie_to_upgrade = len(movies_to_upgrade)
|
||||
|
@ -1486,7 +1460,7 @@ def upgrade_subtitles():
|
|||
providers_list = get_providers()
|
||||
providers_auth = get_providers_auth()
|
||||
|
||||
if settings.general.getboolean('use_sonarr'):
|
||||
if settings.general.getboolean('use_series'):
|
||||
for i, episode in enumerate(episodes_to_upgrade):
|
||||
show_progress(id='upgrade_episodes_progress',
|
||||
header='Upgrading episodes subtitles...',
|
||||
|
@ -1514,13 +1488,13 @@ def upgrade_subtitles():
|
|||
is_forced = "False"
|
||||
is_hi = "False"
|
||||
|
||||
audio_language_list = get_audio_profile_languages(episode_id=episode['sonarrEpisodeId'])
|
||||
audio_language_list = get_audio_profile_languages(episode_id=episode['episodeId'])
|
||||
if len(audio_language_list) > 0:
|
||||
audio_language = audio_language_list[0]['name']
|
||||
else:
|
||||
audio_language = 'None'
|
||||
|
||||
result = download_subtitle(path_mappings.path_replace(episode['video_path']),
|
||||
result = download_subtitle(episode['video_path'],
|
||||
language,
|
||||
audio_language,
|
||||
is_hi,
|
||||
|
@ -1546,14 +1520,14 @@ def upgrade_subtitles():
|
|||
score = result[4]
|
||||
subs_id = result[6]
|
||||
subs_path = result[7]
|
||||
store_subtitles(episode['video_path'], path_mappings.path_replace(episode['video_path']))
|
||||
history_log(3, episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message, path,
|
||||
store_subtitles(episode['video_path'])
|
||||
history_log(3, episode['seriesId'], episode['episodeId'], message, path,
|
||||
language_code, provider, score, subs_id, subs_path)
|
||||
send_notifications(episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message)
|
||||
send_notifications(episode['seriesId'], episode['episodeId'], message)
|
||||
|
||||
hide_progress(id='upgrade_episodes_progress')
|
||||
|
||||
if settings.general.getboolean('use_radarr'):
|
||||
if settings.general.getboolean('use_movies'):
|
||||
for i, movie in enumerate(movies_to_upgrade):
|
||||
show_progress(id='upgrade_movies_progress',
|
||||
header='Upgrading movies subtitles...',
|
||||
|
@ -1581,13 +1555,13 @@ def upgrade_subtitles():
|
|||
is_forced = "False"
|
||||
is_hi = "False"
|
||||
|
||||
audio_language_list = get_audio_profile_languages(movie_id=movie['radarrId'])
|
||||
audio_language_list = get_audio_profile_languages(movie_id=movie['movieId'])
|
||||
if len(audio_language_list) > 0:
|
||||
audio_language = audio_language_list[0]['name']
|
||||
else:
|
||||
audio_language = 'None'
|
||||
|
||||
result = download_subtitle(path_mappings.path_replace_movie(movie['video_path']),
|
||||
result = download_subtitle(movie['video_path'],
|
||||
language,
|
||||
audio_language,
|
||||
is_hi,
|
||||
|
@ -1613,10 +1587,9 @@ def upgrade_subtitles():
|
|||
score = result[4]
|
||||
subs_id = result[6]
|
||||
subs_path = result[7]
|
||||
store_subtitles_movie(movie['video_path'],
|
||||
path_mappings.path_replace_movie(movie['video_path']))
|
||||
history_log_movie(3, movie['radarrId'], message, path, language_code, provider, score, subs_id, subs_path)
|
||||
send_notifications_movie(movie['radarrId'], message)
|
||||
store_subtitles_movie(movie['video_path'])
|
||||
history_log_movie(3, movie['movieId'], message, path, language_code, provider, score, subs_id, subs_path)
|
||||
send_notifications_movie(movie['movieId'], message)
|
||||
|
||||
hide_progress(id='upgrade_movies_progress')
|
||||
|
||||
|
@ -1653,8 +1626,8 @@ def postprocessing(command, path):
|
|||
logging.info('BAZARR Post-processing result for file ' + path + ' : ' + out)
|
||||
|
||||
|
||||
def sync_subtitles(video_path, srt_path, srt_lang, media_type, percent_score, sonarr_series_id=None,
|
||||
sonarr_episode_id=None, radarr_id=None):
|
||||
def sync_subtitles(video_path, srt_path, srt_lang, media_type, percent_score, series_id=None,
|
||||
episode_id=None, movie_id=None):
|
||||
if settings.subsync.getboolean('use_subsync'):
|
||||
if media_type == 'series':
|
||||
use_subsync_threshold = settings.subsync.getboolean('use_subsync_threshold')
|
||||
|
@ -1665,7 +1638,7 @@ def sync_subtitles(video_path, srt_path, srt_lang, media_type, percent_score, so
|
|||
|
||||
if not use_subsync_threshold or (use_subsync_threshold and percent_score < float(subsync_threshold)):
|
||||
subsync.sync(video_path=video_path, srt_path=srt_path, srt_lang=srt_lang, media_type=media_type,
|
||||
sonarr_series_id=sonarr_series_id, sonarr_episode_id=sonarr_episode_id, radarr_id=radarr_id)
|
||||
series_id=series_id, episode_id=episode_id, movie_id=movie_id)
|
||||
return True
|
||||
else:
|
||||
logging.debug("BAZARR subsync skipped because subtitles score isn't below this "
|
||||
|
|
|
@ -11,91 +11,6 @@ from bs4 import UnicodeDammit
|
|||
from config import settings, get_array_from
|
||||
|
||||
|
||||
class PathMappings:
|
||||
def __init__(self):
|
||||
self.path_mapping_series = []
|
||||
self.path_mapping_movies = []
|
||||
|
||||
def update(self):
|
||||
self.path_mapping_series = [x for x in get_array_from(settings.general.path_mappings) if x[0] != x[1]]
|
||||
self.path_mapping_movies = [x for x in get_array_from(settings.general.path_mappings_movie) if x[0] != x[1]]
|
||||
|
||||
def path_replace(self, path):
|
||||
if path is None:
|
||||
return None
|
||||
|
||||
for path_mapping in self.path_mapping_series:
|
||||
if path_mapping[0] == path_mapping[1]:
|
||||
continue
|
||||
if '' in path_mapping:
|
||||
continue
|
||||
if path_mapping[0] in path:
|
||||
path = path.replace(path_mapping[0], path_mapping[1])
|
||||
if path.startswith('\\\\') or re.match(r'^[a-zA-Z]:\\', path):
|
||||
path = path.replace('/', '\\')
|
||||
elif path.startswith('/'):
|
||||
path = path.replace('\\', '/')
|
||||
break
|
||||
return path
|
||||
|
||||
def path_replace_reverse(self, path):
|
||||
if path is None:
|
||||
return None
|
||||
|
||||
for path_mapping in self.path_mapping_series:
|
||||
if path_mapping[0] == path_mapping[1]:
|
||||
continue
|
||||
if '' in path_mapping:
|
||||
continue
|
||||
if path_mapping[1] in path:
|
||||
path = path.replace(path_mapping[1], path_mapping[0])
|
||||
if path.startswith('\\\\') or re.match(r'^[a-zA-Z]:\\', path):
|
||||
path = path.replace('/', '\\')
|
||||
elif path.startswith('/'):
|
||||
path = path.replace('\\', '/')
|
||||
break
|
||||
return path
|
||||
|
||||
def path_replace_movie(self, path):
|
||||
if path is None:
|
||||
return None
|
||||
|
||||
for path_mapping in self.path_mapping_movies:
|
||||
if path_mapping[0] == path_mapping[1]:
|
||||
continue
|
||||
if '' in path_mapping:
|
||||
continue
|
||||
if path_mapping[0] in path:
|
||||
path = path.replace(path_mapping[0], path_mapping[1])
|
||||
if path.startswith('\\\\') or re.match(r'^[a-zA-Z]:\\', path):
|
||||
path = path.replace('/', '\\')
|
||||
elif path.startswith('/'):
|
||||
path = path.replace('\\', '/')
|
||||
break
|
||||
return path
|
||||
|
||||
def path_replace_reverse_movie(self, path):
|
||||
if path is None:
|
||||
return None
|
||||
|
||||
for path_mapping in self.path_mapping_movies:
|
||||
if path_mapping[0] == path_mapping[1]:
|
||||
continue
|
||||
if '' in path_mapping:
|
||||
continue
|
||||
if path_mapping[1] in path:
|
||||
path = path.replace(path_mapping[1], path_mapping[0])
|
||||
if path.startswith('\\\\') or re.match(r'^[a-zA-Z]:\\', path):
|
||||
path = path.replace('/', '\\')
|
||||
elif path.startswith('/'):
|
||||
path = path.replace('\\', '/')
|
||||
break
|
||||
return path
|
||||
|
||||
|
||||
path_mappings = PathMappings()
|
||||
|
||||
|
||||
def pp_replace(pp_command, episode, subtitles, language, language_code2, language_code3, episode_language, episode_language_code2, episode_language_code3, forced, score, subtitle_id, provider, series_id, episode_id, hi):
|
||||
if hi:
|
||||
modifier_string = " HI"
|
||||
|
|
|
@ -9,7 +9,6 @@ import subprocess
|
|||
from config import settings, configure_captcha_func
|
||||
from get_args import args
|
||||
from logger import configure_logging
|
||||
from helper import path_mappings
|
||||
|
||||
from dogpile.cache.region import register_backend as register_cache_backend
|
||||
import subliminal
|
||||
|
@ -187,4 +186,3 @@ from database import init_db, migrate_db
|
|||
init_db()
|
||||
migrate_db()
|
||||
init_binaries()
|
||||
path_mappings.update()
|
||||
|
|
|
@ -14,7 +14,7 @@ from custom_lang import CustomLanguage
|
|||
from database import get_profiles_list, get_profile_cutoff, TableEpisodes, TableShows, TableMovies
|
||||
from get_languages import alpha2_from_alpha3, language_from_alpha2, get_language_set
|
||||
from config import settings
|
||||
from helper import path_mappings, get_subtitle_destination_folder
|
||||
from helper import get_subtitle_destination_folder
|
||||
|
||||
from embedded_subs_reader import embedded_subs_reader
|
||||
from event_handler import event_stream, show_progress, hide_progress
|
||||
|
@ -26,18 +26,18 @@ global hi_regex
|
|||
hi_regex = re.compile(r'[*¶♫♪].{3,}[*¶♫♪]|[\[\(\{].{3,}[\]\)\}](?<!{\\an\d})')
|
||||
|
||||
|
||||
def store_subtitles(original_path, reversed_path, use_cache=True):
|
||||
logging.debug('BAZARR started subtitles indexing for this file: ' + reversed_path)
|
||||
def store_subtitles(path, use_cache=True):
|
||||
logging.debug('BAZARR started subtitles indexing for this file: ' + path)
|
||||
actual_subtitles = []
|
||||
if os.path.exists(reversed_path):
|
||||
if os.path.exists(path):
|
||||
if settings.general.getboolean('use_embedded_subs'):
|
||||
logging.debug("BAZARR is trying to index embedded subtitles.")
|
||||
try:
|
||||
item = TableEpisodes.select(TableEpisodes.episode_file_id, TableEpisodes.file_size)\
|
||||
.where(TableEpisodes.path == original_path)\
|
||||
.where(TableEpisodes.path == path)\
|
||||
.dicts()\
|
||||
.get()
|
||||
subtitle_languages = embedded_subs_reader(reversed_path,
|
||||
subtitle_languages = embedded_subs_reader(path,
|
||||
file_size=item['file_size'],
|
||||
episode_file_id=item['episode_file_id'],
|
||||
use_cache=use_cache)
|
||||
|
@ -64,19 +64,19 @@ def store_subtitles(original_path, reversed_path, use_cache=True):
|
|||
pass
|
||||
except Exception as e:
|
||||
logging.exception(
|
||||
"BAZARR error when trying to analyze this %s file: %s" % (os.path.splitext(reversed_path)[1], reversed_path))
|
||||
"BAZARR error when trying to analyze this %s file: %s" % (os.path.splitext(path)[1], path))
|
||||
pass
|
||||
try:
|
||||
dest_folder = get_subtitle_destination_folder()
|
||||
core.CUSTOM_PATHS = [dest_folder] if dest_folder else []
|
||||
subtitles = search_external_subtitles(reversed_path, languages=get_language_set(),
|
||||
subtitles = search_external_subtitles(path, languages=get_language_set(),
|
||||
only_one=settings.general.getboolean('single_language'))
|
||||
full_dest_folder_path = os.path.dirname(reversed_path)
|
||||
full_dest_folder_path = os.path.dirname(path)
|
||||
if dest_folder:
|
||||
if settings.general.subfolder == "absolute":
|
||||
full_dest_folder_path = dest_folder
|
||||
elif settings.general.subfolder == "relative":
|
||||
full_dest_folder_path = os.path.join(os.path.dirname(reversed_path), dest_folder)
|
||||
full_dest_folder_path = os.path.join(os.path.dirname(path), dest_folder)
|
||||
subtitles = guess_external_subtitles(full_dest_folder_path, subtitles)
|
||||
except Exception:
|
||||
logging.exception("BAZARR unable to index external subtitles.")
|
||||
|
@ -85,11 +85,11 @@ def store_subtitles(original_path, reversed_path, use_cache=True):
|
|||
if not language:
|
||||
continue
|
||||
|
||||
subtitle_path = get_external_subtitles_path(reversed_path, subtitle)
|
||||
subtitle_path = get_external_subtitles_path(path, subtitle)
|
||||
|
||||
custom = CustomLanguage.found_external(subtitle, subtitle_path)
|
||||
if custom is not None:
|
||||
actual_subtitles.append([custom, path_mappings.path_replace_reverse(subtitle_path)])
|
||||
actual_subtitles.append([custom, subtitle_path])
|
||||
|
||||
elif str(language) != 'und':
|
||||
if language.forced:
|
||||
|
@ -99,41 +99,41 @@ def store_subtitles(original_path, reversed_path, use_cache=True):
|
|||
else:
|
||||
language_str = str(language)
|
||||
logging.debug("BAZARR external subtitles detected: " + language_str)
|
||||
actual_subtitles.append([language_str, path_mappings.path_replace_reverse(subtitle_path)])
|
||||
actual_subtitles.append([language_str, subtitle_path])
|
||||
|
||||
TableEpisodes.update({TableEpisodes.subtitles: str(actual_subtitles)})\
|
||||
.where(TableEpisodes.path == original_path)\
|
||||
.where(TableEpisodes.path == path)\
|
||||
.execute()
|
||||
matching_episodes = TableEpisodes.select(TableEpisodes.sonarrEpisodeId, TableEpisodes.sonarrSeriesId)\
|
||||
.where(TableEpisodes.path == original_path)\
|
||||
matching_episodes = TableEpisodes.select(TableEpisodes.episodeId, TableEpisodes.seriesId)\
|
||||
.where(TableEpisodes.path == path)\
|
||||
.dicts()
|
||||
|
||||
for episode in matching_episodes:
|
||||
if episode:
|
||||
logging.debug("BAZARR storing those languages to DB: " + str(actual_subtitles))
|
||||
list_missing_subtitles(epno=episode['sonarrEpisodeId'])
|
||||
list_missing_subtitles(epno=episode['episodeId'])
|
||||
else:
|
||||
logging.debug("BAZARR haven't been able to update existing subtitles to DB : " + str(actual_subtitles))
|
||||
else:
|
||||
logging.debug("BAZARR this file doesn't seems to exist or isn't accessible.")
|
||||
|
||||
logging.debug('BAZARR ended subtitles indexing for this file: ' + reversed_path)
|
||||
logging.debug('BAZARR ended subtitles indexing for this file: ' + path)
|
||||
|
||||
return actual_subtitles
|
||||
|
||||
|
||||
def store_subtitles_movie(original_path, reversed_path, use_cache=True):
|
||||
logging.debug('BAZARR started subtitles indexing for this file: ' + reversed_path)
|
||||
def store_subtitles_movie(path, use_cache=True):
|
||||
logging.debug('BAZARR started subtitles indexing for this file: ' + path)
|
||||
actual_subtitles = []
|
||||
if os.path.exists(reversed_path):
|
||||
if os.path.exists(path):
|
||||
if settings.general.getboolean('use_embedded_subs'):
|
||||
logging.debug("BAZARR is trying to index embedded subtitles.")
|
||||
try:
|
||||
item = TableMovies.select(TableMovies.movie_file_id, TableMovies.file_size)\
|
||||
.where(TableMovies.path == original_path)\
|
||||
.where(TableMovies.path == path)\
|
||||
.dicts()\
|
||||
.get()
|
||||
subtitle_languages = embedded_subs_reader(reversed_path,
|
||||
subtitle_languages = embedded_subs_reader(path,
|
||||
file_size=item['file_size'],
|
||||
movie_file_id=item['movie_file_id'],
|
||||
use_cache=use_cache)
|
||||
|
@ -160,19 +160,19 @@ def store_subtitles_movie(original_path, reversed_path, use_cache=True):
|
|||
pass
|
||||
except Exception:
|
||||
logging.exception(
|
||||
"BAZARR error when trying to analyze this %s file: %s" % (os.path.splitext(reversed_path)[1], reversed_path))
|
||||
"BAZARR error when trying to analyze this %s file: %s" % (os.path.splitext(path)[1], path))
|
||||
pass
|
||||
|
||||
try:
|
||||
dest_folder = get_subtitle_destination_folder() or ''
|
||||
core.CUSTOM_PATHS = [dest_folder] if dest_folder else []
|
||||
subtitles = search_external_subtitles(reversed_path, languages=get_language_set())
|
||||
full_dest_folder_path = os.path.dirname(reversed_path)
|
||||
subtitles = search_external_subtitles(path, languages=get_language_set())
|
||||
full_dest_folder_path = os.path.dirname(path)
|
||||
if dest_folder:
|
||||
if settings.general.subfolder == "absolute":
|
||||
full_dest_folder_path = dest_folder
|
||||
elif settings.general.subfolder == "relative":
|
||||
full_dest_folder_path = os.path.join(os.path.dirname(reversed_path), dest_folder)
|
||||
full_dest_folder_path = os.path.join(os.path.dirname(path), dest_folder)
|
||||
subtitles = guess_external_subtitles(full_dest_folder_path, subtitles)
|
||||
except Exception as e:
|
||||
logging.exception("BAZARR unable to index external subtitles.")
|
||||
|
@ -182,11 +182,11 @@ def store_subtitles_movie(original_path, reversed_path, use_cache=True):
|
|||
if not language:
|
||||
continue
|
||||
|
||||
subtitle_path = get_external_subtitles_path(reversed_path, subtitle)
|
||||
subtitle_path = get_external_subtitles_path(path, subtitle)
|
||||
custom = CustomLanguage.found_external(subtitle, subtitle_path)
|
||||
|
||||
if custom is not None:
|
||||
actual_subtitles.append([custom, path_mappings.path_replace_reverse_movie(subtitle_path)])
|
||||
actual_subtitles.append([custom, subtitle_path])
|
||||
|
||||
elif str(language.basename) != 'und':
|
||||
if language.forced:
|
||||
|
@ -196,12 +196,12 @@ def store_subtitles_movie(original_path, reversed_path, use_cache=True):
|
|||
else:
|
||||
language_str = str(language)
|
||||
logging.debug("BAZARR external subtitles detected: " + language_str)
|
||||
actual_subtitles.append([language_str, path_mappings.path_replace_reverse_movie(subtitle_path)])
|
||||
actual_subtitles.append([language_str, subtitle_path])
|
||||
|
||||
TableMovies.update({TableMovies.subtitles: str(actual_subtitles)})\
|
||||
.where(TableMovies.path == original_path)\
|
||||
.where(TableMovies.path == path)\
|
||||
.execute()
|
||||
matching_movies = TableMovies.select(TableMovies.radarrId).where(TableMovies.path == original_path).dicts()
|
||||
matching_movies = TableMovies.select(TableMovies.radarrId).where(TableMovies.path == path).dicts()
|
||||
|
||||
for movie in matching_movies:
|
||||
if movie:
|
||||
|
@ -212,24 +212,24 @@ def store_subtitles_movie(original_path, reversed_path, use_cache=True):
|
|||
else:
|
||||
logging.debug("BAZARR this file doesn't seems to exist or isn't accessible.")
|
||||
|
||||
logging.debug('BAZARR ended subtitles indexing for this file: ' + reversed_path)
|
||||
logging.debug('BAZARR ended subtitles indexing for this file: ' + path)
|
||||
|
||||
return actual_subtitles
|
||||
|
||||
|
||||
def list_missing_subtitles(no=None, epno=None, send_event=True):
|
||||
if epno is not None:
|
||||
episodes_subtitles_clause = (TableEpisodes.sonarrEpisodeId == epno)
|
||||
episodes_subtitles_clause = (TableEpisodes.episodeId == epno)
|
||||
elif no is not None:
|
||||
episodes_subtitles_clause = (TableEpisodes.sonarrSeriesId == no)
|
||||
episodes_subtitles_clause = (TableEpisodes.seriesId == no)
|
||||
else:
|
||||
episodes_subtitles_clause = None
|
||||
episodes_subtitles = TableEpisodes.select(TableShows.sonarrSeriesId,
|
||||
TableEpisodes.sonarrEpisodeId,
|
||||
episodes_subtitles = TableEpisodes.select(TableShows.seriesId,
|
||||
TableEpisodes.episodeId,
|
||||
TableEpisodes.subtitles,
|
||||
TableShows.profileId,
|
||||
TableEpisodes.audio_language)\
|
||||
.join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId))\
|
||||
.join(TableShows, on=(TableEpisodes.seriesId == TableShows.seriesId))\
|
||||
.where(episodes_subtitles_clause)\
|
||||
.dicts()
|
||||
if isinstance(episodes_subtitles, str):
|
||||
|
@ -328,11 +328,11 @@ def list_missing_subtitles(no=None, epno=None, send_event=True):
|
|||
missing_subtitles_text = str(missing_subtitles_output_list)
|
||||
|
||||
TableEpisodes.update({TableEpisodes.missing_subtitles: missing_subtitles_text})\
|
||||
.where(TableEpisodes.sonarrEpisodeId == episode_subtitles['sonarrEpisodeId'])\
|
||||
.where(TableEpisodes.episodeId == episode_subtitles['episodeId'])\
|
||||
.execute()
|
||||
|
||||
if send_event:
|
||||
event_stream(type='episode', payload=episode_subtitles['sonarrEpisodeId'])
|
||||
event_stream(type='episode', payload=episode_subtitles['episodeId'])
|
||||
event_stream(type='badges')
|
||||
|
||||
|
||||
|
@ -458,7 +458,7 @@ def series_full_scan_subtitles():
|
|||
name='Episodes subtitles',
|
||||
value=i,
|
||||
count=count_episodes)
|
||||
store_subtitles(episode['path'], path_mappings.path_replace(episode['path']), use_cache=use_ffprobe_cache)
|
||||
store_subtitles(episode['path'], use_cache=use_ffprobe_cache)
|
||||
|
||||
hide_progress(id='episodes_disk_scan')
|
||||
|
||||
|
@ -478,8 +478,7 @@ def movies_full_scan_subtitles():
|
|||
name='Movies subtitles',
|
||||
value=i,
|
||||
count=count_movies)
|
||||
store_subtitles_movie(movie['path'], path_mappings.path_replace_movie(movie['path']),
|
||||
use_cache=use_ffprobe_cache)
|
||||
store_subtitles_movie(movie['path'], use_cache=use_ffprobe_cache)
|
||||
|
||||
hide_progress(id='movies_disk_scan')
|
||||
|
||||
|
@ -488,13 +487,13 @@ def movies_full_scan_subtitles():
|
|||
|
||||
def series_scan_subtitles(no):
|
||||
episodes = TableEpisodes.select(TableEpisodes.path)\
|
||||
.where(TableEpisodes.sonarrSeriesId == no)\
|
||||
.order_by(TableEpisodes.sonarrEpisodeId)\
|
||||
.where(TableEpisodes.seriesId == no)\
|
||||
.order_by(TableEpisodes.episodeId)\
|
||||
.dicts()
|
||||
|
||||
for episode in episodes:
|
||||
sleep()
|
||||
store_subtitles(episode['path'], path_mappings.path_replace(episode['path']), use_cache=False)
|
||||
store_subtitles(episode['path'], use_cache=False)
|
||||
|
||||
|
||||
def movies_scan_subtitles(no):
|
||||
|
@ -505,7 +504,7 @@ def movies_scan_subtitles(no):
|
|||
|
||||
for movie in movies:
|
||||
sleep()
|
||||
store_subtitles_movie(movie['path'], path_mappings.path_replace_movie(movie['path']), use_cache=False)
|
||||
store_subtitles_movie(movie['path'], use_cache=False)
|
||||
|
||||
|
||||
def get_external_subtitles_path(file, subtitle):
|
||||
|
|
|
@ -23,7 +23,7 @@ os.environ["BAZARR_VERSION"] = bazarr_version.lstrip('v')
|
|||
import libs
|
||||
|
||||
from get_args import args
|
||||
from config import settings, url_sonarr, url_radarr, configure_proxy_func, base_url
|
||||
from config import settings, configure_proxy_func, base_url
|
||||
|
||||
from init import *
|
||||
from database import System
|
||||
|
@ -38,12 +38,11 @@ from flask import make_response, request, redirect, abort, render_template, Resp
|
|||
from get_series import *
|
||||
from get_episodes import *
|
||||
from get_movies import *
|
||||
from signalr_client import sonarr_signalr_client, radarr_signalr_client
|
||||
|
||||
from check_update import apply_update, check_if_new_update, check_releases
|
||||
from server import app, webserver
|
||||
from functools import wraps
|
||||
from utils import check_credentials, get_sonarr_info, get_radarr_info
|
||||
from utils import check_credentials
|
||||
|
||||
# Install downloaded update
|
||||
if bazarr_version != '':
|
||||
|
@ -127,48 +126,14 @@ def download_log():
|
|||
|
||||
@check_login
|
||||
@app.route('/images/series/<path:url>', methods=['GET'])
|
||||
def series_images(url):
|
||||
url = url.strip("/")
|
||||
apikey = settings.sonarr.apikey
|
||||
baseUrl = settings.sonarr.base_url
|
||||
if get_sonarr_info.is_legacy():
|
||||
url_image = (url_sonarr() + '/api/' + url.lstrip(baseUrl) + '?apikey=' +
|
||||
apikey).replace('poster-250', 'poster-500')
|
||||
else:
|
||||
url_image = (url_sonarr() + '/api/v3/' + url.lstrip(baseUrl) + '?apikey=' +
|
||||
apikey).replace('poster-250', 'poster-500')
|
||||
try:
|
||||
req = requests.get(url_image, stream=True, timeout=15, verify=False, headers=headers)
|
||||
except:
|
||||
return '', 404
|
||||
else:
|
||||
return Response(stream_with_context(req.iter_content(2048)), content_type=req.headers['content-type'])
|
||||
def series_images(seriesId):
|
||||
pass
|
||||
|
||||
|
||||
@check_login
|
||||
@app.route('/images/movies/<path:url>', methods=['GET'])
|
||||
def movies_images(url):
|
||||
apikey = settings.radarr.apikey
|
||||
baseUrl = settings.radarr.base_url
|
||||
if get_radarr_info.is_legacy():
|
||||
url_image = url_radarr() + '/api/' + url.lstrip(baseUrl) + '?apikey=' + apikey
|
||||
else:
|
||||
url_image = url_radarr() + '/api/v3/' + url.lstrip(baseUrl) + '?apikey=' + apikey
|
||||
try:
|
||||
req = requests.get(url_image, stream=True, timeout=15, verify=False, headers=headers)
|
||||
except:
|
||||
return '', 404
|
||||
else:
|
||||
return Response(stream_with_context(req.iter_content(2048)), content_type=req.headers['content-type'])
|
||||
|
||||
|
||||
# @app.route('/check_update')
|
||||
# @authenticate
|
||||
# def check_update():
|
||||
# if not args.no_update:
|
||||
# check_and_apply_update()
|
||||
|
||||
# return '', 200
|
||||
def movies_images(movieId):
|
||||
pass
|
||||
|
||||
|
||||
def configured():
|
||||
|
@ -202,12 +167,5 @@ def proxy(protocol, url):
|
|||
return dict(status=False, error=result.raise_for_status())
|
||||
|
||||
|
||||
greenlets = []
|
||||
if settings.general.getboolean('use_sonarr'):
|
||||
greenlets.append(Greenlet.spawn(sonarr_signalr_client.start))
|
||||
if settings.general.getboolean('use_radarr'):
|
||||
greenlets.append(Greenlet.spawn(radarr_signalr_client.start))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
webserver.start()
|
||||
|
|
|
@ -46,43 +46,43 @@ def get_notifier_providers():
|
|||
return providers
|
||||
|
||||
|
||||
def get_series(sonarr_series_id):
|
||||
def get_series(series_id):
|
||||
data = TableShows.select(TableShows.title, TableShows.year)\
|
||||
.where(TableShows.sonarrSeriesId == sonarr_series_id)\
|
||||
.where(TableShows.seriesId == series_id)\
|
||||
.dicts()\
|
||||
.get()
|
||||
|
||||
return {'title': data['title'], 'year': data['year']}
|
||||
|
||||
|
||||
def get_episode_name(sonarr_episode_id):
|
||||
def get_episode_name(episode_id):
|
||||
data = TableEpisodes.select(TableEpisodes.title, TableEpisodes.season, TableEpisodes.episode)\
|
||||
.where(TableEpisodes.sonarrEpisodeId == sonarr_episode_id)\
|
||||
.where(TableEpisodes.episodeId == episode_id)\
|
||||
.dicts()\
|
||||
.get()
|
||||
|
||||
return data['title'], data['season'], data['episode']
|
||||
|
||||
|
||||
def get_movie(radarr_id):
|
||||
def get_movie(movie_id):
|
||||
data = TableMovies.select(TableMovies.title, TableMovies.year)\
|
||||
.where(TableMovies.radarrId == radarr_id)\
|
||||
.where(TableMovies.movieId == movie_id)\
|
||||
.dicts()\
|
||||
.get()
|
||||
|
||||
return {'title': data['title'], 'year': data['year']}
|
||||
|
||||
|
||||
def send_notifications(sonarr_series_id, sonarr_episode_id, message):
|
||||
def send_notifications(series_id, episode_id, message):
|
||||
providers = get_notifier_providers()
|
||||
series = get_series(sonarr_series_id)
|
||||
series = get_series(series_id)
|
||||
series_title = series['title']
|
||||
series_year = series['year']
|
||||
if series_year not in [None, '', '0']:
|
||||
series_year = ' ({})'.format(series_year)
|
||||
else:
|
||||
series_year = ''
|
||||
episode = get_episode_name(sonarr_episode_id)
|
||||
episode = get_episode_name(episode_id)
|
||||
|
||||
asset = apprise.AppriseAsset(async_mode=False)
|
||||
|
||||
|
@ -99,9 +99,9 @@ def send_notifications(sonarr_series_id, sonarr_episode_id, message):
|
|||
)
|
||||
|
||||
|
||||
def send_notifications_movie(radarr_id, message):
|
||||
def send_notifications_movie(movie_id, message):
|
||||
providers = get_notifier_providers()
|
||||
movie = get_movie(radarr_id)
|
||||
movie = get_movie(movie_id)
|
||||
movie_title = movie['title']
|
||||
movie_year = movie['year']
|
||||
if movie_year not in [None, '', '0']:
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
# coding=utf-8
|
||||
|
||||
from get_episodes import sync_episodes, update_all_episodes
|
||||
from get_movies import update_movies, update_all_movies
|
||||
from get_series import update_series
|
||||
from get_episodes import update_all_episodes
|
||||
from get_movies import update_all_movies
|
||||
from config import settings
|
||||
from get_subtitle import wanted_search_missing_subtitles_series, wanted_search_missing_subtitles_movies, \
|
||||
upgrade_subtitles
|
||||
|
@ -54,10 +53,6 @@ class Scheduler:
|
|||
self.aps_scheduler.start()
|
||||
|
||||
def update_configurable_tasks(self):
|
||||
self.__sonarr_update_task()
|
||||
self.__radarr_update_task()
|
||||
self.__sonarr_full_update_task()
|
||||
self.__radarr_full_update_task()
|
||||
self.__update_bazarr_task()
|
||||
self.__search_wanted_subtitles_task()
|
||||
self.__upgrade_subtitles_task()
|
||||
|
@ -140,24 +135,6 @@ class Scheduler:
|
|||
|
||||
return task_list
|
||||
|
||||
def __sonarr_update_task(self):
|
||||
if settings.general.getboolean('use_sonarr'):
|
||||
self.aps_scheduler.add_job(
|
||||
update_series, IntervalTrigger(minutes=int(settings.sonarr.series_sync)), max_instances=1,
|
||||
coalesce=True, misfire_grace_time=15, id='update_series', name='Update Series list from Sonarr',
|
||||
replace_existing=True)
|
||||
self.aps_scheduler.add_job(
|
||||
sync_episodes, IntervalTrigger(minutes=int(settings.sonarr.episodes_sync)), max_instances=1,
|
||||
coalesce=True, misfire_grace_time=15, id='sync_episodes', name='Sync episodes with Sonarr',
|
||||
replace_existing=True)
|
||||
|
||||
def __radarr_update_task(self):
|
||||
if settings.general.getboolean('use_radarr'):
|
||||
self.aps_scheduler.add_job(
|
||||
update_movies, IntervalTrigger(minutes=int(settings.radarr.movies_sync)), max_instances=1,
|
||||
coalesce=True, misfire_grace_time=15, id='update_movies', name='Update Movie list from Radarr',
|
||||
replace_existing=True)
|
||||
|
||||
def __cache_cleanup_task(self):
|
||||
self.aps_scheduler.add_job(cache_maintenance, IntervalTrigger(hours=24), max_instances=1, coalesce=True,
|
||||
misfire_grace_time=15, id='cache_cleanup', name='Cache maintenance')
|
||||
|
@ -166,45 +143,6 @@ class Scheduler:
|
|||
self.aps_scheduler.add_job(check_health, IntervalTrigger(hours=6), max_instances=1, coalesce=True,
|
||||
misfire_grace_time=15, id='check_health', name='Check health')
|
||||
|
||||
def __sonarr_full_update_task(self):
|
||||
if settings.general.getboolean('use_sonarr'):
|
||||
full_update = settings.sonarr.full_update
|
||||
if full_update == "Daily":
|
||||
self.aps_scheduler.add_job(
|
||||
update_all_episodes, CronTrigger(hour=settings.sonarr.full_update_hour), max_instances=1,
|
||||
coalesce=True, misfire_grace_time=15, id='update_all_episodes',
|
||||
name='Update all Episode Subtitles from disk', replace_existing=True)
|
||||
elif full_update == "Weekly":
|
||||
self.aps_scheduler.add_job(
|
||||
update_all_episodes,
|
||||
CronTrigger(day_of_week=settings.sonarr.full_update_day, hour=settings.sonarr.full_update_hour),
|
||||
max_instances=1, coalesce=True, misfire_grace_time=15, id='update_all_episodes',
|
||||
name='Update all Episode Subtitles from disk', replace_existing=True)
|
||||
elif full_update == "Manually":
|
||||
self.aps_scheduler.add_job(
|
||||
update_all_episodes, CronTrigger(year='2100'), max_instances=1, coalesce=True,
|
||||
misfire_grace_time=15, id='update_all_episodes',
|
||||
name='Update all Episode Subtitles from disk', replace_existing=True)
|
||||
|
||||
def __radarr_full_update_task(self):
|
||||
if settings.general.getboolean('use_radarr'):
|
||||
full_update = settings.radarr.full_update
|
||||
if full_update == "Daily":
|
||||
self.aps_scheduler.add_job(
|
||||
update_all_movies, CronTrigger(hour=settings.radarr.full_update_hour), max_instances=1,
|
||||
coalesce=True, misfire_grace_time=15,
|
||||
id='update_all_movies', name='Update all Movie Subtitles from disk', replace_existing=True)
|
||||
elif full_update == "Weekly":
|
||||
self.aps_scheduler.add_job(
|
||||
update_all_movies,
|
||||
CronTrigger(day_of_week=settings.radarr.full_update_day, hour=settings.radarr.full_update_hour),
|
||||
max_instances=1, coalesce=True, misfire_grace_time=15, id='update_all_movies',
|
||||
name='Update all Movie Subtitles from disk', replace_existing=True)
|
||||
elif full_update == "Manually":
|
||||
self.aps_scheduler.add_job(
|
||||
update_all_movies, CronTrigger(year='2100'), max_instances=1, coalesce=True, misfire_grace_time=15,
|
||||
id='update_all_movies', name='Update all Movie Subtitles from disk', replace_existing=True)
|
||||
|
||||
def __update_bazarr_task(self):
|
||||
if not args.no_update and os.environ["BAZARR_VERSION"] != '':
|
||||
task_name = 'Update Bazarr'
|
||||
|
@ -227,12 +165,12 @@ class Scheduler:
|
|||
id='update_release', name='Update Release Info', replace_existing=True)
|
||||
|
||||
def __search_wanted_subtitles_task(self):
|
||||
if settings.general.getboolean('use_sonarr'):
|
||||
if settings.general.getboolean('use_series'):
|
||||
self.aps_scheduler.add_job(
|
||||
wanted_search_missing_subtitles_series, IntervalTrigger(hours=int(settings.general.wanted_search_frequency)),
|
||||
max_instances=1, coalesce=True, misfire_grace_time=15, id='wanted_search_missing_subtitles_series',
|
||||
name='Search for wanted Series Subtitles', replace_existing=True)
|
||||
if settings.general.getboolean('use_radarr'):
|
||||
if settings.general.getboolean('use_movies'):
|
||||
self.aps_scheduler.add_job(
|
||||
wanted_search_missing_subtitles_movies, IntervalTrigger(hours=int(settings.general.wanted_search_frequency_movie)),
|
||||
max_instances=1, coalesce=True, misfire_grace_time=15, id='wanted_search_missing_subtitles_movies',
|
||||
|
@ -240,7 +178,7 @@ class Scheduler:
|
|||
|
||||
def __upgrade_subtitles_task(self):
|
||||
if settings.general.getboolean('upgrade_subs') and \
|
||||
(settings.general.getboolean('use_sonarr') or settings.general.getboolean('use_radarr')):
|
||||
(settings.general.getboolean('use_series') or settings.general.getboolean('use_movies')):
|
||||
self.aps_scheduler.add_job(
|
||||
upgrade_subtitles, IntervalTrigger(hours=int(settings.general.upgrade_frequency)), max_instances=1,
|
||||
coalesce=True, misfire_grace_time=15, id='upgrade_subtitles',
|
||||
|
@ -257,12 +195,3 @@ class Scheduler:
|
|||
|
||||
|
||||
scheduler = Scheduler()
|
||||
|
||||
# Force the execution of the sync process with Sonarr and Radarr after migration to v0.9.1
|
||||
if 'BAZARR_AUDIO_PROFILES_MIGRATION' in os.environ:
|
||||
if settings.general.getboolean('use_sonarr'):
|
||||
scheduler.aps_scheduler.modify_job('update_series', next_run_time=datetime.now())
|
||||
scheduler.aps_scheduler.modify_job('sync_episodes', next_run_time=datetime.now())
|
||||
if settings.general.getboolean('use_radarr'):
|
||||
scheduler.aps_scheduler.modify_job('update_movies', next_run_time=datetime.now())
|
||||
del os.environ['BAZARR_AUDIO_PROFILES_MIGRATION']
|
||||
|
|
|
@ -22,7 +22,7 @@ class Server:
|
|||
def __init__(self):
|
||||
# Mute DeprecationWarning
|
||||
warnings.simplefilter("ignore", DeprecationWarning)
|
||||
# Mute Insecure HTTPS requests made to Sonarr and Radarr
|
||||
# Mute Insecure outgoing HTTPS requests
|
||||
warnings.filterwarnings('ignore', message='Unverified HTTPS request')
|
||||
# Mute Python3 BrokenPipeError
|
||||
warnings.simplefilter("ignore", BrokenPipeError)
|
||||
|
|
|
@ -1,192 +0,0 @@
|
|||
# coding=utf-8
|
||||
|
||||
import logging
|
||||
|
||||
import gevent
|
||||
import json
|
||||
import os
|
||||
from requests import Session
|
||||
from signalr import Connection
|
||||
from requests.exceptions import ConnectionError
|
||||
from signalrcore.hub_connection_builder import HubConnectionBuilder
|
||||
|
||||
from config import settings, url_sonarr, url_radarr
|
||||
from get_episodes import sync_episodes, sync_one_episode
|
||||
from get_series import update_series, update_one_series
|
||||
from get_movies import update_movies, update_one_movie
|
||||
from scheduler import scheduler
|
||||
from utils import get_sonarr_info
|
||||
from get_args import args
|
||||
|
||||
|
||||
headers = {"User-Agent": os.environ["SZ_USER_AGENT"]}
|
||||
|
||||
|
||||
class SonarrSignalrClient:
|
||||
def __init__(self):
|
||||
super(SonarrSignalrClient, self).__init__()
|
||||
self.apikey_sonarr = None
|
||||
self.session = Session()
|
||||
self.session.timeout = 60
|
||||
self.session.verify = False
|
||||
self.session.headers = headers
|
||||
self.connection = None
|
||||
|
||||
def start(self):
|
||||
if get_sonarr_info.is_legacy():
|
||||
logging.warning('BAZARR can only sync from Sonarr v3 SignalR feed to get real-time update. You should '
|
||||
'consider upgrading your version({}).'.format(get_sonarr_info.version()))
|
||||
raise gevent.GreenletExit
|
||||
else:
|
||||
logging.info('BAZARR trying to connect to Sonarr SignalR feed...')
|
||||
self.configure()
|
||||
while not self.connection.started:
|
||||
try:
|
||||
self.connection.start()
|
||||
except ConnectionError:
|
||||
gevent.sleep(5)
|
||||
except json.decoder.JSONDecodeError:
|
||||
logging.error("BAZARR cannot parse JSON returned by SignalR feed. This is caused by a permissions "
|
||||
"issue when Sonarr try to access its /config/.config directory. You should fix "
|
||||
"permissions on that directory and restart Sonarr. Also, if you're a Docker image "
|
||||
"user, you should make sure you properly defined PUID/PGID environment variables. "
|
||||
"Otherwise, please contact Sonarr support.")
|
||||
raise gevent.GreenletExit
|
||||
else:
|
||||
logging.info('BAZARR SignalR client for Sonarr is connected and waiting for events.')
|
||||
finally:
|
||||
if not args.dev:
|
||||
scheduler.add_job(update_series, kwargs={'send_event': True}, max_instances=1)
|
||||
scheduler.add_job(sync_episodes, kwargs={'send_event': True}, max_instances=1)
|
||||
|
||||
def stop(self, log=True):
|
||||
try:
|
||||
self.connection.close()
|
||||
except Exception as e:
|
||||
pass
|
||||
if log:
|
||||
logging.info('BAZARR SignalR client for Sonarr is now disconnected.')
|
||||
|
||||
def restart(self):
|
||||
if self.connection:
|
||||
if self.connection.started:
|
||||
try:
|
||||
self.stop(log=False)
|
||||
except:
|
||||
self.connection.started = False
|
||||
if settings.general.getboolean('use_sonarr'):
|
||||
self.start()
|
||||
|
||||
def exception_handler(self, type, exception, traceback):
|
||||
logging.error('BAZARR connection to Sonarr SignalR feed has been lost.')
|
||||
self.restart()
|
||||
|
||||
def configure(self):
|
||||
self.apikey_sonarr = settings.sonarr.apikey
|
||||
self.connection = Connection(url_sonarr() + "/signalr", self.session)
|
||||
self.connection.qs = {'apikey': self.apikey_sonarr}
|
||||
sonarr_hub = self.connection.register_hub('') # Sonarr doesn't use named hub
|
||||
|
||||
sonarr_method = ['series', 'episode']
|
||||
for item in sonarr_method:
|
||||
sonarr_hub.client.on(item, dispatcher)
|
||||
|
||||
self.connection.exception += self.exception_handler
|
||||
|
||||
|
||||
class RadarrSignalrClient:
|
||||
def __init__(self):
|
||||
super(RadarrSignalrClient, self).__init__()
|
||||
self.apikey_radarr = None
|
||||
self.connection = None
|
||||
|
||||
def start(self):
|
||||
self.configure()
|
||||
logging.info('BAZARR trying to connect to Radarr SignalR feed...')
|
||||
while self.connection.transport.state.value not in [0, 1, 2]:
|
||||
try:
|
||||
self.connection.start()
|
||||
except ConnectionError:
|
||||
gevent.sleep(5)
|
||||
|
||||
def stop(self):
|
||||
logging.info('BAZARR SignalR client for Radarr is now disconnected.')
|
||||
self.connection.stop()
|
||||
|
||||
def restart(self):
|
||||
if self.connection:
|
||||
if self.connection.transport.state.value in [0, 1, 2]:
|
||||
self.stop()
|
||||
if settings.general.getboolean('use_radarr'):
|
||||
self.start()
|
||||
|
||||
def exception_handler(self):
|
||||
logging.error("BAZARR connection to Radarr SignalR feed has failed. We'll try to reconnect.")
|
||||
self.restart()
|
||||
|
||||
@staticmethod
|
||||
def on_connect_handler():
|
||||
logging.info('BAZARR SignalR client for Radarr is connected and waiting for events.')
|
||||
if not args.dev:
|
||||
scheduler.add_job(update_movies, kwargs={'send_event': True}, max_instances=1)
|
||||
|
||||
def configure(self):
|
||||
self.apikey_radarr = settings.radarr.apikey
|
||||
self.connection = HubConnectionBuilder() \
|
||||
.with_url(url_radarr() + "/signalr/messages?access_token={}".format(self.apikey_radarr),
|
||||
options={
|
||||
"verify_ssl": False,
|
||||
"headers": headers
|
||||
}) \
|
||||
.with_automatic_reconnect({
|
||||
"type": "raw",
|
||||
"keep_alive_interval": 5,
|
||||
"reconnect_interval": 180,
|
||||
"max_attempts": None
|
||||
}).build()
|
||||
self.connection.on_open(self.on_connect_handler)
|
||||
self.connection.on_reconnect(lambda: logging.error('BAZARR SignalR client for Radarr connection as been lost. '
|
||||
'Trying to reconnect...'))
|
||||
self.connection.on_close(lambda: logging.debug('BAZARR SignalR client for Radarr is disconnected.'))
|
||||
self.connection.on_error(self.exception_handler)
|
||||
self.connection.on("receiveMessage", dispatcher)
|
||||
|
||||
|
||||
def dispatcher(data):
|
||||
try:
|
||||
topic = media_id = action = None
|
||||
episodesChanged = None
|
||||
if isinstance(data, dict):
|
||||
topic = data['name']
|
||||
try:
|
||||
media_id = data['body']['resource']['id']
|
||||
action = data['body']['action']
|
||||
if 'episodesChanged' in data['body']['resource']:
|
||||
episodesChanged = data['body']['resource']['episodesChanged']
|
||||
except KeyError:
|
||||
return
|
||||
elif isinstance(data, list):
|
||||
topic = data[0]['name']
|
||||
try:
|
||||
media_id = data[0]['body']['resource']['id']
|
||||
action = data[0]['body']['action']
|
||||
except KeyError:
|
||||
return
|
||||
|
||||
if topic == 'series':
|
||||
update_one_series(series_id=media_id, action=action)
|
||||
if episodesChanged:
|
||||
# this will happen if a season monitored status is changed.
|
||||
sync_episodes(series_id=media_id, send_event=True)
|
||||
elif topic == 'episode':
|
||||
sync_one_episode(episode_id=media_id)
|
||||
elif topic == 'movie':
|
||||
update_one_movie(movie_id=media_id, action=action)
|
||||
except Exception as e:
|
||||
logging.debug('BAZARR an exception occurred while parsing SignalR feed: {}'.format(repr(e)))
|
||||
finally:
|
||||
return
|
||||
|
||||
|
||||
sonarr_signalr_client = SonarrSignalrClient()
|
||||
radarr_signalr_client = RadarrSignalrClient()
|
|
@ -4,7 +4,6 @@ from ffsubsync.ffsubsync import run, make_parser
|
|||
from utils import get_binary
|
||||
from utils import history_log, history_log_movie
|
||||
from get_languages import language_from_alpha2
|
||||
from helper import path_mappings
|
||||
from config import settings
|
||||
from get_args import args
|
||||
|
||||
|
@ -24,8 +23,8 @@ class SubSyncer:
|
|||
self.vad = 'subs_then_webrtc'
|
||||
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):
|
||||
def sync(self, video_path, srt_path, srt_lang, media_type, series_id=None, episode_id=None,
|
||||
movie_id=None):
|
||||
self.reference = video_path
|
||||
self.srtin = srt_path
|
||||
self.srtout = '{}.synced.srt'.format(os.path.splitext(self.srtin)[0])
|
||||
|
@ -72,13 +71,12 @@ class SubSyncer:
|
|||
"{:.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=srt_lang, subtitles_path=srt_path)
|
||||
history_log(action=5, series_id=series_id, episode_id=episode_id,
|
||||
description=message, video_path=self.reference, language=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=srt_lang, subtitles_path=srt_path)
|
||||
history_log_movie(action=5, movie_id=movie_id, description=message,
|
||||
video_path=self.reference, language=srt_lang, subtitles_path=srt_path)
|
||||
else:
|
||||
logging.error('BAZARR unable to sync subtitles: {0}'.format(self.srtin))
|
||||
|
||||
|
|
205
bazarr/utils.py
205
bazarr/utils.py
|
@ -12,13 +12,12 @@ import stat
|
|||
|
||||
from whichcraft import which
|
||||
from get_args import args
|
||||
from config import settings, url_sonarr, url_radarr
|
||||
from config import settings
|
||||
from custom_lang import CustomLanguage
|
||||
from database import TableHistory, TableHistoryMovie, TableBlacklist, TableBlacklistMovie, TableShowsRootfolder, \
|
||||
TableMoviesRootfolder
|
||||
from event_handler import event_stream
|
||||
from get_languages import alpha2_from_alpha3, language_from_alpha3, language_from_alpha2, alpha3_from_alpha2
|
||||
from helper import path_mappings
|
||||
from list_subtitles import store_subtitles, store_subtitles_movie
|
||||
from subliminal_patch.subtitle import Subtitle
|
||||
from subliminal_patch.core import get_subtitle_path
|
||||
|
@ -37,12 +36,12 @@ class BinaryNotFound(Exception):
|
|||
pass
|
||||
|
||||
|
||||
def history_log(action, sonarr_series_id, sonarr_episode_id, description, video_path=None, language=None, provider=None,
|
||||
def history_log(action, series_id, episode_id, description, video_path=None, language=None, provider=None,
|
||||
score=None, subs_id=None, subtitles_path=None):
|
||||
TableHistory.insert({
|
||||
TableHistory.action: action,
|
||||
TableHistory.sonarrSeriesId: sonarr_series_id,
|
||||
TableHistory.sonarrEpisodeId: sonarr_episode_id,
|
||||
TableHistory.seriesId: series_id,
|
||||
TableHistory.episodeId: episode_id,
|
||||
TableHistory.timestamp: time.time(),
|
||||
TableHistory.description: description,
|
||||
TableHistory.video_path: video_path,
|
||||
|
@ -55,10 +54,10 @@ def history_log(action, sonarr_series_id, sonarr_episode_id, description, video_
|
|||
event_stream(type='episode-history')
|
||||
|
||||
|
||||
def blacklist_log(sonarr_series_id, sonarr_episode_id, provider, subs_id, language):
|
||||
def blacklist_log(series_id, episode_id, provider, subs_id, language):
|
||||
TableBlacklist.insert({
|
||||
TableBlacklist.sonarr_series_id: sonarr_series_id,
|
||||
TableBlacklist.sonarr_episode_id: sonarr_episode_id,
|
||||
TableBlacklist.series_id: series_id,
|
||||
TableBlacklist.episode_id: episode_id,
|
||||
TableBlacklist.timestamp: time.time(),
|
||||
TableBlacklist.provider: provider,
|
||||
TableBlacklist.subs_id: subs_id,
|
||||
|
@ -79,11 +78,11 @@ def blacklist_delete_all():
|
|||
event_stream(type='episode-blacklist', action='delete')
|
||||
|
||||
|
||||
def history_log_movie(action, radarr_id, description, video_path=None, language=None, provider=None, score=None,
|
||||
def history_log_movie(action, movie_id, description, video_path=None, language=None, provider=None, score=None,
|
||||
subs_id=None, subtitles_path=None):
|
||||
TableHistoryMovie.insert({
|
||||
TableHistoryMovie.action: action,
|
||||
TableHistoryMovie.radarrId: radarr_id,
|
||||
TableHistoryMovie.movieId: movie_id,
|
||||
TableHistoryMovie.timestamp: time.time(),
|
||||
TableHistoryMovie.description: description,
|
||||
TableHistoryMovie.video_path: video_path,
|
||||
|
@ -96,9 +95,9 @@ def history_log_movie(action, radarr_id, description, video_path=None, language=
|
|||
event_stream(type='movie-history')
|
||||
|
||||
|
||||
def blacklist_log_movie(radarr_id, provider, subs_id, language):
|
||||
def blacklist_log_movie(movie_id, provider, subs_id, language):
|
||||
TableBlacklistMovie.insert({
|
||||
TableBlacklistMovie.radarr_id: radarr_id,
|
||||
TableBlacklistMovie.movie_id: movie_id,
|
||||
TableBlacklistMovie.timestamp: time.time(),
|
||||
TableBlacklistMovie.provider: provider,
|
||||
TableBlacklistMovie.subs_id: subs_id,
|
||||
|
@ -236,126 +235,8 @@ def cache_maintenance():
|
|||
remove_expired(fn, pack_cache_validity)
|
||||
|
||||
|
||||
class GetSonarrInfo:
|
||||
@staticmethod
|
||||
def version():
|
||||
"""
|
||||
Call system/status API endpoint and get the Sonarr version
|
||||
@return: str
|
||||
"""
|
||||
sonarr_version = region.get("sonarr_version", expiration_time=datetime.timedelta(seconds=60).total_seconds())
|
||||
if sonarr_version:
|
||||
region.set("sonarr_version", sonarr_version)
|
||||
return sonarr_version
|
||||
else:
|
||||
sonarr_version = ''
|
||||
if settings.general.getboolean('use_sonarr'):
|
||||
try:
|
||||
sv = url_sonarr() + "/api/system/status?apikey=" + settings.sonarr.apikey
|
||||
sonarr_json = requests.get(sv, timeout=60, verify=False, headers=headers).json()
|
||||
if 'version' in sonarr_json:
|
||||
sonarr_version = sonarr_json['version']
|
||||
else:
|
||||
sv = url_sonarr() + "/api/v3/system/status?apikey=" + settings.sonarr.apikey
|
||||
sonarr_version = requests.get(sv, timeout=60, verify=False, headers=headers).json()['version']
|
||||
except Exception:
|
||||
logging.debug('BAZARR cannot get Sonarr version')
|
||||
sonarr_version = 'unknown'
|
||||
logging.debug('BAZARR got this Sonarr version from its API: {}'.format(sonarr_version))
|
||||
region.set("sonarr_version", sonarr_version)
|
||||
return sonarr_version
|
||||
|
||||
def is_legacy(self):
|
||||
"""
|
||||
Call self.version() and parse the result to determine if it's a legacy version of Sonarr API
|
||||
@return: bool
|
||||
"""
|
||||
sonarr_version = self.version()
|
||||
if sonarr_version.startswith(('0.', '2.')):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
get_sonarr_info = GetSonarrInfo()
|
||||
|
||||
|
||||
def notify_sonarr(sonarr_series_id):
|
||||
try:
|
||||
if get_sonarr_info.is_legacy():
|
||||
url = url_sonarr() + "/api/command?apikey=" + settings.sonarr.apikey
|
||||
else:
|
||||
url = url_sonarr() + "/api/v3/command?apikey=" + settings.sonarr.apikey
|
||||
data = {
|
||||
'name': 'RescanSeries',
|
||||
'seriesId': int(sonarr_series_id)
|
||||
}
|
||||
requests.post(url, json=data, timeout=60, verify=False, headers=headers)
|
||||
except Exception as e:
|
||||
logging.exception('BAZARR cannot notify Sonarr')
|
||||
|
||||
|
||||
class GetRadarrInfo:
|
||||
@staticmethod
|
||||
def version():
|
||||
"""
|
||||
Call system/status API endpoint and get the Radarr version
|
||||
@return: str
|
||||
"""
|
||||
radarr_version = region.get("radarr_version", expiration_time=datetime.timedelta(seconds=60).total_seconds())
|
||||
if radarr_version:
|
||||
region.set("radarr_version", radarr_version)
|
||||
return radarr_version
|
||||
else:
|
||||
radarr_version = ''
|
||||
if settings.general.getboolean('use_radarr'):
|
||||
try:
|
||||
rv = url_radarr() + "/api/system/status?apikey=" + settings.radarr.apikey
|
||||
radarr_json = requests.get(rv, timeout=60, verify=False, headers=headers).json()
|
||||
if 'version' in radarr_json:
|
||||
radarr_version = radarr_json['version']
|
||||
else:
|
||||
rv = url_radarr() + "/api/v3/system/status?apikey=" + settings.radarr.apikey
|
||||
radarr_version = requests.get(rv, timeout=60, verify=False, headers=headers).json()['version']
|
||||
except Exception as e:
|
||||
logging.debug('BAZARR cannot get Radarr version')
|
||||
radarr_version = 'unknown'
|
||||
logging.debug('BAZARR got this Radarr version from its API: {}'.format(radarr_version))
|
||||
region.set("radarr_version", radarr_version)
|
||||
return radarr_version
|
||||
|
||||
def is_legacy(self):
|
||||
"""
|
||||
Call self.version() and parse the result to determine if it's a legacy version of Radarr
|
||||
@return: bool
|
||||
"""
|
||||
radarr_version = self.version()
|
||||
if radarr_version.startswith('0.'):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
get_radarr_info = GetRadarrInfo()
|
||||
|
||||
|
||||
def notify_radarr(radarr_id):
|
||||
try:
|
||||
if get_radarr_info.is_legacy():
|
||||
url = url_radarr() + "/api/command?apikey=" + settings.radarr.apikey
|
||||
else:
|
||||
url = url_radarr() + "/api/v3/command?apikey=" + settings.radarr.apikey
|
||||
data = {
|
||||
'name': 'RescanMovie',
|
||||
'movieId': int(radarr_id)
|
||||
}
|
||||
requests.post(url, json=data, timeout=60, verify=False, headers=headers)
|
||||
except Exception as e:
|
||||
logging.exception('BAZARR cannot notify Radarr')
|
||||
|
||||
|
||||
def delete_subtitles(media_type, language, forced, hi, media_path, subtitles_path, sonarr_series_id=None,
|
||||
sonarr_episode_id=None, radarr_id=None):
|
||||
def delete_subtitles(media_type, language, forced, hi, media_path, subtitles_path, series_id=None,
|
||||
episode_id=None, movie_id=None):
|
||||
if not subtitles_path.endswith('.srt'):
|
||||
logging.error('BAZARR can only delete .srt files.')
|
||||
return False
|
||||
|
@ -373,33 +254,29 @@ def delete_subtitles(media_type, language, forced, hi, media_path, subtitles_pat
|
|||
|
||||
if media_type == 'series':
|
||||
try:
|
||||
os.remove(path_mappings.path_replace(subtitles_path))
|
||||
os.remove(subtitles_path)
|
||||
except OSError:
|
||||
logging.exception('BAZARR cannot delete subtitles file: ' + subtitles_path)
|
||||
store_subtitles(path_mappings.path_replace_reverse(media_path), media_path)
|
||||
store_subtitles(media_path)
|
||||
return False
|
||||
else:
|
||||
history_log(0, sonarr_series_id, sonarr_episode_id, result, language=language_log,
|
||||
video_path=path_mappings.path_replace_reverse(media_path),
|
||||
subtitles_path=path_mappings.path_replace_reverse(subtitles_path))
|
||||
store_subtitles(path_mappings.path_replace_reverse(media_path), media_path)
|
||||
notify_sonarr(sonarr_series_id)
|
||||
event_stream(type='episode-wanted', action='update', payload=sonarr_episode_id)
|
||||
history_log(0, series_id, episode_id, result, language=language_log,
|
||||
video_path=media_path, subtitles_path=subtitles_path)
|
||||
store_subtitles(media_path)
|
||||
event_stream(type='episode-wanted', action='update', payload=episode_id)
|
||||
return True
|
||||
else:
|
||||
try:
|
||||
os.remove(path_mappings.path_replace_movie(subtitles_path))
|
||||
os.remove(subtitles_path)
|
||||
except OSError:
|
||||
logging.exception('BAZARR cannot delete subtitles file: ' + subtitles_path)
|
||||
store_subtitles_movie(path_mappings.path_replace_reverse_movie(media_path), media_path)
|
||||
store_subtitles_movie(media_path)
|
||||
return False
|
||||
else:
|
||||
history_log_movie(0, radarr_id, result, language=language_log,
|
||||
video_path=path_mappings.path_replace_reverse_movie(media_path),
|
||||
subtitles_path=path_mappings.path_replace_reverse_movie(subtitles_path))
|
||||
store_subtitles_movie(path_mappings.path_replace_reverse_movie(media_path), media_path)
|
||||
notify_radarr(radarr_id)
|
||||
event_stream(type='movie-wanted', action='update', payload=radarr_id)
|
||||
history_log_movie(0, movie_id, result, language=language_log, video_path=media_path,
|
||||
subtitles_path=subtitles_path)
|
||||
store_subtitles_movie(media_path)
|
||||
event_stream(type='movie-wanted', action='update', payload=movie_id)
|
||||
return True
|
||||
|
||||
|
||||
|
@ -492,11 +369,11 @@ def check_credentials(user, pw):
|
|||
|
||||
|
||||
def check_health():
|
||||
from get_rootfolder import check_sonarr_rootfolder, check_radarr_rootfolder
|
||||
if settings.general.getboolean('use_sonarr'):
|
||||
check_sonarr_rootfolder()
|
||||
if settings.general.getboolean('use_radarr'):
|
||||
check_radarr_rootfolder()
|
||||
from get_rootfolder import check_series_rootfolder, check_movies_rootfolder
|
||||
if settings.general.getboolean('use_series'):
|
||||
check_series_rootfolder()
|
||||
if settings.general.getboolean('use_movies'):
|
||||
check_movies_rootfolder()
|
||||
event_stream(type='badges')
|
||||
|
||||
|
||||
|
@ -504,26 +381,4 @@ def get_health_issues():
|
|||
# this function must return a list of dictionaries consisting of to keys: object and issue
|
||||
health_issues = []
|
||||
|
||||
# get Sonarr rootfolder issues
|
||||
if settings.general.getboolean('use_sonarr'):
|
||||
rootfolder = TableShowsRootfolder.select(TableShowsRootfolder.path,
|
||||
TableShowsRootfolder.accessible,
|
||||
TableShowsRootfolder.error)\
|
||||
.where(TableShowsRootfolder.accessible == 0)\
|
||||
.dicts()
|
||||
for item in rootfolder:
|
||||
health_issues.append({'object': path_mappings.path_replace(item['path']),
|
||||
'issue': item['error']})
|
||||
|
||||
# get Radarr rootfolder issues
|
||||
if settings.general.getboolean('use_radarr'):
|
||||
rootfolder = TableMoviesRootfolder.select(TableMoviesRootfolder.path,
|
||||
TableMoviesRootfolder.accessible,
|
||||
TableMoviesRootfolder.error)\
|
||||
.where(TableMoviesRootfolder.accessible == 0)\
|
||||
.dicts()
|
||||
for item in rootfolder:
|
||||
health_issues.append({'object': path_mappings.path_replace_movie(item['path']),
|
||||
'issue': item['error']})
|
||||
|
||||
return health_issues
|
||||
|
|
179
data_autonomous/config/config.ini
Normal file
179
data_autonomous/config/config.ini
Normal file
|
@ -0,0 +1,179 @@
|
|||
[general]
|
||||
ip = 0.0.0.0
|
||||
port = 6767
|
||||
base_url =
|
||||
debug = False
|
||||
branch = master
|
||||
auto_update = True
|
||||
single_language = False
|
||||
minimum_score = 90
|
||||
use_postprocessing = False
|
||||
postprocessing_cmd =
|
||||
postprocessing_threshold = 90
|
||||
use_postprocessing_threshold = False
|
||||
postprocessing_threshold_movie = 70
|
||||
use_postprocessing_threshold_movie = False
|
||||
use_series = False
|
||||
use_movies = False
|
||||
serie_default_enabled = False
|
||||
serie_default_profile =
|
||||
movie_default_enabled = False
|
||||
movie_default_profile =
|
||||
page_size = 25
|
||||
page_size_manual_search = 10
|
||||
minimum_score_movie = 70
|
||||
use_embedded_subs = True
|
||||
embedded_subs_show_desired = True
|
||||
utf8_encode = True
|
||||
ignore_pgs_subs = False
|
||||
ignore_vobsub_subs = False
|
||||
ignore_ass_subs = False
|
||||
adaptive_searching = False
|
||||
enabled_providers = []
|
||||
multithreading = True
|
||||
chmod_enabled = False
|
||||
chmod = 0640
|
||||
subfolder = current
|
||||
subfolder_custom =
|
||||
upgrade_subs = True
|
||||
upgrade_frequency = 12
|
||||
days_to_upgrade_subs = 7
|
||||
upgrade_manual = True
|
||||
anti_captcha_provider = None
|
||||
wanted_search_frequency = 3
|
||||
wanted_search_frequency_movie = 3
|
||||
subzero_mods = []
|
||||
dont_notify_manual_actions = False
|
||||
flask_secret_key = d80a91a952aca19d18b53e9c9e1eae0f
|
||||
|
||||
[auth]
|
||||
type = None
|
||||
username =
|
||||
password =
|
||||
apikey = 85730a918fb9e1d77e6ada8b035a88c1
|
||||
|
||||
[series]
|
||||
full_update = Daily
|
||||
full_update_day = 6
|
||||
full_update_hour = 4
|
||||
only_monitored = False
|
||||
excluded_tags = []
|
||||
excluded_series_types = []
|
||||
use_ffprobe_cache = True
|
||||
|
||||
[movies]
|
||||
full_update = Daily
|
||||
full_update_day = 6
|
||||
full_update_hour = 5
|
||||
only_monitored = False
|
||||
excluded_tags = []
|
||||
use_ffprobe_cache = True
|
||||
|
||||
[proxy]
|
||||
type = None
|
||||
url =
|
||||
port =
|
||||
username =
|
||||
password =
|
||||
exclude = ["localhost","127.0.0.1"]
|
||||
|
||||
[anticaptcha]
|
||||
anti_captcha_key =
|
||||
|
||||
[deathbycaptcha]
|
||||
username =
|
||||
password =
|
||||
|
||||
[analytics]
|
||||
enabled = True
|
||||
|
||||
[subsync]
|
||||
use_subsync = False
|
||||
use_subsync_threshold = False
|
||||
subsync_threshold = 90
|
||||
use_subsync_movie_threshold = False
|
||||
subsync_movie_threshold = 70
|
||||
debug = False
|
||||
|
||||
[series_scores]
|
||||
hash = 359
|
||||
series = 180
|
||||
year = 90
|
||||
season = 30
|
||||
episode = 30
|
||||
release_group = 15
|
||||
source = 7
|
||||
audio_codec = 3
|
||||
resolution = 2
|
||||
video_codec = 2
|
||||
hearing_impaired = 1
|
||||
streaming_service = 0
|
||||
edition = 0
|
||||
|
||||
[movie_scores]
|
||||
hash = 119
|
||||
title = 60
|
||||
year = 30
|
||||
release_group = 15
|
||||
source = 7
|
||||
audio_codec = 3
|
||||
resolution = 2
|
||||
video_codec = 2
|
||||
hearing_impaired = 1
|
||||
streaming_service = 0
|
||||
edition = 0
|
||||
|
||||
[opensubtitles]
|
||||
username =
|
||||
password =
|
||||
use_tag_search = False
|
||||
vip = False
|
||||
ssl = False
|
||||
timeout = 15
|
||||
skip_wrong_fps = False
|
||||
|
||||
[opensubtitlescom]
|
||||
username =
|
||||
password =
|
||||
use_hash = True
|
||||
|
||||
[addic7ed]
|
||||
username =
|
||||
password =
|
||||
|
||||
[legendasdivx]
|
||||
username =
|
||||
password =
|
||||
skip_wrong_fps = False
|
||||
|
||||
[ktuvit]
|
||||
email =
|
||||
hashed_password =
|
||||
|
||||
[legendastv]
|
||||
username =
|
||||
password =
|
||||
featured_only = False
|
||||
|
||||
[xsubs]
|
||||
username =
|
||||
password =
|
||||
|
||||
[assrt]
|
||||
token =
|
||||
|
||||
[napisy24]
|
||||
username =
|
||||
password =
|
||||
|
||||
[subscene]
|
||||
username =
|
||||
password =
|
||||
|
||||
[betaseries]
|
||||
token =
|
||||
|
||||
[titlovi]
|
||||
username =
|
||||
password =
|
||||
|
BIN
data_autonomous/db/bazarr.db
Normal file
BIN
data_autonomous/db/bazarr.db
Normal file
Binary file not shown.
BIN
data_autonomous/db/bazarr.db-shm
Normal file
BIN
data_autonomous/db/bazarr.db-shm
Normal file
Binary file not shown.
BIN
data_autonomous/db/bazarr.db-wal
Normal file
BIN
data_autonomous/db/bazarr.db-wal
Normal file
Binary file not shown.
15
data_autonomous/log/bazarr.log
Normal file
15
data_autonomous/log/bazarr.log
Normal file
|
@ -0,0 +1,15 @@
|
|||
03/09/2021 00:00:10|ERROR |root |Error trying to get releases from Github. Http error.|'Traceback (most recent call last):\n File "/Users/morpheus/Desktop/bazarr/bazarr/check_update.py", line 23, in check_releases\n r.raise_for_status()\n File "/Users/morpheus/Desktop/bazarr/bazarr/../libs/requests/models.py", line 943, in raise_for_status\n raise HTTPError(http_error_msg, response=self)\nrequests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://api.github.com/repos/Bazarr/Bazarr2/releases?per_page=100'|
|
||||
03/09/2021 13:38:48|ERROR |root |Error trying to get releases from Github. Http error.|'Traceback (most recent call last):\n File "/Users/morpheus/Desktop/bazarr/bazarr/check_update.py", line 23, in check_releases\n r.raise_for_status()\n File "/Users/morpheus/Desktop/bazarr/bazarr/../libs/requests/models.py", line 943, in raise_for_status\n raise HTTPError(http_error_msg, response=self)\nrequests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://api.github.com/repos/Bazarr/Bazarr2/releases?per_page=100'|
|
||||
03/09/2021 13:39:03|ERROR |root |Error trying to get releases from Github. Http error.|'Traceback (most recent call last):\n File "/Users/morpheus/Desktop/bazarr/bazarr/check_update.py", line 23, in check_releases\n r.raise_for_status()\n File "/Users/morpheus/Desktop/bazarr/bazarr/../libs/requests/models.py", line 943, in raise_for_status\n raise HTTPError(http_error_msg, response=self)\nrequests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://api.github.com/repos/Bazarr/Bazarr2/releases?per_page=100'|
|
||||
03/09/2021 13:39:25|ERROR |root |Error trying to get releases from Github. Http error.|'Traceback (most recent call last):\n File "/Users/morpheus/Desktop/bazarr/bazarr/check_update.py", line 23, in check_releases\n r.raise_for_status()\n File "/Users/morpheus/Desktop/bazarr/bazarr/../libs/requests/models.py", line 943, in raise_for_status\n raise HTTPError(http_error_msg, response=self)\nrequests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://api.github.com/repos/Bazarr/Bazarr2/releases?per_page=100'|
|
||||
03/09/2021 13:39:48|ERROR |root |Error trying to get releases from Github. Http error.|'Traceback (most recent call last):\n File "/Users/morpheus/Desktop/bazarr/bazarr/check_update.py", line 23, in check_releases\n r.raise_for_status()\n File "/Users/morpheus/Desktop/bazarr/bazarr/../libs/requests/models.py", line 943, in raise_for_status\n raise HTTPError(http_error_msg, response=self)\nrequests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://api.github.com/repos/Bazarr/Bazarr2/releases?per_page=100'|
|
||||
03/09/2021 13:39:59|ERROR |root |Error trying to get releases from Github. Http error.|'Traceback (most recent call last):\n File "/Users/morpheus/Desktop/bazarr/bazarr/check_update.py", line 23, in check_releases\n r.raise_for_status()\n File "/Users/morpheus/Desktop/bazarr/bazarr/../libs/requests/models.py", line 943, in raise_for_status\n raise HTTPError(http_error_msg, response=self)\nrequests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://api.github.com/repos/Bazarr/Bazarr2/releases?per_page=100'|
|
||||
03/09/2021 13:40:01|ERROR |root |Error trying to get releases from Github. Http error.|'Traceback (most recent call last):\n File "/Users/morpheus/Desktop/bazarr/bazarr/check_update.py", line 23, in check_releases\n r.raise_for_status()\n File "/Users/morpheus/Desktop/bazarr/bazarr/../libs/requests/models.py", line 943, in raise_for_status\n raise HTTPError(http_error_msg, response=self)\nrequests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://api.github.com/repos/Bazarr/Bazarr2/releases?per_page=100'|
|
||||
03/09/2021 13:40:01|INFO |root |BAZARR is started and waiting for request on http://***.***.***.***:6767|
|
||||
03/09/2021 13:40:47|ERROR |root |Error trying to get releases from Github. Http error.|'Traceback (most recent call last):\n File "/Users/morpheus/Desktop/bazarr/bazarr/check_update.py", line 23, in check_releases\n r.raise_for_status()\n File "/Users/morpheus/Desktop/bazarr/bazarr/../libs/requests/models.py", line 943, in raise_for_status\n raise HTTPError(http_error_msg, response=self)\nrequests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://api.github.com/repos/Bazarr/Bazarr2/releases?per_page=100'|
|
||||
03/09/2021 13:40:52|ERROR |root |Error trying to get releases from Github. Http error.|'Traceback (most recent call last):\n File "/Users/morpheus/Desktop/bazarr/bazarr/check_update.py", line 23, in check_releases\n r.raise_for_status()\n File "/Users/morpheus/Desktop/bazarr/bazarr/../libs/requests/models.py", line 943, in raise_for_status\n raise HTTPError(http_error_msg, response=self)\nrequests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://api.github.com/repos/Bazarr/Bazarr2/releases?per_page=100'|
|
||||
03/09/2021 13:40:52|INFO |root |BAZARR is started and waiting for request on http://***.***.***.***:6767|
|
||||
03/09/2021 13:43:39|ERROR |root |Error trying to get releases from Github. Http error.|'Traceback (most recent call last):\n File "/Users/morpheus/Desktop/bazarr/bazarr/check_update.py", line 23, in check_releases\n r.raise_for_status()\n File "/Users/morpheus/Desktop/bazarr/bazarr/../libs/requests/models.py", line 943, in raise_for_status\n raise HTTPError(http_error_msg, response=self)\nrequests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://api.github.com/repos/Bazarr/Bazarr2/releases?per_page=100'|
|
||||
03/09/2021 13:43:41|ERROR |root |Error trying to get releases from Github. Http error.|'Traceback (most recent call last):\n File "/Users/morpheus/Desktop/bazarr/bazarr/check_update.py", line 23, in check_releases\n r.raise_for_status()\n File "/Users/morpheus/Desktop/bazarr/bazarr/../libs/requests/models.py", line 943, in raise_for_status\n raise HTTPError(http_error_msg, response=self)\nrequests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://api.github.com/repos/Bazarr/Bazarr2/releases?per_page=100'|
|
||||
03/09/2021 13:43:41|INFO |root |BAZARR is started and waiting for request on http://***.***.***.***:6767|
|
||||
03/09/2021 13:46:48|ERROR |root |BAZARR cannot parse releases caching file: /Users/morpheus/Desktop/bazarr/data_autonomous/config/releases.txt|'Traceback (most recent call last):\n File "/Users/morpheus/Desktop/bazarr/bazarr/api.py", line 604, in get\n with io.open(os.path.join(args.config_dir, \'config\', \'releases.txt\'), \'r\', encoding=\'UTF-8\') as f:\nFileNotFoundError: [Errno 2] No such file or directory: \'/Users/morpheus/Desktop/bazarr/data_autonomous/config/releases.txt\''|
|
4
data_autonomous/log/bazarr.log.2021-09-02
Normal file
4
data_autonomous/log/bazarr.log.2021-09-02
Normal file
|
@ -0,0 +1,4 @@
|
|||
02/09/2021 23:56:23|ERROR |root |Error trying to get releases from Github. Http error.|'Traceback (most recent call last):\n File "/Users/morpheus/Desktop/bazarr/bazarr/check_update.py", line 23, in check_releases\n r.raise_for_status()\n File "/Users/morpheus/Desktop/bazarr/bazarr/../libs/requests/models.py", line 943, in raise_for_status\n raise HTTPError(http_error_msg, response=self)\nrequests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://api.github.com/repos/Bazarr/Bazarr2/releases?per_page=100'|
|
||||
02/09/2021 23:56:57|ERROR |root |Error trying to get releases from Github. Http error.|'Traceback (most recent call last):\n File "/Users/morpheus/Desktop/bazarr/bazarr/check_update.py", line 23, in check_releases\n r.raise_for_status()\n File "/Users/morpheus/Desktop/bazarr/bazarr/../libs/requests/models.py", line 943, in raise_for_status\n raise HTTPError(http_error_msg, response=self)\nrequests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://api.github.com/repos/Bazarr/Bazarr2/releases?per_page=100'|
|
||||
02/09/2021 23:58:27|ERROR |root |Error trying to get releases from Github. Http error.|'Traceback (most recent call last):\n File "/Users/morpheus/Desktop/bazarr/bazarr/check_update.py", line 23, in check_releases\n r.raise_for_status()\n File "/Users/morpheus/Desktop/bazarr/bazarr/../libs/requests/models.py", line 943, in raise_for_status\n raise HTTPError(http_error_msg, response=self)\nrequests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://api.github.com/repos/Bazarr/Bazarr2/releases?per_page=100'|
|
||||
02/09/2021 23:59:24|ERROR |root |Error trying to get releases from Github. Http error.|'Traceback (most recent call last):\n File "/Users/morpheus/Desktop/bazarr/bazarr/check_update.py", line 23, in check_releases\n r.raise_for_status()\n File "/Users/morpheus/Desktop/bazarr/bazarr/../libs/requests/models.py", line 943, in raise_for_status\n raise HTTPError(http_error_msg, response=self)\nrequests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://api.github.com/repos/Bazarr/Bazarr2/releases?per_page=100'|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "bazarr",
|
||||
"version": "1.0.0",
|
||||
"description": "Bazarr is a companion application to Sonarr and Radarr. It manages and downloads subtitles based on your requirements. You define your preferences by TV show or movie and Bazarr takes care of everything for you.",
|
||||
"description": "Bazarr manages and downloads subtitles based on your requirements. You define your preferences by TV show or movie and Bazarr takes care of everything for you.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/morpheus65535/bazarr.git"
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
/>
|
||||
<meta
|
||||
name="description"
|
||||
content="Bazarr is a companion application to Sonarr and Radarr. It manages and downloads subtitles based on your requirements. You define your preferences by TV show or movie and Bazarr takes care of everything for you."
|
||||
content="Bazarr manages and downloads subtitles based on your requirements. You define your preferences by TV show or movie and Bazarr takes care of everything for you."
|
||||
/>
|
||||
<link rel="manifest" href="%PUBLIC_URL%/static/manifest.json" />
|
||||
</head>
|
||||
|
|
|
@ -56,7 +56,7 @@ export function useEpisodesBy(seriesId: number) {
|
|||
const episodes = useReduxStore((d) => d.series.episodeList);
|
||||
|
||||
const newContent = useMemo(() => {
|
||||
return episodes.content.filter((v) => v.sonarrSeriesId === seriesId);
|
||||
return episodes.content.filter((v) => v.seriesId === seriesId);
|
||||
}, [seriesId, episodes.content]);
|
||||
|
||||
const newList: Async.List<Item.Episode> = useMemo(
|
||||
|
|
|
@ -23,14 +23,14 @@ export function useIsOffline() {
|
|||
return useReduxStore((s) => s.site.offline);
|
||||
}
|
||||
|
||||
export function useIsSonarrEnabled() {
|
||||
export function useIsSeriesEnabled() {
|
||||
const settings = useSystemSettings();
|
||||
return settings.content?.general.use_sonarr ?? true;
|
||||
return settings.content?.general.use_series ?? true;
|
||||
}
|
||||
|
||||
export function useIsRadarrEnabled() {
|
||||
export function useIsMoviesEnabled() {
|
||||
const settings = useSystemSettings();
|
||||
return settings.content?.general.use_radarr ?? true;
|
||||
return settings.content?.general.use_movies ?? true;
|
||||
}
|
||||
|
||||
export function useShowOnlyDesired() {
|
||||
|
|
|
@ -30,8 +30,8 @@ interface Movie {
|
|||
}
|
||||
|
||||
const defaultMovie: Movie = {
|
||||
movieList: AsyncUtility.getDefaultEntity("radarrId"),
|
||||
wantedMovieList: AsyncUtility.getDefaultEntity("radarrId"),
|
||||
movieList: AsyncUtility.getDefaultEntity("movieId"),
|
||||
wantedMovieList: AsyncUtility.getDefaultEntity("movieId"),
|
||||
historyList: AsyncUtility.getDefaultEntity("id"),
|
||||
blacklist: AsyncUtility.getDefaultItem(),
|
||||
};
|
||||
|
|
|
@ -36,9 +36,9 @@ interface Series {
|
|||
}
|
||||
|
||||
const defaultSeries: Series = {
|
||||
seriesList: AsyncUtility.getDefaultEntity("sonarrSeriesId"),
|
||||
wantedEpisodesList: AsyncUtility.getDefaultEntity("sonarrEpisodeId"),
|
||||
episodeList: AsyncUtility.getDefaultList("sonarrEpisodeId"),
|
||||
seriesList: AsyncUtility.getDefaultEntity("seriesId"),
|
||||
wantedEpisodesList: AsyncUtility.getDefaultEntity("episodeId"),
|
||||
episodeList: AsyncUtility.getDefaultList("episodeId"),
|
||||
historyList: AsyncUtility.getDefaultEntity("id"),
|
||||
blacklist: AsyncUtility.getDefaultItem(),
|
||||
};
|
||||
|
@ -61,8 +61,8 @@ const reducer = createReducer(defaultSeries, (builder) => {
|
|||
const episodes = state.episodeList;
|
||||
const dirtyIdsSet = new Set(dirtyIds);
|
||||
const dirtyEpisodeIds = episodes.content
|
||||
.filter((v) => dirtyIdsSet.has(v.sonarrSeriesId.toString()))
|
||||
.map((v) => String(v.sonarrEpisodeId));
|
||||
.filter((v) => dirtyIdsSet.has(v.seriesId.toString()))
|
||||
.map((v) => String(v.episodeId));
|
||||
|
||||
ReducerUtility.markDirty(episodes, dirtyEpisodeIds);
|
||||
});
|
||||
|
|
10
frontend/src/@types/api.d.ts
vendored
10
frontend/src/@types/api.d.ts
vendored
|
@ -73,11 +73,11 @@ interface TagType {
|
|||
}
|
||||
|
||||
interface SeriesIdType {
|
||||
sonarrSeriesId: number;
|
||||
seriesId: number;
|
||||
}
|
||||
|
||||
type EpisodeIdType = SeriesIdType & {
|
||||
sonarrEpisodeId: number;
|
||||
episodeId: number;
|
||||
};
|
||||
|
||||
interface EpisodeTitleType {
|
||||
|
@ -86,7 +86,7 @@ interface EpisodeTitleType {
|
|||
}
|
||||
|
||||
interface MovieIdType {
|
||||
radarrId: number;
|
||||
movieId: number;
|
||||
}
|
||||
|
||||
interface TitleType {
|
||||
|
@ -121,7 +121,7 @@ declare namespace Item {
|
|||
hearing_impaired: boolean;
|
||||
episodeFileCount: number;
|
||||
episodeMissingCount: number;
|
||||
seriesType: SonarrSeriesType;
|
||||
seriesType: SeriesType;
|
||||
tvdbId: number;
|
||||
};
|
||||
|
||||
|
@ -168,7 +168,7 @@ declare namespace Wanted {
|
|||
EpisodeIdType &
|
||||
EpisodeTitleType & {
|
||||
episode_number: string;
|
||||
seriesType: SonarrSeriesType;
|
||||
seriesType: SeriesType;
|
||||
};
|
||||
|
||||
type Movie = Base & MovieIdType & TitleType;
|
||||
|
|
4
frontend/src/@types/basic.d.ts
vendored
4
frontend/src/@types/basic.d.ts
vendored
|
@ -1,5 +1,5 @@
|
|||
// Sonarr
|
||||
type SonarrSeriesType = "Standard" | "Daily" | "Anime";
|
||||
// Series
|
||||
type SeriesType = "Standard" | "Daily" | "Anime";
|
||||
|
||||
type PythonBoolean = "True" | "False";
|
||||
|
||||
|
|
6
frontend/src/@types/form.d.ts
vendored
6
frontend/src/@types/form.d.ts
vendored
|
@ -10,7 +10,7 @@ declare namespace FormType {
|
|||
|
||||
interface OneMovieAction {
|
||||
action: "search-missing" | "scan-disk";
|
||||
radarrid: number;
|
||||
movieid: number;
|
||||
}
|
||||
|
||||
interface OneSerieAction {
|
||||
|
@ -49,8 +49,8 @@ declare namespace FormType {
|
|||
language: string;
|
||||
hi: boolean;
|
||||
forced: boolean;
|
||||
sonarrSeriesId: number;
|
||||
sonarrEpisodeId: number;
|
||||
seriesId: number;
|
||||
episodeId: number;
|
||||
title: string;
|
||||
}
|
||||
|
||||
|
|
32
frontend/src/@types/settings.d.ts
vendored
32
frontend/src/@types/settings.d.ts
vendored
|
@ -4,8 +4,8 @@ interface Settings {
|
|||
auth: Settings.Auth;
|
||||
subsync: Settings.Subsync;
|
||||
analytics: Settings.Analytic;
|
||||
sonarr: Settings.Sonarr;
|
||||
radarr: Settings.Radarr;
|
||||
series: Settings.Series;
|
||||
movies: Settings.Movies;
|
||||
// Anitcaptcha
|
||||
anticaptcha: Settings.Anticaptcha;
|
||||
deathbycaptcha: Settings.DeathByCaptche;
|
||||
|
@ -49,8 +49,6 @@ declare namespace Settings {
|
|||
movie_default_profile?: number;
|
||||
serie_default_enabled: boolean;
|
||||
serie_default_profile?: number;
|
||||
path_mappings: [string, string][];
|
||||
path_mappings_movie: [string, string][];
|
||||
port: number;
|
||||
upgrade_subs: boolean;
|
||||
postprocessing_cmd?: string;
|
||||
|
@ -68,9 +66,8 @@ declare namespace Settings {
|
|||
use_postprocessing: boolean;
|
||||
use_postprocessing_threshold: boolean;
|
||||
use_postprocessing_threshold_movie: boolean;
|
||||
use_radarr: boolean;
|
||||
use_scenename: boolean;
|
||||
use_sonarr: boolean;
|
||||
use_movies: boolean;
|
||||
use_series: boolean;
|
||||
utf8_encode: boolean;
|
||||
wanted_search_frequency: number;
|
||||
wanted_search_frequency_movie: number;
|
||||
|
@ -115,36 +112,23 @@ declare namespace Settings {
|
|||
url: string | null;
|
||||
}
|
||||
|
||||
// Sonarr / Radarr
|
||||
// Series / Movies
|
||||
type FullUpdateOptions = "Manually" | "Daily" | "Weekly";
|
||||
|
||||
interface Sonarr {
|
||||
ip: string;
|
||||
port: number;
|
||||
base_url?: string;
|
||||
ssl: boolean;
|
||||
apikey?: string;
|
||||
interface Series {
|
||||
full_update: FullUpdateOptions;
|
||||
full_update_day: number;
|
||||
full_update_hour: number;
|
||||
only_monitored: boolean;
|
||||
series_sync: number;
|
||||
episodes_sync: number;
|
||||
excluded_tags: string[];
|
||||
excluded_series_types: SonarrSeriesType[];
|
||||
excluded_series_types: SeriesType[];
|
||||
}
|
||||
|
||||
interface Radarr {
|
||||
ip: string;
|
||||
port: number;
|
||||
base_url?: string;
|
||||
ssl: boolean;
|
||||
apikey?: string;
|
||||
interface Movies {
|
||||
full_update: FullUpdateOptions;
|
||||
full_update_day: number;
|
||||
full_update_hour: number;
|
||||
only_monitored: boolean;
|
||||
movies_sync: number;
|
||||
excluded_tags: string[];
|
||||
}
|
||||
|
||||
|
|
2
frontend/src/@types/system.d.ts
vendored
2
frontend/src/@types/system.d.ts
vendored
|
@ -14,8 +14,6 @@ declare namespace System {
|
|||
bazarr_version: string;
|
||||
operating_system: string;
|
||||
python_version: string;
|
||||
radarr_version: string;
|
||||
sonarr_version: string;
|
||||
}
|
||||
|
||||
interface Health {
|
||||
|
|
|
@ -35,10 +35,10 @@ async function SearchItem(text: string) {
|
|||
|
||||
return results.map<SearchResult>((v) => {
|
||||
let link: string;
|
||||
if (v.sonarrSeriesId) {
|
||||
link = `/series/${v.sonarrSeriesId}`;
|
||||
} else if (v.radarrId) {
|
||||
link = `/movies/${v.radarrId}`;
|
||||
if (v.seriesId) {
|
||||
link = `/series/${v.seriesId}`;
|
||||
} else if (v.movieId) {
|
||||
link = `/movies/${v.movieId}`;
|
||||
} else {
|
||||
link = "";
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ const Table: FunctionComponent<Props> = ({ blacklist }) => {
|
|||
accessor: "title",
|
||||
className: "text-nowrap",
|
||||
Cell: (row) => {
|
||||
const target = `/movies/${row.row.original.radarrId}`;
|
||||
const target = `/movies/${row.row.original.movieId}`;
|
||||
return (
|
||||
<Link to={target}>
|
||||
<span>{row.value}</span>
|
||||
|
|
|
@ -23,7 +23,7 @@ const Table: FunctionComponent<Props> = ({ blacklist }) => {
|
|||
accessor: "seriesTitle",
|
||||
className: "text-nowrap",
|
||||
Cell: (row) => {
|
||||
const target = `/series/${row.row.original.sonarrSeriesId}`;
|
||||
const target = `/series/${row.row.original.seriesId}`;
|
||||
return (
|
||||
<Link to={target}>
|
||||
<span>{row.value}</span>
|
||||
|
|
|
@ -70,7 +70,7 @@ const SeriesEpisodesView: FunctionComponent<Props> = (props) => {
|
|||
const profile = useProfileBy(series.content?.profileId);
|
||||
|
||||
const hasTask = useIsAnyTaskRunningWithId([
|
||||
...episodes.content.map((v) => v.sonarrEpisodeId),
|
||||
...episodes.content.map((v) => v.episodeId),
|
||||
id,
|
||||
]);
|
||||
|
||||
|
|
|
@ -34,17 +34,13 @@ interface Props {
|
|||
|
||||
const download = (item: Item.Episode, result: SearchResultType) => {
|
||||
const { language, hearing_impaired, forced, provider, subtitle } = result;
|
||||
return ProvidersApi.downloadEpisodeSubtitle(
|
||||
item.sonarrSeriesId,
|
||||
item.sonarrEpisodeId,
|
||||
{
|
||||
language,
|
||||
hi: hearing_impaired,
|
||||
forced,
|
||||
provider,
|
||||
subtitle,
|
||||
}
|
||||
);
|
||||
return ProvidersApi.downloadEpisodeSubtitle(item.seriesId, item.episodeId, {
|
||||
language,
|
||||
hi: hearing_impaired,
|
||||
forced,
|
||||
provider,
|
||||
subtitle,
|
||||
});
|
||||
};
|
||||
|
||||
const Table: FunctionComponent<Props> = ({
|
||||
|
@ -109,10 +105,10 @@ const Table: FunctionComponent<Props> = ({
|
|||
Cell: ({ row }) => {
|
||||
const episode = row.original;
|
||||
|
||||
const seriesid = episode.sonarrSeriesId;
|
||||
const seriesid = episode.seriesId;
|
||||
|
||||
const elements = useMemo(() => {
|
||||
const episodeid = episode.sonarrEpisodeId;
|
||||
const episodeid = episode.episodeId;
|
||||
|
||||
const missing = episode.missing_subtitles.map((val, idx) => (
|
||||
<SubtitleAction
|
||||
|
@ -146,7 +142,7 @@ const Table: FunctionComponent<Props> = ({
|
|||
},
|
||||
{
|
||||
Header: "Actions",
|
||||
accessor: "sonarrEpisodeId",
|
||||
accessor: "episodeId",
|
||||
Cell: ({ row, update }) => {
|
||||
return (
|
||||
<ButtonGroup>
|
||||
|
|
|
@ -33,7 +33,7 @@ import Table from "./table";
|
|||
|
||||
const download = (item: Item.Movie, result: SearchResultType) => {
|
||||
const { language, hearing_impaired, forced, provider, subtitle } = result;
|
||||
return ProvidersApi.downloadMovieSubtitle(item.radarrId, {
|
||||
return ProvidersApi.downloadMovieSubtitle(item.movieId, {
|
||||
language,
|
||||
hi: hearing_impaired,
|
||||
forced,
|
||||
|
@ -92,7 +92,7 @@ const MovieDetailView: FunctionComponent<Props> = ({ match }) => {
|
|||
item.title,
|
||||
id,
|
||||
MoviesApi.action.bind(MoviesApi),
|
||||
{ action: "scan-disk", radarrid: id }
|
||||
{ action: "scan-disk", movieid: id }
|
||||
);
|
||||
dispatchTask("Scaning Disk...", [task], "Scaning...");
|
||||
}}
|
||||
|
@ -109,7 +109,7 @@ const MovieDetailView: FunctionComponent<Props> = ({ match }) => {
|
|||
MoviesApi.action.bind(MoviesApi),
|
||||
{
|
||||
action: "search-missing",
|
||||
radarrid: id,
|
||||
movieid: id,
|
||||
}
|
||||
);
|
||||
dispatchTask("Searching subtitles...", [task], "Searching...");
|
||||
|
|
|
@ -67,7 +67,7 @@ const Table: FunctionComponent<Props> = ({ movie, profile, disabled }) => {
|
|||
<AsyncButton
|
||||
disabled={disabled}
|
||||
promise={() =>
|
||||
MoviesApi.downloadSubtitles(movie.radarrId, {
|
||||
MoviesApi.downloadSubtitles(movie.movieId, {
|
||||
language: original.code2,
|
||||
hi: original.hi,
|
||||
forced: original.forced,
|
||||
|
@ -86,7 +86,7 @@ const Table: FunctionComponent<Props> = ({ movie, profile, disabled }) => {
|
|||
variant="light"
|
||||
size="sm"
|
||||
promise={() =>
|
||||
MoviesApi.deleteSubtitles(movie.radarrId, {
|
||||
MoviesApi.deleteSubtitles(movie.movieId, {
|
||||
language: original.code2,
|
||||
hi: original.hi,
|
||||
forced: original.forced,
|
||||
|
|
|
@ -40,7 +40,7 @@ const MovieView: FunctionComponent<Props> = () => {
|
|||
if (select) {
|
||||
return value;
|
||||
} else {
|
||||
const target = `/movies/${row.original.radarrId}`;
|
||||
const target = `/movies/${row.original.movieId}`;
|
||||
return (
|
||||
<TextPopover text={row.original.sceneName} delay={1}>
|
||||
<Link to={target}>
|
||||
|
@ -91,7 +91,7 @@ const MovieView: FunctionComponent<Props> = () => {
|
|||
},
|
||||
},
|
||||
{
|
||||
accessor: "radarrId",
|
||||
accessor: "movieId",
|
||||
selectHide: true,
|
||||
Cell: ({ row, update }) => {
|
||||
return (
|
||||
|
|
|
@ -27,7 +27,7 @@ const SeriesView: FunctionComponent<Props> = () => {
|
|||
if (select) {
|
||||
return value;
|
||||
} else {
|
||||
const target = `/series/${row.original.sonarrSeriesId}`;
|
||||
const target = `/series/${row.original.seriesId}`;
|
||||
return (
|
||||
<Link to={target}>
|
||||
<span>{value}</span>
|
||||
|
@ -91,7 +91,7 @@ const SeriesView: FunctionComponent<Props> = () => {
|
|||
},
|
||||
},
|
||||
{
|
||||
accessor: "sonarrSeriesId",
|
||||
accessor: "seriesId",
|
||||
selectHide: true,
|
||||
Cell: ({ row, update }) => (
|
||||
<ActionBadge
|
||||
|
|
|
@ -30,7 +30,7 @@ const MoviesHistoryView: FunctionComponent<Props> = () => {
|
|||
accessor: "title",
|
||||
className: "text-nowrap",
|
||||
Cell: (row) => {
|
||||
const target = `/movies/${row.row.original.radarrId}`;
|
||||
const target = `/movies/${row.row.original.movieId}`;
|
||||
|
||||
return (
|
||||
<Link to={target}>
|
||||
|
@ -116,9 +116,7 @@ const MoviesHistoryView: FunctionComponent<Props> = () => {
|
|||
return (
|
||||
<BlacklistButton
|
||||
history={original}
|
||||
promise={(form) =>
|
||||
MoviesApi.addBlacklist(original.radarrId, form)
|
||||
}
|
||||
promise={(form) => MoviesApi.addBlacklist(original.movieId, form)}
|
||||
></BlacklistButton>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -29,7 +29,7 @@ const SeriesHistoryView: FunctionComponent<Props> = () => {
|
|||
Header: "Series",
|
||||
accessor: "seriesTitle",
|
||||
Cell: (row) => {
|
||||
const target = `/series/${row.row.original.sonarrSeriesId}`;
|
||||
const target = `/series/${row.row.original.seriesId}`;
|
||||
|
||||
return (
|
||||
<Link to={target}>
|
||||
|
@ -121,12 +121,12 @@ const SeriesHistoryView: FunctionComponent<Props> = () => {
|
|||
Cell: ({ row }) => {
|
||||
const original = row.original;
|
||||
|
||||
const { sonarrEpisodeId, sonarrSeriesId } = original;
|
||||
const { episodeId, seriesId } = original;
|
||||
return (
|
||||
<BlacklistButton
|
||||
history={original}
|
||||
promise={(form) =>
|
||||
EpisodesApi.addBlacklist(sonarrSeriesId, sonarrEpisodeId, form)
|
||||
EpisodesApi.addBlacklist(seriesId, episodeId, form)
|
||||
}
|
||||
></BlacklistButton>
|
||||
);
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
faPlay,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { useMemo } from "react";
|
||||
import { useIsRadarrEnabled, useIsSonarrEnabled } from "../@redux/hooks";
|
||||
import { useIsMoviesEnabled, useIsSeriesEnabled } from "../@redux/hooks";
|
||||
import { useReduxStore } from "../@redux/hooks/base";
|
||||
import BlacklistMoviesView from "../Blacklist/Movies";
|
||||
import BlacklistSeriesView from "../Blacklist/Series";
|
||||
|
@ -21,11 +21,11 @@ import SeriesHistoryView from "../History/Series";
|
|||
import HistoryStats from "../History/Statistics";
|
||||
import SettingsGeneralView from "../Settings/General";
|
||||
import SettingsLanguagesView from "../Settings/Languages";
|
||||
import SettingsMoviesView from "../Settings/Movies";
|
||||
import SettingsNotificationsView from "../Settings/Notifications";
|
||||
import SettingsProvidersView from "../Settings/Providers";
|
||||
import SettingsRadarrView from "../Settings/Radarr";
|
||||
import SettingsSchedulerView from "../Settings/Scheduler";
|
||||
import SettingsSonarrView from "../Settings/Sonarr";
|
||||
import SettingsSeriesView from "../Settings/Series";
|
||||
import SettingsSubtitlesView from "../Settings/Subtitles";
|
||||
import SettingsUIView from "../Settings/UI";
|
||||
import EmptyPage, { RouterEmptyPath } from "../special-pages/404";
|
||||
|
@ -39,8 +39,8 @@ import WantedSeriesView from "../Wanted/Series";
|
|||
import { Navigation } from "./nav";
|
||||
|
||||
export function useNavigationItems() {
|
||||
const sonarr = useIsSonarrEnabled();
|
||||
const radarr = useIsRadarrEnabled();
|
||||
const seriesEnabled = useIsSeriesEnabled();
|
||||
const moviesEnabled = useIsMoviesEnabled();
|
||||
const { movies, episodes, providers } = useReduxStore((s) => s.site.badges);
|
||||
|
||||
const items = useMemo<Navigation.RouteItem[]>(
|
||||
|
@ -56,7 +56,7 @@ export function useNavigationItems() {
|
|||
name: "Series",
|
||||
path: "/series",
|
||||
component: SeriesView,
|
||||
enabled: sonarr,
|
||||
enabled: seriesEnabled,
|
||||
routes: [
|
||||
{
|
||||
name: "Episode",
|
||||
|
@ -71,7 +71,7 @@ export function useNavigationItems() {
|
|||
name: "Movies",
|
||||
path: "/movies",
|
||||
component: MovieView,
|
||||
enabled: radarr,
|
||||
enabled: moviesEnabled,
|
||||
routes: [
|
||||
{
|
||||
name: "Movie Details",
|
||||
|
@ -89,13 +89,13 @@ export function useNavigationItems() {
|
|||
{
|
||||
name: "Series",
|
||||
path: "/series",
|
||||
enabled: sonarr,
|
||||
enabled: seriesEnabled,
|
||||
component: SeriesHistoryView,
|
||||
},
|
||||
{
|
||||
name: "Movies",
|
||||
path: "/movies",
|
||||
enabled: radarr,
|
||||
enabled: moviesEnabled,
|
||||
component: MoviesHistoryView,
|
||||
},
|
||||
{
|
||||
|
@ -113,13 +113,13 @@ export function useNavigationItems() {
|
|||
{
|
||||
name: "Series",
|
||||
path: "/series",
|
||||
enabled: sonarr,
|
||||
enabled: seriesEnabled,
|
||||
component: BlacklistSeriesView,
|
||||
},
|
||||
{
|
||||
name: "Movies",
|
||||
path: "/movies",
|
||||
enabled: radarr,
|
||||
enabled: moviesEnabled,
|
||||
component: BlacklistMoviesView,
|
||||
},
|
||||
],
|
||||
|
@ -133,14 +133,14 @@ export function useNavigationItems() {
|
|||
name: "Series",
|
||||
path: "/series",
|
||||
badge: episodes,
|
||||
enabled: sonarr,
|
||||
enabled: seriesEnabled,
|
||||
component: WantedSeriesView,
|
||||
},
|
||||
{
|
||||
name: "Movies",
|
||||
path: "/movies",
|
||||
badge: movies,
|
||||
enabled: radarr,
|
||||
enabled: moviesEnabled,
|
||||
component: WantedMoviesView,
|
||||
},
|
||||
],
|
||||
|
@ -171,14 +171,14 @@ export function useNavigationItems() {
|
|||
component: SettingsSubtitlesView,
|
||||
},
|
||||
{
|
||||
name: "Sonarr",
|
||||
path: "/sonarr",
|
||||
component: SettingsSonarrView,
|
||||
name: "Series",
|
||||
path: "/series",
|
||||
component: SettingsSeriesView,
|
||||
},
|
||||
{
|
||||
name: "Radarr",
|
||||
path: "/radarr",
|
||||
component: SettingsRadarrView,
|
||||
name: "Movies",
|
||||
path: "/movies",
|
||||
component: SettingsMoviesView,
|
||||
},
|
||||
{
|
||||
name: "Notifications",
|
||||
|
@ -231,7 +231,7 @@ export function useNavigationItems() {
|
|||
],
|
||||
},
|
||||
],
|
||||
[episodes, movies, providers, radarr, sonarr]
|
||||
[episodes, movies, providers, moviesEnabled, seriesEnabled]
|
||||
);
|
||||
|
||||
return items;
|
||||
|
|
|
@ -177,10 +177,10 @@ const SettingsGeneralView: FunctionComponent = () => {
|
|||
<Message>
|
||||
Send anonymous usage information, nothing that can identify you.
|
||||
This includes information on which providers you use, what languages
|
||||
you search for, Bazarr, Python, Sonarr, Radarr and what OS version
|
||||
you are using. We will use this information to prioritize features
|
||||
and bug fixes. Please, keep this enabled as this is the only way we
|
||||
have to better understand how you use Bazarr.
|
||||
you search for, Bazarr, Python and what OS version you are using. We
|
||||
will use this information to prioritize features and bug fixes.
|
||||
Please, keep this enabled as this is the only way we have to better
|
||||
understand how you use Bazarr.
|
||||
</Message>
|
||||
</Input>
|
||||
</Group>
|
||||
|
|
56
frontend/src/Settings/Movies/index.tsx
Normal file
56
frontend/src/Settings/Movies/index.tsx
Normal file
|
@ -0,0 +1,56 @@
|
|||
import React, { FunctionComponent } from "react";
|
||||
import {
|
||||
Check,
|
||||
Chips,
|
||||
CollapseBox,
|
||||
Group,
|
||||
Input,
|
||||
Message,
|
||||
SettingsProvider,
|
||||
Slider,
|
||||
} from "../components";
|
||||
import { moviesEnabledKey } from "../keys";
|
||||
|
||||
interface Props {}
|
||||
|
||||
const SettingsMoviesView: FunctionComponent<Props> = () => {
|
||||
return (
|
||||
<SettingsProvider title="Movies - Bazarr (Settings)">
|
||||
<CollapseBox>
|
||||
<CollapseBox.Control>
|
||||
<Group header="Use Movies">
|
||||
<Input>
|
||||
<Check label="Enabled" settingKey={moviesEnabledKey}></Check>
|
||||
</Input>
|
||||
</Group>
|
||||
</CollapseBox.Control>
|
||||
<CollapseBox.Content indent={false}>
|
||||
<Group header="Options">
|
||||
<Input name="Minimum Score">
|
||||
<Slider settingKey="settings-general-minimum_score_movie"></Slider>
|
||||
</Input>
|
||||
<Input name="Excluded Tags">
|
||||
<Chips settingKey="settings-movies-excluded_tags"></Chips>
|
||||
<Message>
|
||||
Movies with those tags (case sensitive) will be excluded from
|
||||
automatic download of subtitles.
|
||||
</Message>
|
||||
</Input>
|
||||
<Input>
|
||||
<Check
|
||||
label="Download Only Monitored"
|
||||
settingKey="settings-movies-only_monitored"
|
||||
></Check>
|
||||
<Message>
|
||||
Automatic download of subtitles will only happen for monitored
|
||||
movies.
|
||||
</Message>
|
||||
</Input>
|
||||
</Group>
|
||||
</CollapseBox.Content>
|
||||
</CollapseBox>
|
||||
</SettingsProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsMoviesView;
|
|
@ -1,97 +0,0 @@
|
|||
import React, { FunctionComponent, useCallback } from "react";
|
||||
import { InputGroup } from "react-bootstrap";
|
||||
import {
|
||||
Check,
|
||||
Chips,
|
||||
CollapseBox,
|
||||
Group,
|
||||
Input,
|
||||
Message,
|
||||
SettingsProvider,
|
||||
Slider,
|
||||
Text,
|
||||
URLTestButton,
|
||||
} from "../components";
|
||||
import { PathMappingTable } from "../components/pathMapper";
|
||||
import { moviesEnabledKey } from "../keys";
|
||||
|
||||
interface Props {}
|
||||
|
||||
const SettingsRadarrView: FunctionComponent<Props> = () => {
|
||||
const baseUrlOverride = useCallback((settings: Settings) => {
|
||||
return settings.radarr.base_url?.slice(1) ?? "";
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<SettingsProvider title="Radarr - Bazarr (Settings)">
|
||||
<CollapseBox>
|
||||
<CollapseBox.Control>
|
||||
<Group header="Use Radarr">
|
||||
<Input>
|
||||
<Check label="Enabled" settingKey={moviesEnabledKey}></Check>
|
||||
</Input>
|
||||
</Group>
|
||||
</CollapseBox.Control>
|
||||
<CollapseBox.Content indent={false}>
|
||||
<Group header="Host">
|
||||
<Input name="Address">
|
||||
<Text settingKey="settings-radarr-ip"></Text>
|
||||
<Message>Hostname or IPv4 Address</Message>
|
||||
</Input>
|
||||
<Input name="Port">
|
||||
<Text settingKey="settings-radarr-port"></Text>
|
||||
</Input>
|
||||
<Input name="Base URL">
|
||||
<InputGroup>
|
||||
<InputGroup.Prepend>
|
||||
<InputGroup.Text>/</InputGroup.Text>
|
||||
</InputGroup.Prepend>
|
||||
<Text
|
||||
settingKey="settings-radarr-base_url"
|
||||
override={baseUrlOverride}
|
||||
beforeStaged={(v) => "/" + v}
|
||||
></Text>
|
||||
</InputGroup>
|
||||
</Input>
|
||||
<Input name="API Key">
|
||||
<Text settingKey="settings-radarr-apikey"></Text>
|
||||
</Input>
|
||||
<Input>
|
||||
<Check label="SSL" settingKey="settings-radarr-ssl"></Check>
|
||||
</Input>
|
||||
<Input>
|
||||
<URLTestButton category="radarr"></URLTestButton>
|
||||
</Input>
|
||||
</Group>
|
||||
<Group header="Options">
|
||||
<Input name="Minimum Score">
|
||||
<Slider settingKey="settings-general-minimum_score_movie"></Slider>
|
||||
</Input>
|
||||
<Input name="Excluded Tags">
|
||||
<Chips settingKey="settings-radarr-excluded_tags"></Chips>
|
||||
<Message>
|
||||
Movies with those tags (case sensitive) in Radarr will be
|
||||
excluded from automatic download of subtitles.
|
||||
</Message>
|
||||
</Input>
|
||||
<Input>
|
||||
<Check
|
||||
label="Download Only Monitored"
|
||||
settingKey="settings-radarr-only_monitored"
|
||||
></Check>
|
||||
<Message>
|
||||
Automatic download of subtitles will only happen for monitored
|
||||
movies in Radarr.
|
||||
</Message>
|
||||
</Input>
|
||||
</Group>
|
||||
<Group header="Path Mappings">
|
||||
<PathMappingTable type="radarr"></PathMappingTable>
|
||||
</Group>
|
||||
</CollapseBox.Content>
|
||||
</CollapseBox>
|
||||
</SettingsProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsRadarrView;
|
|
@ -8,14 +8,7 @@ import {
|
|||
Selector,
|
||||
SettingsProvider,
|
||||
} from "../components";
|
||||
import {
|
||||
dayOptions,
|
||||
diskUpdateOptions,
|
||||
episodesSyncOptions,
|
||||
moviesSyncOptions,
|
||||
seriesSyncOptions,
|
||||
upgradeOptions,
|
||||
} from "./options";
|
||||
import { dayOptions, diskUpdateOptions, upgradeOptions } from "./options";
|
||||
|
||||
const SettingsSchedulerView: FunctionComponent = () => {
|
||||
const timeOptions = useMemo(() => {
|
||||
|
@ -29,32 +22,12 @@ const SettingsSchedulerView: FunctionComponent = () => {
|
|||
|
||||
return (
|
||||
<SettingsProvider title="Scheduler - Bazarr (Settings)">
|
||||
<Group header="Sonarr/Radarr Sync">
|
||||
<Input name="Update Series List from Sonarr">
|
||||
<Selector
|
||||
options={seriesSyncOptions}
|
||||
settingKey="settings-sonarr-series_sync"
|
||||
></Selector>
|
||||
</Input>
|
||||
<Input name="Update Episodes List from Sonarr">
|
||||
<Selector
|
||||
options={episodesSyncOptions}
|
||||
settingKey="settings-sonarr-episodes_sync"
|
||||
></Selector>
|
||||
</Input>
|
||||
<Input name="Update Movies List from Radarr">
|
||||
<Selector
|
||||
options={moviesSyncOptions}
|
||||
settingKey="settings-radarr-movies_sync"
|
||||
></Selector>
|
||||
</Input>
|
||||
</Group>
|
||||
<Group header="Disk Indexing">
|
||||
<CollapseBox>
|
||||
<CollapseBox.Control>
|
||||
<Input name="Update all Episode Subtitles from Disk">
|
||||
<Selector
|
||||
settingKey="settings-sonarr-full_update"
|
||||
settingKey="settings-series-full_update"
|
||||
options={diskUpdateOptions}
|
||||
></Selector>
|
||||
</Input>
|
||||
|
@ -62,7 +35,7 @@ const SettingsSchedulerView: FunctionComponent = () => {
|
|||
<CollapseBox.Content on={(k) => k === "Weekly"}>
|
||||
<Input name="Day of The Week">
|
||||
<Selector
|
||||
settingKey="settings-sonarr-full_update_day"
|
||||
settingKey="settings-series-full_update_day"
|
||||
options={dayOptions}
|
||||
></Selector>
|
||||
</Input>
|
||||
|
@ -70,7 +43,7 @@ const SettingsSchedulerView: FunctionComponent = () => {
|
|||
<CollapseBox.Content on={(k) => k === "Daily" || k === "Weekly"}>
|
||||
<Input name="Time of The Day">
|
||||
<Selector
|
||||
settingKey="settings-sonarr-full_update_hour"
|
||||
settingKey="settings-series-full_update_hour"
|
||||
options={timeOptions}
|
||||
></Selector>
|
||||
</Input>
|
||||
|
@ -78,7 +51,7 @@ const SettingsSchedulerView: FunctionComponent = () => {
|
|||
<Input>
|
||||
<Check
|
||||
label="Use cached ffprobe results"
|
||||
settingKey="settings-sonarr-use_ffprobe_cache"
|
||||
settingKey="settings-series-use_ffprobe_cache"
|
||||
></Check>
|
||||
<Message>
|
||||
If disabled, Bazarr will use ffprobe to index video file
|
||||
|
@ -90,7 +63,7 @@ const SettingsSchedulerView: FunctionComponent = () => {
|
|||
<CollapseBox.Control>
|
||||
<Input name="Update all Movie Subtitles from Disk">
|
||||
<Selector
|
||||
settingKey="settings-radarr-full_update"
|
||||
settingKey="settings-movies-full_update"
|
||||
options={diskUpdateOptions}
|
||||
></Selector>
|
||||
</Input>
|
||||
|
@ -98,7 +71,7 @@ const SettingsSchedulerView: FunctionComponent = () => {
|
|||
<CollapseBox.Content on={(k) => k === "Weekly"}>
|
||||
<Input name="Day of The Week">
|
||||
<Selector
|
||||
settingKey="settings-radarr-full_update_day"
|
||||
settingKey="settings-movies-full_update_day"
|
||||
options={dayOptions}
|
||||
></Selector>
|
||||
</Input>
|
||||
|
@ -106,7 +79,7 @@ const SettingsSchedulerView: FunctionComponent = () => {
|
|||
<CollapseBox.Content on={(k) => k === "Daily" || k === "Weekly"}>
|
||||
<Input name="Time of The Day">
|
||||
<Selector
|
||||
settingKey="settings-radarr-full_update_hour"
|
||||
settingKey="settings-movies-full_update_hour"
|
||||
options={timeOptions}
|
||||
></Selector>
|
||||
</Input>
|
||||
|
@ -114,7 +87,7 @@ const SettingsSchedulerView: FunctionComponent = () => {
|
|||
<Input>
|
||||
<Check
|
||||
label="Use cached ffprobe results"
|
||||
settingKey="settings-radarr-use_ffprobe_cache"
|
||||
settingKey="settings-movies-use_ffprobe_cache"
|
||||
></Check>
|
||||
<Message>
|
||||
If disabled, Bazarr will use ffprobe to index video file
|
||||
|
|
69
frontend/src/Settings/Series/index.tsx
Normal file
69
frontend/src/Settings/Series/index.tsx
Normal file
|
@ -0,0 +1,69 @@
|
|||
import React, { FunctionComponent } from "react";
|
||||
import {
|
||||
Check,
|
||||
Chips,
|
||||
CollapseBox,
|
||||
Group,
|
||||
Input,
|
||||
Message,
|
||||
Selector,
|
||||
SettingsProvider,
|
||||
Slider,
|
||||
} from "../components";
|
||||
import { seriesEnabledKey } from "../keys";
|
||||
import { seriesTypeOptions } from "../options";
|
||||
|
||||
interface Props {}
|
||||
|
||||
const SettingsSeriesView: FunctionComponent<Props> = () => {
|
||||
return (
|
||||
<SettingsProvider title="Series - Bazarr (Settings)">
|
||||
<CollapseBox>
|
||||
<CollapseBox.Control>
|
||||
<Group header="Use Series">
|
||||
<Input>
|
||||
<Check label="Enabled" settingKey={seriesEnabledKey}></Check>
|
||||
</Input>
|
||||
</Group>
|
||||
</CollapseBox.Control>
|
||||
<CollapseBox.Content indent={false}>
|
||||
<Group header="Options">
|
||||
<Input name="Minimum Score">
|
||||
<Slider settingKey="settings-general-minimum_score"></Slider>
|
||||
</Input>
|
||||
<Input name="Excluded Tags">
|
||||
<Chips settingKey="settings-series-excluded_tags"></Chips>
|
||||
<Message>
|
||||
Episodes from series with those tags (case sensitive) will be
|
||||
excluded from automatic download of subtitles.
|
||||
</Message>
|
||||
</Input>
|
||||
<Input name="Excluded Series Types">
|
||||
<Selector
|
||||
settingKey="settings-series-excluded_series_types"
|
||||
multiple
|
||||
options={seriesTypeOptions}
|
||||
></Selector>
|
||||
<Message>
|
||||
Episodes from series with those types will be excluded from
|
||||
automatic download of subtitles.
|
||||
</Message>
|
||||
</Input>
|
||||
<Input>
|
||||
<Check
|
||||
label="Download Only Monitored"
|
||||
settingKey="settings-series-only_monitored"
|
||||
></Check>
|
||||
<Message>
|
||||
Automatic download of subtitles will only happen for monitored
|
||||
episodes.
|
||||
</Message>
|
||||
</Input>
|
||||
</Group>
|
||||
</CollapseBox.Content>
|
||||
</CollapseBox>
|
||||
</SettingsProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsSeriesView;
|
|
@ -1,110 +0,0 @@
|
|||
import React, { FunctionComponent, useCallback } from "react";
|
||||
import { InputGroup } from "react-bootstrap";
|
||||
import {
|
||||
Check,
|
||||
Chips,
|
||||
CollapseBox,
|
||||
Group,
|
||||
Input,
|
||||
Message,
|
||||
Selector,
|
||||
SettingsProvider,
|
||||
Slider,
|
||||
Text,
|
||||
URLTestButton,
|
||||
} from "../components";
|
||||
import { PathMappingTable } from "../components/pathMapper";
|
||||
import { seriesEnabledKey } from "../keys";
|
||||
import { seriesTypeOptions } from "../options";
|
||||
|
||||
interface Props {}
|
||||
|
||||
const SettingsSonarrView: FunctionComponent<Props> = () => {
|
||||
const baseUrlOverride = useCallback((settings: Settings) => {
|
||||
return settings.sonarr.base_url?.slice(1) ?? "";
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<SettingsProvider title="Sonarr - Bazarr (Settings)">
|
||||
<CollapseBox>
|
||||
<CollapseBox.Control>
|
||||
<Group header="Use Sonarr">
|
||||
<Input>
|
||||
<Check label="Enabled" settingKey={seriesEnabledKey}></Check>
|
||||
</Input>
|
||||
</Group>
|
||||
</CollapseBox.Control>
|
||||
<CollapseBox.Content indent={false}>
|
||||
<Group header="Host">
|
||||
<Input name="Address">
|
||||
<Text settingKey="settings-sonarr-ip"></Text>
|
||||
<Message>Hostname or IPv4 Address</Message>
|
||||
</Input>
|
||||
<Input name="Port">
|
||||
<Text settingKey="settings-sonarr-port"></Text>
|
||||
</Input>
|
||||
<Input name="Base URL">
|
||||
<InputGroup>
|
||||
<InputGroup.Prepend>
|
||||
<InputGroup.Text>/</InputGroup.Text>
|
||||
</InputGroup.Prepend>
|
||||
<Text
|
||||
settingKey="settings-sonarr-base_url"
|
||||
override={baseUrlOverride}
|
||||
beforeStaged={(v) => "/" + v}
|
||||
></Text>
|
||||
</InputGroup>
|
||||
</Input>
|
||||
<Input name="API Key">
|
||||
<Text settingKey="settings-sonarr-apikey"></Text>
|
||||
</Input>
|
||||
<Input>
|
||||
<Check label="SSL" settingKey="settings-sonarr-ssl"></Check>
|
||||
</Input>
|
||||
<Input>
|
||||
<URLTestButton category="sonarr"></URLTestButton>
|
||||
</Input>
|
||||
</Group>
|
||||
<Group header="Options">
|
||||
<Input name="Minimum Score">
|
||||
<Slider settingKey="settings-general-minimum_score"></Slider>
|
||||
</Input>
|
||||
<Input name="Excluded Tags">
|
||||
<Chips settingKey="settings-sonarr-excluded_tags"></Chips>
|
||||
<Message>
|
||||
Episodes from series with those tags (case sensitive) in Sonarr
|
||||
will be excluded from automatic download of subtitles.
|
||||
</Message>
|
||||
</Input>
|
||||
<Input name="Excluded Series Types">
|
||||
<Selector
|
||||
settingKey="settings-sonarr-excluded_series_types"
|
||||
multiple
|
||||
options={seriesTypeOptions}
|
||||
></Selector>
|
||||
<Message>
|
||||
Episodes from series with those types in Sonarr will be excluded
|
||||
from automatic download of subtitles.
|
||||
</Message>
|
||||
</Input>
|
||||
<Input>
|
||||
<Check
|
||||
label="Download Only Monitored"
|
||||
settingKey="settings-sonarr-only_monitored"
|
||||
></Check>
|
||||
<Message>
|
||||
Automatic download of subtitles will only happen for monitored
|
||||
episodes in Sonarr.
|
||||
</Message>
|
||||
</Input>
|
||||
</Group>
|
||||
<Group header="Path Mappings">
|
||||
<PathMappingTable type="sonarr"></PathMappingTable>
|
||||
</Group>
|
||||
</CollapseBox.Content>
|
||||
</CollapseBox>
|
||||
</SettingsProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsSonarrView;
|
|
@ -461,10 +461,10 @@ const SettingsSubtitlesView: FunctionComponent = () => {
|
|||
<b>{"{{subtitle_id}}"}</b> Provider ID of the subtitle file
|
||||
</Message>
|
||||
<Message>
|
||||
<b>{"{{series_id}}"}</b> Sonarr series ID (Empty if movie)
|
||||
<b>{"{{series_id}}"}</b> Series ID (Empty if movie)
|
||||
</Message>
|
||||
<Message>
|
||||
<b>{"{{episode_id}}"}</b> Sonarr episode ID or Radarr movie ID
|
||||
<b>{"{{episode_id}}"}</b> Episode ID or movie ID
|
||||
</Message>
|
||||
</CollapseBox.Content>
|
||||
</CollapseBox>
|
||||
|
|
|
@ -1,67 +1,3 @@
|
|||
import { isBoolean, isNumber, isString } from "lodash";
|
||||
import React, { FunctionComponent, useCallback, useState } from "react";
|
||||
import { Button } from "react-bootstrap";
|
||||
import { UtilsApi } from "../../apis";
|
||||
import { useLatest } from "./hooks";
|
||||
|
||||
export const URLTestButton: FunctionComponent<{
|
||||
category: "sonarr" | "radarr";
|
||||
}> = ({ category }) => {
|
||||
const [title, setTitle] = useState("Test");
|
||||
const [variant, setVar] = useState("primary");
|
||||
|
||||
const address = useLatest<string>(`settings-${category}-ip`, isString);
|
||||
const port = useLatest<number>(`settings-${category}-port`, isNumber);
|
||||
const url = useLatest<string>(`settings-${category}-base_url`, isString);
|
||||
const apikey = useLatest<string>(`settings-${category}-apikey`, isString);
|
||||
const ssl = useLatest<boolean>(`settings-${category}-ssl`, isBoolean);
|
||||
|
||||
const click = useCallback(() => {
|
||||
if (address && apikey && ssl !== null) {
|
||||
let testUrl: string;
|
||||
if (port) {
|
||||
testUrl = `${address}:${port}${url ?? ""}`;
|
||||
} else {
|
||||
testUrl = `${address}${url ?? ""}`;
|
||||
}
|
||||
const request = {
|
||||
protocol: ssl ? "https" : "http",
|
||||
url: testUrl,
|
||||
params: {
|
||||
apikey: apikey,
|
||||
},
|
||||
};
|
||||
|
||||
if (!request.url.endsWith("/")) {
|
||||
request.url += "/";
|
||||
}
|
||||
|
||||
UtilsApi.urlTest(request.protocol, request.url, request.params).then(
|
||||
(result) => {
|
||||
if (result.status) {
|
||||
setTitle(`Version: ${result.version}`);
|
||||
setVar("success");
|
||||
} else {
|
||||
setTitle(result.error);
|
||||
setVar("danger");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}, [address, port, url, apikey, ssl]);
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={click}
|
||||
variant={variant}
|
||||
title={title}
|
||||
className="text-truncate text-nowrap"
|
||||
>
|
||||
{title}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
export * from "./collapse";
|
||||
export { default as CollapseBox } from "./collapse";
|
||||
export * from "./container";
|
||||
|
|
|
@ -1,176 +0,0 @@
|
|||
import { faArrowCircleRight, faTrash } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { capitalize, isArray, isBoolean } from "lodash";
|
||||
import React, { FunctionComponent, useCallback, useMemo } from "react";
|
||||
import { Button } from "react-bootstrap";
|
||||
import { Column, TableUpdater } from "react-table";
|
||||
import { FilesApi } from "../../apis";
|
||||
import { ActionButton, FileBrowser, SimpleTable } from "../../components";
|
||||
import {
|
||||
moviesEnabledKey,
|
||||
pathMappingsKey,
|
||||
pathMappingsMovieKey,
|
||||
seriesEnabledKey,
|
||||
} from "../keys";
|
||||
import { Message } from "./forms";
|
||||
import { useExtract, useLatest, useSingleUpdate } from "./hooks";
|
||||
|
||||
type SupportType = "sonarr" | "radarr";
|
||||
|
||||
function getSupportKey(type: SupportType) {
|
||||
if (type === "sonarr") {
|
||||
return pathMappingsKey;
|
||||
} else {
|
||||
return pathMappingsMovieKey;
|
||||
}
|
||||
}
|
||||
|
||||
function getEnabledKey(type: SupportType) {
|
||||
if (type === "sonarr") {
|
||||
return seriesEnabledKey;
|
||||
} else {
|
||||
return moviesEnabledKey;
|
||||
}
|
||||
}
|
||||
|
||||
interface PathMappingItem {
|
||||
from: string;
|
||||
to: string;
|
||||
}
|
||||
|
||||
interface TableProps {
|
||||
type: SupportType;
|
||||
}
|
||||
|
||||
export const PathMappingTable: FunctionComponent<TableProps> = ({ type }) => {
|
||||
const key = getSupportKey(type);
|
||||
const items = useLatest<[string, string][]>(key, isArray);
|
||||
|
||||
const enabledKey = getEnabledKey(type);
|
||||
const enabled = useExtract<boolean>(enabledKey, isBoolean);
|
||||
|
||||
const update = useSingleUpdate();
|
||||
|
||||
const updateRow = useCallback(
|
||||
(newItems: PathMappingItem[]) => {
|
||||
update(
|
||||
newItems.map((v) => [v.from, v.to]),
|
||||
key
|
||||
);
|
||||
},
|
||||
[key, update]
|
||||
);
|
||||
|
||||
const addRow = useCallback(() => {
|
||||
if (items) {
|
||||
const newItems = [...items, ["", ""]];
|
||||
update(newItems, key);
|
||||
}
|
||||
}, [items, key, update]);
|
||||
|
||||
const data = useMemo<PathMappingItem[]>(
|
||||
() => items?.map((v) => ({ from: v[0], to: v[1] })) ?? [],
|
||||
[items]
|
||||
);
|
||||
|
||||
const request = useMemo(() => {
|
||||
if (type === "sonarr") {
|
||||
return (path: string) => FilesApi.sonarr(path);
|
||||
} else {
|
||||
return (path: string) => FilesApi.radarr(path);
|
||||
}
|
||||
}, [type]);
|
||||
|
||||
const updateCell = useCallback<TableUpdater<PathMappingItem>>(
|
||||
(row, item?: PathMappingItem) => {
|
||||
const newItems = [...data];
|
||||
if (item) {
|
||||
newItems[row.index] = item;
|
||||
} else {
|
||||
newItems.splice(row.index, 1);
|
||||
}
|
||||
updateRow(newItems);
|
||||
},
|
||||
[data, updateRow]
|
||||
);
|
||||
|
||||
const columns = useMemo<Column<PathMappingItem>[]>(
|
||||
() => [
|
||||
{
|
||||
Header: capitalize(type),
|
||||
accessor: "from",
|
||||
Cell: ({ value, row, update }) => (
|
||||
<FileBrowser
|
||||
drop="up"
|
||||
defaultValue={value}
|
||||
load={request}
|
||||
onChange={(path) => {
|
||||
const newItem = { ...row.original };
|
||||
newItem.from = path;
|
||||
update && update(row, newItem);
|
||||
}}
|
||||
></FileBrowser>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "arrow",
|
||||
className: "text-center",
|
||||
Cell: () => (
|
||||
<FontAwesomeIcon icon={faArrowCircleRight}></FontAwesomeIcon>
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: "Bazarr",
|
||||
accessor: "to",
|
||||
Cell: ({ value, row, update }) => (
|
||||
<FileBrowser
|
||||
drop="up"
|
||||
defaultValue={value}
|
||||
load={(path) => FilesApi.bazarr(path)}
|
||||
onChange={(path) => {
|
||||
const newItem = { ...row.original };
|
||||
newItem.to = path;
|
||||
update && update(row, newItem);
|
||||
}}
|
||||
></FileBrowser>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "action",
|
||||
accessor: "to",
|
||||
Cell: ({ row, update }) => (
|
||||
<ActionButton
|
||||
icon={faTrash}
|
||||
onClick={() => {
|
||||
update && update(row);
|
||||
}}
|
||||
></ActionButton>
|
||||
),
|
||||
},
|
||||
],
|
||||
[type, request]
|
||||
);
|
||||
|
||||
if (enabled) {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<SimpleTable
|
||||
emptyText="No Mapping"
|
||||
responsive={false}
|
||||
columns={columns}
|
||||
data={data}
|
||||
update={updateCell}
|
||||
></SimpleTable>
|
||||
<Button block variant="light" onClick={addRow}>
|
||||
Add
|
||||
</Button>
|
||||
</React.Fragment>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Message>
|
||||
Path Mappings will be available after staged changes are saved
|
||||
</Message>
|
||||
);
|
||||
}
|
||||
};
|
|
@ -2,8 +2,5 @@ export const enabledLanguageKey = "languages-enabled";
|
|||
export const languageProfileKey = "languages-profiles";
|
||||
export const notificationsKey = "notifications-providers";
|
||||
|
||||
export const pathMappingsKey = "settings-general-path_mappings";
|
||||
export const pathMappingsMovieKey = "settings-general-path_mappings_movie";
|
||||
|
||||
export const seriesEnabledKey = "settings-general-use_sonarr";
|
||||
export const moviesEnabledKey = "settings-general-use_radarr";
|
||||
export const seriesEnabledKey = "settings-general-use_seris";
|
||||
export const moviesEnabledKey = "settings-general-use_movies";
|
||||
|
|
|
@ -88,12 +88,6 @@ const SystemStatusView: FunctionComponent<Props> = () => {
|
|||
<CRow title="Bazarr Version">
|
||||
<span>{status?.bazarr_version}</span>
|
||||
</CRow>
|
||||
<CRow title="Sonarr Version">
|
||||
<span>{status?.sonarr_version}</span>
|
||||
</CRow>
|
||||
<CRow title="Radarr Version">
|
||||
<span>{status?.radarr_version}</span>
|
||||
</CRow>
|
||||
<CRow title="Operating System">
|
||||
<span>{status?.operating_system}</span>
|
||||
</CRow>
|
||||
|
|
|
@ -30,7 +30,7 @@ const WantedMoviesView: FunctionComponent<Props> = () => {
|
|||
Header: "Name",
|
||||
accessor: "title",
|
||||
Cell: (row) => {
|
||||
const target = `/movies/${row.row.original.radarrId}`;
|
||||
const target = `/movies/${row.row.original.movieId}`;
|
||||
return (
|
||||
<Link to={target}>
|
||||
<span>{row.value}</span>
|
||||
|
@ -44,7 +44,7 @@ const WantedMoviesView: FunctionComponent<Props> = () => {
|
|||
Cell: ({ row, value, update }) => {
|
||||
const wanted = row.original;
|
||||
const hi = wanted.hearing_impaired;
|
||||
const movieid = wanted.radarrId;
|
||||
const movieid = wanted.movieId;
|
||||
|
||||
return value.map((item, idx) => (
|
||||
<AsyncButton
|
||||
|
|
|
@ -30,7 +30,7 @@ const WantedSeriesView: FunctionComponent<Props> = () => {
|
|||
Header: "Name",
|
||||
accessor: "seriesTitle",
|
||||
Cell: (row) => {
|
||||
const target = `/series/${row.row.original.sonarrSeriesId}`;
|
||||
const target = `/series/${row.row.original.seriesId}`;
|
||||
return (
|
||||
<Link to={target}>
|
||||
<span>{row.value}</span>
|
||||
|
@ -51,8 +51,8 @@ const WantedSeriesView: FunctionComponent<Props> = () => {
|
|||
Cell: ({ row, update, value }) => {
|
||||
const wanted = row.original;
|
||||
const hi = wanted.hearing_impaired;
|
||||
const seriesid = wanted.sonarrSeriesId;
|
||||
const episodeid = wanted.sonarrEpisodeId;
|
||||
const seriesid = wanted.seriesId;
|
||||
const episodeid = wanted.episodeId;
|
||||
|
||||
return value.map((item, idx) => (
|
||||
<AsyncButton
|
||||
|
|
|
@ -13,14 +13,6 @@ class FilesApi extends BaseApi {
|
|||
async bazarr(path?: string) {
|
||||
return this.browse("", path);
|
||||
}
|
||||
|
||||
async sonarr(path?: string) {
|
||||
return this.browse("/sonarr", path);
|
||||
}
|
||||
|
||||
async radarr(path?: string) {
|
||||
return this.browse("/radarr", path);
|
||||
}
|
||||
}
|
||||
|
||||
export default new FilesApi();
|
||||
|
|
|
@ -12,17 +12,17 @@ class MovieApi extends BaseApi {
|
|||
return response.data;
|
||||
}
|
||||
|
||||
async addBlacklist(radarrid: number, form: FormType.AddBlacklist) {
|
||||
await this.post("/blacklist", form, { radarrid });
|
||||
async addBlacklist(movieid: number, form: FormType.AddBlacklist) {
|
||||
await this.post("/blacklist", form, { movieid });
|
||||
}
|
||||
|
||||
async deleteBlacklist(all?: boolean, form?: FormType.DeleteBlacklist) {
|
||||
await this.delete("/blacklist", form, { all });
|
||||
}
|
||||
|
||||
async movies(radarrid?: number[]) {
|
||||
async movies(movieid?: number[]) {
|
||||
const response = await this.get<AsyncDataWrapper<Item.Movie>>("", {
|
||||
radarrid,
|
||||
movieid,
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ class MovieApi extends BaseApi {
|
|||
}
|
||||
|
||||
async modify(form: FormType.ModifyItem) {
|
||||
await this.post("", { radarrid: form.id, profileid: form.profileid });
|
||||
await this.post("", { movieid: form.id, profileid: form.profileid });
|
||||
}
|
||||
|
||||
async wanted(params: Parameter.Range) {
|
||||
|
@ -44,9 +44,9 @@ class MovieApi extends BaseApi {
|
|||
return response;
|
||||
}
|
||||
|
||||
async wantedBy(radarrid: number[]) {
|
||||
async wantedBy(movieid: number[]) {
|
||||
const response = await this.get<AsyncDataWrapper<Wanted.Movie>>("/wanted", {
|
||||
radarrid,
|
||||
movieid,
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
@ -59,10 +59,10 @@ class MovieApi extends BaseApi {
|
|||
return response;
|
||||
}
|
||||
|
||||
async historyBy(radarrid: number) {
|
||||
async historyBy(movieid: number) {
|
||||
const response = await this.get<AsyncDataWrapper<History.Movie>>(
|
||||
"/history",
|
||||
{ radarrid }
|
||||
{ movieid }
|
||||
);
|
||||
return response;
|
||||
}
|
||||
|
@ -71,16 +71,16 @@ class MovieApi extends BaseApi {
|
|||
await this.patch("", action);
|
||||
}
|
||||
|
||||
async downloadSubtitles(radarrid: number, form: FormType.Subtitle) {
|
||||
await this.patch("/subtitles", form, { radarrid });
|
||||
async downloadSubtitles(movieid: number, form: FormType.Subtitle) {
|
||||
await this.patch("/subtitles", form, { movieid });
|
||||
}
|
||||
|
||||
async uploadSubtitles(radarrid: number, form: FormType.UploadSubtitle) {
|
||||
await this.post("/subtitles", form, { radarrid });
|
||||
async uploadSubtitles(movieid: number, form: FormType.UploadSubtitle) {
|
||||
await this.post("/subtitles", form, { movieid });
|
||||
}
|
||||
|
||||
async deleteSubtitles(radarrid: number, form: FormType.DeleteSubtitle) {
|
||||
await this.delete("/subtitles", form, { radarrid });
|
||||
async deleteSubtitles(movieid: number, form: FormType.DeleteSubtitle) {
|
||||
await this.delete("/subtitles", form, { movieid });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,13 +19,13 @@ class ProviderApi extends BaseApi {
|
|||
async movies(id: number) {
|
||||
const response = await this.get<DataWrapper<SearchResultType[]>>(
|
||||
"/movies",
|
||||
{ radarrid: id }
|
||||
{ movieid: id }
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async downloadMovieSubtitle(radarrid: number, form: FormType.ManualDownload) {
|
||||
await this.post("/movies", form, { radarrid });
|
||||
async downloadMovieSubtitle(movieid: number, form: FormType.ManualDownload) {
|
||||
await this.post("/movies", form, { movieid });
|
||||
}
|
||||
|
||||
async episodes(episodeid: number) {
|
||||
|
|
|
@ -19,13 +19,13 @@ export const MovieHistoryModal: FunctionComponent<BaseModalProps> = (props) => {
|
|||
|
||||
const update = useCallback(() => {
|
||||
if (movie) {
|
||||
updateHistory(movie.radarrId);
|
||||
updateHistory(movie.movieId);
|
||||
}
|
||||
}, [movie, updateHistory]);
|
||||
|
||||
useDidUpdate(() => {
|
||||
update();
|
||||
}, [movie?.radarrId]);
|
||||
}, [movie?.movieId]);
|
||||
|
||||
const columns = useMemo<Column<History.Movie>[]>(
|
||||
() => [
|
||||
|
@ -78,9 +78,7 @@ export const MovieHistoryModal: FunctionComponent<BaseModalProps> = (props) => {
|
|||
return (
|
||||
<BlacklistButton
|
||||
update={update}
|
||||
promise={(form) =>
|
||||
MoviesApi.addBlacklist(original.radarrId, form)
|
||||
}
|
||||
promise={(form) => MoviesApi.addBlacklist(original.movieId, form)}
|
||||
history={original}
|
||||
></BlacklistButton>
|
||||
);
|
||||
|
@ -118,13 +116,13 @@ export const EpisodeHistoryModal: FunctionComponent<
|
|||
|
||||
const update = useCallback(() => {
|
||||
if (episode) {
|
||||
updateHistory(episode.sonarrEpisodeId);
|
||||
updateHistory(episode.episodeId);
|
||||
}
|
||||
}, [episode, updateHistory]);
|
||||
|
||||
useDidUpdate(() => {
|
||||
update();
|
||||
}, [episode?.sonarrEpisodeId]);
|
||||
}, [episode?.episodeId]);
|
||||
|
||||
const columns = useMemo<Column<History.Episode>[]>(
|
||||
() => [
|
||||
|
@ -174,13 +172,13 @@ export const EpisodeHistoryModal: FunctionComponent<
|
|||
accessor: "blacklisted",
|
||||
Cell: ({ row }) => {
|
||||
const original = row.original;
|
||||
const { sonarrSeriesId, sonarrEpisodeId } = original;
|
||||
const { seriesId, episodeId } = original;
|
||||
return (
|
||||
<BlacklistButton
|
||||
history={original}
|
||||
update={update}
|
||||
promise={(form) =>
|
||||
EpisodesApi.addBlacklist(sonarrSeriesId, sonarrEpisodeId, form)
|
||||
EpisodesApi.addBlacklist(seriesId, episodeId, form)
|
||||
}
|
||||
></BlacklistButton>
|
||||
);
|
||||
|
|
|
@ -65,9 +65,9 @@ export function ManualSearchModal<T extends SupportType>(
|
|||
setSearchState(SearchState.Searching);
|
||||
let results: SearchResultType[] = [];
|
||||
if (isMovie(item)) {
|
||||
results = await ProvidersApi.movies(item.radarrId);
|
||||
results = await ProvidersApi.movies(item.movieId);
|
||||
} else {
|
||||
results = await ProvidersApi.episodes(item.sonarrEpisodeId);
|
||||
results = await ProvidersApi.episodes(item.episodeId);
|
||||
}
|
||||
setResult(results);
|
||||
setSearchState(SearchState.Finished);
|
||||
|
|
|
@ -57,7 +57,7 @@ const MovieUploadModal: FunctionComponent<BaseModalProps> = (props) => {
|
|||
return;
|
||||
}
|
||||
|
||||
const { radarrId } = payload;
|
||||
const { movieId } = payload;
|
||||
|
||||
const tasks = items
|
||||
.filter((v) => v.language !== null)
|
||||
|
@ -66,9 +66,9 @@ const MovieUploadModal: FunctionComponent<BaseModalProps> = (props) => {
|
|||
|
||||
return createTask(
|
||||
file.name,
|
||||
radarrId,
|
||||
movieId,
|
||||
MoviesApi.uploadSubtitles.bind(MoviesApi),
|
||||
radarrId,
|
||||
movieId,
|
||||
{
|
||||
file,
|
||||
forced,
|
||||
|
|
|
@ -85,14 +85,14 @@ const SeriesUploadModal: FunctionComponent<SeriesProps & BaseModalProps> = ({
|
|||
return;
|
||||
}
|
||||
|
||||
const { sonarrSeriesId: seriesid } = payload;
|
||||
const { seriesId: seriesid } = payload;
|
||||
|
||||
const tasks = items
|
||||
.filter((v) => v.payload.instance !== undefined)
|
||||
.map((v) => {
|
||||
const { hi, forced, payload, language } = v;
|
||||
const { code2 } = language!;
|
||||
const { sonarrEpisodeId: episodeid } = payload.instance!;
|
||||
const { episodeId: episodeid } = payload.instance!;
|
||||
|
||||
const form: FormType.UploadSubtitle = {
|
||||
file: v.file,
|
||||
|
|
|
@ -58,9 +58,9 @@ type TableColumnType = FormType.ModifySubtitle & {
|
|||
|
||||
function getIdAndType(item: SupportType): [number, "episode" | "movie"] {
|
||||
if (isMovie(item)) {
|
||||
return [item.radarrId, "movie"];
|
||||
return [item.movieId, "movie"];
|
||||
} else {
|
||||
return [item.sonarrEpisodeId, "episode"];
|
||||
return [item.episodeId, "episode"];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,11 +27,11 @@ export function submodProcessColor(s: string) {
|
|||
|
||||
export function GetItemId<T extends object>(item: T): number {
|
||||
if (isMovie(item)) {
|
||||
return item.radarrId;
|
||||
return item.movieId;
|
||||
} else if (isEpisode(item)) {
|
||||
return item.sonarrEpisodeId;
|
||||
return item.episodeId;
|
||||
} else if (isSeries(item)) {
|
||||
return item.sonarrSeriesId;
|
||||
return item.seriesId;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -14,11 +14,11 @@ export function isNonNullable(v: any): v is NonNullable<any> {
|
|||
}
|
||||
|
||||
export function isMovie(v: any): v is Item.Movie {
|
||||
return "radarrId" in v;
|
||||
return "movieId" in v;
|
||||
}
|
||||
|
||||
export function isEpisode(v: any): v is Item.Episode {
|
||||
return "sonarrEpisodeId" in v;
|
||||
return "episodeId" in v;
|
||||
}
|
||||
|
||||
export function isSeries(v: any): v is Item.Series {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue