Merge branch 'development' into python3

# Conflicts:
#	bazarr/main.py
#	views/movie.tpl
This commit is contained in:
Louis Vézina 2019-11-19 22:04:54 -05:00
commit 985219f9af
4 changed files with 309 additions and 103 deletions

View File

@ -53,7 +53,7 @@ def track_event(category=None, action=None, label=None):
try:
tracker.track_event(event, session, visitor)
tracker.track_pageview(page, session, visitor)
# tracker.track_pageview(page, session, visitor) ## Commented because we were having too much hits on GA.
except:
pass
else:

View File

@ -47,7 +47,8 @@ from beaker.middleware import SessionMiddleware
from cork import Cork
from bottle import route, template, static_file, request, redirect, response, HTTPError, app, hook
from datetime import timedelta
from get_languages import load_language_in_db, language_from_alpha3
from get_languages import load_language_in_db, language_from_alpha3, language_from_alpha2, alpha2_from_alpha3
from get_providers import get_providers, get_providers_auth, list_throttled_providers
from get_series import *
from get_episodes import *
@ -1738,7 +1739,7 @@ def remove_subtitles():
try:
os.remove(subtitlesPath)
result = language_from_alpha3(language) + " subtitles deleted from disk."
history_log(0, sonarrSeriesId, sonarrEpisodeId, result)
history_log(0, sonarrSeriesId, sonarrEpisodeId, result, language=alpha2_from_alpha3(language))
except OSError as e:
logging.exception('BAZARR cannot delete subtitles file: ' + subtitlesPath)
store_subtitles(path_replace_reverse(episodePath), episodePath)
@ -1756,7 +1757,7 @@ def remove_subtitles_movie():
try:
os.remove(subtitlesPath)
result = language_from_alpha3(language) + " subtitles deleted from disk."
history_log_movie(0, radarrId, result)
history_log_movie(0, radarrId, result, language=alpha2_from_alpha3(language))
except OSError as e:
logging.exception('BAZARR cannot delete subtitles file: ' + subtitlesPath)
store_subtitles_movie(path_replace_reverse_movie(moviePath), moviePath)
@ -2136,6 +2137,85 @@ def running_tasks_list():
return dict(tasks=running_tasks)
@route(base_url + 'episode_history/<no:int>')
@custom_auth_basic(check_credentials)
def episode_history(no):
authorize()
episode_history = database.execute("SELECT action, timestamp, language, provider, score FROM table_history "
"WHERE sonarrEpisodeId=? ORDER BY timestamp DESC", (no,))
for item in episode_history:
if item['action'] == 0:
item['action'] = "<div class ='ui inverted basic compact icon' data-tooltip='Subtitle file has been " \
"erased.' data-inverted='' data-position='top left'><i class='ui trash icon'></i></div>"
elif item['action'] == 1:
item['action'] = "<div class ='ui inverted basic compact icon' data-tooltip='Subtitle file has been " \
"downloaded.' data-inverted='' data-position='top left'><i class='ui download " \
"icon'></i></div>"
elif item['action'] == 2:
item['action'] = "<div class ='ui inverted basic compact icon' data-tooltip='Subtitle file has been " \
"manually downloaded.' data-inverted='' data-position='top left'><i class='ui user " \
"icon'></i></div>"
elif item['action'] == 3:
item['action'] = "<div class ='ui inverted basic compact icon' data-tooltip='Subtitle file has been " \
"upgraded.' data-inverted='' data-position='top left'><i class='ui recycle " \
"icon'></i></div>"
elif item['action'] == 4:
item['action'] = "<div class ='ui inverted basic compact icon' data-tooltip='Subtitle file has been " \
"manually uploaded.' data-inverted='' data-position='top left'><i class='ui cloud " \
"upload icon'></i></div>"
item['timestamp'] = "<div data-tooltip='" + \
time.strftime('%d/%m/%Y %H:%M:%S', time.localtime(item['timestamp'])) + "'>" + \
pretty.date(datetime.fromtimestamp(item['timestamp'])) + "</div>"
if item['language']:
item['language'] = language_from_alpha2(item['language'])
else:
item['language'] = "<i>undefined</i>"
if item['score']:
item['score'] = str(round((int(item['score']) * 100 / 360), 2)) + "%"
return dict(data=episode_history)
@route(base_url + 'movie_history/<no:int>')
@custom_auth_basic(check_credentials)
def movie_history(no):
authorize()
movie_history = database.execute("SELECT action, timestamp, language, provider, score FROM table_history_movie "
"WHERE radarrId=? ORDER BY timestamp DESC", (no,))
for item in movie_history:
if item['action'] == 0:
item['action'] = "<div class ='ui inverted basic compact icon' data-tooltip='Subtitle file has been " \
"erased.' data-inverted='' data-position='top left'><i class='ui trash icon'></i></div>"
elif item['action'] == 1:
item['action'] = "<div class ='ui inverted basic compact icon' data-tooltip='Subtitle file has been " \
"downloaded.' data-inverted='' data-position='top left'><i class='ui download " \
"icon'></i></div>"
elif item['action'] == 2:
item['action'] = "<div class ='ui inverted basic compact icon' data-tooltip='Subtitle file has been " \
"manually downloaded.' data-inverted='' data-position='top left'><i class='ui user " \
"icon'></i></div>"
elif item['action'] == 3:
item['action'] = "<div class ='ui inverted basic compact icon' data-tooltip='Subtitle file has been " \
"upgraded.' data-inverted='' data-position='top left'><i class='ui recycle " \
"icon'></i></div>"
elif item['action'] == 4:
item['action'] = "<div class ='ui inverted basic compact icon' data-tooltip='Subtitle file has been " \
"manually uploaded.' data-inverted='' data-position='top left'><i class='ui cloud " \
"upload icon'></i></div>"
item['timestamp'] = "<div data-tooltip='" + \
time.strftime('%d/%m/%Y %H:%M:%S', time.localtime(item['timestamp'])) + "'>" + \
pretty.date(datetime.fromtimestamp(item['timestamp'])) + "</div>"
if item['language']:
item['language'] = language_from_alpha2(item['language'])
else:
item['language'] = "<i>undefined</i>"
if item['score']:
item['score'] = str(round((int(item['score']) * 100 / 120), 2)) + '%'
return dict(data=movie_history)
# Mute DeprecationWarning
warnings.simplefilter("ignore", DeprecationWarning)
server = CherryPyWSGIServer((str(settings.general.ip), (int(args.port) if args.port else int(settings.general.port))), app)

