mirror of https://github.com/morpheus65535/bazarr
Merge remote-tracking branch 'origin/development' into development
This commit is contained in:
commit
e774bd41d0
|
@ -19,7 +19,7 @@ from subzero.video import parse_video
|
|||
from subliminal import region, score as subliminal_scores, \
|
||||
list_subtitles, Episode, Movie
|
||||
from subliminal_patch.core import SZAsyncProviderPool, download_best_subtitles, save_subtitles, download_subtitles, \
|
||||
list_all_subtitles
|
||||
list_all_subtitles, get_subtitle_path
|
||||
from subliminal_patch.score import compute_score
|
||||
from subliminal.refiners.tvdb import series_re
|
||||
from get_languages import language_from_alpha3, alpha2_from_alpha3, alpha3_from_alpha2, language_from_alpha2
|
||||
|
@ -491,6 +491,51 @@ def manual_download_subtitle(path, language, hi, forced, subtitle, provider, pro
|
|||
logging.debug('BAZARR Ended manually downloading subtitles for file: ' + path)
|
||||
|
||||
|
||||
def manual_upload_subtitle(path, language, forced, title, scene_name, media_type, subtitle):
|
||||
logging.debug('BAZARR Manually uploading subtitles for this file: ' + path)
|
||||
|
||||
single = settings.general.getboolean('single_language')
|
||||
|
||||
chmod = int(settings.general.chmod, 8) if not sys.platform.startswith(
|
||||
'win') and settings.general.getboolean('chmod_enabled') else None
|
||||
|
||||
_, ext = os.path.splitext(subtitle.filename)
|
||||
|
||||
language = alpha3_from_alpha2(language)
|
||||
|
||||
if language == 'pob':
|
||||
lang_obj = Language('por', 'BR')
|
||||
else:
|
||||
lang_obj = Language(language)
|
||||
|
||||
if forced:
|
||||
lang_obj = Language.rebuild(lang_obj, forced=True)
|
||||
|
||||
subtitle_path = get_subtitle_path(video_path=force_unicode(path),
|
||||
language=None if single else lang_obj,
|
||||
extension=ext,
|
||||
forced_tag=forced)
|
||||
|
||||
subtitle_path = force_unicode(subtitle_path)
|
||||
|
||||
if os.path.exists(subtitle_path):
|
||||
os.remove(subtitle_path)
|
||||
|
||||
subtitle.save(subtitle_path)
|
||||
|
||||
if chmod:
|
||||
os.chmod(subtitle_path, chmod)
|
||||
|
||||
message = language_from_alpha3(language) + (" forced" if forced else "") + " subtitles manually uploaded."
|
||||
|
||||
if media_type == 'series':
|
||||
reversed_path = path_replace_reverse(path)
|
||||
else:
|
||||
reversed_path = path_replace_reverse_movie(path)
|
||||
|
||||
return message, reversed_path
|
||||
|
||||
|
||||
def series_download_subtitles(no):
|
||||
if settings.sonarr.getboolean('only_monitored'):
|
||||
monitored_only_query_string = ' AND monitored = "True"'
|
||||
|
|
|
@ -61,12 +61,13 @@ from get_episodes import *
|
|||
from list_subtitles import store_subtitles, store_subtitles_movie, series_scan_subtitles, movies_scan_subtitles, \
|
||||
list_missing_subtitles, list_missing_subtitles_movies
|
||||
from get_subtitle import download_subtitle, series_download_subtitles, movies_download_subtitles, \
|
||||
manual_search, manual_download_subtitle
|
||||
manual_search, manual_download_subtitle, manual_upload_subtitle
|
||||
from utils import history_log, history_log_movie
|
||||
from scheduler import *
|
||||
from notifier import send_notifications, send_notifications_movie
|
||||
from config import settings, url_sonarr, url_radarr, url_radarr_short, url_sonarr_short, base_url
|
||||
from helper import path_replace_movie
|
||||
from subliminal_patch.core import SUBTITLE_EXTENSIONS
|
||||
from subliminal_patch.extensions import provider_registry as provider_manager
|
||||
|
||||
reload(sys)
|
||||
|
@ -1911,6 +1912,51 @@ def manual_get_subtitle():
|
|||
pass
|
||||
|
||||
|
||||
@route(base_url + 'manual_upload_subtitle', method='POST')
|
||||
@custom_auth_basic(check_credentials)
|
||||
def perform_manual_upload_subtitle():
|
||||
authorize()
|
||||
ref = request.environ['HTTP_REFERER']
|
||||
|
||||
episodePath = request.forms.get('episodePath')
|
||||
sceneName = request.forms.get('sceneName')
|
||||
language = request.forms.get('language')
|
||||
forced = True if request.forms.get('forced') == '1' else False
|
||||
upload = request.files.get('upload')
|
||||
sonarrSeriesId = request.forms.get('sonarrSeriesId')
|
||||
sonarrEpisodeId = request.forms.get('sonarrEpisodeId')
|
||||
title = request.forms.get('title')
|
||||
|
||||
_, ext = os.path.splitext(upload.filename)
|
||||
|
||||
if ext not in SUBTITLE_EXTENSIONS:
|
||||
raise ValueError('A subtitle of an invalid format was uploaded.')
|
||||
|
||||
try:
|
||||
result = manual_upload_subtitle(path=episodePath,
|
||||
language=language,
|
||||
forced=forced,
|
||||
title=title,
|
||||
scene_name=sceneName,
|
||||
media_type='series',
|
||||
subtitle=upload)
|
||||
|
||||
if result is not None:
|
||||
message = result[0]
|
||||
path = result[1]
|
||||
language_code = language + ":forced" if forced else language
|
||||
provider = "manual"
|
||||
score = 360
|
||||
history_log(4, sonarrSeriesId, sonarrEpisodeId, message, path, language_code, provider, score)
|
||||
send_notifications(sonarrSeriesId, sonarrEpisodeId, message)
|
||||
store_subtitles(unicode(episodePath))
|
||||
list_missing_subtitles(sonarrSeriesId)
|
||||
|
||||
redirect(ref)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
@route(base_url + 'get_subtitle_movie', method='POST')
|
||||
@custom_auth_basic(check_credentials)
|
||||
def get_subtitle_movie():
|
||||
|
@ -2003,6 +2049,50 @@ def manual_get_subtitle_movie():
|
|||
pass
|
||||
|
||||
|
||||
@route(base_url + 'manual_upload_subtitle_movie', method='POST')
|
||||
@custom_auth_basic(check_credentials)
|
||||
def perform_manual_upload_subtitle_movie():
|
||||
authorize()
|
||||
ref = request.environ['HTTP_REFERER']
|
||||
|
||||
moviePath = request.forms.get('moviePath')
|
||||
sceneName = request.forms.get('sceneName')
|
||||
language = request.forms.get('language')
|
||||
forced = True if request.forms.get('forced') == '1' else False
|
||||
upload = request.files.get('upload')
|
||||
radarrId = request.forms.get('radarrId')
|
||||
title = request.forms.get('title')
|
||||
|
||||
_, ext = os.path.splitext(upload.filename)
|
||||
|
||||
if ext not in SUBTITLE_EXTENSIONS:
|
||||
raise ValueError('A subtitle of an invalid format was uploaded.')
|
||||
|
||||
try:
|
||||
result = manual_upload_subtitle(path=moviePath,
|
||||
language=language,
|
||||
forced=forced,
|
||||
title=title,
|
||||
scene_name=sceneName,
|
||||
media_type='series',
|
||||
subtitle=upload)
|
||||
|
||||
if result is not None:
|
||||
message = result[0]
|
||||
path = result[1]
|
||||
language_code = language + ":forced" if forced else language
|
||||
provider = "manual"
|
||||
score = 120
|
||||
history_log_movie(4, radarrId, message, path, language_code, provider, score)
|
||||
send_notifications_movie(radarrId, message)
|
||||
store_subtitles_movie(unicode(moviePath))
|
||||
list_missing_subtitles_movies(radarrId)
|
||||
|
||||
redirect(ref)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def configured():
|
||||
conn = sqlite3.connect(os.path.join(args.config_dir, 'db', 'bazarr.db'), timeout=30)
|
||||
c = conn.cursor()
|
||||
|
|
|
@ -194,6 +194,7 @@
|
|||
<th class="collapsing">Existing<br>subtitles</th>
|
||||
<th class="collapsing">Missing<br>subtitles</th>
|
||||
<th class="collapsing">Manual<br>search</th>
|
||||
<th class="collapsing">Manual<br>upload</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -286,6 +287,11 @@
|
|||
<a data-episodePath="{{episode[1]}}" data-scenename="{{episode[8]}}" data-language="{{subs_languages_list}}" data-hi="{{details[4]}}" data-forced="{{details[9]}}" data-series_title="{{details[0]}}" data-season="{{episode[2]}}" data-episode="{{episode[3]}}" data-episode_title="{{episode[0]}}" data-sonarrSeriesId="{{episode[5]}}" data-sonarrEpisodeId="{{episode[7]}}" class="manual_search ui tiny label"><i class="ui user icon" style="margin-right:0px" ></i></a>
|
||||
%end
|
||||
</td>
|
||||
<td>
|
||||
%if subs_languages is not None:
|
||||
<a data-episodePath="{{episode[1]}}" data-scenename="{{episode[8]}}" data-language="{{subs_languages_list}}" data-hi="{{details[4]}}" data-series_title="{{details[0]}}" data-season="{{episode[2]}}" data-episode="{{episode[3]}}" data-episode_title="{{episode[0]}}" data-sonarrSeriesId="{{episode[5]}}" data-sonarrEpisodeId="{{episode[7]}}" class="manual_upload ui tiny label"><i class="ui cloud upload icon" style="margin-right:0px" ></i></a>
|
||||
%end
|
||||
</td>
|
||||
</tr>
|
||||
%end
|
||||
</tbody>
|
||||
|
@ -392,6 +398,59 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="upload_dialog ui small modal">
|
||||
<i class="close icon"></i>
|
||||
<div class="header">
|
||||
<span id="series_title_span_u"></span> - <span id="season_u"></span>x<span id="episode_u"></span> - <span id="episode_title_u"></span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<form class="ui form" name="upload_form" id="upload_form" action="{{base_url}}manual_upload_subtitle" method="post" enctype="multipart/form-data">
|
||||
<div class="ui grid">
|
||||
<div class="middle aligned row">
|
||||
<div class="right aligned three wide column">
|
||||
<label>Language</label>
|
||||
</div>
|
||||
<div class="thirteen wide column">
|
||||
<select class="ui search dropdown" id="language" name="language">
|
||||
%for language in subs_languages_list:
|
||||
<option value="{{language}}">{{language_from_alpha2(language)}}</option>
|
||||
%end
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="middle aligned row">
|
||||
<div class="right aligned three wide column">
|
||||
<label>Forced</label>
|
||||
</div>
|
||||
<div class="thirteen wide column">
|
||||
<div class="ui toggle checkbox">
|
||||
<input name="forced" type="checkbox" value="1">
|
||||
<label></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="middle aligned row">
|
||||
<div class="right aligned three wide column">
|
||||
<label>File</label>
|
||||
</div>
|
||||
<div class="thirteen wide column">
|
||||
<input type="file" name="upload">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" id="upload_episodePath" name="episodePath" value="" />
|
||||
<input type="hidden" id="upload_sceneName" name="sceneName" value="" />
|
||||
<input type="hidden" id="upload_sonarrSeriesId" name="sonarrSeriesId" value="" />
|
||||
<input type="hidden" id="upload_sonarrEpisodeId" name="sonarrEpisodeId" value="" />
|
||||
<input type="hidden" id="upload_title" name="title" value="" />
|
||||
</form>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="ui cancel button" >Cancel</button>
|
||||
<button type="submit" name="save" value="save" form="upload_form" class="ui blue approve button">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
% include('footer.tpl')
|
||||
</body>
|
||||
</html>
|
||||
|
@ -464,15 +523,10 @@
|
|||
});
|
||||
});
|
||||
|
||||
$('a:not(.manual_search), .menu .item, button:not(#config, .cancel, #search_missing_subtitles)').on('click', function(){
|
||||
$('a:not(.manual_search, .manual_upload), .menu .item, button:not(#config, .cancel, #search_missing_subtitles)').on('click', function(){
|
||||
$('#loader').addClass('active');
|
||||
});
|
||||
|
||||
$('.modal')
|
||||
.modal({
|
||||
autofocus: false
|
||||
});
|
||||
|
||||
$('#config').on('click', function(){
|
||||
$('#series_form').attr('action', '{{base_url}}edit_series/{{no}}');
|
||||
|
||||
|
@ -494,7 +548,12 @@
|
|||
$("#series_hearing-impaired_div").checkbox('uncheck');
|
||||
}
|
||||
|
||||
$('.config_dialog').modal('show');
|
||||
$('.config_dialog')
|
||||
.modal({
|
||||
centered: false,
|
||||
autofocus: false
|
||||
})
|
||||
.modal('show');
|
||||
});
|
||||
|
||||
$('.manual_search').on('click', function(){
|
||||
|
@ -599,7 +658,41 @@
|
|||
|
||||
$('.search_dialog')
|
||||
.modal({
|
||||
centered: false
|
||||
centered: false,
|
||||
autofocus: false
|
||||
})
|
||||
.modal('show');
|
||||
});
|
||||
|
||||
$('.manual_upload').on('click', function(){
|
||||
$("#series_title_span_u").html($(this).data("series_title"));
|
||||
$("#season_u").html($(this).data("season"));
|
||||
$("#episode_u").html($(this).data("episode"));
|
||||
$("#episode_title_u").html($(this).data("episode_title"));
|
||||
|
||||
episodePath = $(this).attr("data-episodePath");
|
||||
sceneName = $(this).attr("data-sceneName");
|
||||
language = $(this).attr("data-language");
|
||||
hi = $(this).attr("data-hi");
|
||||
sonarrSeriesId = $(this).attr("data-sonarrSeriesId");
|
||||
sonarrEpisodeId = $(this).attr("data-sonarrEpisodeId");
|
||||
var languages = Array.from({{!subs_languages_list}});
|
||||
var is_pb = languages.includes('pb');
|
||||
var is_pt = languages.includes('pt');
|
||||
var title = "{{!details[0].replace("'", "\'")}}";
|
||||
|
||||
$('#language').dropdown();
|
||||
|
||||
$('#upload_episodePath').val(episodePath);
|
||||
$('#upload_sceneName').val(sceneName);
|
||||
$('#upload_sonarrSeriesId').val(sonarrSeriesId);
|
||||
$('#upload_sonarrEpisodeId').val(sonarrEpisodeId);
|
||||
$('#upload_title').val(title);
|
||||
|
||||
$('.upload_dialog')
|
||||
.modal({
|
||||
centered: false,
|
||||
autofocus: false
|
||||
})
|
||||
.modal('show');
|
||||
});
|
||||
|
|
|
@ -74,6 +74,10 @@
|
|||
<div class="ui inverted basic compact icon" data-tooltip="Subtitles file has been upgraded." data-inverted="" data-position="top left">
|
||||
<i class="ui recycle icon"></i>
|
||||
</div>
|
||||
%elif row[0] == 4:
|
||||
<div class="ui inverted basic compact icon" data-tooltip="Subtitles file has been manually uploaded." data-inverted="" data-position="top left">
|
||||
<i class="ui cloud upload icon"></i>
|
||||
</div>
|
||||
%end
|
||||
</td>
|
||||
<td>
|
||||
|
|
|
@ -76,6 +76,10 @@
|
|||
<div class="ui inverted basic compact icon" data-tooltip="Subtitles file has been upgraded." data-inverted="" data-position="top left">
|
||||
<i class="ui recycle icon"></i>
|
||||
</div>
|
||||
%elif row[0] == 4:
|
||||
<div class="ui inverted basic compact icon" data-tooltip="Subtitles file has been manually uploaded." data-inverted="" data-position="top left">
|
||||
<i class="ui cloud upload icon"></i>
|
||||
</div>
|
||||
%end
|
||||
</td>
|
||||
<td>
|
||||
|
|
|
@ -125,6 +125,7 @@
|
|||
%>
|
||||
%if subs_languages is not None:
|
||||
<button class="manual_search ui button" data-tooltip="Manually search for subtitles" data-inverted="" data-moviePath="{{details[8]}}" data-scenename="{{details[12]}}" data-language="{{subs_languages_list}}" data-hi="{{details[4]}}" data-forced="{{details[15]}}" data-movie_title="{{details[0]}}" data-radarrId="{{details[10]}}"><i class="ui inverted large compact user icon"></i></button>
|
||||
<button class="manual_upload ui button" data-tooltip="Manually upload subtitles" data-inverted="" data-moviePath="{{details[8]}}" data-scenename="{{details[12]}}" data-language="{{subs_languages_list}}" data-hi="{{details[4]}}" data-movie_title="{{details[0]}}" data-radarrId="{{details[10]}}"><i class="ui inverted large compact cloud upload icon"></i></button>
|
||||
%end
|
||||
<button id="config" class="ui button" data-tooltip="Edit movie" data-inverted="" data-tmdbid="{{details[5]}}" data-title="{{details[0]}}" data-poster="{{details[2]}}" data-audio="{{details[6]}}" data-languages="{{!subs_languages_list}}" data-hearing-impaired="{{details[4]}}" data-forced="{{details[15]}}"><i class="ui inverted large compact configure icon"></i></button>
|
||||
</div>
|
||||
|
@ -354,6 +355,58 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="upload_dialog ui small modal">
|
||||
<i class="close icon"></i>
|
||||
<div class="header">
|
||||
<span id="movie_title_upload_span"></span>
|
||||
</div>
|
||||
<div class="scrolling content">
|
||||
<form class="ui form" name="upload_form" id="upload_form" action="{{base_url}}manual_upload_subtitle_movie" method="post" enctype="multipart/form-data">
|
||||
<div class="ui grid">
|
||||
<div class="middle aligned row">
|
||||
<div class="right aligned three wide column">
|
||||
<label>Language</label>
|
||||
</div>
|
||||
<div class="thirteen wide column">
|
||||
<select class="ui search dropdown" id="language" name="language">
|
||||
%for language in subs_languages_list:
|
||||
<option value="{{language}}">{{language_from_alpha2(language)}}</option>
|
||||
%end
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="middle aligned row">
|
||||
<div class="right aligned three wide column">
|
||||
<label>Forced</label>
|
||||
</div>
|
||||
<div class="thirteen wide column">
|
||||
<div class="ui toggle checkbox">
|
||||
<input name="forced" type="checkbox" value="1">
|
||||
<label></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="middle aligned row">
|
||||
<div class="right aligned three wide column">
|
||||
<label>File</label>
|
||||
</div>
|
||||
<div class="thirteen wide column">
|
||||
<input type="file" name="upload">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" id="upload_moviePath" name="moviePath" value="" />
|
||||
<input type="hidden" id="upload_sceneName" name="sceneName" value="" />
|
||||
<input type="hidden" id="upload_radarrId" name="radarrId" value="" />
|
||||
<input type="hidden" id="upload_title" name="title" value="" />
|
||||
</form>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="ui cancel button" >Cancel</button>
|
||||
<button type="submit" name="save" value="save" form="upload_form" class="ui blue approve button">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
% include('footer.tpl')
|
||||
</body>
|
||||
</html>
|
||||
|
@ -425,15 +478,10 @@
|
|||
});
|
||||
});
|
||||
|
||||
$('a, .menu .item, button:not(#config, .cancel, .manual_search, #search_missing_subtitles_movie)').on('click', function(){
|
||||
$('a, .menu .item, button:not(#config, .cancel, .manual_search, .manual_upload, #search_missing_subtitles_movie)').on('click', function(){
|
||||
$('#loader').addClass('active');
|
||||
});
|
||||
|
||||
$('.modal')
|
||||
.modal({
|
||||
autofocus: false
|
||||
});
|
||||
|
||||
$('#config').on('click', function(){
|
||||
$('#movie_form').attr('action', '{{base_url}}edit_movie/{{no}}');
|
||||
|
||||
|
@ -455,7 +503,12 @@
|
|||
$("#movie_hearing-impaired_div").checkbox('uncheck');
|
||||
}
|
||||
|
||||
$('.config_dialog').modal('show');
|
||||
$('.config_dialog')
|
||||
.modal({
|
||||
centered: false,
|
||||
autofocus: false
|
||||
})
|
||||
.modal('show');
|
||||
});
|
||||
|
||||
$('.manual_search').on('click', function(){
|
||||
|
@ -557,7 +610,33 @@
|
|||
|
||||
$('.search_dialog')
|
||||
.modal({
|
||||
centered: false
|
||||
centered: false,
|
||||
autofocus: false
|
||||
})
|
||||
.modal('show')
|
||||
;
|
||||
});
|
||||
|
||||
$('.manual_upload').on('click', function() {
|
||||
$("#movie_title_upload_span").html($(this).data("movie_title"));
|
||||
|
||||
moviePath = $(this).attr("data-moviePath");
|
||||
sceneName = $(this).attr("data-sceneName");
|
||||
language = $(this).attr("data-language");
|
||||
radarrId = $(this).attr("data-radarrId");
|
||||
var title = "{{!details[0].replace("'", "\'")}}";
|
||||
|
||||
$('#language').dropdown();
|
||||
|
||||
$('#upload_moviePath').val(moviePath);
|
||||
$('#upload_sceneName').val(sceneName);
|
||||
$('#upload_radarrId').val(radarrId);
|
||||
$('#upload_title').val(title);
|
||||
|
||||
$('.upload_dialog')
|
||||
.modal({
|
||||
centered: false,
|
||||
autofocus: false
|
||||
})
|
||||
.modal('show')
|
||||
;
|
||||
|
|
Loading…
Reference in New Issue