Improved UI feedback on multiple search, download and upload issues. #2235

This commit is contained in:
morpheus65535 2023-08-29 17:17:30 -04:00
parent 162dbc0eee
commit 995ae1b5b8
16 changed files with 181 additions and 86 deletions

View File

@ -88,6 +88,7 @@ class EpisodesBlacklist(Resource):
@api_ns_episodes_blacklist.response(200, 'Success')
@api_ns_episodes_blacklist.response(401, 'Not Authenticated')
@api_ns_episodes_blacklist.response(404, 'Episode not found')
@api_ns_episodes_blacklist.response(410, 'Subtitles file not found or permission issue.')
def post(self):
"""Add an episodes subtitles to blacklist"""
args = self.post_request_parser.parse_args()
@ -113,17 +114,19 @@ class EpisodesBlacklist(Resource):
provider=provider,
subs_id=subs_id,
language=language)
delete_subtitles(media_type='series',
language=language,
forced=False,
hi=False,
media_path=path_mappings.path_replace(media_path),
subtitles_path=subtitles_path,
sonarr_series_id=sonarr_series_id,
sonarr_episode_id=sonarr_episode_id)
episode_download_subtitles(sonarr_episode_id)
event_stream(type='episode-history')
return '', 200
if delete_subtitles(media_type='series',
language=language,
forced=False,
hi=False,
media_path=path_mappings.path_replace(media_path),
subtitles_path=subtitles_path,
sonarr_series_id=sonarr_series_id,
sonarr_episode_id=sonarr_episode_id):
episode_download_subtitles(sonarr_episode_id)
event_stream(type='episode-history')
return '', 200
else:
return 'Subtitles file not found or permission issue.', 410
delete_request_parser = reqparse.RequestParser()
delete_request_parser.add_argument('all', type=str, required=False, help='Empty episodes subtitles blacklist')

View File

@ -37,6 +37,8 @@ class EpisodesSubtitles(Resource):
@api_ns_episodes_subtitles.response(204, 'Success')
@api_ns_episodes_subtitles.response(401, 'Not Authenticated')
@api_ns_episodes_subtitles.response(404, 'Episode not found')
@api_ns_episodes_subtitles.response(409, 'Unable to save subtitles file. Permission of path mapping issue?')
@api_ns_episodes_subtitles.response(410, 'Episode file not found. Path mapping issue?')
def patch(self):
"""Download an episode subtitles"""
args = self.patch_request_parser.parse_args()
@ -55,10 +57,15 @@ class EpisodesSubtitles(Resource):
if not episodeInfo:
return 'Episode not found', 404
title = episodeInfo.title
episodePath = path_mappings.path_replace(episodeInfo.path)
if not os.path.exists(episodePath):
return 'Episode file not found. Path mapping issue?', 410
sceneName = episodeInfo.sceneName or "None"
title = episodeInfo.title
language = args.get('language')
hi = args.get('hi').capitalize()
forced = args.get('forced').capitalize()
@ -79,11 +86,10 @@ class EpisodesSubtitles(Resource):
store_subtitles(result.path, episodePath)
else:
event_stream(type='episode', payload=sonarrEpisodeId)
except OSError:
pass
return '', 204
return 'Unable to save subtitles file. Permission of path mapping issue?', 409
else:
return '', 204
post_request_parser = reqparse.RequestParser()
post_request_parser.add_argument('seriesid', type=int, required=True, help='Series ID')
@ -99,6 +105,8 @@ class EpisodesSubtitles(Resource):
@api_ns_episodes_subtitles.response(204, 'Success')
@api_ns_episodes_subtitles.response(401, 'Not Authenticated')
@api_ns_episodes_subtitles.response(404, 'Episode not found')
@api_ns_episodes_subtitles.response(409, 'Unable to save subtitles file. Permission of path mapping issue?')
@api_ns_episodes_subtitles.response(410, 'Episode file not found. Path mapping issue?')
def post(self):
"""Upload an episode subtitles"""
args = self.post_request_parser.parse_args()
@ -115,6 +123,9 @@ class EpisodesSubtitles(Resource):
episodePath = path_mappings.path_replace(episodeInfo.path)
if not os.path.exists(episodePath):
return 'Episode file not found. Path mapping issue?', 410
audio_language = get_audio_profile_languages(episodeInfo.audio_language)
if len(audio_language) and isinstance(audio_language[0], dict):
audio_language = audio_language[0]
@ -149,11 +160,10 @@ class EpisodesSubtitles(Resource):
if not settings.general.getboolean('dont_notify_manual_actions'):
send_notifications(sonarrSeriesId, sonarrEpisodeId, result.message)
store_subtitles(result.path, episodePath)
except OSError:
pass
return '', 204
return 'Unable to save subtitles file. Permission of path mapping issue?', 409
else:
return '', 204
delete_request_parser = reqparse.RequestParser()
delete_request_parser.add_argument('seriesid', type=int, required=True, help='Series ID')
@ -168,6 +178,7 @@ class EpisodesSubtitles(Resource):
@api_ns_episodes_subtitles.response(204, 'Success')
@api_ns_episodes_subtitles.response(401, 'Not Authenticated')
@api_ns_episodes_subtitles.response(404, 'Episode not found')
@api_ns_episodes_subtitles.response(410, 'Subtitles file not found or permission issue.')
def delete(self):
"""Delete an episode subtitles"""
args = self.delete_request_parser.parse_args()
@ -190,13 +201,14 @@ class EpisodesSubtitles(Resource):
subtitlesPath = path_mappings.path_replace_reverse(subtitlesPath)
delete_subtitles(media_type='series',
language=language,
forced=forced,
hi=hi,
media_path=episodePath,
subtitles_path=subtitlesPath,
sonarr_series_id=sonarrSeriesId,
sonarr_episode_id=sonarrEpisodeId)
return '', 204
if delete_subtitles(media_type='series',
language=language,
forced=forced,
hi=hi,
media_path=episodePath,
subtitles_path=subtitlesPath,
sonarr_series_id=sonarrSeriesId,
sonarr_episode_id=sonarrEpisodeId):
return '', 204
else:
return 'Subtitles file not found or permission issue.', 410

