jellyfin-lookin theme, changes from master, bump to 0.3.0
Now uses a customized bootstrap that looks something like Jellyfin. Some small ui changes were needed. This be overridden by downloading bs5's css and using the custom_css option if you don't like it. sass file is included for your own modification. Changes made to master have been added also.
This commit is contained in:
parent
81bb2520ad
commit
ade935da4e
|
@ -11,7 +11,8 @@ pw-reset/
|
||||||
jfa/
|
jfa/
|
||||||
colors.txt
|
colors.txt
|
||||||
theme.css
|
theme.css
|
||||||
jellyfin_accounts/data/static/bootstrap-jf.css
|
jellyfin_accounts/__pycache__/
|
||||||
old/
|
old/
|
||||||
.jf-accounts/
|
.jf-accounts/
|
||||||
requirements.txt
|
requirements.txt
|
||||||
|
package-lock.json
|
||||||
|
|
|
@ -15,7 +15,7 @@ A basic account management system for [Jellyfin](https://github.com/jellyfin/jel
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://raw.githubusercontent.com/hrfee/jellyfin-accounts/master/images/jfa.gif" width="48%" style="margin-right: 1.5%;" alt="Admin page"></img>
|
<img src="https://raw.githubusercontent.com/hrfee/jellyfin-accounts/master/images/admin.png" width="48%" style="margin-right: 1.5%;" alt="Admin page"></img>
|
||||||
<img src="https://raw.githubusercontent.com/hrfee/jellyfin-accounts/master/images/create.png" width="48%" style="margin-left: 1.5%;" alt="Account creation page"></img>
|
<img src="https://raw.githubusercontent.com/hrfee/jellyfin-accounts/master/images/create.png" width="48%" style="margin-left: 1.5%;" alt="Account creation page"></img>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
__version__ = "0.2.6"
|
__version__ = "0.3.0"
|
||||||
|
|
||||||
import secrets
|
import secrets
|
||||||
import configparser
|
import configparser
|
||||||
|
@ -35,6 +35,9 @@ parser.add_argument(
|
||||||
),
|
),
|
||||||
action="store_true",
|
action="store_true",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-i", "--install", help="attempt to install a system service.", action="store_true"
|
||||||
|
)
|
||||||
|
|
||||||
args, leftovers = parser.parse_known_args()
|
args, leftovers = parser.parse_known_args()
|
||||||
|
|
||||||
|
@ -70,10 +73,11 @@ else:
|
||||||
temp_config = configparser.RawConfigParser()
|
temp_config = configparser.RawConfigParser()
|
||||||
temp_config.read(config_path)
|
temp_config.read(config_path)
|
||||||
|
|
||||||
|
|
||||||
def create_log(name):
|
def create_log(name):
|
||||||
log = logging.getLogger(name)
|
log = logging.getLogger(name)
|
||||||
handler = logging.StreamHandler(sys.stdout)
|
handler = logging.StreamHandler(sys.stdout)
|
||||||
if temp_config.getboolean('ui', 'debug'):
|
if temp_config.getboolean("ui", "debug"):
|
||||||
log.setLevel(logging.DEBUG)
|
log.setLevel(logging.DEBUG)
|
||||||
handler.setLevel(logging.DEBUG)
|
handler.setLevel(logging.DEBUG)
|
||||||
else:
|
else:
|
||||||
|
@ -89,6 +93,7 @@ def create_log(name):
|
||||||
|
|
||||||
log = create_log("main")
|
log = create_log("main")
|
||||||
|
|
||||||
|
|
||||||
def load_config(config_path, data_dir):
|
def load_config(config_path, data_dir):
|
||||||
config = configparser.RawConfigParser()
|
config = configparser.RawConfigParser()
|
||||||
config.read(config_path)
|
config.read(config_path)
|
||||||
|
@ -139,6 +144,7 @@ def load_config(config_path, data_dir):
|
||||||
config["jellyfin"]["public_server"] = config["jellyfin"]["server"]
|
config["jellyfin"]["public_server"] = config["jellyfin"]["server"]
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
config = load_config(config_path, data_dir)
|
config = load_config(config_path, data_dir)
|
||||||
|
|
||||||
web_log = create_log("waitress")
|
web_log = create_log("waitress")
|
||||||
|
@ -180,38 +186,20 @@ data_store = JSONStorage(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def default_css():
|
css_file = "bs5-jf.css"
|
||||||
css = {}
|
|
||||||
css[
|
|
||||||
"href"
|
|
||||||
] = "https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
|
|
||||||
css[
|
|
||||||
"integrity"
|
|
||||||
] = "sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
|
|
||||||
css["crossorigin"] = "anonymous"
|
|
||||||
return css
|
|
||||||
|
|
||||||
|
|
||||||
css = {}
|
|
||||||
css = default_css()
|
|
||||||
if "custom_css" in config["files"]:
|
if "custom_css" in config["files"]:
|
||||||
if config["files"]["custom_css"] != "":
|
if config["files"]["custom_css"] != "":
|
||||||
try:
|
try:
|
||||||
shutil.copy(
|
css_path = Path(config["files"]["custom_css"])
|
||||||
config["files"]["custom_css"], (local_dir / "static" / "bootstrap.css")
|
shutil.copy(css_path, (local_dir / "static" / css_path.name))
|
||||||
)
|
log.debug('Loaded custom CSS "{css_path.name}"')
|
||||||
log.debug("Loaded custom CSS")
|
css_file = css_path.name
|
||||||
css["href"] = "/bootstrap.css"
|
|
||||||
css["integrity"] = ""
|
|
||||||
css["crossorigin"] = ""
|
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
log.error(
|
log.error(
|
||||||
f'Custom CSS {config["files"]["custom_css"]} not found, using default.'
|
f'Custom CSS {config["files"]["custom_css"]} not found, using default.'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def resp(success=True, code=500):
|
def resp(success=True, code=500):
|
||||||
if success:
|
if success:
|
||||||
r = jsonify({"success": True})
|
r = jsonify({"success": True})
|
||||||
|
@ -226,7 +214,29 @@ def resp(success=True, code=500):
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
if args.get_defaults:
|
if args.install:
|
||||||
|
executable = sys.argv[0]
|
||||||
|
print(f'Assuming executable path "{executable}".')
|
||||||
|
options = ["systemd"]
|
||||||
|
for i, opt in enumerate(options):
|
||||||
|
print(f"{i+1}: {opt}")
|
||||||
|
success = False
|
||||||
|
while not success:
|
||||||
|
try:
|
||||||
|
method = options[int(input(">: ")) - 1]
|
||||||
|
success = True
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
if method == "systemd":
|
||||||
|
with open(local_dir / "services" / "jf-accounts.service", "r") as f:
|
||||||
|
data = f.read()
|
||||||
|
data = data.replace("{executable}", executable)
|
||||||
|
service_path = str(Path("jf-accounts.service").resolve())
|
||||||
|
with open(service_path, "w") as f:
|
||||||
|
f.write(data)
|
||||||
|
print(f"service written to the current directory\n({service_path}).")
|
||||||
|
print("Place this in the appropriate directory, and reload daemons.")
|
||||||
|
elif args.get_defaults:
|
||||||
import json
|
import json
|
||||||
from jellyfin_accounts.jf_api import Jellyfin
|
from jellyfin_accounts.jf_api import Jellyfin
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
[Unit]
|
||||||
|
Description=A basic account management system for Jellyfin.
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart={executable}
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
|
@ -44,7 +44,7 @@ function addItem(invite) {
|
||||||
// listCode.appendChild(document.createTextNode(" "));
|
// listCode.appendChild(document.createTextNode(" "));
|
||||||
var codeCopy = document.createElement('i');
|
var codeCopy = document.createElement('i');
|
||||||
codeCopy.onclick = function(){toClipboard(inviteCode)};
|
codeCopy.onclick = function(){toClipboard(inviteCode)};
|
||||||
codeCopy.classList.add('fa', 'fa-clipboard');
|
codeCopy.classList.add('fa', 'fa-clipboard', 'icon-button');
|
||||||
listCode.appendChild(codeCopy);
|
listCode.appendChild(codeCopy);
|
||||||
if (typeof(invite[3]) != 'undefined') {
|
if (typeof(invite[3]) != 'undefined') {
|
||||||
var sentTo = document.createElement('span');
|
var sentTo = document.createElement('span');
|
||||||
|
@ -145,7 +145,7 @@ function addOptions(le, sel) {
|
||||||
function toClipboard(str) {
|
function toClipboard(str) {
|
||||||
const el = document.createElement('textarea');
|
const el = document.createElement('textarea');
|
||||||
el.value = str;
|
el.value = str;
|
||||||
el.setAttribute('readonly', '');
|
el.setAttribute('readOnly', '');
|
||||||
el.style.position = 'absolute';
|
el.style.position = 'absolute';
|
||||||
el.style.left = '-9999px';
|
el.style.left = '-9999px';
|
||||||
document.body.appendChild(el);
|
document.body.appendChild(el);
|
||||||
|
@ -392,30 +392,45 @@ document.getElementById('openUsers').onclick = function () {
|
||||||
var users = req.response['users'];
|
var users = req.response['users'];
|
||||||
for (var i = 0; i < users.length; i++) {
|
for (var i = 0; i < users.length; i++) {
|
||||||
var user = users[i]
|
var user = users[i]
|
||||||
var entry = document.createElement('p');
|
var entry = document.createElement('div');
|
||||||
|
entry.classList.add('form-group', 'list-group-item', 'py-1');
|
||||||
entry.id = 'user_' + user['name'];
|
entry.id = 'user_' + user['name'];
|
||||||
entry.appendChild(document.createTextNode(user['name']));
|
var label = document.createElement('label');
|
||||||
var address = document.createElement('span');
|
label.classList.add('d-inline-block');
|
||||||
address.setAttribute('style', 'margin-left: 2%; margin-right: 2%; color: grey;');
|
label.setAttribute('for', 'address_' + user['email']);
|
||||||
|
label.appendChild(document.createTextNode(user['name']));
|
||||||
|
entry.appendChild(label);
|
||||||
|
var address = document.createElement('input');
|
||||||
|
address.setAttribute('type', 'email');
|
||||||
|
address.readOnly = true;
|
||||||
|
address.classList.add('form-control-plaintext', 'text-muted', 'd-inline-block');
|
||||||
|
//address.setAttribute('style', 'margin-left: 2%; margin-right: 2%; color: grey;');
|
||||||
address.classList.add('addressText');
|
address.classList.add('addressText');
|
||||||
address.id = 'address_' + user['email'];
|
address.id = 'address_' + user['email'];
|
||||||
if (typeof(user['email']) != 'undefined') {
|
if (typeof(user['email']) != 'undefined') {
|
||||||
address.appendChild(document.createTextNode(user['email']));
|
address.value = user['email'];
|
||||||
|
address.setAttribute('style', 'width: auto; margin-left: 2%;');
|
||||||
};
|
};
|
||||||
var editButton = document.createElement('i');
|
var editButton = document.createElement('i');
|
||||||
editButton.classList.add('fa', 'fa-edit');
|
editButton.classList.add('fa', 'fa-edit', 'd-inline-block', 'icon-button');
|
||||||
|
editButton.setAttribute('style', 'margin-left: 2%;');
|
||||||
editButton.onclick = function() {
|
editButton.onclick = function() {
|
||||||
this.classList.remove('fa', 'fa-edit');
|
this.classList.remove('fa', 'fa-edit');
|
||||||
var input = document.createElement('input');
|
// var input = document.createElement('input');
|
||||||
input.setAttribute('type', 'email');
|
// input.setAttribute('type', 'email');
|
||||||
input.setAttribute('style', 'margin-left: 2%; color: grey;');
|
// input.classList.add('email-input');
|
||||||
var addressElement = this.parentNode.getElementsByClassName('addressText')[0];
|
//var addressElement = this.parentNode.getElementsByClassName('addressText')[0];
|
||||||
if (addressElement.textContent != '') {
|
var addressElement = this.parentNode.getElementsByClassName('form-control-plaintext')[0];
|
||||||
input.value = addressElement.textContent;
|
addressElement.classList.remove('form-control-plaintext', 'text-muted');
|
||||||
} else {
|
addressElement.classList.add('form-control');
|
||||||
input.placeholder = 'Email Address';
|
addressElement.readOnly = false;
|
||||||
|
if (addressElement.value == '') {
|
||||||
|
// input.value = addressElement.textContent;
|
||||||
|
// } else {
|
||||||
|
addressElement.placeholder = 'Email Address';
|
||||||
|
address.setAttribute('style', 'width: auto; margin-left: 2%;');
|
||||||
};
|
};
|
||||||
this.parentNode.replaceChild(input, addressElement);
|
// this.parentNode.replaceChild(input, addressElement);
|
||||||
if (document.getElementById('saveUsers') == null) {
|
if (document.getElementById('saveUsers') == null) {
|
||||||
var footer = document.getElementById('userFooter')
|
var footer = document.getElementById('userFooter')
|
||||||
var saveUsers = document.createElement('input');
|
var saveUsers = document.createElement('input');
|
||||||
|
@ -451,8 +466,8 @@ document.getElementById('openUsers').onclick = function () {
|
||||||
footer.appendChild(saveUsers);
|
footer.appendChild(saveUsers);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
entry.appendChild(address);
|
|
||||||
entry.appendChild(editButton);
|
entry.appendChild(editButton);
|
||||||
|
entry.appendChild(address);
|
||||||
list.appendChild(entry);
|
list.appendChild(entry);
|
||||||
};
|
};
|
||||||
var button = document.getElementById('openUsers');
|
var button = document.getElementById('openUsers');
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,35 +1,34 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
|
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
|
||||||
<link rel="manifest" href="/site.webmanifest">
|
<link rel="manifest" href="/site.webmanifest">
|
||||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">
|
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">
|
||||||
<meta name="msapplication-TileColor" content="#603cba">
|
<meta name="msapplication-TileColor" content="#603cba">
|
||||||
<meta name="theme-color" content="#ffffff">
|
<meta name="theme-color" content="#ffffff">
|
||||||
|
|
||||||
<title>404</title>
|
<title>404</title>
|
||||||
<link rel="stylesheet" href="{{ css_href }}" integrity="{{ css_integrity }}" crossorigin="{{ css_crossorigin }}">
|
<link rel="stylesheet" type="text/css" href="{{ css_file }}">
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
|
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha1/js/bootstrap.min.js" integrity="sha384-oesi62hOLfzrys4LxRF63OJCXdXDipiYWBnvTl9Y9/TRlw5xlKIEHpNyvvDShgf/" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
|
<style>
|
||||||
<style>
|
|
||||||
.messageBox {
|
.messageBox {
|
||||||
margin: 20%;
|
margin: 20%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="messageBox">
|
<div class="messageBox">
|
||||||
<h1>Page not found.</h1>
|
<h1>Page not found.</h1>
|
||||||
<p>
|
<p>
|
||||||
{{ contactMessage }}
|
{{ contactMessage }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<meta name="theme-color" content="#ffffff">
|
<meta name="theme-color" content="#ffffff">
|
||||||
|
|
||||||
<!-- Bootstrap CSS -->
|
<!-- Bootstrap CSS -->
|
||||||
<link rel="stylesheet" type="text/css" href="bs5-jf.css">
|
<link rel="stylesheet" type="text/css" href="{{ css_file }}">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
|
||||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha1/js/bootstrap.min.js" integrity="sha384-oesi62hOLfzrys4LxRF63OJCXdXDipiYWBnvTl9Y9/TRlw5xlKIEHpNyvvDShgf/" crossorigin="anonymous"></script>
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha1/js/bootstrap.min.js" integrity="sha384-oesi62hOLfzrys4LxRF63OJCXdXDipiYWBnvTl9Y9/TRlw5xlKIEHpNyvvDShgf/" crossorigin="anonymous"></script>
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||||
|
@ -46,12 +46,6 @@
|
||||||
margin-top: 5%;
|
margin-top: 5%;
|
||||||
color: grey;
|
color: grey;
|
||||||
}
|
}
|
||||||
.fa-clipboard {
|
|
||||||
color: grey;
|
|
||||||
}
|
|
||||||
.fa-clipboard:hover {
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
<title>Admin</title>
|
<title>Admin</title>
|
||||||
</head>
|
</head>
|
||||||
|
@ -173,13 +167,13 @@
|
||||||
<button type="button" class="btn btn-secondary" id="openSettings">
|
<button type="button" class="btn btn-secondary" id="openSettings">
|
||||||
Settings <i class="fa fa-cog"></i>
|
Settings <i class="fa fa-cog"></i>
|
||||||
</button>
|
</button>
|
||||||
<div class="card bg-light mb-3 linkGroup">
|
<div class="card mb-3 linkGroup">
|
||||||
<div class="card-header">Current Invites</div>
|
<div class="card-header">Current Invites</div>
|
||||||
<ul class="list-group list-group-flush" id="invites">
|
<ul class="list-group list-group-flush" id="invites">
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="linkForm">
|
<div class="linkForm">
|
||||||
<div class="card bg-light mb-3">
|
<div class="card mb-3">
|
||||||
<div class="card-header">Generate Invite</div>
|
<div class="card-header">Generate Invite</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form action="#" method="POST" id="inviteForm">
|
<form action="#" method="POST" id="inviteForm">
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<meta name="theme-color" content="#ffffff">
|
<meta name="theme-color" content="#ffffff">
|
||||||
|
|
||||||
<!-- Bootstrap CSS -->
|
<!-- Bootstrap CSS -->
|
||||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha1/css/bootstrap.min.css" integrity="sha384-r4NyP46KrjDleawBgD5tp8Y7UzmLA05oM1iAEQ17CSuDqnUK2+k9luXQOfXJCJ4I" crossorigin="anonymous">
|
<link rel="stylesheet" type="text/css" href="{{ css_file }}">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
|
||||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha1/js/bootstrap.min.js" integrity="sha384-oesi62hOLfzrys4LxRF63OJCXdXDipiYWBnvTl9Y9/TRlw5xlKIEHpNyvvDShgf/" crossorigin="anonymous"></script>
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha1/js/bootstrap.min.js" integrity="sha384-oesi62hOLfzrys4LxRF63OJCXdXDipiYWBnvTl9Y9/TRlw5xlKIEHpNyvvDShgf/" crossorigin="anonymous"></script>
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||||
|
@ -61,7 +61,7 @@
|
||||||
<div class="container" id="container">
|
<div class="container" id="container">
|
||||||
<div class="row" id="cardContainer">
|
<div class="row" id="cardContainer">
|
||||||
<div class="col-sm">
|
<div class="col-sm">
|
||||||
<div class="card bg-light mb-3">
|
<div class="card mb-3">
|
||||||
<div class="card-header">Details</div>
|
<div class="card-header">Details</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form action="#" method="POST" id="accountForm">
|
<form action="#" method="POST" id="accountForm">
|
||||||
|
@ -90,7 +90,7 @@
|
||||||
</div>
|
</div>
|
||||||
{% if validate %}
|
{% if validate %}
|
||||||
<div class="col-sm" id="requirementBox">
|
<div class="col-sm" id="requirementBox">
|
||||||
<div class="card bg-light mb-3 requirementBox">
|
<div class="card mb-3 requirementBox">
|
||||||
<div class="card-header">Password Requirements</div>
|
<div class="card-header">Password Requirements</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<title>Invalid Code</title>
|
<title>Invalid Code</title>
|
||||||
<link rel="stylesheet" href="{{ css_href }}" integrity="{{ css_integrity }}" crossorigin="{{ css_crossorigin }}">
|
<!-- Bootstrap CSS -->
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
|
<link rel="stylesheet" type="text/css" href="{{ css_file }}">
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha1/js/bootstrap.min.js" integrity="sha384-oesi62hOLfzrys4LxRF63OJCXdXDipiYWBnvTl9Y9/TRlw5xlKIEHpNyvvDShgf/" crossorigin="anonymous"></script>
|
||||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||||
<style>
|
<style>
|
||||||
.messageBox {
|
.messageBox {
|
||||||
margin: 20%;
|
margin: 20%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="messageBox">
|
<div class="messageBox">
|
||||||
<h1>Invalid Code.</h1>
|
<h1>Invalid Code.</h1>
|
||||||
<p>The above code is either incorrect, or has expired.</p>
|
<p>The above code is either incorrect, or has expired.</p>
|
||||||
<p>{{ contactMessage }}</p>
|
<p>{{ contactMessage }}</p>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from flask import Flask, send_from_directory, render_template
|
from flask import Flask, send_from_directory, render_template
|
||||||
|
|
||||||
from jellyfin_accounts import app, g, css, data_store
|
from jellyfin_accounts import app, g, css_file, data_store
|
||||||
from jellyfin_accounts import web_log as log
|
from jellyfin_accounts import web_log as log
|
||||||
from jellyfin_accounts.web_api import config, checkInvite, validator
|
from jellyfin_accounts.web_api import config, checkInvite, validator
|
||||||
|
|
||||||
|
@ -11,9 +11,7 @@ def page_not_found(e):
|
||||||
return (
|
return (
|
||||||
render_template(
|
render_template(
|
||||||
"404.html",
|
"404.html",
|
||||||
css_href=css["href"],
|
css_file=css_file,
|
||||||
css_integrity=css["integrity"],
|
|
||||||
css_crossorigin=css["crossorigin"],
|
|
||||||
contactMessage=config["ui"]["contact_message"],
|
contactMessage=config["ui"]["contact_message"],
|
||||||
),
|
),
|
||||||
404,
|
404,
|
||||||
|
@ -25,9 +23,7 @@ def admin():
|
||||||
# return app.send_static_file('admin.html')
|
# return app.send_static_file('admin.html')
|
||||||
return render_template(
|
return render_template(
|
||||||
"admin.html",
|
"admin.html",
|
||||||
css_href=css["href"],
|
css_file=css_file,
|
||||||
css_integrity=css["integrity"],
|
|
||||||
css_crossorigin=css["crossorigin"],
|
|
||||||
contactMessage="",
|
contactMessage="",
|
||||||
email_enabled=config.getboolean("invite_emails", "enabled"),
|
email_enabled=config.getboolean("invite_emails", "enabled"),
|
||||||
)
|
)
|
||||||
|
@ -40,9 +36,7 @@ def static_proxy(path):
|
||||||
return (
|
return (
|
||||||
render_template(
|
render_template(
|
||||||
"404.html",
|
"404.html",
|
||||||
css_href=css["href"],
|
css_file=css_file,
|
||||||
css_integrity=css["integrity"],
|
|
||||||
css_crossorigin=css["crossorigin"],
|
|
||||||
contactMessage=config["ui"]["contact_message"],
|
contactMessage=config["ui"]["contact_message"],
|
||||||
),
|
),
|
||||||
404,
|
404,
|
||||||
|
@ -59,9 +53,7 @@ def inviteProxy(path):
|
||||||
email = ""
|
email = ""
|
||||||
return render_template(
|
return render_template(
|
||||||
"form.html",
|
"form.html",
|
||||||
css_href=css["href"],
|
css_file=css_file,
|
||||||
css_integrity=css["integrity"],
|
|
||||||
css_crossorigin=css["crossorigin"],
|
|
||||||
contactMessage=config["ui"]["contact_message"],
|
contactMessage=config["ui"]["contact_message"],
|
||||||
helpMessage=config["ui"]["help_message"],
|
helpMessage=config["ui"]["help_message"],
|
||||||
successMessage=config["ui"]["success_message"],
|
successMessage=config["ui"]["success_message"],
|
||||||
|
@ -77,8 +69,6 @@ def inviteProxy(path):
|
||||||
log.debug("Attempted use of invalid invite")
|
log.debug("Attempted use of invalid invite")
|
||||||
return render_template(
|
return render_template(
|
||||||
"invalidCode.html",
|
"invalidCode.html",
|
||||||
css_href=css["href"],
|
css_file=css_file,
|
||||||
css_integrity=css["integrity"],
|
|
||||||
css_crossorigin=css["crossorigin"],
|
|
||||||
contactMessage=config["ui"]["contact_message"],
|
contactMessage=config["ui"]["contact_message"],
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
[Unit]
|
||||||
|
Description=A basic account management system for Jellyfin.
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/home/hrfee/.cache/pypoetry/virtualenvs/jellyfin-accounts-r2jcKHws-py3.8/bin/jf-accounts
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "jellyfin-accounts"
|
name = "jellyfin-accounts"
|
||||||
version = "0.2.6"
|
version = "0.3.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
description = "A simple account management system for Jellyfin"
|
description = "A simple account management system for Jellyfin"
|
||||||
authors = ["Harvey Tindall <harveyltindall@gmail.com>"]
|
authors = ["Harvey Tindall <harveyltindall@gmail.com>"]
|
||||||
|
@ -9,7 +9,7 @@ homepage = "https://github.com/hrfee/jellyfin-accounts"
|
||||||
repository = "https://github.com/hrfee/jellyfin-accounts"
|
repository = "https://github.com/hrfee/jellyfin-accounts"
|
||||||
keywords = ["jellyfin", "jf-accounts"]
|
keywords = ["jellyfin", "jf-accounts"]
|
||||||
include = ["jellyfin_accounts/data/*"]
|
include = ["jellyfin_accounts/data/*"]
|
||||||
exclude = ["images/*"]
|
exclude = ["images/*", "scss/*"]
|
||||||
classifiers = [
|
classifiers = [
|
||||||
"Programming Language :: Python :: 3",
|
"Programming Language :: Python :: 3",
|
||||||
"License :: OSI Approved :: MIT License",
|
"License :: OSI Approved :: MIT License",
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
## SCSS
|
||||||
|
|
||||||
|
* `bs5-jf.scss` contains the source for the customizations to bootstrap. To customize the UI, you can make modifications to this file and then compile it.
|
||||||
|
|
||||||
|
Note: assumes that bootstrap is installed in `../node_modules/bootstrap` relative to itself.
|
||||||
|
* Compilation requires a sass compiler of your choice, and `postcss-cli`, `autoprefixer` + `clean-css-cli` from npm.
|
||||||
|
* If you're using `sassc`, run `./compile.sh bs5-jf.scss` in this directory. This will create a .css file, and minified .css file.
|
||||||
|
* For `node-sass`, replace the `sassc` line in `compile.sh` with
|
||||||
|
```
|
||||||
|
node-sass --output-style expanded --precision 6 $1 $css_file
|
||||||
|
```
|
||||||
|
and run as above.
|
||||||
|
* If you're building from source, copy the minified css to `<jf-accounts git directory>/jellyfin_accounts/data/static/bs5-jf.css`.
|
||||||
|
* If you're just customizing your install, set `custom_css` in your config as the path to your minified css.
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
119
scss/bs5-jf.scss
119
scss/bs5-jf.scss
|
@ -1,27 +1,40 @@
|
||||||
$jf-blue: rgb(0, 164, 220);
|
$jf-blue: rgb(0, 164, 220);
|
||||||
$jf-blue-hover: rgba(0, 164, 220, 0.2);
|
$jf-blue-hover: rgba(0, 164, 220, 0.2);
|
||||||
$jf-blue-focus: rgb(12, 176, 232);
|
$jf-blue-focus: rgb(12, 176, 232);
|
||||||
|
$jf-blue-light: #4bb3dd;
|
||||||
|
|
||||||
$jf-red: rgb(204, 0, 0);
|
$jf-red: rgb(204, 0, 0);
|
||||||
|
$jf-red-light: #e12026;
|
||||||
|
$jf-yellower: #ffc107;
|
||||||
|
$jf-yellow: #e1b222;
|
||||||
|
$jf-orange: #ff870f;
|
||||||
|
$jf-green: #6fbd45;
|
||||||
|
$jf-green-dark: #008040;
|
||||||
|
|
||||||
|
|
||||||
$jf-black: rgb(16, 16, 16);
|
$jf-black: #101010; // 16 16 16
|
||||||
$jf-gray-90: rgb(32, 32, 32);
|
$jf-gray-90: #202020; // 32 32 32
|
||||||
$jf-gray-80: rgb(36, 36, 36); // jf-card
|
$jf-gray-80: #242424; // jf-card 36 36 36
|
||||||
$jf-gray-70: rgb(41, 41, 41); // jf-input
|
$jf-gray-70: #292929; // jf-input 41 41 41
|
||||||
$jf-gray-60: rgb(48, 48, 48); // jf-button
|
$jf-gray-60: #303030; // jf-button 48 48 48
|
||||||
$jf-gray-50: rgb(56, 56, 56); // jf-button-focus
|
$jf-gray-50: #383838; // jf-button-focus 56 56 56
|
||||||
$jf-text-bold: rgba(255, 255, 255, 0.87);
|
$jf-text-bold: rgba(255, 255, 255, 0.87);
|
||||||
$jf-text-primary: rgba(255, 255, 255, 0.8);
|
$jf-text-primary: rgba(255, 255, 255, 0.8);
|
||||||
$jf-text-secondary: rgb(153, 153, 153);
|
$jf-text-secondary: rgb(153, 153, 153);
|
||||||
|
|
||||||
$theme-colors: (
|
$primary: $jf-blue;
|
||||||
"primary": $jf-blue,
|
$secondary: $jf-gray-50;
|
||||||
"secondary": $jf-gray-50,
|
$success: $jf-green-dark;
|
||||||
"success": $jf-blue-focus,
|
$danger: $jf-red-light;
|
||||||
"danger": $jf-red,
|
$light: $jf-text-primary;
|
||||||
"light": $jf-text-primary,
|
$dark: $jf-gray-90;
|
||||||
"dark": $jf-gray-90
|
$info: $jf-yellow;
|
||||||
);
|
$warning: $jf-yellower;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$enable-gradients: false;
|
||||||
|
$enable-shadows: false;
|
||||||
|
|
||||||
$enable-rounded: false;
|
$enable-rounded: false;
|
||||||
$body-bg: $jf-black;
|
$body-bg: $jf-black;
|
||||||
|
@ -32,11 +45,14 @@ $component-active-bg: $jf-blue-focus;
|
||||||
$text-muted: $jf-text-secondary;
|
$text-muted: $jf-text-secondary;
|
||||||
$link-color: $jf-blue-focus;
|
$link-color: $jf-blue-focus;
|
||||||
$btn-link-disabled-color: $jf-text-secondary;
|
$btn-link-disabled-color: $jf-text-secondary;
|
||||||
$input-bg: $jf-gray-70;
|
$input-bg: $jf-gray-90;
|
||||||
$input-color: $jf-text-primary;
|
$input-color: $jf-text-primary;
|
||||||
$input-focus-bg: $jf-gray-60;
|
$input-focus-bg: $jf-gray-60;
|
||||||
$input-focus-border-color: $jf-blue-focus;
|
$input-focus-border-color: $jf-blue-focus;
|
||||||
$input-disabled-bg: $jf-gray-90;
|
$input-disabled-bg: $jf-gray-70;
|
||||||
|
input:disabled {
|
||||||
|
color: $text-muted;
|
||||||
|
}
|
||||||
$input-border-color: $jf-gray-60;
|
$input-border-color: $jf-gray-60;
|
||||||
$input-placeholder-color: $text-muted;
|
$input-placeholder-color: $text-muted;
|
||||||
|
|
||||||
|
@ -45,7 +61,76 @@ $form-check-input-border: $jf-gray-50;
|
||||||
$form-check-input-checked-color: $jf-blue-focus;
|
$form-check-input-checked-color: $jf-blue-focus;
|
||||||
$form-check-input-checked-bg-color: $jf-blue-hover;
|
$form-check-input-checked-bg-color: $jf-blue-hover;
|
||||||
|
|
||||||
|
$input-group-addon-bg: $input-bg;
|
||||||
|
|
||||||
// roughly @ line 696
|
$form-select-disabled-color: $jf-text-secondary;
|
||||||
|
$form-select-disabled-bg: $input-disabled-bg;
|
||||||
|
$form-select-indicator-color: $jf-gray-50;
|
||||||
|
|
||||||
|
$card-bg: $jf-gray-80;
|
||||||
|
$card-border-color: null;
|
||||||
|
|
||||||
|
$tooltip-color: $jf-text-bold;
|
||||||
|
$tooltip-bg: $jf-gray-50;
|
||||||
|
|
||||||
|
$modal-content-bg: $jf-gray-80;
|
||||||
|
$modal-content-border-color: $jf-gray-50;
|
||||||
|
$modal-header-border-color: null;
|
||||||
|
$modal-footer-border-color: null;
|
||||||
|
|
||||||
|
$list-group-bg: $card-bg;
|
||||||
|
$list-group-border-color: $jf-gray-50;
|
||||||
|
$list-group-hover-bg: $jf-blue-hover;
|
||||||
|
$list-group-active-bg: $jf-blue-focus;
|
||||||
|
$list-group-action-color: $jf-text-primary;
|
||||||
|
$list-group-action-hover-color: $jf-text-bold;
|
||||||
|
$list-group-action-active-color: $jf-text-bold;
|
||||||
|
$list-group-action-active-bg: $jf-blue-focus;
|
||||||
|
|
||||||
|
// idk why but i had to put these above and below the import
|
||||||
|
.list-group-item-danger {
|
||||||
|
color: $jf-text-bold;
|
||||||
|
background-color: $danger;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-group-item-success {
|
||||||
|
color: $jf-text-bold;
|
||||||
|
background-color: $success;
|
||||||
|
}
|
||||||
|
|
||||||
@import "../node_modules/bootstrap/scss/bootstrap";
|
@import "../node_modules/bootstrap/scss/bootstrap";
|
||||||
|
|
||||||
|
.btn-primary, .btn-outline-primary:hover, .btn-outline-primary:active {
|
||||||
|
color: $jf-text-bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
color: $jf-text-secondary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close:hover, .close:active {
|
||||||
|
color: $jf-text-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-button {
|
||||||
|
color: $text-muted;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-button:hover {
|
||||||
|
color: $jf-text-bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-bright {
|
||||||
|
color: $jf-text-bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-group-item-danger {
|
||||||
|
color: $jf-text-bold;
|
||||||
|
background-color: $danger;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-group-item-success {
|
||||||
|
color: $jf-text-bold;
|
||||||
|
background-color: $success;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
#!/bin/bash
|
||||||
|
css_file=$(echo $1 | sed 's/scss/css/g')
|
||||||
|
min_file=$(echo $1 | sed 's/scss/min.css/g')
|
||||||
|
sassc -t expanded -p 6 $1 $css_file
|
||||||
|
echo "Compiled."
|
||||||
|
postcss $css_file --replace --use autoprefixer
|
||||||
|
echo "Prefixed."
|
||||||
|
echo "Written to $css_file."
|
||||||
|
cleancss --level 1 --format breakWith=lf --source-map --source-map-inline-sources --output $min_file $css_file
|
||||||
|
echo "Minified version written to $min_file."
|
Loading…
Reference in New Issue