Continuing development

pull/26/head
Louis Vézina 7 years ago
parent 1568d0e363
commit 546c3ac066

Binary file not shown.

@ -17,6 +17,8 @@ from init_db import *
from get_languages import *
from get_providers import *
from get_series import *
from get_episodes import *
from get_general_settings import *
from get_sonarr_settings import *
from list_subtitles import *
@ -74,18 +76,28 @@ def edit_series(no):
redirect('/')
@route('/update_series')
def update_series_list():
update_series()
redirect('/')
@route('/update_all_episodes')
def update_all_episodes_list():
update_all_episodes()
redirect('/')
@route('/episodes/<no:int>', method='GET')
def episodes(no):
conn = sqlite3.connect('bazarr.db')
conn.create_function("path_substitution", 1, path_replace)
conn.create_function("missing_subtitles", 1, list_missing_subtitles)
c = conn.cursor()
series_details = []
series_details = c.execute("SELECT title, overview, poster, fanart, hearing_impaired FROM table_shows WHERE sonarrSeriesId LIKE ?", (str(no),)).fetchone()
sqlite3.enable_callback_tracebacks(True)
episodes = c.execute("SELECT title, path_substitution(path), season, episode, subtitles, sonarrSeriesId, missing_subtitles(path), sonarrEpisodeId FROM table_episodes WHERE sonarrSeriesId LIKE ?", (str(no),)).fetchall()
episodes = c.execute("SELECT title, path_substitution(path), season, episode, subtitles, sonarrSeriesId, missing_subtitles, sonarrEpisodeId FROM table_episodes WHERE sonarrSeriesId LIKE ?", (str(no),)).fetchall()
episodes = reversed(sorted(episodes, key=operator.itemgetter(2)))
seasons_list = []
for key,season in itertools.groupby(episodes,operator.itemgetter(2)):
@ -98,11 +110,41 @@ def episodes(no):
def history():
db = sqlite3.connect('bazarr.db')
c = db.cursor()
c.execute("SELECT table_history.action, table_shows.title, table_episodes.season || 'x' || table_episodes.episode, table_episodes.title, table_history.timestamp, table_history.description, table_history.sonarrSeriesId FROM table_history INNER JOIN table_shows on table_shows.sonarrSeriesId = table_history.sonarrSeriesId INNER JOIN table_episodes on table_episodes.sonarrEpisodeId = table_history.sonarrEpisodeId ORDER BY id DESC LIMIT 15")
c.execute("SELECT COUNT(*) FROM table_history")
row_count = c.fetchone()
row_count = row_count[0]
page = request.GET.page
if page == "":
page = "1"
offset = (int(page) - 1) * 15
max_page = (row_count / 15) + 1
c.execute("SELECT table_history.action, table_shows.title, table_episodes.season || 'x' || table_episodes.episode, table_episodes.title, table_history.timestamp, table_history.description, table_history.sonarrSeriesId FROM table_history INNER JOIN table_shows on table_shows.sonarrSeriesId = table_history.sonarrSeriesId INNER JOIN table_episodes on table_episodes.sonarrEpisodeId = table_history.sonarrEpisodeId ORDER BY id DESC LIMIT 15 OFFSET ?", (offset,))
data = c.fetchall()
data = reversed(sorted(data, key=operator.itemgetter(4)))
c.close()
return template('history', rows=data)
return template('history', rows=data, row_count=row_count, page=page, max_page=max_page)
@route('/wanted')
def wanted():
db = sqlite3.connect('bazarr.db')
db.create_function("path_substitution", 1, path_replace)
c = db.cursor()
c.execute("SELECT COUNT(*) FROM table_episodes WHERE missing_subtitles != '[]'")
missing_count = c.fetchone()
missing_count = missing_count[0]
page = request.GET.page
if page == "":
page = "1"
offset = (int(page) - 1) * 15
max_page = (missing_count / 15) + 1
c.execute("SELECT table_shows.title, table_episodes.season || 'x' || table_episodes.episode, table_episodes.title, table_episodes.missing_subtitles, table_episodes.sonarrSeriesId, path_substitution(table_episodes.path), table_shows.hearing_impaired, table_episodes.sonarrEpisodeId FROM table_episodes INNER JOIN table_shows on table_shows.sonarrSeriesId = table_episodes.sonarrSeriesId WHERE table_episodes.missing_subtitles != '[]' LIMIT 15 OFFSET ?", (offset,))
data = c.fetchall()
c.close()
return template('wanted', rows=data, missing_count=missing_count, page=page, max_page=max_page)
@route('/settings')
def settings():
@ -121,27 +163,41 @@ def settings():
@route('/save_settings', method='POST')
def save_settings():
lang = request.forms.getall('languages')
if len(lang) > 0:
if lang[0] == '':
lang = None
else:
pass
else:
lang = None
hi = request.forms.get('hearing_impaired')
conn = sqlite3.connect('bazarr.db')
c = conn.cursor()
if hi == "on":
hi = "True"
settings_general_ip = request.forms.get('settings_general_ip')
settings_general_port = request.forms.get('settings_general_port')
settings_general_baseurl = request.forms.get('settings_general_baseurl')
settings_general_sourcepath = request.forms.getall('settings_general_sourcepath')
settings_general_destpath = request.forms.getall('settings_general_destpath')
settings_general_pathmapping = []
settings_general_pathmapping.extend([list(a) for a in zip(settings_general_sourcepath, settings_general_destpath)])
c.execute("UPDATE table_settings_general SET ip = ?, port = ?, base_url = ?, path_mapping = ?", (settings_general_ip, settings_general_port, settings_general_baseurl, str(settings_general_pathmapping)))
settings_sonarr_ip = request.forms.get('settings_sonarr_ip')
settings_sonarr_port = request.forms.get('settings_sonarr_port')
settings_sonarr_baseurl = request.forms.get('settings_sonarr_baseurl')
settings_sonarr_ssl = request.forms.get('settings_sonarr_ssl')
if settings_sonarr_ssl is None:
settings_sonarr_ssl = 'False'
else:
hi = "False"
conn = sqlite3.connect('bazarr.db')
c = conn.cursor()
c.execute("UPDATE table_shows SET languages = ?, hearing_impaired = ? WHERE tvdbId LIKE ?", (str(lang), hi, no))
settings_sonarr_ssl = 'True'
settings_sonarr_apikey = request.forms.get('settings_sonarr_apikey')
c.execute("UPDATE table_settings_sonarr SET ip = ?, port = ?, base_url = ?, ssl = ?, apikey = ?", (settings_sonarr_ip, settings_sonarr_port, settings_sonarr_baseurl, settings_sonarr_ssl, settings_sonarr_apikey))
settings_subliminal_providers = request.forms.getall('settings_subliminal_providers')
c.execute("UPDATE table_settings_providers SET enabled = 0")
for item in settings_subliminal_providers:
c.execute("UPDATE table_settings_providers SET enabled = '1' WHERE name = ?", (item,))
settings_subliminal_languages = request.forms.getall('settings_subliminal_languages')
c.execute("UPDATE table_settings_languages SET enabled = 0")
for item in settings_subliminal_languages:
c.execute("UPDATE table_settings_languages SET enabled = '1' WHERE code2 = ?", (item,))
conn.commit()
c.close()
redirect('/settings')
@route('/system')
@ -168,22 +224,37 @@ def remove_subtitles():
except OSError:
pass
store_subtitles(episodePath)
list_missing_subtitles()
redirect('/episodes/' + sonarrSeriesId)
@route('/get_subtitle', method='GET')
def get_subtitle():
ref = request.environ['HTTP_REFERER']
episodePath = request.GET.episodePath
language = request.GET.language
hi = request.GET.hi
sonarrSeriesId = request.GET.sonarrSeriesId
sonarrEpisodeId = request.GET.sonarrEpisodeId
db = sqlite3.connect('bazarr.db')
c = db.cursor()
c.execute("SELECT name FROM table_settings_providers WHERE enabled = 1")
providers = c.fetchall()
c.close()
providers_list = []
for provider in providers:
providers_list.append(provider[0])
try:
result = download_subtitle(episodePath, language, hi, None)
history_log(1, sonarrSeriesId, sonarrEpisodeId, result)
store_subtitles(episodePath)
redirect('/episodes/' + sonarrSeriesId)
result = download_subtitle(episodePath, language, hi, providers_list)
if result is not None:
history_log(1, sonarrSeriesId, sonarrEpisodeId, result)
store_subtitles(episodePath)
list_missing_subtitles()
redirect(ref)
except OSError:
redirect('/episodes/' + sonarrSeriesId + '?error=2')
redirect(ref + '?error=2')
run(host=ip, port=port, server='waitress')