View File

@ -81,6 +81,7 @@ class MoviesBlacklist(Resource):
@api_ns_movies_blacklist.response(200, 'Success')
@api_ns_movies_blacklist.response(401, 'Not Authenticated')
@api_ns_movies_blacklist.response(404, 'Movie not found')
@api_ns_movies_blacklist.response(410, 'Subtitles file not found or permission issue.')
def post(self):
"""Add a movies subtitles to blacklist"""
args = self.post_request_parser.parse_args()
@ -107,16 +108,18 @@ class MoviesBlacklist(Resource):
provider=provider,
subs_id=subs_id,
language=language)
delete_subtitles(media_type='movie',
language=language,
forced=forced,
hi=hi,
media_path=path_mappings.path_replace_movie(media_path),
subtitles_path=subtitles_path,
radarr_id=radarr_id)
movies_download_subtitles(radarr_id)
event_stream(type='movie-history')
return '', 200
if delete_subtitles(media_type='movie',
language=language,
forced=forced,
hi=hi,
media_path=path_mappings.path_replace_movie(media_path),
subtitles_path=subtitles_path,
radarr_id=radarr_id):
movies_download_subtitles(radarr_id)
event_stream(type='movie-history')
return '', 200
else:
return 'Subtitles file not found or permission issue.', 410
delete_request_parser = reqparse.RequestParser()
delete_request_parser.add_argument('all', type=str, required=False, help='Empty movies subtitles blacklist')

View File

@ -166,6 +166,7 @@ class Movies(Resource):
@api_ns_movies.response(204, 'Success')
@api_ns_movies.response(400, 'Unknown action')
@api_ns_movies.response(401, 'Not Authenticated')
@api_ns_movies.response(410, 'Movie file not found. Path mapping issue?')
def patch(self):
"""Run actions on specific movies"""
args = self.patch_request_parser.parse_args()
@ -175,8 +176,12 @@ class Movies(Resource):
movies_scan_subtitles(radarrid)
return '', 204
elif action == "search-missing":
movies_download_subtitles(radarrid)
return '', 204
try:
movies_download_subtitles(radarrid)
except OSError:
return 'Movie file not found. Path mapping issue?', 410
else:
return '', 204
elif action == "search-wanted":
wanted_search_missing_subtitles_movies()
return '', 204