View File

@ -217,7 +217,7 @@
% if episode['scene_name'] is not None:
<span data-tooltip="Scenename is: {{episode['scene_name']}}" data-inverted='' data-position="top left"><i class="info circle icon"></i></span>
% end
<span data-tooltip="Path is: {{episode['path']}}" data-inverted='' data-position="top left">{{episode['title']}}</span>
<span data-tooltip="Path is: {{episode['path']}}" data-inverted='' data-position="top left"><a data-series_title="{{details['title']}}" data-season="{{episode['season']}}" data-episode="{{episode['episode']}}" data-episode_title="{{episode['title']}}" data-sonarrEpisodeId="{{episode['sonarrEpisodeId']}}" class="episode_history">{{episode['title']}}</a></span>
</td>
<td>
%if episode['subtitles'] is not None:
@ -379,6 +379,29 @@
</div>
</div>
<div class="episode_dialog ui modal">
<i class="close icon"></i>
<div class="header">
<span id="series_title_span"></span> - <span id="season"></span>x<span id="episode"></span> - <span id="episode_title"></span>
</div>
<div class="scrolling content">
<table id="episode_result" class="display" style="width:100%">
<thead>
<tr>
<th></th>
<th style="text-align: left;">Language.:</th>
<th style="text-align: left;">Provider:</th>
<th style="text-align: left;">Score:</th>
<th style="text-align: left;">Date:</th>
</tr>
</thead>
</table>
</div>
<div class="actions">
<button class="ui cancel button" >Cancel</button>
</div>
</div>
<div class="search_dialog ui modal">
<i class="close icon"></i>
<div class="header">
@ -531,7 +554,7 @@
});
});
$('a:not(.manual_search, .manual_upload), .menu .item, button:not(#config, .cancel, #search_missing_subtitles)').on('click', function(){
$('a:not(.manual_search, .manual_upload, .episode_history), .menu .item, button:not(#config, .cancel, #search_missing_subtitles)').on('click', function(){
$('#loader').addClass('active');
});
@ -564,6 +587,51 @@
.modal('show');
});
$('.episode_history').on('click', function(){
$("#series_title_span").html($(this).data("series_title"));
$("#season").html($(this).data("season"));
$("#episode").html($(this).data("episode"));
$("#episode_title").html($(this).data("episode_title"));
sonarrEpisodeId = $(this).attr("data-sonarrEpisodeId");
$('#episode_result').DataTable( {
destroy: true,
language: {
loadingRecords: '<br><div class="ui active inverted dimmer" style="width: 95%;"><div class="ui centered inline loader"></div></div><br>',
zeroRecords: 'No History Records Found For This Episode'
},
paging: true,
lengthChange: false,
pageLength: 5,
searching: true,
ordering: true,
processing: false,
serverSide: false,
ajax: {
url: '{{base_url}}episode_history/' + sonarrEpisodeId
},
drawCallback: function(settings) {
$('.inline.dropdown').dropdown();
$('.ui.accordion').accordion();
},
columns: [
{ data: 'action'},
{ data: 'language' },
{ data: 'provider' },
{ data: 'score'},
{ data: 'timestamp' }
]
} );
$('.episode_dialog')
.modal({
centered: false,
autofocus: false
})
.modal('show');
});
$('.manual_search').on('click', function(){
$("#series_title_span").html($(this).data("series_title"));
$("#season").html($(this).data("season"));

View File

@ -41,6 +41,7 @@
}
#fondblanc {
background-color: #ffffff;
color: #000000;
opacity: 0.9;
border-radius: 1px;
box-shadow: 0 0 3px 3px #ffffff;
@ -160,107 +161,130 @@
</div>
<div id="fondblanc" class="ui container">
<table class="ui very basic single line selectable table">
<thead>
<tr>
<th>Subtitles Path</th>
<th>Language(s)</th>
<th></th>
</tr>
</thead>
<tbody>
<div class="ui top attached tabular menu">
<a id="subtitles_tab" class="tabs item active" data-tab="subtitles">Subtitles</a>
<a id="history_tab" class="tabs item" data-tab="history">History</a>
</div>
<div class="ui bottom attached tab active segment" data-tab="subtitles">
<div class="content">
<table class="ui very basic single line selectable table">
<thead>
<tr>
<th>Subtitles Path</th>
<th>Language(s)</th>
<th></th>
</tr>
</thead>
<tbody>
<%
subtitles_files = ast.literal_eval(str(details['subtitles']))
subtitles_files.sort(key=lambda x: x[0])
if subtitles_files is not None:
for subtitles_file in subtitles_files:
if subtitles_file[0].endswith(':forced'):
forced = True
else:
forced = False
end
%>
<tr>
%if subtitles_file[1] is not None:
<td><span data-tooltip="Path is: {{path_replace_movie(subtitles_file[1])}}" data-inverted='' data-position="top left">{{path.basename(path_replace_movie(subtitles_file[1]))}}</span></td>
%else:
<td>Video File Subtitles Track</td>
%end
<td><div class="ui tiny inverted label" style='background-color: #777777;'>{{language_from_alpha2(subtitles_file[0].split(':')[0])}}{{' forced' if forced else ''}}</div></td>
<td>
%if subtitles_file[1] is not None:
<a class="remove_subtitles ui inverted basic compact icon" data-tooltip="Delete Subtitle File" data-inverted="" data-position="top right" data-moviePath="{{details['path']}}" data-subtitlesPath="{{path_replace_movie(subtitles_file[1])}}" data-language="{{alpha3_from_alpha2(subtitles_file[0].split(':')[0])}}" data-radarrId={{details['radarrId']}}>
<i class="ui black delete icon"></i>
</a>
%end
</td>
</tr>
<%
end
if len(subtitles_files) == 0:
%>
<tr><td colspan="3">No Subtitles Detected For This Movie.</td></tr>
<%
end
end
%>
</tbody>
</table>
<%
subtitles_files = ast.literal_eval(str(details['subtitles']))
subtitles_files.sort(key=lambda x: x[0])
if subtitles_files is not None:
for subtitles_file in subtitles_files:
if subtitles_file[0].endswith(':forced'):
forced = True
if details['missing_subtitles'] is not None:
missing_subs_languages = ast.literal_eval(details['missing_subtitles'])
else:
missing_subs_languages = []
end
from get_subtitle import search_active
if missing_subs_languages is not None:
%>
<table class="ui very basic single line selectable table">
<thead>
<tr>
<th>Missing Subtitles</th>
</tr>
</thead>
</table>
<%
for missing_subs_language in missing_subs_languages:
if len(missing_subs_language) > 2:
forced = missing_subs_language[2]
forced_bool = True
else:
forced = False
forced_bool = False
end
%>
<tr>
%if subtitles_file[1] is not None:
<td><span data-tooltip="Path is: {{path_replace_movie(subtitles_file[1])}}" data-inverted='' data-position="top left">{{path.basename(path_replace_movie(subtitles_file[1]))}}</span></td>
%else:
<td>Video File Subtitles Track</td>
%end
<td><div class="ui tiny inverted label" style='background-color: #777777;'>{{language_from_alpha2(subtitles_file[0].split(':')[0])}}{{' forced' if forced else ''}}</div></td>
<td>
%if subtitles_file[1] is not None:
<a class="remove_subtitles ui inverted basic compact icon" data-tooltip="Delete Subtitle File" data-inverted="" data-position="top right" data-moviePath="{{details['path']}}" data-subtitlesPath="{{path_replace_movie(subtitles_file[1])}}" data-language="{{alpha3_from_alpha2(subtitles_file[0].split(':')[0])}}" data-radarrId={{details['radarrId']}}>
<i class="ui black delete icon"></i>
</a>
%end
</td>
</tr>
<%
end
if len(subtitles_files) == 0:
%>
<tr><td colspan="3">No Subtitles Detected For This Movie.</td></tr>
<%
end
end
%>
</tbody>
</table>
<%
if details['missing_subtitles'] is not None:
missing_subs_languages = ast.literal_eval(details['missing_subtitles'])
else:
missing_subs_languages = []
end
from get_subtitle import search_active
if missing_subs_languages is not None:
%>
<table class="ui very basic single line selectable table">
<thead>
<tr>
<th>Missing Subtitles</th>
</tr>
</thead>
</table>
<%
for missing_subs_language in missing_subs_languages:
if len(missing_subs_language) > 2:
forced = missing_subs_language[2]
forced_bool = True
else:
forced = False
forced_bool = False
end
if details['failedAttempts'] is not None and settings.general.getboolean('adaptive_searching') and missing_subs_language in details['failedAttempts']:
for lang in ast.literal_eval(details['failedAttempts']):
if missing_subs_language in lang:
if search_active(lang[1]):
%>
<a class="get_subtitle ui small blue label" data-moviePath="{{details['path']}}" data-scenename="{{details['sceneName']}}" data-language="{{alpha3_from_alpha2(str(missing_subs_language.split(':')[0]))}}" data-hi="{{details['hearing_impaired']}}" data-forced="{{details['forced']}}" data-radarrId={{details['radarrId']}}>
{{language_from_alpha2(str(missing_subs_language.split(':')[0]))}}{{' forced' if forced else ''}}
<i style="margin-left:3px; margin-right:0" class="search icon"></i>
</a>
%else:
<a data-tooltip="Automatic Searching Delayed (Adaptive Search)" data-position="top left" data-inverted="" class="get_subtitle ui small red label" data-moviePath="{{details['path']}}" data-scenename="{{details['sceneName']}}" data-language="{{alpha3_from_alpha2(str(missing_subs_language.split(':')[0]))}}" data-hi="{{details['hearing_impaired']}}" data-forced="{{details['forced']}}" data-radarrId={{details['radarrId']}}>
{{language_from_alpha2(str(missing_subs_language.split(':')[0]))}}{{' forced' if forced else ''}}
<i style="margin-left:3px; margin-right:0" class="search icon"></i>
</a>
<%
end
end
end
else:
%>
<a class="get_subtitle ui small blue label" data-moviePath="{{details['path']}}" data-scenename="{{details['sceneName']}}" data-language="{{alpha3_from_alpha2(str(missing_subs_language.split(':')[0]))}}" data-hi="{{details['hearing_impaired']}}" data-forced="{{details['forced']}}" data-radarrId={{details['radarrId']}}>
{{language_from_alpha2(str(missing_subs_language.split(':')[0]))}}{{' forced' if forced else ''}}
<i style="margin-left:3px; margin-right:0" class="search icon"></i>
</a>
<%
end
end
end
%>
if details['failedAttempts'] is not None and settings.general.getboolean('adaptive_searching') and missing_subs_language in details['failedAttempts']:
for lang in ast.literal_eval(details['failedAttempts']):
if missing_subs_language in lang:
if search_active(lang[1]):
%>
<a class="get_subtitle ui small blue label" data-moviePath="{{details['path']}}" data-scenename="{{details['sceneName']}}" data-language="{{alpha3_from_alpha2(str(missing_subs_language.split(':')[0]))}}" data-hi="{{details['hearing_impaired']}}" data-forced="{{details['forced']}}" data-radarrId={{details['radarrId']}}>
{{language_from_alpha2(str(missing_subs_language.split(':')[0]))}}{{' forced' if forced else ''}}
<i style="margin-left:3px; margin-right:0" class="search icon"></i>
</a>
%else:
<a data-tooltip="Automatic Searching Delayed (Adaptive Search)" data-position="top left" data-inverted="" class="get_subtitle ui small red label" data-moviePath="{{details['path']}}" data-scenename="{{details['sceneName']}}" data-language="{{alpha3_from_alpha2(str(missing_subs_language.split(':')[0]))}}" data-hi="{{details['hearing_impaired']}}" data-forced="{{details['forced']}}" data-radarrId={{details['radarrId']}}>
{{language_from_alpha2(str(missing_subs_language.split(':')[0]))}}{{' forced' if forced else ''}}
<i style="margin-left:3px; margin-right:0" class="search icon"></i>
</a>
<%
end
end
end
else:
%>
<a class="get_subtitle ui small blue label" data-moviePath="{{details['path']}}" data-scenename="{{details['sceneName']}}" data-language="{{alpha3_from_alpha2(str(missing_subs_language.split(':')[0]))}}" data-hi="{{details['hearing_impaired']}}" data-forced="{{details['forced']}}" data-radarrId={{details['radarrId']}}>
{{language_from_alpha2(str(missing_subs_language.split(':')[0]))}}{{' forced' if forced else ''}}
<i style="margin-left:3px; margin-right:0" class="search icon"></i>
</a>
<%
end
end
end
%>
</div>
</div>
<div class="ui bottom attached tab segment" data-tab="history">
<div class="content">
<table id="movie_result" class="display" style="width:100%">
<thead>
<tr>
<th></th>
<th style="text-align: left;">Language.:</th>
<th style="text-align: left;">Provider:</th>
<th style="text-align: left;">Score:</th>
<th style="text-align: left;">Date:</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
</div>
@ -420,6 +444,9 @@
</html>
<script>
$('.menu .item')
.tab();
$('#scan_disk').on('click', function(){
$('#loader_text').text("Scanning Disk For Existing Subtitles...");
window.location = '{{base_url}}scan_disk_movie/{{no}}';
@ -486,7 +513,7 @@
});
});
$('a, .menu .item, button:not(#config, .cancel, .manual_search, .manual_upload, #search_missing_subtitles_movie)').on('click', function(){
$('a:not(.tabs), button:not(#config, .cancel, .manual_search, .manual_upload, #search_missing_subtitles_movie)').on('click', function(){
$('#loader').addClass('active');
});
@ -519,6 +546,37 @@
.modal('show');
});
$('#history_tab').on('click', function(){
$('#movie_result').DataTable( {
destroy: true,
language: {
loadingRecords: '<br><div class="ui active inverted dimmer" style="width: 95%;"><div class="ui centered inline loader"></div></div><br>',
zeroRecords: 'No History Records Found For This Movie'
},
paging: true,
lengthChange: false,
pageLength: 5,
searching: true,
ordering: true,
processing: false,
serverSide: false,
ajax: {
url: '{{base_url}}movie_history/{{no}}'
},
drawCallback: function(settings) {
$('.inline.dropdown').dropdown();
$('.ui.accordion').accordion();
},
columns: [
{ data: 'action'},
{ data: 'language' },
{ data: 'provider' },
{ data: 'score'},
{ data: 'timestamp' }
]
} );
});
$('.manual_search').on('click', function(){
$("#movie_title_span").html($(this).data("movie_title"));
$("#movie_path_span").html($(this).attr("data-moviePath"));