@ -3,44 +3,46 @@ import requests
from list_subtitles import *
# Open database connection
db = sqlite3.connect('bazarr.db')
c = db.cursor()
def update_all_episodes():
# Open database connection
db = sqlite3.connect('bazarr.db')
c = db.cursor()
# Get Sonarr API URL from database config table
c.execute('''SELECT * FROM table_settings_sonarr''')
config_sonarr = c.fetchone()
if config_sonarr[3] == 1:
protocol_sonarr = "https"
else:
protocol_sonarr = "http"
if config_sonarr[2] == "":
base_url_sonarr = ""
else:
base_url_sonarr = "/" + config_sonarr[2].strip("/")
# Get Sonarr API URL from database config table
c.execute('''SELECT * FROM table_settings_sonarr''')
config_sonarr = c.fetchone()
if config_sonarr[3] == 1:
protocol_sonarr = "https"
else:
protocol_sonarr = "http"
if config_sonarr[2] == "":
base_url_sonarr = ""
else:
base_url_sonarr = "/" + config_sonarr[2].strip("/")
# Get sonarrId for each series from database
c.execute("SELECT sonarrSeriesId FROM table_shows")
seriesIdList = c.fetchall()
for seriesId in seriesIdList:
# Get episodes data for a series from Sonarr
url_sonarr_api_episode = protocol_sonarr + "://" + config_sonarr[0] + ":" + str(config_sonarr[1]) + base_url_sonarr + "/api/episode?seriesId=" + str(seriesId[0]) + "&apikey=" + config_sonarr[4]
r = requests.get(url_sonarr_api_episode)
for episode in r.json():
if episode['hasFile']:
try:
c.execute('''INSERT INTO table_episodes(sonarrSeriesId, sonarrEpisodeId, title, path, season, episode) VALUES (?, ?, ?, ?, ?, ?)''', (episode['seriesId'], episode['id'], episode['title'], episode['episodeFile']['path'], episode['seasonNumber'], episode['episodeNumber']))
except sqlite3.Error:
test = c.execute('''UPDATE table_episodes SET sonarrSeriesId = ?, sonarrEpisodeId = ?, title = ?, path = ?, season = ?, episode = ? WHERE path = ?''', (episode['seriesId'], episode['id'], episode['title'], episode['episodeFile']['path'], episode['seasonNumber'], episode['episodeNumber'], episode['episodeFile']['path']))
else:
continue
continue
# Get sonarrId for each series from database
c.execute("SELECT sonarrSeriesId FROM table_shows")
seriesIdList = c.fetchall()
for seriesId in seriesIdList:
# Get episodes data for a series from Sonarr
url_sonarr_api_episode = protocol_sonarr + "://" + config_sonarr[0] + ":" + str(config_sonarr[1]) + base_url_sonarr + "/api/episode?seriesId=" + str(seriesId[0]) + "&apikey=" + config_sonarr[4]
r = requests.get(url_sonarr_api_episode)
for episode in r.json():
if episode['hasFile']:
try:
c.execute('''INSERT INTO table_episodes(sonarrSeriesId, sonarrEpisodeId, title, path, season, episode) VALUES (?, ?, ?, ?, ?, ?)''', (episode['seriesId'], episode['id'], episode['title'], episode['episodeFile']['path'], episode['seasonNumber'], episode['episodeNumber']))
except sqlite3.Error:
test = c.execute('''UPDATE table_episodes SET sonarrSeriesId = ?, sonarrEpisodeId = ?, title = ?, path = ?, season = ?, episode = ? WHERE path = ?''', (episode['seriesId'], episode['id'], episode['title'], episode['episodeFile']['path'], episode['seasonNumber'], episode['episodeNumber'], episode['episodeFile']['path']))
else:
continue
continue
# Commit changes to database table
db.commit()
# Commit changes to database table
db.commit()
# Close database connection
c.close()
# Store substitles from episodes we've just added
new_scan_subtitles()
# Close database connection
c.close()
# Store substitles from episodes we've just added
new_scan_subtitles()
list_missing_subtitles()

