mirror of https://github.com/morpheus65535/bazarr
Added Sync Optimization for Movies, Series and Episodes to ignore unmonitored items
This commit is contained in:
parent
3922d52d5c
commit
990448b06e
|
@ -161,6 +161,8 @@ validators = [
|
||||||
Validator('sonarr.use_ffprobe_cache', must_exist=True, default=True, is_type_of=bool),
|
Validator('sonarr.use_ffprobe_cache', must_exist=True, default=True, is_type_of=bool),
|
||||||
Validator('sonarr.exclude_season_zero', must_exist=True, default=False, is_type_of=bool),
|
Validator('sonarr.exclude_season_zero', must_exist=True, default=False, is_type_of=bool),
|
||||||
Validator('sonarr.defer_search_signalr', must_exist=True, default=False, is_type_of=bool),
|
Validator('sonarr.defer_search_signalr', must_exist=True, default=False, is_type_of=bool),
|
||||||
|
Validator('sonarr.sync_only_monitored_series', must_exist=True, default=False, is_type_of=bool),
|
||||||
|
Validator('sonarr.sync_only_monitored_episodes', must_exist=True, default=False, is_type_of=bool),
|
||||||
|
|
||||||
# radarr section
|
# radarr section
|
||||||
Validator('radarr.ip', must_exist=True, default='127.0.0.1', is_type_of=str),
|
Validator('radarr.ip', must_exist=True, default='127.0.0.1', is_type_of=str),
|
||||||
|
@ -180,6 +182,7 @@ validators = [
|
||||||
Validator('radarr.excluded_tags', must_exist=True, default=[], is_type_of=list),
|
Validator('radarr.excluded_tags', must_exist=True, default=[], is_type_of=list),
|
||||||
Validator('radarr.use_ffprobe_cache', must_exist=True, default=True, is_type_of=bool),
|
Validator('radarr.use_ffprobe_cache', must_exist=True, default=True, is_type_of=bool),
|
||||||
Validator('radarr.defer_search_signalr', must_exist=True, default=False, is_type_of=bool),
|
Validator('radarr.defer_search_signalr', must_exist=True, default=False, is_type_of=bool),
|
||||||
|
Validator('radarr.sync_only_monitored_movies', must_exist=True, default=False, is_type_of=bool),
|
||||||
|
|
||||||
# proxy section
|
# proxy section
|
||||||
Validator('proxy.type', must_exist=True, default=None, is_type_of=(NoneType, str),
|
Validator('proxy.type', must_exist=True, default=None, is_type_of=(NoneType, str),
|
||||||
|
|
|
@ -8,3 +8,6 @@ headers = {"User-Agent": os.environ["SZ_USER_AGENT"]}
|
||||||
|
|
||||||
# hearing-impaired detection regex
|
# hearing-impaired detection regex
|
||||||
hi_regex = re.compile(r'[*¶♫♪].{3,}[*¶♫♪]|[\[\(\{].{3,}[\]\)\}](?<!{\\an\d})')
|
hi_regex = re.compile(r'[*¶♫♪].{3,}[*¶♫♪]|[\[\(\{].{3,}[\]\)\}](?<!{\\an\d})')
|
||||||
|
|
||||||
|
# minimum file size for Bazarr to consider it a video
|
||||||
|
MINIMUM_VIDEO_SIZE = 20480
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
|
from constants import MINIMUM_VIDEO_SIZE
|
||||||
|
|
||||||
from sqlalchemy.exc import IntegrityError
|
from sqlalchemy.exc import IntegrityError
|
||||||
|
|
||||||
|
@ -16,6 +17,13 @@ from app.event_handler import event_stream, show_progress, hide_progress
|
||||||
from .utils import get_profile_list, get_tags, get_movies_from_radarr_api
|
from .utils import get_profile_list, get_tags, get_movies_from_radarr_api
|
||||||
from .parser import movieParser
|
from .parser import movieParser
|
||||||
|
|
||||||
|
# map between booleans and strings in DB
|
||||||
|
bool_map = {"True": True, "False": False}
|
||||||
|
|
||||||
|
FEATURE_PREFIX = "SYNC_MOVIES "
|
||||||
|
def trace(message):
|
||||||
|
if settings.general.debug:
|
||||||
|
logging.debug(FEATURE_PREFIX + message)
|
||||||
|
|
||||||
def update_all_movies():
|
def update_all_movies():
|
||||||
movies_full_scan_subtitles()
|
movies_full_scan_subtitles()
|
||||||
|
@ -45,6 +53,16 @@ def update_movie(updated_movie, send_event):
|
||||||
event_stream(type='movie', action='update', payload=updated_movie['radarrId'])
|
event_stream(type='movie', action='update', payload=updated_movie['radarrId'])
|
||||||
|
|
||||||
|
|
||||||
|
def get_movie_monitored_status(movie_id):
|
||||||
|
existing_movie_monitored = database.execute(
|
||||||
|
select(TableMovies.monitored)
|
||||||
|
.where(TableMovies.tmdbId == movie_id))\
|
||||||
|
.first()
|
||||||
|
if existing_movie_monitored is None:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return bool_map[existing_movie_monitored[0]]
|
||||||
|
|
||||||
# Insert new movies in DB
|
# Insert new movies in DB
|
||||||
def add_movie(added_movie, send_event):
|
def add_movie(added_movie, send_event):
|
||||||
try:
|
try:
|
||||||
|
@ -104,12 +122,12 @@ def update_movies(send_event=True):
|
||||||
|
|
||||||
current_movies_radarr = [str(movie['tmdbId']) for movie in movies if movie['hasFile'] and
|
current_movies_radarr = [str(movie['tmdbId']) for movie in movies if movie['hasFile'] and
|
||||||
'movieFile' in movie and
|
'movieFile' in movie and
|
||||||
(movie['movieFile']['size'] > 20480 or
|
(movie['movieFile']['size'] > MINIMUM_VIDEO_SIZE or
|
||||||
get_movie_file_size_from_db(movie['movieFile']['path']) > 20480)]
|
get_movie_file_size_from_db(movie['movieFile']['path']) > MINIMUM_VIDEO_SIZE)]
|
||||||
|
|
||||||
# Remove old movies from DB
|
# Remove movies from DB that either no longer exist in Radarr or exist and Radarr says do not have a movie file
|
||||||
movies_to_delete = list(set(current_movies_id_db) - set(current_movies_radarr))
|
movies_to_delete = list(set(current_movies_id_db) - set(current_movies_radarr))
|
||||||
|
movies_deleted = []
|
||||||
if len(movies_to_delete):
|
if len(movies_to_delete):
|
||||||
try:
|
try:
|
||||||
database.execute(delete(TableMovies).where(TableMovies.tmdbId.in_(movies_to_delete)))
|
database.execute(delete(TableMovies).where(TableMovies.tmdbId.in_(movies_to_delete)))
|
||||||
|
@ -117,11 +135,19 @@ def update_movies(send_event=True):
|
||||||
logging.error(f"BAZARR cannot delete movies because of {e}")
|
logging.error(f"BAZARR cannot delete movies because of {e}")
|
||||||
else:
|
else:
|
||||||
for removed_movie in movies_to_delete:
|
for removed_movie in movies_to_delete:
|
||||||
|
movies_deleted.append(removed_movie['title'])
|
||||||
if send_event:
|
if send_event:
|
||||||
event_stream(type='movie', action='delete', payload=removed_movie)
|
event_stream(type='movie', action='delete', payload=removed_movie)
|
||||||
|
|
||||||
# Build new and updated movies
|
# Add new movies and update movies that Radarr says have media files
|
||||||
|
# Any new movies added to Radarr that don't have media files yet will not be added to DB
|
||||||
movies_count = len(movies)
|
movies_count = len(movies)
|
||||||
|
sync_monitored = settings.radarr.sync_only_monitored_movies
|
||||||
|
if sync_monitored:
|
||||||
|
skipped_count = 0
|
||||||
|
files_missing = 0
|
||||||
|
movies_added = []
|
||||||
|
movies_updated = []
|
||||||
for i, movie in enumerate(movies):
|
for i, movie in enumerate(movies):
|
||||||
if send_event:
|
if send_event:
|
||||||
show_progress(id='movies_progress',
|
show_progress(id='movies_progress',
|
||||||
|
@ -129,12 +155,22 @@ def update_movies(send_event=True):
|
||||||
name=movie['title'],
|
name=movie['title'],
|
||||||
value=i,
|
value=i,
|
||||||
count=movies_count)
|
count=movies_count)
|
||||||
|
# Only movies that Radarr says have files downloaded will be kept up to date in the DB
|
||||||
if movie['hasFile'] is True:
|
if movie['hasFile'] is True:
|
||||||
if 'movieFile' in movie:
|
if 'movieFile' in movie:
|
||||||
if (movie['movieFile']['size'] > 20480 or
|
if sync_monitored:
|
||||||
get_movie_file_size_from_db(movie['movieFile']['path']) > 20480):
|
if get_movie_monitored_status(movie['tmdbId']) != movie['monitored']:
|
||||||
# Add movies in radarr to current movies list
|
# monitored status is not the same as our DB
|
||||||
|
trace(f"{i}: (Monitor Status Mismatch) {movie['title']}")
|
||||||
|
elif not movie['monitored']:
|
||||||
|
trace(f"{i}: (Skipped Unmonitored) {movie['title']}")
|
||||||
|
skipped_count += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
if (movie['movieFile']['size'] > MINIMUM_VIDEO_SIZE or
|
||||||
|
get_movie_file_size_from_db(movie['movieFile']['path']) > MINIMUM_VIDEO_SIZE):
|
||||||
|
# Add/update movies from Radarr that have a movie file to current movies list
|
||||||
|
trace(f"{i}: (Processing) {movie['title']}")
|
||||||
if str(movie['tmdbId']) in current_movies_id_db:
|
if str(movie['tmdbId']) in current_movies_id_db:
|
||||||
parsed_movie = movieParser(movie, action='update',
|
parsed_movie = movieParser(movie, action='update',
|
||||||
tags_dict=tagsDict,
|
tags_dict=tagsDict,
|
||||||
|
@ -142,16 +178,29 @@ def update_movies(send_event=True):
|
||||||
audio_profiles=audio_profiles)
|
audio_profiles=audio_profiles)
|
||||||
if not any([parsed_movie.items() <= x for x in current_movies_db_kv]):
|
if not any([parsed_movie.items() <= x for x in current_movies_db_kv]):
|
||||||
update_movie(parsed_movie, send_event)
|
update_movie(parsed_movie, send_event)
|
||||||
|
movies_updated.append(parsed_movie['title'])
|
||||||
else:
|
else:
|
||||||
parsed_movie = movieParser(movie, action='insert',
|
parsed_movie = movieParser(movie, action='insert',
|
||||||
tags_dict=tagsDict,
|
tags_dict=tagsDict,
|
||||||
movie_default_profile=movie_default_profile,
|
movie_default_profile=movie_default_profile,
|
||||||
audio_profiles=audio_profiles)
|
audio_profiles=audio_profiles)
|
||||||
add_movie(parsed_movie, send_event)
|
add_movie(parsed_movie, send_event)
|
||||||
|
movies_added.append(parsed_movie['title'])
|
||||||
|
else:
|
||||||
|
trace(f"{i}: (Skipped File Missing) {movie['title']}")
|
||||||
|
files_missing += 1
|
||||||
|
|
||||||
if send_event:
|
if send_event:
|
||||||
hide_progress(id='movies_progress')
|
hide_progress(id='movies_progress')
|
||||||
|
|
||||||
|
trace(f"Skipped {files_missing} file missing movies out of {i}")
|
||||||
|
if sync_monitored:
|
||||||
|
trace(f"Skipped {skipped_count} unmonitored movies out of {i}")
|
||||||
|
trace(f"Processed {i - files_missing - skipped_count} movies out of {i} " +
|
||||||
|
f"with {len(movies_added)} added, {len(movies_updated)} updated and {len(movies_deleted)} deleted")
|
||||||
|
else:
|
||||||
|
trace(f"Processed {i - files_missing} movies out of {i} with {len(movies_added)} added and {len(movies_updated)} updated")
|
||||||
|
|
||||||
logging.debug('BAZARR All movies synced from Radarr into database.')
|
logging.debug('BAZARR All movies synced from Radarr into database.')
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,12 +13,6 @@ from .converter import RadarrFormatAudioCodec, RadarrFormatVideoCodec
|
||||||
|
|
||||||
def movieParser(movie, action, tags_dict, movie_default_profile, audio_profiles):
|
def movieParser(movie, action, tags_dict, movie_default_profile, audio_profiles):
|
||||||
if 'movieFile' in movie:
|
if 'movieFile' in movie:
|
||||||
# Detect file separator
|
|
||||||
if movie['path'][0] == "/":
|
|
||||||
separator = "/"
|
|
||||||
else:
|
|
||||||
separator = "\\"
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
overview = str(movie['overview'])
|
overview = str(movie['overview'])
|
||||||
except Exception:
|
except Exception:
|
||||||
|
@ -120,10 +114,9 @@ def movieParser(movie, action, tags_dict, movie_default_profile, audio_profiles)
|
||||||
|
|
||||||
tags = [d['label'] for d in tags_dict if d['id'] in movie['tags']]
|
tags = [d['label'] for d in tags_dict if d['id'] in movie['tags']]
|
||||||
|
|
||||||
if action == 'update':
|
parsed_movie = {'radarrId': int(movie["id"]),
|
||||||
return {'radarrId': int(movie["id"]),
|
|
||||||
'title': movie["title"],
|
'title': movie["title"],
|
||||||
'path': movie["path"] + separator + movie['movieFile']['relativePath'],
|
'path': os.path.join(movie["path"], movie['movieFile']['relativePath']),
|
||||||
'tmdbId': str(movie["tmdbId"]),
|
'tmdbId': str(movie["tmdbId"]),
|
||||||
'poster': poster,
|
'poster': poster,
|
||||||
'fanart': fanart,
|
'fanart': fanart,
|
||||||
|
@ -142,30 +135,12 @@ def movieParser(movie, action, tags_dict, movie_default_profile, audio_profiles)
|
||||||
'movie_file_id': int(movie['movieFile']['id']),
|
'movie_file_id': int(movie['movieFile']['id']),
|
||||||
'tags': str(tags),
|
'tags': str(tags),
|
||||||
'file_size': movie['movieFile']['size']}
|
'file_size': movie['movieFile']['size']}
|
||||||
else:
|
|
||||||
return {'radarrId': int(movie["id"]),
|
if action == 'insert':
|
||||||
'title': movie["title"],
|
parsed_movie['subtitles'] = '[]'
|
||||||
'path': movie["path"] + separator + movie['movieFile']['relativePath'],
|
parsed_movie['profileId'] = movie_default_profile
|
||||||
'tmdbId': str(movie["tmdbId"]),
|
|
||||||
'subtitles': '[]',
|
return parsed_movie
|
||||||
'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 profile_id_to_language(id, profiles):
|
def profile_id_to_language(id, profiles):
|
||||||
|
|
|
@ -2,10 +2,11 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
|
from constants import MINIMUM_VIDEO_SIZE
|
||||||
|
|
||||||
from sqlalchemy.exc import IntegrityError
|
from sqlalchemy.exc import IntegrityError
|
||||||
|
|
||||||
from app.database import database, TableEpisodes, delete, update, insert, select
|
from app.database import database, TableShows, TableEpisodes, delete, update, insert, select
|
||||||
from app.config import settings
|
from app.config import settings
|
||||||
from utilities.path_mappings import path_mappings
|
from utilities.path_mappings import path_mappings
|
||||||
from subtitles.indexer.series import store_subtitles, series_full_scan_subtitles
|
from subtitles.indexer.series import store_subtitles, series_full_scan_subtitles
|
||||||
|
@ -16,14 +17,29 @@ from sonarr.info import get_sonarr_info, url_sonarr
|
||||||
from .parser import episodeParser
|
from .parser import episodeParser
|
||||||
from .utils import get_episodes_from_sonarr_api, get_episodesFiles_from_sonarr_api
|
from .utils import get_episodes_from_sonarr_api, get_episodesFiles_from_sonarr_api
|
||||||
|
|
||||||
|
# map between booleans and strings in DB
|
||||||
|
bool_map = {"True": True, "False": False}
|
||||||
|
|
||||||
|
FEATURE_PREFIX = "SYNC_EPISODES "
|
||||||
|
def trace(message):
|
||||||
|
if settings.general.debug:
|
||||||
|
logging.debug(FEATURE_PREFIX + message)
|
||||||
|
|
||||||
|
def get_episodes_monitored_table(series_id):
|
||||||
|
episodes_monitored = database.execute(
|
||||||
|
select(TableEpisodes.episode_file_id, TableEpisodes.monitored)
|
||||||
|
.where(TableEpisodes.sonarrSeriesId == series_id))\
|
||||||
|
.all()
|
||||||
|
episode_dict = dict((x, y) for x, y in episodes_monitored)
|
||||||
|
return episode_dict
|
||||||
|
|
||||||
def update_all_episodes():
|
def update_all_episodes():
|
||||||
series_full_scan_subtitles()
|
series_full_scan_subtitles()
|
||||||
logging.info('BAZARR All existing episode subtitles indexed from disk.')
|
logging.info('BAZARR All existing episode subtitles indexed from disk.')
|
||||||
|
|
||||||
|
|
||||||
def sync_episodes(series_id, send_event=True):
|
def sync_episodes(series_id, send_event=True):
|
||||||
logging.debug('BAZARR Starting episodes sync from Sonarr.')
|
logging.debug(f'BAZARR Starting episodes sync from Sonarr for series ID {series_id}.')
|
||||||
apikey_sonarr = settings.sonarr.apikey
|
apikey_sonarr = settings.sonarr.apikey
|
||||||
|
|
||||||
# Get current episodes id in DB
|
# Get current episodes id in DB
|
||||||
|
@ -58,16 +74,42 @@ def sync_episodes(series_id, send_event=True):
|
||||||
if item:
|
if item:
|
||||||
episode['episodeFile'] = item[0]
|
episode['episodeFile'] = item[0]
|
||||||
|
|
||||||
|
|
||||||
|
sync_monitored = settings.sonarr.sync_only_monitored_series and settings.sonarr.sync_only_monitored_episodes
|
||||||
|
if sync_monitored:
|
||||||
|
episodes_monitored = get_episodes_monitored_table(series_id)
|
||||||
|
skipped_count = 0
|
||||||
|
|
||||||
for episode in episodes:
|
for episode in episodes:
|
||||||
if 'hasFile' in episode:
|
if 'hasFile' in episode:
|
||||||
if episode['hasFile'] is True:
|
if episode['hasFile'] is True:
|
||||||
if 'episodeFile' in episode:
|
if 'episodeFile' in episode:
|
||||||
|
# monitored_status_db = get_episodes_monitored_status(episode['episodeFileId'])
|
||||||
|
if sync_monitored:
|
||||||
|
try:
|
||||||
|
monitored_status_db = bool_map[episodes_monitored[episode['episodeFileId']]]
|
||||||
|
except KeyError:
|
||||||
|
monitored_status_db = None
|
||||||
|
|
||||||
|
if monitored_status_db is None:
|
||||||
|
# not in db, might need to add, if we have a file on disk
|
||||||
|
pass
|
||||||
|
elif monitored_status_db != episode['monitored']:
|
||||||
|
# monitored status changed and we don't know about it until now
|
||||||
|
trace(f"(Monitor Status Mismatch) {episode['title']}")
|
||||||
|
# pass
|
||||||
|
elif not episode['monitored']:
|
||||||
|
# Add unmonitored episode in sonarr to current episode list, otherwise it will be deleted from db
|
||||||
|
current_episodes_sonarr.append(episode['id'])
|
||||||
|
skipped_count += 1
|
||||||
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
bazarr_file_size = \
|
bazarr_file_size = \
|
||||||
os.path.getsize(path_mappings.path_replace(episode['episodeFile']['path']))
|
os.path.getsize(path_mappings.path_replace(episode['episodeFile']['path']))
|
||||||
except OSError:
|
except OSError:
|
||||||
bazarr_file_size = 0
|
bazarr_file_size = 0
|
||||||
if episode['episodeFile']['size'] > 20480 or bazarr_file_size > 20480:
|
if episode['episodeFile']['size'] > MINIMUM_VIDEO_SIZE or bazarr_file_size > MINIMUM_VIDEO_SIZE:
|
||||||
# Add episodes in sonarr to current episode list
|
# Add episodes in sonarr to current episode list
|
||||||
current_episodes_sonarr.append(episode['id'])
|
current_episodes_sonarr.append(episode['id'])
|
||||||
|
|
||||||
|
@ -80,6 +122,12 @@ def sync_episodes(series_id, send_event=True):
|
||||||
episodes_to_add.append(episodeParser(episode))
|
episodes_to_add.append(episodeParser(episode))
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if sync_monitored:
|
||||||
|
# try to avoid unnecessary database calls
|
||||||
|
if settings.general.debug:
|
||||||
|
series_title = database.execute(select(TableShows.title).where(TableShows.sonarrSeriesId == series_id)).first()[0]
|
||||||
|
trace(f"Skipped {skipped_count} unmonitored episodes out of {len(episodes)} for {series_title}")
|
||||||
|
|
||||||
# Remove old episodes from DB
|
# Remove old episodes from DB
|
||||||
episodes_to_delete = list(set(current_episodes_id_db_list) - set(current_episodes_sonarr))
|
episodes_to_delete = list(set(current_episodes_id_db_list) - set(current_episodes_sonarr))
|
||||||
|
|
|
@ -4,6 +4,7 @@ import os
|
||||||
|
|
||||||
from app.config import settings
|
from app.config import settings
|
||||||
from app.database import TableShows, database, select
|
from app.database import TableShows, database, select
|
||||||
|
from constants import MINIMUM_VIDEO_SIZE
|
||||||
from utilities.path_mappings import path_mappings
|
from utilities.path_mappings import path_mappings
|
||||||
from utilities.video_analyzer import embedded_audio_reader
|
from utilities.video_analyzer import embedded_audio_reader
|
||||||
from sonarr.info import get_sonarr_info
|
from sonarr.info import get_sonarr_info
|
||||||
|
@ -92,7 +93,7 @@ def episodeParser(episode):
|
||||||
bazarr_file_size = os.path.getsize(path_mappings.path_replace(episode['episodeFile']['path']))
|
bazarr_file_size = os.path.getsize(path_mappings.path_replace(episode['episodeFile']['path']))
|
||||||
except OSError:
|
except OSError:
|
||||||
bazarr_file_size = 0
|
bazarr_file_size = 0
|
||||||
if episode['episodeFile']['size'] > 20480 or bazarr_file_size > 20480:
|
if episode['episodeFile']['size'] > MINIMUM_VIDEO_SIZE or bazarr_file_size > MINIMUM_VIDEO_SIZE:
|
||||||
if 'sceneName' in episode['episodeFile']:
|
if 'sceneName' in episode['episodeFile']:
|
||||||
sceneName = episode['episodeFile']['sceneName']
|
sceneName = episode['episodeFile']['sceneName']
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -16,6 +16,20 @@ from .episodes import sync_episodes
|
||||||
from .parser import seriesParser
|
from .parser import seriesParser
|
||||||
from .utils import get_profile_list, get_tags, get_series_from_sonarr_api
|
from .utils import get_profile_list, get_tags, get_series_from_sonarr_api
|
||||||
|
|
||||||
|
# map between booleans and strings in DB
|
||||||
|
bool_map = {"True": True, "False": False}
|
||||||
|
|
||||||
|
FEATURE_PREFIX = "SYNC_SERIES "
|
||||||
|
def trace(message):
|
||||||
|
if settings.general.debug:
|
||||||
|
logging.debug(FEATURE_PREFIX + message)
|
||||||
|
|
||||||
|
def get_series_monitored_table():
|
||||||
|
series_monitored = database.execute(
|
||||||
|
select(TableShows.tvdbId, TableShows.monitored))\
|
||||||
|
.all()
|
||||||
|
series_dict = dict((x, y) for x, y in series_monitored)
|
||||||
|
return series_dict
|
||||||
|
|
||||||
def update_series(send_event=True):
|
def update_series(send_event=True):
|
||||||
check_sonarr_rootfolder()
|
check_sonarr_rootfolder()
|
||||||
|
@ -55,6 +69,12 @@ def update_series(send_event=True):
|
||||||
current_shows_sonarr = []
|
current_shows_sonarr = []
|
||||||
|
|
||||||
series_count = len(series)
|
series_count = len(series)
|
||||||
|
sync_monitored = settings.sonarr.sync_only_monitored_series
|
||||||
|
if sync_monitored:
|
||||||
|
series_monitored = get_series_monitored_table()
|
||||||
|
skipped_count = 0
|
||||||
|
trace(f"Starting sync for {series_count} shows")
|
||||||
|
|
||||||
for i, show in enumerate(series):
|
for i, show in enumerate(series):
|
||||||
if send_event:
|
if send_event:
|
||||||
show_progress(id='series_progress',
|
show_progress(id='series_progress',
|
||||||
|
@ -63,6 +83,26 @@ def update_series(send_event=True):
|
||||||
value=i,
|
value=i,
|
||||||
count=series_count)
|
count=series_count)
|
||||||
|
|
||||||
|
if sync_monitored:
|
||||||
|
try:
|
||||||
|
monitored_status_db = bool_map[series_monitored[show['tvdbId']]]
|
||||||
|
except KeyError:
|
||||||
|
monitored_status_db = None
|
||||||
|
if monitored_status_db is None:
|
||||||
|
# not in db, need to add
|
||||||
|
pass
|
||||||
|
elif monitored_status_db != show['monitored']:
|
||||||
|
# monitored status changed and we don't know about it until now
|
||||||
|
trace(f"{i}: (Monitor Status Mismatch) {show['title']}")
|
||||||
|
# pass
|
||||||
|
elif not show['monitored']:
|
||||||
|
# Add unmonitored series in sonarr to current series list, otherwise it will be deleted from db
|
||||||
|
trace(f"{i}: (Skipped Unmonitored) {show['title']}")
|
||||||
|
current_shows_sonarr.append(show['id'])
|
||||||
|
skipped_count += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
trace(f"{i}: (Processing) {show['title']}")
|
||||||
# Add shows in Sonarr to current shows list
|
# Add shows in Sonarr to current shows list
|
||||||
current_shows_sonarr.append(show['id'])
|
current_shows_sonarr.append(show['id'])
|
||||||
|
|
||||||
|
@ -76,6 +116,7 @@ def update_series(send_event=True):
|
||||||
.filter_by(**updated_series))\
|
.filter_by(**updated_series))\
|
||||||
.first():
|
.first():
|
||||||
try:
|
try:
|
||||||
|
trace(f"Updating {show['title']}")
|
||||||
database.execute(
|
database.execute(
|
||||||
update(TableShows)
|
update(TableShows)
|
||||||
.values(updated_series)
|
.values(updated_series)
|
||||||
|
@ -92,6 +133,7 @@ def update_series(send_event=True):
|
||||||
audio_profiles=audio_profiles)
|
audio_profiles=audio_profiles)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
trace(f"Inserting {show['title']}")
|
||||||
database.execute(
|
database.execute(
|
||||||
insert(TableShows)
|
insert(TableShows)
|
||||||
.values(added_series))
|
.values(added_series))
|
||||||
|
@ -110,6 +152,10 @@ def update_series(send_event=True):
|
||||||
removed_series = list(set(current_shows_db) - set(current_shows_sonarr))
|
removed_series = list(set(current_shows_db) - set(current_shows_sonarr))
|
||||||
|
|
||||||
for series in removed_series:
|
for series in removed_series:
|
||||||
|
# try to avoid unnecessary database calls
|
||||||
|
if settings.general.debug:
|
||||||
|
series_title = database.execute(select(TableShows.title).where(TableShows.sonarrSeriesId == series)).first()[0]
|
||||||
|
trace(f"Deleting {series_title}")
|
||||||
database.execute(
|
database.execute(
|
||||||
delete(TableShows)
|
delete(TableShows)
|
||||||
.where(TableShows.sonarrSeriesId == series))
|
.where(TableShows.sonarrSeriesId == series))
|
||||||
|
@ -120,6 +166,8 @@ def update_series(send_event=True):
|
||||||
if send_event:
|
if send_event:
|
||||||
hide_progress(id='series_progress')
|
hide_progress(id='series_progress')
|
||||||
|
|
||||||
|
if sync_monitored:
|
||||||
|
trace(f"skipped {skipped_count} unmonitored series out of {i}")
|
||||||
logging.debug('BAZARR All series synced from Sonarr into database.')
|
logging.debug('BAZARR All series synced from Sonarr into database.')
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -266,6 +266,11 @@ def parse_video_metadata(file, file_size, episode_file_id=None, movie_file_id=No
|
||||||
elif embedded_subs_parser == 'mediainfo':
|
elif embedded_subs_parser == 'mediainfo':
|
||||||
mediainfo_path = get_binary("mediainfo")
|
mediainfo_path = get_binary("mediainfo")
|
||||||
|
|
||||||
|
# see if file exists (perhaps offline)
|
||||||
|
if not os.path.exists(file):
|
||||||
|
logging.error(f'Video file "{file}" cannot be found for analysis')
|
||||||
|
return None
|
||||||
|
|
||||||
# if we have ffprobe available
|
# if we have ffprobe available
|
||||||
if ffprobe_path:
|
if ffprobe_path:
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -35,11 +35,55 @@ const SettingsSchedulerView: FunctionComponent = () => {
|
||||||
options={seriesSyncOptions}
|
options={seriesSyncOptions}
|
||||||
settingKey="settings-sonarr-series_sync"
|
settingKey="settings-sonarr-series_sync"
|
||||||
></Selector>
|
></Selector>
|
||||||
|
<Check
|
||||||
|
label="Sync Only Monitored Series"
|
||||||
|
settingKey={"settings-sonarr-sync_only_monitored_series"}
|
||||||
|
></Check>
|
||||||
|
<CollapseBox settingKey={"settings-sonarr-sync_only_monitored_series"}>
|
||||||
|
<Message>
|
||||||
|
If enabled, only series with a monitored status in Sonarr will be
|
||||||
|
synced. If you make changes to a specific unmonitored Sonarr series
|
||||||
|
and you want Bazarr to know about those changes, simply toggle the
|
||||||
|
monitored status back on in Sonarr and Bazarr will sync any changes.
|
||||||
|
</Message>
|
||||||
|
</CollapseBox>
|
||||||
|
<CollapseBox settingKey={"settings-sonarr-sync_only_monitored_series"}>
|
||||||
|
<Check
|
||||||
|
label="Sync Only Monitored Episodes"
|
||||||
|
settingKey={"settings-sonarr-sync_only_monitored_episodes"}
|
||||||
|
></Check>
|
||||||
|
<CollapseBox
|
||||||
|
settingKey={"settings-sonarr-sync_only_monitored_episodes"}
|
||||||
|
>
|
||||||
|
<Message>
|
||||||
|
If enabled, only episodes with a monitored status in Sonarr will
|
||||||
|
be synced. If you make changes to a specific unmonitored Sonarr
|
||||||
|
episode (or season) and you want Bazarr to know about those
|
||||||
|
changes, simply toggle the monitored status back on in Sonarr and
|
||||||
|
Bazarr will sync any changes. This setting is especially helpful
|
||||||
|
for long running TV series with many seasons and many episodes,
|
||||||
|
but that are still actively producing new episodes (e.g. Saturday
|
||||||
|
Night Live).
|
||||||
|
</Message>
|
||||||
|
</CollapseBox>
|
||||||
|
</CollapseBox>
|
||||||
<Selector
|
<Selector
|
||||||
label="Sync with Radarr"
|
label="Sync with Radarr"
|
||||||
options={moviesSyncOptions}
|
options={moviesSyncOptions}
|
||||||
settingKey="settings-radarr-movies_sync"
|
settingKey="settings-radarr-movies_sync"
|
||||||
></Selector>
|
></Selector>
|
||||||
|
<Check
|
||||||
|
label="Sync Only Monitored Movies"
|
||||||
|
settingKey={"settings-radarr-sync_only_monitored_movies"}
|
||||||
|
></Check>
|
||||||
|
<CollapseBox settingKey={"settings-radarr-sync_only_monitored_movies"}>
|
||||||
|
<Message>
|
||||||
|
If enabled, only movies with a monitored status in Radarr will be
|
||||||
|
synced. If you make changes to a specific unmonitored Radarr movie
|
||||||
|
and you want Bazarr to know about those changes, simply toggle the
|
||||||
|
monitored status back on in Radarr and Bazarr will sync any changes.
|
||||||
|
</Message>
|
||||||
|
</CollapseBox>
|
||||||
</Section>
|
</Section>
|
||||||
<Section header="Disk Indexing">
|
<Section header="Disk Indexing">
|
||||||
<Selector
|
<Selector
|
||||||
|
|
Loading…
Reference in New Issue