@ -0,0 +1,144 @@
|
||||
from bottle import route, run, template, static_file, request, redirect
|
||||
import bottle
|
||||
bottle.debug(True)
|
||||
bottle.TEMPLATES.clear()
|
||||
|
||||
application = bottle.default_app()
|
||||
|
||||
from paste import httpserver
|
||||
|
||||
import sqlite3
|
||||
import itertools
|
||||
import operator
|
||||
import requests
|
||||
from PIL import Image
|
||||
from io import BytesIO
|
||||
from fdsend import send_file
|
||||
import urllib
|
||||
|
||||
from init_db import *
|
||||
from get_languages import *
|
||||
from get_general_settings import *
|
||||
from get_sonarr_settings import *
|
||||
from list_subtitles import *
|
||||
|
||||
@route('/static/:path#.+#', name='static')
|
||||
def static(path):
|
||||
return static_file(path, root='static')
|
||||
|
||||
@route('/image_proxy/<url:path>', method='GET')
|
||||
def image_proxy(url):
|
||||
img_pil = Image.open(BytesIO(requests.get(url_sonarr_short + '/' + url).content))
|
||||
img_buffer = BytesIO()
|
||||
img_pil.tobytes()
|
||||
img_pil.save(img_buffer, img_pil.format)
|
||||
img_buffer.seek(0)
|
||||
return send_file(img_buffer, ctype=img_pil.format)
|
||||
|
||||
@route('/')
|
||||
def series():
|
||||
db = sqlite3.connect('bazarr.db')
|
||||
db.create_function("path_substitution", 1, path_replace)
|
||||
c = db.cursor()
|
||||
c.execute("SELECT tvdbId, title, path_substitution(path), languages, hearing_impaired, sonarrSeriesId, poster FROM table_shows ORDER BY title")
|
||||
data = c.fetchall()
|
||||
c.execute("SELECT code2, name FROM table_settings_languages WHERE enabled = 1")
|
||||
languages = c.fetchall()
|
||||
c.close()
|
||||
output = template('series', rows=data, languages=languages, url_sonarr_short=url_sonarr_short)
|
||||
return output
|
||||
|
||||
@route('/edit_series/<no:int>', method='POST')
|
||||
def edit_series(no):
|
||||
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')
|
||||
|
||||
if hi == "on":
|
||||
hi = "True"
|
||||
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))
|
||||
conn.commit()
|
||||
c.close()
|
||||
|
||||
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 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) 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)):
|
||||
seasons_list.append(list(season))
|
||||
c.close()
|
||||
|
||||
return template('episodes', details=series_details, seasons=seasons_list, url_sonarr_short=url_sonarr_short)
|
||||
|
||||
@route('/history')
|
||||
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 LIMIT 15")
|
||||
data = c.fetchall()
|
||||
c.close()
|
||||
return template('history', rows=data)
|
||||
|
||||
@route('/settings')
|
||||
def settings():
|
||||
db = sqlite3.connect('bazarr.db')
|
||||
c = db.cursor()
|
||||
c.execute("SELECT * FROM table_settings_general")
|
||||
settings_general = c.fetchone()
|
||||
c.execute("SELECT * FROM table_settings_languages")
|
||||
settings_languages = c.fetchall()
|
||||
c.execute("SELECT * FROM table_settings_providers")
|
||||
settings_providers = c.fetchall()
|
||||
c.execute("SELECT * FROM table_settings_sonarr")
|
||||
settings_sonarr = c.fetchone()
|
||||
c.execute("SELECT * FROM table_settings_subliminal")
|
||||
settings_subliminal = c.fetchone()
|
||||
c.close()
|
||||
return template('settings', settings_general=settings_general, settings_languages=settings_languages, settings_providers=settings_providers, settings_sonarr=settings_sonarr, settings_subliminal=settings_subliminal)
|
||||
|
||||
@route('/system')
|
||||
def system():
|
||||
db = sqlite3.connect('bazarr.db')
|
||||
c = db.cursor()
|
||||
c.execute("SELECT * FROM table_scheduler")
|
||||
data = c.fetchall()
|
||||
c.close()
|
||||
return template('system', rows=data)
|
||||
|
||||
@route('/remove_subtitles', method='GET')
|
||||
def remove_subtitles():
|
||||
episodePath = request.GET.episodePath
|
||||
subtitlesPath = request.GET.subtitlesPath
|
||||
sonarrSeriesId = request.GET.sonarrSeriesId
|
||||
|
||||
try:
|
||||
os.remove(subtitlesPath)
|
||||
store_subtitles(episodePath)
|
||||
redirect('/episodes/' + sonarrSeriesId)
|
||||
except OSError:
|
||||
redirect('/episodes/' + sonarrSeriesId + '?error=1')
|
||||
|
||||
httpserver.serve(application, host=ip, port=port)
|
@ -0,0 +1,49 @@
|
||||
CREATE TABLE `table_shows` (
|
||||
`tvdbId` INTEGER NOT NULL PRIMARY KEY UNIQUE,
|
||||
`title` TEXT NOT NULL,
|
||||
`path` TEXT NOT NULL UNIQUE,
|
||||
`languages` TEXT,
|
||||
`hearing_impaired` TEXT
|
||||
);
|
||||
CREATE TABLE `table_settings_subliminal` (
|
||||
`age` TEXT,
|
||||
`max-workers` INTEGER
|
||||
);
|
||||
CREATE TABLE `table_settings_providers` (
|
||||
`name` TEXT NOT NULL UNIQUE,
|
||||
`username` TEXT,
|
||||
`password` TEXT,
|
||||
`enabled` INTEGER,
|
||||
PRIMARY KEY(`name`)
|
||||
);
|
||||
CREATE TABLE `table_settings_languages` (
|
||||
`code` TEXT NOT NULL UNIQUE,
|
||||
`name` TEXT NOT NULL,
|
||||
`enabled` INTEGER,
|
||||
PRIMARY KEY(`code`)
|
||||
);
|
||||
CREATE TABLE `table_settings_general` (
|
||||
`ip` TEXT NOT NULL,
|
||||
`port` INTEGER NOT NULL,
|
||||
`base_url` TEXT,
|
||||
`ssl` INTEGER
|
||||
);
|
||||
INSERT INTO `table_settings_general` (ip,port,base_url,ssl) VALUES ('0.0.0.0',6767,NULL,NULL);
|
||||
CREATE TABLE `table_settings_connect` (
|
||||
`ip` TEXT NOT NULL,
|
||||
`port` INTEGER NOT NULL,
|
||||
`base_url` TEXT,
|
||||
`ssl` INTEGER,
|
||||
`apikey` TEXT NOT NULL
|
||||
);
|
||||
CREATE TABLE `table_scheduler` (
|
||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||
`name` TEXT NOT NULL,
|
||||
`frequency` TEXT NOT NULL
|
||||
);
|
||||
CREATE TABLE `table_history` (
|
||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||
`timestamp` TEXT NOT NULL,
|
||||
`file` TEXT NOT NULL,
|
||||
`provider` TEXT NOT NULL
|
||||
);
|
@ -0,0 +1,43 @@
|
||||
import sqlite3
|
||||
import requests
|
||||
|
||||
# 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 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:
|
||||
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()
|
||||
|
||||
# Close database connection
|
||||
c.close()
|
||||
|
||||
|
@ -0,0 +1,48 @@
|
||||
import sqlite3
|
||||
import os
|
||||
import ast
|
||||
|
||||
# Open database connection
|
||||
db = sqlite3.connect('bazarr.db')
|
||||
c = db.cursor()
|
||||
|
||||
# Get general settings from database table
|
||||
c.execute("SELECT * FROM table_settings_general")
|
||||
general_settings = c.fetchone()
|
||||
|
||||
# Close database connection
|
||||
db.close()
|
||||
|
||||
ip = general_settings[0]
|
||||
port = general_settings[1]
|
||||
base_url = general_settings[2]
|
||||
ssl = general_settings[3]
|
||||
path_mappings = ast.literal_eval(general_settings[4])
|
||||
|
||||
def path_replace(path):
|
||||
for path_mapping in path_mappings:
|
||||
path = path.replace(path_mapping[0], path_mapping[1], 1)
|
||||
|
||||
if '\\' in path:
|
||||
path = path.replace('/', '\\')
|
||||
|
||||
return path
|
||||
|
||||
def path_replace_reverse(path):
|
||||
for path_mapping in path_mappings:
|
||||
if path.startswith('\\\\\\\\'):
|
||||
if '\\\\' in path:
|
||||
path = path.replace('\\\\', '\\')
|
||||
elif '\\' in path:
|
||||
path = path.replace('\\\\', '\\')
|
||||
|
||||
path = path.replace(path_mapping[1], path_mapping[0], 1)
|
||||
elif path.startswith('\\\\'):
|
||||
path = path.replace(path_mapping[1], path_mapping[0], 1)
|
||||
|
||||
if '\\' in path:
|
||||
path = path.replace('\\', '/')
|
||||
|
||||
return path
|
||||
|
||||
#print path_replace_reverse(r'\\\\serveur\\media\\Series TV\\Vikings\\Season 03\\Vikings.S03E01.720p.HDTV.x264-KILLERS.mkv')
|
@ -0,0 +1,20 @@
|
||||
import sqlite3
|
||||
import pycountry
|
||||
|
||||
# Get languages list in langs tuple
|
||||
langs = [[lang.alpha_3,lang.alpha_2,lang.name]
|
||||
for lang in pycountry.languages
|
||||
if hasattr(lang, 'alpha_2')]
|
||||
|
||||
# Open database connection
|
||||
db = sqlite3.connect('bazarr.db')
|
||||
c = db.cursor()
|
||||
|
||||
# Insert languages in database table
|
||||
c.executemany('''INSERT OR IGNORE INTO table_settings_languages(code3, code2, name) VALUES(?, ?, ?)''', langs)
|
||||
|
||||
# Commit changes to database table
|
||||
db.commit()
|
||||
|
||||
# Close database connection
|
||||
db.close()
|
@ -0,0 +1,41 @@
|
||||
import os
|
||||
import sqlite3
|
||||
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 = []
|
||||
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 = ""
|
||||
# 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))
|
||||
|
||||
# Commit changes to database table
|
||||
db.commit()
|
||||
|
||||
# Close database connection
|
||||
db.close()
|
@ -0,0 +1,33 @@
|
||||
import sqlite3
|
||||
import os
|
||||
import ast
|
||||
|
||||
# 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()
|
||||
|
||||
# Close database connection
|
||||
db.close()
|
||||
|
||||
# Build Sonarr URL
|
||||
ip_sonarr = config_sonarr[0]
|
||||
port_sonarr = str(config_sonarr[1])
|
||||
baseurl_sonarr = config_sonarr[2]
|
||||
ssl_sonarr = config_sonarr[3]
|
||||
apikey_sonarr = config_sonarr[4]
|
||||
|
||||
if ssl_sonarr == 1:
|
||||
protocol_sonarr = "https"
|
||||
else:
|
||||
protocol_sonarr = "http"
|
||||
if baseurl_sonarr == "":
|
||||
base_url_sonarr = ""
|
||||
else:
|
||||
base_url_sonarr = "/" + baseurl_sonarr.strip("/")
|
||||
|
||||
url_sonarr = protocol_sonarr + "://" + ip_sonarr + ":" + port_sonarr + base_url_sonarr
|
||||
url_sonarr_short = protocol_sonarr + "://" + ip_sonarr + ":" + port_sonarr
|
@ -0,0 +1,23 @@
|
||||
import os.path
|
||||
import sqlite3
|
||||
|
||||
# Check if database exist
|
||||
if os.path.exists('bazarr.db') == True:
|
||||
pass
|
||||
else:
|
||||
# Get SQL script from file
|
||||
fd = open('create_db.sql', 'r')
|
||||
script = fd.read()
|
||||
|
||||
# Open database connection
|
||||
db = sqlite3.connect('bazarr.db')
|
||||
c = db.cursor()
|
||||
|
||||
# Execute script and commit change to database
|
||||
c.executescript(script)
|
||||
|
||||
# Close database connection
|
||||
db.close()
|
||||
|
||||
# Close SQL script file
|
||||
fd.close()
|
@ -0,0 +1,73 @@
|
||||
import os
|
||||
import enzyme
|
||||
import babelfish
|
||||
import pycountry
|
||||
import sqlite3
|
||||
import ast
|
||||
|
||||
from get_general_settings import *
|
||||
|
||||
def store_subtitles(file):
|
||||
languages = []
|
||||
if os.path.exists(file):
|
||||
if os.path.splitext(file)[1] == '.mkv':
|
||||
try:
|
||||
with open(file, 'rb') as f:
|
||||
mkv = enzyme.MKV(f)
|
||||
|
||||
for subtitle_track in mkv.subtitle_tracks:
|
||||
try:
|
||||
languages.append([str(pycountry.languages.lookup(subtitle_track.language).alpha_2),None])
|
||||
except:
|
||||
pass
|
||||
except:
|
||||
pass
|
||||
|
||||
conn_db = sqlite3.connect('bazarr.db')
|
||||
c_db = conn_db.cursor()
|
||||
enabled_languages = c_db.execute("SELECT code2 FROM table_settings_languages WHERE enabled = 1").fetchall()
|
||||
|
||||
for language in enabled_languages:
|
||||
subtitle_path = os.path.splitext(file)[0] + "." + str(language[0]) + ".srt"
|
||||
if os.path.isfile(subtitle_path):
|
||||
languages.append([str(language[0]),str(path_replace_reverse(subtitle_path))])
|
||||
try:
|
||||
c_db.execute("UPDATE table_episodes SET subtitles = ? WHERE path = ?", (str(languages), path_replace_reverse(file)))
|
||||
conn_db.commit()
|
||||
except:
|
||||
pass
|
||||
c_db.close()
|
||||
|
||||
return languages
|
||||
|
||||
def list_missing_subtitles(file):
|
||||
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])
|
||||
|
||||
desired_subtitles = ast.literal_eval(desired_subtitles[0])
|
||||
|
||||
missing_subtitles = (list(set(desired_subtitles) - set(actual_subtitles)))
|
||||
return str(missing_subtitles)
|
||||
|
||||
def full_scan_subtitles():
|
||||
conn_db = sqlite3.connect('bazarr.db')
|
||||
c_db = conn_db.cursor()
|
||||
all_path = c_db.execute("SELECT path FROM table_episodes").fetchall()
|
||||
c_db.close()
|
||||
|
||||
for path in all_path:
|
||||
print store_subtitles(path_replace(path[0]))
|
||||
|
||||
#print list_missing_subtitles('/tv/Fear the Walking Dead/Season 3/Fear.The.Walking.Dead.S03E01.CONVERT.720p.WEB.h264-TBS[rarbg].mkv')
|
||||
#full_scan_subtitles()
|
After Width: | Height: | Size: 8.2 KiB |
After Width: | Height: | Size: 11 KiB |
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/static/mstile-150x150.png"/>
|
||||
<TileColor>#da532c</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 10 KiB |
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/static/android-chrome-96x96.png",
|
||||
"sizes": "96x96",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,44 @@
|
||||
import datetime
|
||||
|
||||
def pretty_date(time=False):
|
||||
"""
|
||||
Get a datetime object or a int() Epoch timestamp and return a
|
||||
pretty string like 'an hour ago', 'Yesterday', '3 months ago',
|
||||
'just now', etc
|
||||
"""
|
||||
from datetime import datetime
|
||||
now = datetime.now()
|
||||
if type(time) is int:
|
||||
diff = now - datetime.fromtimestamp(time)
|
||||
elif isinstance(time,datetime):
|
||||
diff = now - time
|
||||
elif not time:
|
||||
diff = now - now
|
||||
second_diff = diff.seconds
|
||||
day_diff = diff.days
|
||||
|
||||
if day_diff < 0:
|
||||
return ''
|
||||
|
||||
if day_diff == 0:
|
||||
if second_diff < 10:
|
||||
return "just now"
|
||||
if second_diff < 60:
|
||||
return str(second_diff) + " seconds ago"
|
||||
if second_diff < 120:
|
||||
return "a minute ago"
|
||||
if second_diff < 3600:
|
||||
return str(second_diff / 60) + " minutes ago"
|
||||
if second_diff < 7200:
|
||||
return "an hour ago"
|
||||
if second_diff < 86400:
|
||||
return str(second_diff / 3600) + " hours ago"
|
||||
if day_diff == 1:
|
||||
return "Yesterday"
|
||||
if day_diff < 7:
|
||||
return str(day_diff) + " days ago"
|
||||
if day_diff < 31:
|
||||
return str(day_diff / 7) + " weeks ago"
|
||||
if day_diff < 365:
|
||||
return str(day_diff / 30) + " months ago"
|
||||
return str(day_diff / 365) + " years ago"
|
@ -0,0 +1,200 @@
|
||||
<html>
|
||||
<head>
|
||||
<!DOCTYPE html>
|
||||
<script src="https://code.jquery.com/jquery-latest.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/semantic-ui/latest/semantic.min.js"></script>
|
||||
<script src="https://semantic-ui.com/javascript/library/tablesort.js"></script>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/semantic-ui/latest/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>{{details[0]}} - Bazarr</title>
|
||||
<style>
|
||||
body {
|
||||
background-color: #1b1c1d;
|
||||
background-image: url("/image_proxy/{{details[3]}}");
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
background-size: cover;
|
||||
background-position:center center;
|
||||
}
|
||||
#divmenu {
|
||||
background-color: #1b1c1d;
|
||||
opacity: 0.9;
|
||||
border-radius: 5px;
|
||||
padding-top: 2em;
|
||||
padding-bottom: 1em;
|
||||
padding-left: 1em;
|
||||
padding-right: 128px;
|
||||
}
|
||||
#divdetails {
|
||||
background-color: #000000;
|
||||
opacity: 0.9;
|
||||
color: #ffffff;
|
||||
margin-top: 6em;
|
||||
margin-bottom: 3em;
|
||||
padding: 2em;
|
||||
border-radius: 1px;
|
||||
box-shadow: 0px 0px 5px 5px #000000;
|
||||
min-height: calc(250px + 4em);
|
||||
}
|
||||
#fondblanc {
|
||||
background-color: #ffffff;
|
||||
opacity: 0.9;
|
||||
border-radius: 1px;
|
||||
box-shadow: 0px 0px 3px 3px #ffffff;
|
||||
margin-top: 32px;
|
||||
margin-bottom: 3em;
|
||||
padding-top: 2em;
|
||||
padding-left: 2em;
|
||||
padding-right: 2em;
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
$('.ui.accordion').accordion();
|
||||
var first_season_acc_title = document.getElementsByClassName("title")[0];
|
||||
first_season_acc_title.className += " active";
|
||||
var first_season_acc_content = document.getElementsByClassName("content")[0];
|
||||
first_season_acc_content.className += " active";
|
||||
});
|
||||
|
||||
$(window).on('beforeunload',function(){
|
||||
$('#loader').addClass('active');
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
%import ast
|
||||
<div style="display: none;"><img src="/image_proxy/{{details[3]}}"></div>
|
||||
<div id='loader' class="ui page dimmer">
|
||||
<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="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="/settings">
|
||||
<i class="settings icon"></i>
|
||||
Settings
|
||||
</a>
|
||||
<a class="item" href="/system">
|
||||
<i class="laptop icon"></i>
|
||||
System
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</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]}}">
|
||||
<h2>{{details[0]}}</h2>
|
||||
<p>{{details[1]}}</p>
|
||||
</div>
|
||||
|
||||
%if len(seasons) == 0:
|
||||
<div id="fondblanc" class="ui container">
|
||||
<h2 class="ui header">No episode file available for this series.</h2>
|
||||
</div>
|
||||
%else:
|
||||
%for season in seasons:
|
||||
<div id="fondblanc" class="ui container">
|
||||
<h1 class="ui header">Season {{season[0][2]}}</h1>
|
||||
<div class="ui accordion">
|
||||
<div class="title">
|
||||
<div class="ui one column stackable center aligned page grid">
|
||||
<div class="column twelve wide">
|
||||
<h3 class="ui header"><i class="dropdown icon"></i>
|
||||
Show/Hide Episodes</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<table class="ui very basic single line selectable table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="collapsing">Episode</th>
|
||||
<th>Title</th>
|
||||
<th class="collapsing">Existing subtitles</th>
|
||||
<th class="collapsing">Missing subtitles</th>
|
||||
<th class="no-sort"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
%for episode in season:
|
||||
<tr>
|
||||
<td>{{episode[3]}}</td>
|
||||
<td>{{episode[0]}}</td>
|
||||
<td>
|
||||
%actual_languages = ast.literal_eval(episode[4])
|
||||
%if actual_languages is not None:
|
||||
%for language in actual_languages:
|
||||
|
||||
%if language[1] is not None:
|
||||
<a href="/remove_subtitles?episodePath={{episode[1]}}&subtitlesPath={{language[1]}}&sonarrSeriesId={{episode[5]}}" class="ui tiny label">
|
||||
{{language[0]}}
|
||||
<i class="delete icon"></i>
|
||||
</a>
|
||||
%else:
|
||||
<div class="ui tiny label">
|
||||
{{language[0]}}
|
||||
</div>
|
||||
%end
|
||||
%end
|
||||
%end
|
||||
</td>
|
||||
<td>
|
||||
%missing_languages = ast.literal_eval(episode[6])
|
||||
%if missing_languages is not None:
|
||||
%for language in missing_languages:
|
||||
<div class="ui tiny label">
|
||||
{{language}}
|
||||
</div>
|
||||
%end
|
||||
%end
|
||||
</td>
|
||||
<td class="collapsing">
|
||||
%if len(missing_languages) > 0:
|
||||
<div class="ui inverted basic compact icon" data-tooltip="Download missing subtitles" data-inverted="" onclick="location.href='/edit_series/{{episode[3]}}';">
|
||||
<i class="ui black download icon"></i>
|
||||
</div>
|
||||
%end
|
||||
</td>
|
||||
</tr>
|
||||
%end
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
%end
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,124 @@
|
||||
<html>
|
||||
<head>
|
||||
<!DOCTYPE html>
|
||||
<script src="https://code.jquery.com/jquery-latest.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/semantic-ui/latest/semantic.min.js"></script>
|
||||
<script src="https://semantic-ui.com/javascript/library/tablesort.js"></script>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/semantic-ui/latest/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>History - 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;
|
||||
}
|
||||
#tablehistory {
|
||||
padding: 3em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<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 four 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="/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 sortable table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Series</th>
|
||||
<th>Episode</th>
|
||||
<th>Episode Title</th>
|
||||
<th>Date</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
%import time
|
||||
%from utils import *
|
||||
%for row in rows:
|
||||
<tr class="selectable">
|
||||
<td class="collapsing">
|
||||
%if row[0] == 0:
|
||||
<div class="ui inverted basic compact icon" data-tooltip="Subtitles file have been erased." data-inverted="">
|
||||
<i class="ui trash icon"></i>
|
||||
</div>
|
||||
%elif row[0] == 1:
|
||||
<div class="ui inverted basic compact icon" data-tooltip="Subtitles file have been downloaded." data-inverted="">
|
||||
<i class="ui download icon"></i>
|
||||
</div>
|
||||
%end
|
||||
</td>
|
||||
<td><a href="/episodes/{{row[6]}}">{{row[1]}}</a></td>
|
||||
<td class="collapsing">
|
||||
<%episode = row[2].split('x')%>
|
||||
{{episode[0] + 'x' + episode[1].zfill(2)}}
|
||||
</td>
|
||||
<td>{{row[3]}}</td>
|
||||
<td class="collapsing">
|
||||
<div class="ui inverted" data-tooltip="{{time.strftime('%A, %B %d %Y %H:%M', time.localtime(row[4]))}}" data-inverted="">
|
||||
{{pretty_date(row[4])}}
|
||||
</div>
|
||||
</td>
|
||||
<td>{{row[5]}}</td>
|
||||
</tr>
|
||||
%end
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
<script>
|
||||
$('a').click(function(){
|
||||
$('#loader').addClass('active');
|
||||
})
|
||||
</script>
|
@ -0,0 +1,225 @@
|
||||
<html>
|
||||
<head>
|
||||
<!DOCTYPE html>
|
||||
<script src="https://code.jquery.com/jquery-latest.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/semantic-ui/latest/semantic.min.js"></script>
|
||||
<script src="https://semantic-ui.com/javascript/library/tablesort.js"></script>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/semantic-ui/latest/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>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;
|
||||
}
|
||||
#tableseries {
|
||||
padding: 3em;
|
||||
}
|
||||
#divdetails {
|
||||
min-height: 250px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<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 four 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="/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="tableseries" class="ui very basic selectable sortable table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sorted ascending">Name</th>
|
||||
<th>Status</th>
|
||||
<th>Path</th>
|
||||
<th>Language</th>
|
||||
<th>Hearing-impaired</th>
|
||||
<th class="no-sort"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
%import ast
|
||||
%import os
|
||||
%for row in rows:
|
||||
<tr class="selectable">
|
||||
<td><a href="/episodes/{{row[5]}}">{{row[1]}}</a></td>
|
||||
<td>
|
||||
%if os.path.exists(row[2]):
|
||||
<div class="ui inverted basic compact icon" data-tooltip="Path exist" data-inverted="">
|
||||
<i class="ui checkmark icon"></i>
|
||||
</div>
|
||||
%else:
|
||||
<div class="ui inverted basic compact icon" data-tooltip="Path not found. Is your path substitution settings correct?" data-inverted="">
|
||||
<i class="ui warning sign icon"></i>
|
||||
</div>
|
||||
%end
|
||||
</td>
|
||||
<td>
|
||||
{{row[2]}}
|
||||
</td>
|
||||
<td>
|
||||
%subs_languages = ast.literal_eval(str(row[3]))
|
||||
%if subs_languages is not None:
|
||||
%for subs_language in subs_languages:
|
||||
<div class="ui tiny label">{{subs_language}}</div>
|
||||
%end
|
||||
%end
|
||||
</td>
|
||||
<td>{{row[4]}}</td>
|
||||
<td>
|
||||
<%
|
||||
subs_languages_list = []
|
||||
if subs_languages is not None:
|
||||
for subs_language in subs_languages:
|
||||
subs_languages_list.append(subs_language)
|
||||
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]}}">
|
||||
<i class="ui black configure icon"></i>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
%end
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="ui small modal">
|
||||
<div class="header">
|
||||
<div id="series_title"></div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<form name="series_form" id="series_form" action="" method="post" class="ui form">
|
||||
<div id="divdetails" class="ui grid">
|
||||
<div class="four wide column">
|
||||
<img id="series_poster" class="ui image" src="">
|
||||
</div>
|
||||
<div class="twelve wide column">
|
||||
<div class="ui grid">
|
||||
<div class="middle aligned row">
|
||||
<div class="right aligned five wide column">
|
||||
<label>Languages</label>
|
||||
</div>
|
||||
<div class="nine wide column">
|
||||
<select name="languages" id="series_languages" multiple="" class="ui fluid selection dropdown">
|
||||
<option value="">Languages</option>
|
||||
%for language in languages:
|
||||
<option value="{{language[0]}}">{{language[1]}}</option>
|
||||
%end
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="middle aligned row">
|
||||
<div class="right aligned five wide column">
|
||||
<label>Hearing-impaired</label>
|
||||
</div>
|
||||
<div class="nine wide column">
|
||||
<div id="series_hearing-impaired_div" class="ui toggle checkbox">
|
||||
<input name="hearing_impaired" id="series_hearing-impaired" type="checkbox">
|
||||
<label></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="ui cancel button" >Cancel</button>
|
||||
<button type="submit" name="save" value="save" form="series_form" class="ui blue approve button">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
<script>
|
||||
if (sessionStorage.scrolly) {
|
||||
$(window).scrollTop(sessionStorage.scrolly);
|
||||
sessionStorage.clear();
|
||||
}
|
||||
|
||||
$('table').tablesort();
|
||||
|
||||
$('a').click(function(){
|
||||
$('#loader').addClass('active');
|
||||
})
|
||||
|
||||
$('.modal')
|
||||
.modal({
|
||||
autofocus: false
|
||||
})
|
||||
;
|
||||
|
||||
$('.config').click(function(){
|
||||
sessionStorage.scrolly=$(window).scrollTop();
|
||||
|
||||
$('#series_form').attr('action', '/edit_series/' + $(this).data("tvdbid"));
|
||||
|
||||
$("#series_title").html($(this).data("title"));
|
||||
$("#series_poster").attr("src", "/image_proxy" + $(this).data("poster"));
|
||||
|
||||
$('#series_languages').dropdown('clear');
|
||||
var languages_array = eval($(this).data("languages"));
|
||||
$('#series_languages').dropdown('set selected',languages_array);
|
||||
|
||||
if ($(this).data("hearing-impaired") == "True") {
|
||||
$("#series_hearing-impaired_div").checkbox('check');
|
||||
} else {
|
||||
$("#series_hearing-impaired_div").checkbox('uncheck');
|
||||
}
|
||||
|
||||
$('.small.modal').modal('show');
|
||||
})
|
||||
|
||||
$('#series_languages').dropdown();
|
||||
|
||||
</script>
|
@ -0,0 +1,105 @@
|
||||
<html>
|
||||
<head>
|
||||
<!DOCTYPE html>
|
||||
<script src="https://code.jquery.com/jquery-latest.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/semantic-ui/latest/semantic.min.js"></script>
|
||||
<script src="https://semantic-ui.com/javascript/library/tablesort.js"></script>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/semantic-ui/latest/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>Settings - 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;
|
||||
padding: 1em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<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 four 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="/">
|
||||
<i class="play icon"></i>
|
||||
Series
|
||||
</a>
|
||||
<a class="menu item" href="/history">
|
||||
<i class="wait icon"></i>
|
||||
History
|
||||
</a>
|
||||
<a class="menu item" href="/settings">
|
||||
<i class="settings icon"></i>
|
||||
Settings
|
||||
</a>
|
||||
<a class="menu item" href="/system">
|
||||
<i class="laptop icon"></i>
|
||||
System
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="fondblanc" class="ui container">
|
||||
<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>
|
||||
<a class="item" data-tab="providers">Providers</a>
|
||||
<a class="item" data-tab="languages">Languages</a>
|
||||
</div>
|
||||
<div class="ui bottom attached tab segment active" data-tab="general">
|
||||
General
|
||||
</div>
|
||||
<div class="ui bottom attached tab segment" data-tab="sonarr">
|
||||
Sonarr
|
||||
</div>
|
||||
<div class="ui bottom attached tab segment" data-tab="subliminal">
|
||||
Subliminal
|
||||
</div>
|
||||
<div class="ui bottom attached tab segment" data-tab="providers">
|
||||
Providers
|
||||
</div>
|
||||
<div class="ui bottom attached tab segment" data-tab="languages">
|
||||
Languages
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
<script>
|
||||
$('.menu .item')
|
||||
.tab()
|
||||
;
|
||||
|
||||
$('a.menu').click(function(){
|
||||
$('#loader').addClass('active');
|
||||
})
|
||||
</script>
|
@ -0,0 +1,97 @@
|
||||
<html>
|
||||
<head>
|
||||
<!DOCTYPE html>
|
||||
<script src="https://code.jquery.com/jquery-latest.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/semantic-ui/latest/semantic.min.js"></script>
|
||||
<script src="https://semantic-ui.com/javascript/library/tablesort.js"></script>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/semantic-ui/latest/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>System - 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;
|
||||
padding: 1em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<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 four 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="/">
|
||||
<i class="play icon"></i>
|
||||
Series
|
||||
</a>
|
||||
<a class="menu item" href="/history">
|
||||
<i class="wait icon"></i>
|
||||
History
|
||||
</a>
|
||||
<a class="menu item" href="/settings">
|
||||
<i class="settings icon"></i>
|
||||
Settings
|
||||
</a>
|
||||
<a class="menu item" href="/system">
|
||||
<i class="laptop icon"></i>
|
||||
System
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="fondblanc" class="ui container">
|
||||
<div class="ui top attached tabular menu">
|
||||
<a class="item active" data-tab="tasks">Tasks</a>
|
||||
<a class="item" data-tab="logs">Logs</a>
|
||||
<a class="item" data-tab="about">About</a>
|
||||
</div>
|
||||
<div class="ui bottom attached tab segment active" data-tab="tasks">
|
||||
Tasks
|
||||
</div>
|
||||
<div class="ui bottom attached tab segment" data-tab="logs">
|
||||
Logs
|
||||
</div>
|
||||
<div class="ui bottom attached tab segment" data-tab="about">
|
||||
About
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
<script>
|
||||
$('.menu .item')
|
||||
.tab()
|
||||
;
|
||||
|
||||
$('a.menu').click(function(){
|
||||
$('#loader').addClass('active');
|
||||
})
|
||||
</script>
|