@ -4,56 +4,57 @@ import requests
from get_sonarr_settings import *
# Open database connection
db = sqlite3.connect('bazarr.db')
c = db.cursor()
# Get shows data from Sonarr
url_sonarr_api_series = url_sonarr + "/api/series?apikey=" + apikey_sonarr
r = requests.get(url_sonarr_api_series)
shows_list = []
# Get current shows in DB
current_shows_db = c.execute('SELECT tvdbId FROM table_shows').fetchall()
current_shows_db_list = [x[0] for x in current_shows_db]
current_shows_sonarr = []
# Parsing data returned from Sonarr
for show in r.json():
try:
overview = unicode(show['overview'])
except:
overview = ""
try:
poster_big = show['images'][2]['url'].split('?')[0]
poster = os.path.splitext(poster_big)[0] + '-250' + os.path.splitext(poster_big)[1]
except:
poster = ""
try:
fanart = show['images'][0]['url'].split('?')[0]
except:
fanart = ""
# Add shows in Sonarr to current shows list
current_shows_sonarr.append(show['tvdbId'])
# Update or insert shows list in database table
try:
c.execute('''UPDATE table_shows SET title = ?, path = ?, tvdbId = ?, sonarrSeriesId = ?, overview = ?, poster = ?, fanart = ? WHERE tvdbid = ?''', (show["title"],show["path"],show["tvdbId"],show["id"],overview,poster,fanart,show["tvdbId"]))
except:
print show["title"]
c.execute('''INSERT INTO table_shows(title, path, tvdbId, languages,`hearing_impaired`, sonarrSeriesId, overview, poster, fanart) VALUES (?,?,?,(SELECT languages FROM table_shows WHERE tvdbId = ?),(SELECT `hearing_impaired` FROM table_shows WHERE tvdbId = ?), ?, ?, ?, ?)''', (show["title"],show["path"],show["tvdbId"],show["tvdbId"],show["tvdbId"],show["id"],overview,poster,fanart))
# Delete shows not in Sonarr anymore
deleted_items = []
for item in current_shows_db_list:
if item not in current_shows_sonarr:
deleted_items.append(tuple([item]))
c.executemany('DELETE FROM table_shows WHERE tvdbId = ?',deleted_items)
# Commit changes to database table
db.commit()
# Close database connection
db.close()
def update_series():
# Open database connection
db = sqlite3.connect('bazarr.db')
c = db.cursor()
# Get shows data from Sonarr
url_sonarr_api_series = url_sonarr + "/api/series?apikey=" + apikey_sonarr
r = requests.get(url_sonarr_api_series)
shows_list = []
# Get current shows in DB
current_shows_db = c.execute('SELECT tvdbId FROM table_shows').fetchall()
current_shows_db_list = [x[0] for x in current_shows_db]
current_shows_sonarr = []
# Parsing data returned from Sonarr
for show in r.json():
try:
overview = unicode(show['overview'])
except:
overview = ""
try:
poster_big = show['images'][2]['url'].split('?')[0]
poster = os.path.splitext(poster_big)[0] + '-250' + os.path.splitext(poster_big)[1]
except:
poster = ""
try:
fanart = show['images'][0]['url'].split('?')[0]
except:
fanart = ""
# Add shows in Sonarr to current shows list
current_shows_sonarr.append(show['tvdbId'])
# Update or insert shows list in database table
try:
c.execute('''UPDATE table_shows SET title = ?, path = ?, tvdbId = ?, sonarrSeriesId = ?, overview = ?, poster = ?, fanart = ? WHERE tvdbid = ?''', (show["title"],show["path"],show["tvdbId"],show["id"],overview,poster,fanart,show["tvdbId"]))
except:
print show["title"]
c.execute('''INSERT INTO table_shows(title, path, tvdbId, languages,`hearing_impaired`, sonarrSeriesId, overview, poster, fanart) VALUES (?,?,?,(SELECT languages FROM table_shows WHERE tvdbId = ?),(SELECT `hearing_impaired` FROM table_shows WHERE tvdbId = ?), ?, ?, ?, ?)''', (show["title"],show["path"],show["tvdbId"],show["tvdbId"],show["tvdbId"],show["id"],overview,poster,fanart))
# Delete shows not in Sonarr anymore
deleted_items = []
for item in current_shows_db_list:
if item not in current_shows_sonarr:
deleted_items.append(tuple([item]))
c.executemany('DELETE FROM table_shows WHERE tvdbId = ?',deleted_items)
# Commit changes to database table
db.commit()
# Close database connection
db.close()