View File

@ -1,6 +1,5 @@
# coding=utf-8
import contextlib
import os
import logging
@ -37,6 +36,8 @@ class MoviesSubtitles(Resource):
@api_ns_movies_subtitles.response(204, 'Success')
@api_ns_movies_subtitles.response(401, 'Not Authenticated')
@api_ns_movies_subtitles.response(404, 'Movie not found')
@api_ns_movies_subtitles.response(409, 'Unable to save subtitles file. Permission of path mapping issue?')
@api_ns_movies_subtitles.response(410, 'Movie file not found. Path mapping issue?')
def patch(self):
"""Download a movie subtitles"""
args = self.patch_request_parser.parse_args()
@ -55,6 +56,10 @@ class MoviesSubtitles(Resource):
return 'Movie not found', 404
moviePath = path_mappings.path_replace_movie(movieInfo.path)
if not os.path.exists(moviePath):
return 'Movie file not found. Path mapping issue?', 410
sceneName = movieInfo.sceneName or 'None'
title = movieInfo.title
@ -69,7 +74,7 @@ class MoviesSubtitles(Resource):
else:
audio_language = None
with contextlib.suppress(OSError):
try:
result = list(generate_subtitles(moviePath, [(language, hi, forced)], audio_language,
sceneName, title, 'movie', profile_id=get_profile_id(movie_id=radarrId)))
if result:
@ -78,7 +83,10 @@ class MoviesSubtitles(Resource):
store_subtitles_movie(result.path, moviePath)
else:
event_stream(type='movie', payload=radarrId)
return '', 204
except OSError:
return 'Unable to save subtitles file. Permission of path mapping issue?', 409
else:
return '', 204
# POST: Upload Subtitles
post_request_parser = reqparse.RequestParser()
@ -94,6 +102,8 @@ class MoviesSubtitles(Resource):
@api_ns_movies_subtitles.response(204, 'Success')
@api_ns_movies_subtitles.response(401, 'Not Authenticated')
@api_ns_movies_subtitles.response(404, 'Movie not found')
@api_ns_movies_subtitles.response(409, 'Unable to save subtitles file. Permission of path mapping issue?')
@api_ns_movies_subtitles.response(410, 'Movie file not found. Path mapping issue?')
def post(self):
"""Upload a movie subtitles"""
# TODO: Support Multiply Upload
@ -109,6 +119,9 @@ class MoviesSubtitles(Resource):
moviePath = path_mappings.path_replace_movie(movieInfo.path)
if not os.path.exists(moviePath):
return 'Movie file not found. Path mapping issue?', 410
audio_language = get_audio_profile_languages(movieInfo.audio_language)
if len(audio_language) and isinstance(audio_language[0], dict):
audio_language = audio_language[0]
@ -125,7 +138,7 @@ class MoviesSubtitles(Resource):
if not isinstance(ext, str) or ext.lower() not in SUBTITLE_EXTENSIONS:
raise ValueError('A subtitle of an invalid format was uploaded.')
with contextlib.suppress(OSError):
try:
result = manual_upload_subtitle(path=moviePath,
language=language,
forced=forced,
@ -143,7 +156,10 @@ class MoviesSubtitles(Resource):
if not settings.general.getboolean('dont_notify_manual_actions'):
send_notifications_movie(radarrId, result.message)
store_subtitles_movie(result.path, moviePath)
return '', 204
except OSError:
return 'Unable to save subtitles file. Permission of path mapping issue?', 409
else:
return '', 204
# DELETE: Delete Subtitles
delete_request_parser = reqparse.RequestParser()
@ -158,6 +174,7 @@ class MoviesSubtitles(Resource):
@api_ns_movies_subtitles.response(204, 'Success')
@api_ns_movies_subtitles.response(401, 'Not Authenticated')
@api_ns_movies_subtitles.response(404, 'Movie not found')
@api_ns_movies_subtitles.response(410, 'Subtitles file not found or permission issue.')
def delete(self):
"""Delete a movie subtitles"""
args = self.delete_request_parser.parse_args()
@ -179,12 +196,13 @@ class MoviesSubtitles(Resource):
subtitlesPath = path_mappings.path_replace_reverse_movie(subtitlesPath)
delete_subtitles(media_type='movie',
language=language,
forced=forced,
hi=hi,
media_path=moviePath,
subtitles_path=subtitlesPath,
radarr_id=radarrId)
return '', 204
if delete_subtitles(media_type='movie',
language=language,
forced=forced,
hi=hi,
media_path=moviePath,
subtitles_path=subtitlesPath,
radarr_id=radarrId):
return '', 204
else:
return 'Subtitles file not found or permission issue.', 410

View File

@ -1,5 +1,7 @@
# coding=utf-8
import os
from flask_restx import Resource, Namespace, reqparse, fields
from app.database import TableEpisodes, TableShows, get_audio_profile_languages, get_profile_id, database, select
@ -42,6 +44,7 @@ class ProviderEpisodes(Resource):
@api_ns_providers_episodes.marshal_with(get_response_model, envelope='data', code=200)
@api_ns_providers_episodes.response(401, 'Not Authenticated')
@api_ns_providers_episodes.response(404, 'Episode not found')
@api_ns_providers_episodes.response(410, 'Episode file not found. Path mapping issue?')
@api_ns_providers_episodes.doc(parser=get_request_parser)
def get(self):
"""Search manually for an episode subtitles"""
@ -62,6 +65,10 @@ class ProviderEpisodes(Resource):
title = episodeInfo.title
episodePath = path_mappings.path_replace(episodeInfo.path)
if not os.path.exists(episodePath):
return 'Episode file not found. Path mapping issue?', 410
sceneName = episodeInfo.sceneName or "None"
profileId = episodeInfo.profileId

View File

@ -1,5 +1,7 @@
# coding=utf-8
import os
from flask_restx import Resource, Namespace, reqparse, fields
from app.database import TableMovies, get_audio_profile_languages, get_profile_id, database, select
@ -43,6 +45,7 @@ class ProviderMovies(Resource):
@api_ns_providers_movies.marshal_with(get_response_model, envelope='data', code=200)
@api_ns_providers_movies.response(401, 'Not Authenticated')
@api_ns_providers_movies.response(404, 'Movie not found')
@api_ns_providers_movies.response(410, 'Movie file not found. Path mapping issue?')
@api_ns_providers_movies.doc(parser=get_request_parser)
def get(self):
"""Search manually for a movie subtitles"""
@ -61,6 +64,10 @@ class ProviderMovies(Resource):
title = movieInfo.title
moviePath = path_mappings.path_replace_movie(movieInfo.path)
if not os.path.exists(moviePath):
return 'Movie file not found. Path mapping issue?', 410
sceneName = movieInfo.sceneName or "None"
profileId = movieInfo.profileId

View File

@ -199,6 +199,7 @@ class Series(Resource):
@api_ns_series.response(204, 'Success')
@api_ns_series.response(400, 'Unknown action')
@api_ns_series.response(401, 'Not Authenticated')
@api_ns_series.response(410, 'Series directory not found. Path mapping issue?')
def patch(self):
"""Run actions on specific series"""
args = self.patch_request_parser.parse_args()
@ -208,8 +209,12 @@ class Series(Resource):
series_scan_subtitles(seriesid)
return '', 204
elif action == "search-missing":
series_download_subtitles(seriesid)
return '', 204
try:
series_download_subtitles(seriesid)
except OSError:
return 'Series directory not found. Path mapping issue?', 410
else:
return '', 204
elif action == "search-wanted":
wanted_search_missing_subtitles_series()
return '', 204

View File

@ -42,6 +42,8 @@ class Subtitles(Resource):
@api_ns_subtitles.response(204, 'Success')
@api_ns_subtitles.response(401, 'Not Authenticated')
@api_ns_subtitles.response(404, 'Episode/movie not found')
@api_ns_subtitles.response(409, 'Unable to edit subtitles file. Check logs.')
@api_ns_subtitles.response(410, 'Subtitles file not found. Path mapping issue?')
def patch(self):
"""Apply mods/tools on external subtitles"""
args = self.patch_request_parser.parse_args()
@ -52,6 +54,9 @@ class Subtitles(Resource):
media_type = args.get('type')
id = args.get('id')
if not os.path.exists(subtitles_path):
return 'Subtitles file not found. Path mapping issue?', 410
if media_type == 'episode':
metadata = database.execute(
select(TableEpisodes.path, TableEpisodes.sonarrSeriesId)
@ -80,8 +85,11 @@ class Subtitles(Resource):
srt_lang=language, media_type='series', sonarr_series_id=metadata.sonarrSeriesId,
sonarr_episode_id=id)
else:
subsync.sync(video_path=video_path, srt_path=subtitles_path,
srt_lang=language, media_type='movies', radarr_id=id)
try:
subsync.sync(video_path=video_path, srt_path=subtitles_path,
srt_lang=language, media_type='movies', radarr_id=id)
except OSError:
return 'Unable to edit subtitles file. Check logs.', 409
del subsync
gc.collect()
elif action == 'translate':
@ -89,16 +97,22 @@ class Subtitles(Resource):
dest_language = language
forced = True if args.get('forced') == 'true' else False
hi = True if args.get('hi') == 'true' else False
translate_subtitles_file(video_path=video_path, source_srt_file=subtitles_path,
from_lang=from_language, to_lang=dest_language, forced=forced, hi=hi,
media_type="series" if media_type == "episode" else "movies",
sonarr_series_id=metadata.sonarrSeriesId if media_type == "episode" else None,
sonarr_episode_id=id,
radarr_id=id)
try:
translate_subtitles_file(video_path=video_path, source_srt_file=subtitles_path,
from_lang=from_language, to_lang=dest_language, forced=forced, hi=hi,
media_type="series" if media_type == "episode" else "movies",
sonarr_series_id=metadata.sonarrSeriesId if media_type == "episode" else None,
sonarr_episode_id=id,
radarr_id=id)
except OSError:
return 'Unable to edit subtitles file. Check logs.', 409
else:
use_original_format = True if args.get('original_format') == 'true' else False
subtitles_apply_mods(language=language, subtitle_path=subtitles_path, mods=[action],
use_original_format=use_original_format, video_path=video_path)
try:
subtitles_apply_mods(language=language, subtitle_path=subtitles_path, mods=[action],
use_original_format=use_original_format, video_path=video_path)
except OSError:
return 'Unable to edit subtitles file. Check logs.', 409
# apply chmod if required
chmod = int(settings.general.chmod, 8) if not sys.platform.startswith(

View File

@ -108,7 +108,7 @@ class SystemSettings(Resource):
item = json.loads(item)
database.execute(
update(TableSettingsNotifier).values(
enabled=int(item['enabled'] == True),
enabled=int(item['enabled'] is True),
url=item['url'])
.where(TableSettingsNotifier.name == item['name']))

View File

@ -12,7 +12,7 @@ from datetime import datetime
from sqlalchemy import create_engine, inspect, DateTime, ForeignKey, Integer, LargeBinary, Text, func, text
# importing here to be indirectly imported in other modules later
from sqlalchemy import update, delete, select, func # noqa W0611
from sqlalchemy.orm import scoped_session, sessionmaker, relationship, mapped_column
from sqlalchemy.orm import scoped_session, sessionmaker, mapped_column
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.pool import NullPool

View File

@ -4,6 +4,7 @@
import ast
import logging
import operator
import os
from functools import reduce
@ -36,6 +37,11 @@ def movies_download_subtitles(no):
logging.debug("BAZARR no movie with that radarrId can be found in database:", str(no))
return
moviePath = path_mappings.path_replace_movie(movie.path)
if not os.path.exists(moviePath):
raise OSError
if ast.literal_eval(movie.missing_subtitles):
count_movie = len(ast.literal_eval(movie.missing_subtitles))
else:
@ -67,7 +73,7 @@ def movies_download_subtitles(no):
value=0,
count=count_movie)
for result in generate_subtitles(path_mappings.path_replace_movie(movie.path),
for result in generate_subtitles(moviePath,
languages,
audio_language,
str(movie.sceneName),
@ -76,7 +82,7 @@ def movies_download_subtitles(no):
check_if_still_required=True):
if result:
store_subtitles_movie(movie.path, path_mappings.path_replace_movie(movie.path))
store_subtitles_movie(movie.path, moviePath)
history_log_movie(1, no, result)
send_notifications_movie(no, result.message)

View File

@ -4,6 +4,7 @@
import ast
import logging
import operator
import os
from functools import reduce
@ -19,6 +20,14 @@ from ..download import generate_subtitles
def series_download_subtitles(no):
series_row = database.execute(
select(TableShows.path)
.where(TableShows.sonarrSeriesId == no))\
.first()
if series_row and not os.path.exists(path_mappings.path_replace(series_row.path)):
raise OSError
conditions = [(TableEpisodes.sonarrSeriesId == no),
(TableEpisodes.missing_subtitles != '[]')]
conditions += get_exclusion_clause('series')

View File

@ -8,6 +8,7 @@ from app.config import get_settings
logger = logging.getLogger(__name__)
class Score:
media = None
defaults = {}

View File

@ -52,24 +52,25 @@ class SubSyncer:
logging.debug('BAZARR FFmpeg used is %s', ffmpeg_exe)
self.ffmpeg_path = os.path.dirname(ffmpeg_exe)
unparsed_args = [self.reference, '-i', self.srtin, '-o', self.srtout, '--ffmpegpath', self.ffmpeg_path, '--vad',
self.vad, '--log-dir-path', self.log_dir_path]
if settings.subsync.getboolean('force_audio'):
unparsed_args.append('--no-fix-framerate')
unparsed_args.append('--reference-stream')
unparsed_args.append('a:0')
if settings.subsync.getboolean('debug'):
unparsed_args.append('--make-test-case')
parser = make_parser()
self.args = parser.parse_args(args=unparsed_args)
if os.path.isfile(self.srtout):
os.remove(self.srtout)
logging.debug('BAZARR deleted the previous subtitles synchronization attempt file.')
try:
unparsed_args = [self.reference, '-i', self.srtin, '-o', self.srtout, '--ffmpegpath', self.ffmpeg_path,
'--vad', self.vad, '--log-dir-path', self.log_dir_path]
if settings.subsync.getboolean('force_audio'):
unparsed_args.append('--no-fix-framerate')
unparsed_args.append('--reference-stream')
unparsed_args.append('a:0')
if settings.subsync.getboolean('debug'):
unparsed_args.append('--make-test-case')
parser = make_parser()
self.args = parser.parse_args(args=unparsed_args)
if os.path.isfile(self.srtout):
os.remove(self.srtout)
logging.debug('BAZARR deleted the previous subtitles synchronization attempt file.')
result = run(self.args)
except Exception:
logging.exception('BAZARR an exception occurs during the synchronization process for this subtitles: '
'{0}'.format(self.srtin))
raise OSError
else:
if settings.subsync.getboolean('debug'):
return result

View File

@ -81,7 +81,11 @@ def translate_subtitles_file(video_path, source_srt_file, from_lang, to_lang, fo
except IndexError:
logging.error(f'BAZARR is unable to translate malformed subtitles: {source_srt_file}')
return False
subs.save(dest_srt_file)
try:
subs.save(dest_srt_file)
except OSError:
logging.error(f'BAZARR is unable to save translated subtitles to {dest_srt_file}')
raise OSError
message = f"{language_from_alpha2(from_lang)} subtitles translated to {language_from_alpha3(to_lang)}."