@ -9,11 +9,14 @@ region.configure('dogpile.cache.dbm', arguments={'filename': 'cachefile.dbm'})
def download_subtitle(path, language, hi, providers):
video = scan_video(path)
best_subtitles = download_best_subtitles([video], {Language(language)}, providers=providers, hearing_impaired=hi)
best_subtitle = best_subtitles[video][0]
try:
best_subtitle = best_subtitles[video][0]
result = save_subtitles(video, [best_subtitle])
downloaded_provider = str(result[0]).strip('<>').split(' ')[0][:-8]
downloaded_language = pycountry.languages.lookup(str(str(result[0]).strip('<>').split(' ')[2].strip('[]'))).name
message = downloaded_language + " subtitles downloaded from " + downloaded_provider + "."
result = save_subtitles(video, [best_subtitle])
downloaded_provider = str(result[0]).strip('<>').split(' ')[0][:-8]
downloaded_language = pycountry.languages.lookup(str(str(result[0]).strip('<>').split(' ')[2].strip('[]'))).name
message = downloaded_language + " subtitles downloaded from " + downloaded_provider + "."
return message
return message
except:
return None

@ -65,37 +65,52 @@ def store_subtitles(file):
return actual_subtitles
def list_missing_subtitles(file):
def list_missing_subtitles():
conn_db = sqlite3.connect('bazarr.db')
c_db = conn_db.cursor()
actual_subtitles_long = c_db.execute("SELECT sonarrSeriesId, subtitles FROM table_episodes WHERE path = ?", (file,)).fetchone()
desired_subtitles = c_db.execute("SELECT languages FROM table_shows WHERE sonarrSeriesId = ?", (actual_subtitles_long[0],)).fetchone()
c_db.close()
missing_subtitles = []
actual_subtitles = []
if desired_subtitles[0] == "None":
pass
else:
actual_subtitles_long = ast.literal_eval(actual_subtitles_long[1])
for actual_subtitle in actual_subtitles_long:
actual_subtitles.append(actual_subtitle[0])
episodes_subtitles = c_db.execute("SELECT table_episodes.sonarrEpisodeId, table_episodes.subtitles, table_shows.languages FROM table_episodes INNER JOIN table_shows on table_episodes.sonarrSeriesId = table_shows.sonarrSeriesId").fetchall()
desired_subtitles = ast.literal_eval(desired_subtitles[0])
missing_subtitles = (list(set(desired_subtitles) - set(actual_subtitles)))
return str(missing_subtitles)
missing_subtitles_global = []
for episode_subtitles in episodes_subtitles:
actual_subtitles = []
desired_subtitles = []
missing_subtitles = []
actual_subtitles = ast.literal_eval(episode_subtitles[1])
desired_subtitles = ast.literal_eval(episode_subtitles[2])
actual_subtitles_list = []
if desired_subtitles == None:
pass
else:
actual_subtitles_list = []
for item in actual_subtitles:
actual_subtitles_list.append(item[0])
missing_subtitles = list(set(desired_subtitles) - set(actual_subtitles_list))
missing_subtitles_global.append(tuple([str(missing_subtitles), episode_subtitles[0]]))
c_db.executemany("UPDATE table_episodes SET missing_subtitles = ? WHERE sonarrEpisodeId = ?", (missing_subtitles_global))
conn_db.commit()
c_db.close()
def full_scan_subtitles():
conn_db = sqlite3.connect('bazarr.db')
c_db = conn_db.cursor()
c_db.execute("SELECT path FROM table_episodes").fetchall()
episodes = c_db.execute("SELECT path FROM table_episodes").fetchall()
c_db.close()
for episode in episodes:
store_subtitles(path_replace(episode[0]))
def new_scan_subtitles():
conn_db = sqlite3.connect('bazarr.db')
c_db = conn_db.cursor()
c_db.execute("SELECT path FROM table_episodes WHERE subtitles is null").fetchall()
episodes = c_db.execute("SELECT path FROM table_episodes WHERE subtitles is null").fetchall()
c_db.close()
for episode in episodes:
store_subtitles(path_replace(episode[0]))
#full_scan_subtitles()
#new_scan_subtitles()
#list_missing_subtitles()

Binary file not shown.

@ -26,9 +26,8 @@
background-position:center center;
}
#divmenu {
background-color: #1b1c1d;
background-color: #272727;
opacity: 0.9;
border-radius: 5px;
padding-top: 2em;
padding-bottom: 1em;
padding-left: 1em;
@ -82,8 +81,8 @@
<div class="ui indeterminate text loader">Loading...</div>
</div>
<div id="divmenu" class="ui container">
<div id="menu" class="ui inverted borderless labeled icon huge menu four item">
<a href="/"><img class="logo" src="/static/logo128.png"></a>
<div style="background-color:#272727;" class="ui inverted borderless labeled icon huge menu five item">
<a href="/"><img style="margin-right:32px;" class="logo" src="/static/logo128.png"></a>
<div style="height:80px;" class="ui container">
<a class="item" href="/">
<i class="play icon"></i>
@ -93,6 +92,10 @@
<i class="wait icon"></i>
History
</a>
<a class="item" href="/wanted">
<i class="warning sign icon"></i>
Wanted
</a>
<a class="item" href="/settings">
<i class="settings icon"></i>
Settings
@ -106,17 +109,12 @@
</div>
<div style='padding-left: 2em; padding-right: 2em;' class='ui container'>
<br>
<div class="ui hidden negative message">
<i class="close icon"></i>
<div class="header">
An error occured while trying to delete subtitles file from disk.
</div>
<p>Please try again.</p>
</div>
<div id="divdetails" class="ui container">
<img class="left floated ui image" src="/image_proxy{{details[2]}}">
<div class="ui right floated inverted basic buttons">
<button id="scan_disk" class="ui button"><i class="refresh icon"></i>Scan disk for subtitles</button>
<button id="search_missing_subtitles" class="ui button"><i class="download icon"></i>Download missing subtitles</button>
</div>
<h2>{{details[0]}}</h2>
<p>{{details[1]}}</p>
</div>

@ -35,10 +35,11 @@
box-shadow: 0px 0px 5px 5px #ffffff;
margin-top: 32px;
margin-bottom: 3em;
}
#tablehistory {
padding: 3em;
}
.fast.backward, .backward, .forward, .fast.forward {
cursor: pointer;
}
</style>
</head>
<body>
@ -46,7 +47,7 @@
<div class="ui indeterminate text loader">Loading...</div>
</div>
<div id="divmenu" class="ui container">
<div style="background-color:#272727;" class="ui inverted borderless labeled icon huge menu four item">
<div style="background-color:#272727;" class="ui inverted borderless labeled icon huge menu five item">
<a href="/"><img style="margin-right:32px;" class="logo" src="/static/logo128.png"></a>
<div style="height:80px;" class="ui container">
<a class="item" href="/">
@ -57,6 +58,10 @@
<i class="wait icon"></i>
History
</a>
<a class="item" href="/wanted">
<i class="warning sign icon"></i>
Wanted
</a>
<a class="item" href="/settings">
<i class="settings icon"></i>
Settings
@ -113,6 +118,35 @@
%end
</tbody>
</table>
<div class="ui grid">
<div class="three column row">
<div class="column"></div>
<div class="center aligned column">
<i class="\\
%if page == "1":
disabled\\
%end
fast backward icon"></i>
<i class="\\
%if page == "1":
disabled\\
%end
backward icon"></i>
{{page}} / {{max_page}}
<i class="\\
%if int(page) == int(max_page):
disabled\\
%end
forward icon"></i>
<i class="\\
%if int(page) == int(max_page):
disabled\\
%end
fast forward icon"></i>
</div>
<div class="right floated right aligned column">Total records: {{row_count}}</div>
</div>
</div>
</div>
</body>
</html>
@ -122,4 +156,17 @@
$('a').click(function(){
$('#loader').addClass('active');
})
$('.fast.backward').click(function(){
location.href="?page=1";
})
$('.backward').click(function(){
location.href="?page={{int(page)-1}}";
})
$('.forward').click(function(){
location.href="?page={{int(page)+1}}";
})
$('.fast.forward').click(function(){
location.href="?page={{int(max_page)}}";
})
</script>

@ -35,9 +35,10 @@
box-shadow: 0px 0px 5px 5px #ffffff;
margin-top: 32px;
margin-bottom: 3em;
padding: 2em 3em 2em 3em;
}
#tableseries {
padding: 3em;
padding-top: 1em;
}
#divdetails {
min-height: 250px;
@ -49,7 +50,7 @@
<div class="ui indeterminate text loader">Loading...</div>
</div>
<div id="divmenu" class="ui container">
<div style="background-color:#272727;" class="ui inverted borderless labeled icon huge menu four item">
<div style="background-color:#272727;" class="ui inverted borderless labeled icon huge menu five item">
<a href="/"><img style="margin-right:32px;" class="logo" src="/static/logo128.png"></a>
<div style="height:80px;" class="ui container">
<a class="item" href="/">
@ -60,6 +61,10 @@
<i class="wait icon"></i>
History
</a>
<a class="item" href="/wanted">
<i class="warning sign icon"></i>
Wanted
</a>
<a class="item" href="/settings">
<i class="settings icon"></i>
Settings
@ -73,6 +78,11 @@
</div>
<div id="fondblanc" class="ui container">
<div class="ui basic buttons">
<button id="update_series" class="ui button"><i class="refresh icon"></i>Update Series</button>
<button id="update_all_episodes" class="ui button"><i class="refresh icon"></i>Update All Episodes</button>
</div>
<table id="tableseries" class="ui very basic selectable sortable table">
<thead>
<tr>
@ -110,7 +120,7 @@
end
end
%>
<div class="config ui inverted basic compact icon" data-tooltip="Edit series" data-inverted="" data-tvdbid="{{row[0]}}" data-title="{{row[1]}}" data-poster="{{row[6]}}" data-languages="{{!subs_languages_list}}" data-hearing-impaired="{{row[4]}}">
<div class="ui inverted basic compact icon" data-tooltip="Edit series" data-inverted="" data-tvdbid="{{row[0]}}" data-title="{{row[1]}}" data-poster="{{row[6]}}" data-languages="{{!subs_languages_list}}" data-hearing-impaired="{{row[4]}}">
<i class="ui black configure icon"></i>
</div>
</td>
@ -178,7 +188,7 @@
$('table').tablesort();
$('a').click(function(){
$('a, button').click(function(){
$('#loader').addClass('active');
})
@ -188,6 +198,14 @@
})
;
$('#update_series').click(function(){
window.location = '/update_series';
})
$('#update_all_episodes').click(function(){
window.location = '/update_all_episodes';
})
$('.config').click(function(){
sessionStorage.scrolly=$(window).scrollTop();

@ -44,22 +44,26 @@
<div class="ui indeterminate text loader">Loading...</div>
</div>
<div id="divmenu" class="ui container">
<div style="background-color:#272727;" class="ui inverted borderless labeled icon huge menu four item">
<div style="background-color:#272727;" class="ui inverted borderless labeled icon huge menu five item">
<a href="/"><img style="margin-right:32px;" class="logo" src="/static/logo128.png"></a>
<div style="height:80px;" class="ui container">
<a class="menu item" href="/">
<a class="item" href="/">
<i class="play icon"></i>
Series
</a>
<a class="menu item" href="/history">
<a class="item" href="/history">
<i class="wait icon"></i>
History
</a>
<a class="menu item" href="/settings">
<a class="item" href="/wanted">
<i class="warning sign icon"></i>
Wanted
</a>
<a class="item" href="/settings">
<i class="settings icon"></i>
Settings
</a>
<a class="menu item" href="/system">
<a class="item" href="/system">
<i class="laptop icon"></i>
System
</a>
@ -68,206 +72,209 @@
</div>
<div id="fondblanc" class="ui container">
<form name="settings_form" id="settings_form" action="/save_settings" method="post" class="ui form">
<div class="ui top attached tabular menu">
<a class="item active" data-tab="general">General</a>
<a class="item" data-tab="sonarr">Sonarr</a>
<a class="item" data-tab="subliminal">Subliminal</a>
</div>
<form>
<div class="ui bottom attached tab segment active" data-tab="general">
<div class="ui container"><button class="ui blue right floated button">Save</button></div>
<br>
<div class="ui dividing header">Bazarr settings</div>
<div class="twelve wide column">
<div class="ui grid">
<div class="middle aligned row">
<div class="right aligned four wide column">
<label>Listening IP address</label>
</div>
<div class="eleven wide column">
<div class="ui input">
<input type="text" value="{{settings_general[0]}}">
</div>
</div>
<div class="ui bottom attached tab segment active" data-tab="general">
<div class="ui container"><button class="ui blue right floated button" type="submit" value="Submit" form="settings_form">Save</button></div>
<br>
<div class="ui dividing header">Bazarr settings</div>
<div class="ui negative message">
<p>These changes require that you restart Bazarr.</p>
</div>
<div class="twelve wide column">
<div class="ui grid">
<div class="middle aligned row">
<div class="right aligned four wide column">
<label>Listening IP address</label>
</div>
<div class="eleven wide column">
<div class="ui input">
<input name="settings_general_ip" type="text" value="{{settings_general[0]}}">
</div>
</div>
</div>
<div class="middle aligned row">
<div class="right aligned four wide column">
<label>Listening port</label>
</div>
<div class="eleven wide column">
<div class="ui input">
<input type="text" value="{{settings_general[1]}}">
</div>
</div>
<div class="middle aligned row">
<div class="right aligned four wide column">
<label>Listening port</label>
</div>
<div class="eleven wide column">
<div class="ui input">
<input name="settings_general_port" type="text" value="{{settings_general[1]}}">
</div>
</div>
</div>
<div class="middle aligned row">
<div class="right aligned four wide column">
<label>Base URL</label>
</div>
<div class="eleven wide column">
<div class="ui input">
%if settings_general[2] == None:
% base_url = "/"
%else:
% base_url = settings_general[2]
%end
<input type="text" value="{{base_url}}">
</div>
</div>
<div class="middle aligned row">
<div class="right aligned four wide column">
<label>Base URL</label>
</div>
<div class="eleven wide column">
<div class="ui input">
%if settings_general[2] == None:
% base_url = "/"
%else:
% base_url = settings_general[2]
%end
<input name="settings_general_baseurl" type="text" value="{{base_url}}">
</div>
</div>
</div>
</div>
</div>
<div class="ui dividing header">Remote Path Mappings</div>
<div class="twelve wide column">
<div class="ui grid">
%import ast
%path_substitutions = ast.literal_eval(settings_general[3])
%for x in range(0, 5):
% path = []
% try:
% path = path_substitutions[x]
% except IndexError:
% path = ["", ""]
% end
<div class="middle aligned row">
<div class="right aligned four wide column">
</div>
<div class="four wide column">
<div class="ui fluid input">
<input type="text" value="{{path[0]}}">
</div>
</div>
<div class="center aligned column">
<i class="arrow circle right icon"></i>
</div>
<div class="four wide column">
<div class="ui fluid input">
<input type="text" value="{{path[1]}}">
</div>
</div>
<div class="ui dividing header">Remote Path Mappings</div>
<div class="twelve wide column">
<div class="ui grid">
%import ast
%path_substitutions = ast.literal_eval(settings_general[3])
%for x in range(0, 5):
% path = []
% try:
% path = path_substitutions[x]
% except IndexError:
% path = ["", ""]
% end
<div class="middle aligned row">
<div class="right aligned four wide column">
</div>
<div class="four wide column">
<div class="ui fluid input">
<input name="settings_general_sourcepath" type="text" value="{{path[0]}}">
</div>
</div>
<div class="center aligned column">
<i class="arrow circle right icon"></i>
</div>
<div class="four wide column">
<div class="ui fluid input">
<input name="settings_general_destpath" type="text" value="{{path[1]}}">
</div>
%end
</div>
</div>
%end
</div>
</div>
<div class="ui bottom attached tab segment" data-tab="sonarr">
<div class="ui container"><button class="ui blue right floated button">Save</button></div>
<br>
<div class="ui dividing header">Sonarr settings</div>
<div class="twelve wide column">
<div class="ui grid">
<div class="middle aligned row">
<div class="right aligned four wide column">
<label>Listening IP address</label>
</div>
<div class="eleven wide column">
<div class="ui input">
<input type="text" value="{{settings_sonarr[0]}}">
</div>
</div>
</div>
<div class="ui bottom attached tab segment" data-tab="sonarr">
<div class="ui container"><button class="ui blue right floated button">Save</button></div>
<br>
<div class="ui dividing header">Sonarr settings</div>
<div class="twelve wide column">
<div class="ui grid">
<div class="middle aligned row">
<div class="right aligned four wide column">
<label>Listening IP address</label>
</div>
<div class="eleven wide column">
<div class="ui input">
<input name="settings_sonarr_ip" type="text" value="{{settings_sonarr[0]}}">
</div>
</div>
</div>
<div class="middle aligned row">
<div class="right aligned four wide column">
<label>Listening port</label>
</div>
<div class="eleven wide column">
<div class="ui input">
<input type="text" value="{{settings_sonarr[1]}}">
</div>
</div>
<div class="middle aligned row">
<div class="right aligned four wide column">
<label>Listening port</label>
</div>
<div class="eleven wide column">
<div class="ui input">
<input name="settings_sonarr_port" type="text" value="{{settings_sonarr[1]}}">
</div>
</div>
</div>
<div class="middle aligned row">
<div class="right aligned four wide column">
<label>Base URL</label>
</div>
<div class="eleven wide column">
<div class="ui input">
<input type="text" value="{{settings_sonarr[2]}}">
</div>
</div>
<div class="middle aligned row">
<div class="right aligned four wide column">
<label>Base URL</label>
</div>
<div class="eleven wide column">
<div class="ui input">
<input name="settings_sonarr_baseurl" type="text" value="{{settings_sonarr[2]}}">
</div>
</div>
</div>
<div class="middle aligned row">
<div class="right aligned four wide column">
<label>SSL enabled</label>
</div>
<div class="eleven wide column">
<div id="sonarr_ssl_div" class="ui toggle checkbox" data-ssl={{settings_sonarr[3]}}>
<input type="checkbox">
<label></label>
</div>
</div>
</div>
<div class="middle aligned row">
<div class="right aligned four wide column">
<label>SSL enabled</label>
</div>
<div class="eleven wide column">
<div id="sonarr_ssl_div" class="ui toggle checkbox" data-ssl={{settings_sonarr[3]}}>
<input name="settings_sonarr_ssl" type="checkbox">
<label></label>
</div>
</div>
</div>
<div class="middle aligned row">
<div class="right aligned four wide column">
<label>API key</label>
</div>
<div class="five wide column">
<div class="ui fluid input">
<input type="text" value="{{settings_sonarr[4]}}">
</div>
</div>
<div class="middle aligned row">
<div class="right aligned four wide column">
<label>API key</label>
</div>
<div class="five wide column">
<div class="ui fluid input">
<input name="settings_sonarr_apikey" type="text" value="{{settings_sonarr[4]}}">
</div>
</div>
</div>
</div>
</div>
<div class="ui bottom attached tab segment" data-tab="subliminal">
<div class="ui container"><button class="ui blue right floated button">Save</button></div>
<br>
<div class="ui dividing header">Subtitles providers</div>
<div class="twelve wide column">
<div class="ui grid">
<div class="middle aligned row">
<div class="right aligned four wide column">
<label>Enabled providers</label>
</div>
<div class="eleven wide column">
<select name="settings_providers" id="settings_providers" multiple="" class="ui fluid selection dropdown">
<option value="">Providers</option>
%enabled_providers = []
%for provider in settings_providers:
<option value="{{provider[0]}}">{{provider[0]}}</option>
%if provider[1] == True:
% enabled_providers.append(str(provider[0]))
%end
%end
</select>
</div>
</div>
</div>
<div class="ui bottom attached tab segment" data-tab="subliminal">
<div class="ui container"><button class="ui blue right floated button">Save</button></div>
<br>
<div class="ui dividing header">Subtitles providers</div>
<div class="twelve wide column">
<div class="ui grid">
<div class="middle aligned row">
<div class="right aligned four wide column">
<label>Enabled providers</label>
</div>
<div class="eleven wide column">
<select name="settings_subliminal_providers" id="settings_providers" multiple="" class="ui fluid selection dropdown">
<option value="">Providers</option>
%enabled_providers = []
%for provider in settings_providers:
<option value="{{provider[0]}}">{{provider[0]}}</option>
%if provider[1] == True:
% enabled_providers.append(str(provider[0]))
%end
%end
</select>
</div>
</div>
<div class="ui dividing header">Subtitles languages</div>
<div class="twelve wide column">
<div class="ui grid">
<div class="middle aligned row">
<div class="right aligned four wide column">
<label>Enabled languages</label>
</div>
<div class="eleven wide column">
<select name="settings_languages" id="settings_languages" multiple="" class="ui fluid selection dropdown">
<option value="">Languages</option>
%enabled_languages = []
%for language in settings_languages:
<option value="{{language[1]}}">{{language[2]}}</option>
%if language[3] == True:
% enabled_languages.append(str(language[1]))
%end
%end
</select>
</div>
</div>
</div>
</div>
<div class="ui dividing header">Subtitles languages</div>
<div class="twelve wide column">
<div class="ui grid">
<div class="middle aligned row">
<div class="right aligned four wide column">
<label>Enabled languages</label>
</div>
<div class="eleven wide column">
<select name="settings_subliminal_languages" id="settings_languages" multiple="" class="ui fluid selection dropdown">
<option value="">Languages</option>
%enabled_languages = []
%for language in settings_languages:
<option value="{{language[1]}}">{{language[2]}}</option>
%if language[3] == True:
% enabled_languages.append(str(language[1]))
%end
%end
</select>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</form>
</body>
</html>

@ -43,22 +43,26 @@
<div class="ui indeterminate text loader">Loading...</div>
</div>
<div id="divmenu" class="ui container">
<div style="background-color:#272727;" class="ui inverted borderless labeled icon huge menu four item">
<div style="background-color:#272727;" class="ui inverted borderless labeled icon huge menu five item">
<a href="/"><img style="margin-right:32px;" class="logo" src="/static/logo128.png"></a>
<div style="height:80px;" class="ui container">
<a class="menu item" href="/">
<a class="item" href="/">
<i class="play icon"></i>
Series
</a>
<a class="menu item" href="/history">
<a class="item" href="/history">
<i class="wait icon"></i>
History
</a>
<a class="menu item" href="/settings">
<a class="item" href="/wanted">
<i class="warning sign icon"></i>
Wanted
</a>
<a class="item" href="/settings">
<i class="settings icon"></i>
Settings
</a>
<a class="menu item" href="/system">
<a class="item" href="/system">
<i class="laptop icon"></i>
System
</a>

@ -0,0 +1,166 @@
<html>
<head>
<!DOCTYPE html>
<script src="/static/jquery/jquery-latest.min.js"></script>
<script src="/static/semantic/semantic.min.js"></script>
<script src="/static/jquery/tablesort.js"></script>
<link rel="stylesheet" href="/static/semantic/semantic.min.css">
<link rel="apple-touch-icon" sizes="120x120" href="/static/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/static/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/static/favicon-16x16.png">
<link rel="manifest" href="/static/manifest.json">
<link rel="mask-icon" href="/static/safari-pinned-tab.svg" color="#5bbad5">
<link rel="shortcut icon" href="/static/favicon.ico">
<meta name="msapplication-config" content="/static/browserconfig.xml">
<meta name="theme-color" content="#ffffff">
<title>Wanted - Bazarr</title>
<style>
body {
background-color: #272727;
}
#divmenu {
background-color: #272727;
opacity: 0.9;
padding-top: 2em;
padding-bottom: 1em;
padding-left: 1em;
padding-right: 128px;
}
#fondblanc {
background-color: #ffffff;
border-radius: 0px;
box-shadow: 0px 0px 5px 5px #ffffff;
margin-top: 32px;
margin-bottom: 3em;
padding: 3em;
}
.fast.backward, .backward, .forward, .fast.forward {
cursor: pointer;
}
</style>
</head>
<body>
%import ast
%import pycountry
<div id='loader' class="ui page dimmer">
<div class="ui indeterminate text loader">Loading...</div>
</div>
<div id="divmenu" class="ui container">
<div style="background-color:#272727;" class="ui inverted borderless labeled icon huge menu five item">
<a href="/"><img style="margin-right:32px;" class="logo" src="/static/logo128.png"></a>
<div style="height:80px;" class="ui container">
<a class="item" href="/">
<i class="play icon"></i>
Series
</a>
<a class="item" href="/history">
<i class="wait icon"></i>
History
</a>
<a class="item" href="/wanted">
<i class="warning sign icon"></i>
Wanted
</a>
<a class="item" href="/settings">
<i class="settings icon"></i>
Settings
</a>
<a class="item" href="/system">
<i class="laptop icon"></i>
System
</a>
</div>
</div>
</div>
<div id="fondblanc" class="ui container">
<table id="tablehistory" class="ui very basic selectable table">
<thead>
<tr>
<th>Series</th>
<th>Episode</th>
<th>Episode Title</th>
<th>Missing subtitles</th>
</tr>
</thead>
<tbody>
%import time
%import pretty
%for row in rows:
<tr class="selectable">
<td><a href="/episodes/{{row[4]}}">{{row[0]}}</a></td>
<td class="collapsing">
<%episode = row[1].split('x')%>
{{episode[0] + 'x' + episode[1].zfill(2)}}
</td>
<td>{{row[2]}}</td>
<td>
%missing_languages = ast.literal_eval(row[3])
%if missing_languages is not None:
%for language in missing_languages:
<a href="/get_subtitle?episodePath={{row[5]}}&language={{pycountry.languages.lookup(str(language)).alpha_3}}&hi={{row[6]}}&sonarrSeriesId={{row[4]}}&sonarrEpisodeId={{row[7]}}" class="ui tiny label">
{{language}}
<i style="margin-left:3px; margin-right:0px" class="search icon"></i>
</a>
%end
%end
</td>
</tr>
%end
</tbody>
</table>
<div class="ui grid">
<div class="three column row">
<div class="column"></div>
<div class="center aligned column">
<i class="\\
%if page == "1":
disabled\\
%end
fast backward icon"></i>
<i class="\\
%if page == "1":
disabled\\
%end
backward icon"></i>
{{page}} / {{max_page}}
<i class="\\
%if int(page) == int(max_page):
disabled\\
%end
forward icon"></i>
<i class="\\
%if int(page) == int(max_page):
disabled\\
%end
fast forward icon"></i>
</div>
<div class="right floated right aligned column">Total records: {{missing_count}}</div>
</div>
</div>
</div>
</body>
</html>
<script>
$('a').click(function(){
$('#loader').addClass('active');
})
$('.fast.backward').click(function(){
location.href="?page=1";
})
$('.backward').click(function(){
location.href="?page={{int(page)-1}}";
})
$('.forward').click(function(){
location.href="?page={{int(page)+1}}";
})
$('.fast.forward').click(function(){
location.href="?page={{int(max_page)}}";
})
</script>
Loading…
Cancel
Save