BS4 by default, BS5 optional
Bootstrap 4 w/ jQuery is used by default unless bs5 is enabled in settings/ui. bs4 also now has a jellyfin-style look.
This commit is contained in:
parent
ade935da4e
commit
adef32ef89
|
@ -1,13 +1,11 @@
|
||||||
# ![jellyfin-accounts](https://raw.githubusercontent.com/hrfee/jellyfin-accounts/master/images/jellyfin-accounts-banner-wide.svg)
|
# ![jellyfin-accounts](https://raw.githubusercontent.com/hrfee/jellyfin-accounts/master/images/jellyfin-accounts-banner-wide.svg)
|
||||||
|
|
||||||
**This branch uses the bootstrap 5 alpha, which works well enough for the most part, but can sometimes be a bit glitchy. Also no more jquery.**
|
|
||||||
|
|
||||||
A basic account management system for [Jellyfin](https://github.com/jellyfin/jellyfin).
|
A basic account management system for [Jellyfin](https://github.com/jellyfin/jellyfin).
|
||||||
* Provides a web interface for creating invite codes, and a simple account creation form
|
* Provides a web interface for creating invite codes, and a simple account creation form
|
||||||
* Sends out emails when a user requests a password reset
|
* Sends out emails when a user requests a password reset
|
||||||
* Uses a basic python jellyfin API client for communication with the server.
|
* Uses a basic python jellyfin API client for communication with the server.
|
||||||
* Uses [Flask](https://github.com/pallets/flask), [HTTPAuth](https://github.com/miguelgrinberg/Flask-HTTPAuth), [itsdangerous](https://github.com/pallets/itsdangerous), and [Waitress](https://github.com/Pylons/waitress)
|
* Uses [Flask](https://github.com/pallets/flask), [HTTPAuth](https://github.com/miguelgrinberg/Flask-HTTPAuth), [itsdangerous](https://github.com/pallets/itsdangerous), and [Waitress](https://github.com/Pylons/waitress)
|
||||||
* Frontend uses [Bootstrap 5](https://v5.getbootstrap.com)
|
* Frontend uses [Bootstrap](https://v5.getbootstrap.com)
|
||||||
* Password resets are handled using smtplib, requests, and [jinja](https://github.com/pallets/jinja)
|
* Password resets are handled using smtplib, requests, and [jinja](https://github.com/pallets/jinja)
|
||||||
## Interface
|
## Interface
|
||||||
<p align="center">
|
<p align="center">
|
||||||
|
|
|
@ -9,9 +9,9 @@ server = http://jellyfin.local:8096
|
||||||
public_server = https://jellyf.in:443
|
public_server = https://jellyf.in:443
|
||||||
; this and below settings will show on the jellyfin dashboard when the program connects. you may as well leave them alone.
|
; this and below settings will show on the jellyfin dashboard when the program connects. you may as well leave them alone.
|
||||||
client = jf-accounts
|
client = jf-accounts
|
||||||
version = 0.2.5
|
version = 0.3.0
|
||||||
device = jf-accounts
|
device = jf-accounts
|
||||||
device_id = jf-accounts-0.2.5
|
device_id = jf-accounts-0.3.0
|
||||||
|
|
||||||
[ui]
|
[ui]
|
||||||
; settings related to the ui and program functionality.
|
; settings related to the ui and program functionality.
|
||||||
|
@ -33,6 +33,8 @@ contact_message = Need help? contact me.
|
||||||
help_message = Enter your details to create an account.
|
help_message = Enter your details to create an account.
|
||||||
; displayed when a user creates an account
|
; displayed when a user creates an account
|
||||||
success_message = Your account has been created. Click below to continue to Jellyfin.
|
success_message = Your account has been created. Click below to continue to Jellyfin.
|
||||||
|
; use bootstrap 5 (currently in alpha). this also removes the need for jquery, so the page should load faster.
|
||||||
|
bs5 = false
|
||||||
|
|
||||||
[password_validation]
|
[password_validation]
|
||||||
; password validation (minimum length, etc.)
|
; password validation (minimum length, etc.)
|
||||||
|
|
|
@ -142,6 +142,11 @@ def load_config(config_path, data_dir):
|
||||||
or config["jellyfin"]["public_server"] == ""
|
or config["jellyfin"]["public_server"] == ""
|
||||||
):
|
):
|
||||||
config["jellyfin"]["public_server"] = config["jellyfin"]["server"]
|
config["jellyfin"]["public_server"] = config["jellyfin"]["server"]
|
||||||
|
if (
|
||||||
|
"bs5" not in config["ui"]
|
||||||
|
or config["ui"]["bs5"] == ""
|
||||||
|
):
|
||||||
|
config["ui"]["bs5"] = "false"
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
@ -185,8 +190,12 @@ data_store = JSONStorage(
|
||||||
config["files"]["user_configuration"],
|
config["files"]["user_configuration"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if config.getboolean("ui", "bs5"):
|
||||||
|
css_file = "bs5-jf.css"
|
||||||
|
log.debug('Using Bootstrap 5')
|
||||||
|
else:
|
||||||
|
css_file = "bs4-jf.css"
|
||||||
|
|
||||||
css_file = "bs5-jf.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:
|
||||||
|
|
|
@ -150,6 +150,14 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"value": "Your account has been created. Click below to continue to Jellyfin.",
|
"value": "Your account has been created. Click below to continue to Jellyfin.",
|
||||||
"description": "Displayed when a user creates an account"
|
"description": "Displayed when a user creates an account"
|
||||||
|
},
|
||||||
|
"bs5": {
|
||||||
|
"name": "Use Bootstrap 5",
|
||||||
|
"required": false,
|
||||||
|
"requires_restart": true,
|
||||||
|
"type": "bool",
|
||||||
|
"value": false,
|
||||||
|
"description": "Use Bootstrap 5 (currently in alpha). This also removes the need for jQuery, so the page should load faster."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"password_validation": {
|
"password_validation": {
|
||||||
|
|
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
File diff suppressed because one or more lines are too long
|
@ -14,8 +14,15 @@
|
||||||
|
|
||||||
<title>404</title>
|
<title>404</title>
|
||||||
<link rel="stylesheet" type="text/css" href="{{ css_file }}">
|
<link rel="stylesheet" type="text/css" href="{{ css_file }}">
|
||||||
|
{% if not bs5 %}
|
||||||
|
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
|
||||||
|
{% endif %}
|
||||||
<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>
|
||||||
|
{% if bs5 %}
|
||||||
<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>
|
||||||
|
{% else %}
|
||||||
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
|
||||||
|
{% endif %}
|
||||||
<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">
|
||||||
<style>
|
<style>
|
||||||
.messageBox {
|
.messageBox {
|
||||||
|
|
|
@ -12,11 +12,18 @@
|
||||||
<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">
|
||||||
|
|
||||||
<!-- Bootstrap CSS -->
|
<!-- Bootstrap CSS -->
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="{{ css_file }}">
|
<link rel="stylesheet" type="text/css" href="{{ css_file }}">
|
||||||
|
{% if not bs5 %}
|
||||||
|
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
|
||||||
|
{% endif %}
|
||||||
<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>
|
{% if bs5 %}
|
||||||
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha1/js/bootstrap.min.js" integrity="sha384-oesi62hOLfzrys4LxRF63OJCXdXDipiYWBnvTl9Y9/TRlw5xlKIEHpNyvvDShgf/" crossorigin="anonymous"></script>
|
||||||
|
{% else %}
|
||||||
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
|
||||||
|
{% endif %}
|
||||||
<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">
|
||||||
<style>
|
<style>
|
||||||
.pageContainer {
|
.pageContainer {
|
||||||
|
|
|
@ -1,8 +1,64 @@
|
||||||
var loginModal = new bootstrap.Modal(document.getElementById('login'));
|
var bsVersion = {{ bsVersion }};
|
||||||
var settingsModal = new bootstrap.Modal(document.getElementById('settingsMenu'));
|
|
||||||
var userDefaultsModal = new bootstrap.Modal(document.getElementById('userDefaults'));
|
if (bsVersion == 5) {
|
||||||
var usersModal = new bootstrap.Modal(document.getElementById('users'));
|
function createModal(id, find = false) {
|
||||||
var restartModal = new bootstrap.Modal(document.getElementById('restartModal'));
|
if (find) {
|
||||||
|
return bootstrap.Modal.getInstance(document.getElementById(modalId));
|
||||||
|
};
|
||||||
|
return new bootstrap.Modal(document.getElementById(id));
|
||||||
|
};
|
||||||
|
function triggerTooltips() {
|
||||||
|
document.getElementById('settingsMenu').addEventListener('shown.bs.modal', function() {
|
||||||
|
// Hack to ensure anything dependent on checkboxes are disabled if necessary
|
||||||
|
var checkboxes = document.getElementById('settingsMenu').querySelectorAll('input[type="checkbox"]');
|
||||||
|
for (var i = 0; i < checkboxes.length; i++) {
|
||||||
|
checkboxes[i].click();
|
||||||
|
checkboxes[i].click();
|
||||||
|
};
|
||||||
|
// Initialize tooltips
|
||||||
|
var to_trigger = [].slice.call(document.querySelectorAll('a[data-toggle="tooltip"]'));
|
||||||
|
var tooltips = to_trigger.map(function(el) {
|
||||||
|
return new bootstrap.Tooltip(el);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// var loginModal = new bootstrap.Modal(document.getElementById('login'));
|
||||||
|
// var settingsModal = new bootstrap.Modal(document.getElementById('settingsMenu'));
|
||||||
|
// var userDefaultsModal = new bootstrap.Modal(document.getElementById('userDefaults'));
|
||||||
|
// var usersModal = new bootstrap.Modal(document.getElementById('users'));
|
||||||
|
// var restartModal = new bootstrap.Modal(document.getElementById('restartModal'));
|
||||||
|
} else if (bsVersion == 4) {
|
||||||
|
document.getElementById('send_to_address_enabled').classList.remove('form-check-input');
|
||||||
|
function createModal(id, find = false) {
|
||||||
|
return {
|
||||||
|
show : function() {
|
||||||
|
return $('#' + id).modal('show');
|
||||||
|
},
|
||||||
|
hide : function() {
|
||||||
|
return $('#' + id).modal('hide');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
function triggerTooltips() {
|
||||||
|
$('#settingsMenu').on('shown.bs.modal', function() {
|
||||||
|
var checkboxes = document.getElementById('settingsMenu').querySelectorAll('input[type="checkbox"]');
|
||||||
|
for (var i = 0; i < checkboxes.length; i++) {
|
||||||
|
checkboxes[i].click();
|
||||||
|
checkboxes[i].click();
|
||||||
|
};
|
||||||
|
$("a[data-toggle='tooltip']").each(function (i, obj) {
|
||||||
|
$(obj).tooltip();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
var loginModal = createModal('login');
|
||||||
|
var settingsModal = createModal('settingsMenu');
|
||||||
|
var userDefaultsModal = createModal('userDefaults');
|
||||||
|
var usersModal = createModal('users');
|
||||||
|
var restartModal = createModal('restartModal');
|
||||||
|
|
||||||
function parseInvite(invite, empty = false) {
|
function parseInvite(invite, empty = false) {
|
||||||
if (empty === true) {
|
if (empty === true) {
|
||||||
|
@ -612,19 +668,7 @@ document.getElementById('openSettings').onclick = function () {
|
||||||
settingsModal.show();
|
settingsModal.show();
|
||||||
};
|
};
|
||||||
|
|
||||||
document.getElementById('settingsMenu').addEventListener('shown.bs.modal', function() {
|
triggerTooltips();
|
||||||
// Hack to ensure anything dependent on checkboxes are disabled if necessary
|
|
||||||
var checkboxes = document.getElementById('settingsMenu').querySelectorAll('input[type="checkbox"]');
|
|
||||||
for (var i = 0; i < checkboxes.length; i++) {
|
|
||||||
checkboxes[i].click();
|
|
||||||
checkboxes[i].click();
|
|
||||||
};
|
|
||||||
// Initialize tooltips
|
|
||||||
var to_trigger = [].slice.call(document.querySelectorAll('a[data-toggle="tooltip"]'));
|
|
||||||
var tooltips = to_trigger.map(function(el) {
|
|
||||||
return new bootstrap.Tooltip(el);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
//
|
//
|
||||||
// $('#settingsMenu').on('shown.bs.modal', function() {
|
// $('#settingsMenu').on('shown.bs.modal', function() {
|
||||||
// $("a[data-toggle='tooltip']").each(function (i, obj) {
|
// $("a[data-toggle='tooltip']").each(function (i, obj) {
|
||||||
|
@ -642,7 +686,7 @@ function sendConfig(modalId) {
|
||||||
req.onreadystatechange = function() {
|
req.onreadystatechange = function() {
|
||||||
if (this.readyState == 4) {
|
if (this.readyState == 4) {
|
||||||
if (this.status == 200 || this.status == 204) {
|
if (this.status == 200 || this.status == 204) {
|
||||||
bootstrap.Modal.getInstance(document.getElementById(modalId)).hide();
|
createModal(modalId, true).hide();
|
||||||
if (modalId != 'settingsMenu') {
|
if (modalId != 'settingsMenu') {
|
||||||
settingsModal.hide();
|
settingsModal.hide();
|
||||||
};
|
};
|
||||||
|
@ -699,4 +743,3 @@ document.getElementById('settingsSave').onclick = function() {
|
||||||
settingsModal.hide();
|
settingsModal.hide();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,8 +14,15 @@
|
||||||
|
|
||||||
<!-- Bootstrap CSS -->
|
<!-- Bootstrap CSS -->
|
||||||
<link rel="stylesheet" type="text/css" href="{{ css_file }}">
|
<link rel="stylesheet" type="text/css" href="{{ css_file }}">
|
||||||
|
{% if not bs5 %}
|
||||||
|
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
|
||||||
|
{% endif %}
|
||||||
<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>
|
{% if bs5 %}
|
||||||
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha1/js/bootstrap.min.js" integrity="sha384-oesi62hOLfzrys4LxRF63OJCXdXDipiYWBnvTl9Y9/TRlw5xlKIEHpNyvvDShgf/" crossorigin="anonymous"></script>
|
||||||
|
{% else %}
|
||||||
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
|
||||||
|
{% endif %}
|
||||||
<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">
|
||||||
<style>
|
<style>
|
||||||
.pageContainer {
|
.pageContainer {
|
||||||
|
@ -109,7 +116,23 @@
|
||||||
</div>
|
</div>
|
||||||
<script src="serialize.js"></script>
|
<script src="serialize.js"></script>
|
||||||
<script>
|
<script>
|
||||||
var successBox = new bootstrap.Modal(document.getElementById('successBox'));
|
{% if bs5 %}
|
||||||
|
var bsVersion = 5;
|
||||||
|
{% else %}
|
||||||
|
var bsVersion = 4;
|
||||||
|
{% endif %}
|
||||||
|
if (bsVersion == 5) {
|
||||||
|
var successBox = new bootstrap.Modal(document.getElementById('successBox'));
|
||||||
|
} else if (bsVersion == 4) {
|
||||||
|
var successBox = {
|
||||||
|
show : function() {
|
||||||
|
return $('#successBox').modal('show');
|
||||||
|
},
|
||||||
|
hide : function() {
|
||||||
|
return $('#successBox').modal('hide');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
var code = window.location.href.split('/').pop();
|
var code = window.location.href.split('/').pop();
|
||||||
function toggleSpinner () {
|
function toggleSpinner () {
|
||||||
var submitButton = document.getElementById('submitButton');
|
var submitButton = document.getElementById('submitButton');
|
||||||
|
|
|
@ -6,8 +6,15 @@
|
||||||
<title>Invalid Code</title>
|
<title>Invalid Code</title>
|
||||||
<!-- Bootstrap CSS -->
|
<!-- Bootstrap CSS -->
|
||||||
<link rel="stylesheet" type="text/css" href="{{ css_file }}">
|
<link rel="stylesheet" type="text/css" href="{{ css_file }}">
|
||||||
|
{% if not bs5 %}
|
||||||
|
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
|
||||||
|
{% endif %}
|
||||||
<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>
|
{% if bs5 %}
|
||||||
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha1/js/bootstrap.min.js" integrity="sha384-oesi62hOLfzrys4LxRF63OJCXdXDipiYWBnvTl9Y9/TRlw5xlKIEHpNyvvDShgf/" crossorigin="anonymous"></script>
|
||||||
|
{% else %}
|
||||||
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
|
||||||
|
{% endif %}
|
||||||
<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">
|
||||||
<style>
|
<style>
|
||||||
.messageBox {
|
.messageBox {
|
||||||
|
|
|
@ -11,6 +11,7 @@ def page_not_found(e):
|
||||||
return (
|
return (
|
||||||
render_template(
|
render_template(
|
||||||
"404.html",
|
"404.html",
|
||||||
|
bs5=config.getboolean('ui', 'bs5'),
|
||||||
css_file=css_file,
|
css_file=css_file,
|
||||||
contactMessage=config["ui"]["contact_message"],
|
contactMessage=config["ui"]["contact_message"],
|
||||||
),
|
),
|
||||||
|
@ -23,6 +24,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",
|
||||||
|
bs5=config.getboolean('ui', 'bs5'),
|
||||||
css_file=css_file,
|
css_file=css_file,
|
||||||
contactMessage="",
|
contactMessage="",
|
||||||
email_enabled=config.getboolean("invite_emails", "enabled"),
|
email_enabled=config.getboolean("invite_emails", "enabled"),
|
||||||
|
@ -32,10 +34,18 @@ def admin():
|
||||||
@app.route("/<path:path>")
|
@app.route("/<path:path>")
|
||||||
def static_proxy(path):
|
def static_proxy(path):
|
||||||
if "html" not in path:
|
if "html" not in path:
|
||||||
|
if "admin.js" in path:
|
||||||
|
if config.getboolean('ui', 'bs5'):
|
||||||
|
bsVersion = 5
|
||||||
|
else:
|
||||||
|
bsVersion = 4
|
||||||
|
return render_template("admin.js",
|
||||||
|
bsVersion=bsVersion)
|
||||||
return app.send_static_file(path)
|
return app.send_static_file(path)
|
||||||
return (
|
return (
|
||||||
render_template(
|
render_template(
|
||||||
"404.html",
|
"404.html",
|
||||||
|
bs5=config.getboolean('ui', 'bs5'),
|
||||||
css_file=css_file,
|
css_file=css_file,
|
||||||
contactMessage=config["ui"]["contact_message"],
|
contactMessage=config["ui"]["contact_message"],
|
||||||
),
|
),
|
||||||
|
@ -53,6 +63,7 @@ def inviteProxy(path):
|
||||||
email = ""
|
email = ""
|
||||||
return render_template(
|
return render_template(
|
||||||
"form.html",
|
"form.html",
|
||||||
|
bs5=config.getboolean('ui', 'bs5'),
|
||||||
css_file=css_file,
|
css_file=css_file,
|
||||||
contactMessage=config["ui"]["contact_message"],
|
contactMessage=config["ui"]["contact_message"],
|
||||||
helpMessage=config["ui"]["help_message"],
|
helpMessage=config["ui"]["help_message"],
|
||||||
|
@ -69,6 +80,7 @@ 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",
|
||||||
|
bs5=config.getboolean('ui', 'bs5'),
|
||||||
css_file=css_file,
|
css_file=css_file,
|
||||||
contactMessage=config["ui"]["contact_message"],
|
contactMessage=config["ui"]["contact_message"],
|
||||||
)
|
)
|
||||||
|
|
|
@ -191,6 +191,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.0.0-alpha1.tgz",
|
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.0.0-alpha1.tgz",
|
||||||
"integrity": "sha512-iwKneP2pLXl8lN0YpnOuOARiNPTzmh/4cw+Un86u4OqrMLuQpyMC7nO07hvivvcg0B/ektJPjuPnS1s+YmRK9A=="
|
"integrity": "sha512-iwKneP2pLXl8lN0YpnOuOARiNPTzmh/4cw+Un86u4OqrMLuQpyMC7nO07hvivvcg0B/ektJPjuPnS1s+YmRK9A=="
|
||||||
},
|
},
|
||||||
|
"bootstrap4": {
|
||||||
|
"version": "npm:bootstrap@4.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.5.0.tgz",
|
||||||
|
"integrity": "sha512-Z93QoXvodoVslA+PWNdk23Hze4RBYIkpb5h8I2HY2Tu2h7A0LpAgLcyrhrSUyo2/Oxm2l1fRZPs1e5hnxnliXA=="
|
||||||
|
},
|
||||||
"brace-expansion": {
|
"brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
## SCSS
|
## 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.
|
* `bs<4/5>-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.
|
Note: For BS5, it assumes that bootstrap is installed in `../../node_modules/bootstrap` relative to itself.
|
||||||
|
For BS4, it assumes that bootstrap is installed in `../../node_modules/bootstrap4` relative to itself. (`npm install bootstrap4@npm:bootstrap`)
|
||||||
* Compilation requires a sass compiler of your choice, and `postcss-cli`, `autoprefixer` + `clean-css-cli` from npm.
|
* 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.
|
* If you're using `sassc`, run `./compile.sh bs<4/5>-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
|
* For `node-sass`, replace the `sassc` line in `compile.sh` with
|
||||||
```
|
```
|
||||||
node-sass --output-style expanded --precision 6 $1 $css_file
|
node-sass --output-style expanded --precision 6 $1 $css_file
|
||||||
```
|
```
|
||||||
and run as above.
|
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 building from source, copy the minified css to `<jf-accounts git directory>/jellyfin_accounts/data/static/bs<4/5>-jf.css`.
|
||||||
* If you're just customizing your install, set `custom_css` in your config as the path to your minified 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
|
@ -0,0 +1,136 @@
|
||||||
|
$jf-blue: rgb(0, 164, 220);
|
||||||
|
$jf-blue-hover: rgba(0, 164, 220, 0.2);
|
||||||
|
$jf-blue-focus: rgb(12, 176, 232);
|
||||||
|
$jf-blue-light: #4bb3dd;
|
||||||
|
|
||||||
|
$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: #101010; // 16 16 16
|
||||||
|
$jf-gray-90: #202020; // 32 32 32
|
||||||
|
$jf-gray-80: #242424; // jf-card 36 36 36
|
||||||
|
$jf-gray-70: #292929; // jf-input 41 41 41
|
||||||
|
$jf-gray-60: #303030; // jf-button 48 48 48
|
||||||
|
$jf-gray-50: #383838; // jf-button-focus 56 56 56
|
||||||
|
$jf-text-bold: rgba(255, 255, 255, 0.87);
|
||||||
|
$jf-text-primary: rgba(255, 255, 255, 0.8);
|
||||||
|
$jf-text-secondary: rgb(153, 153, 153);
|
||||||
|
|
||||||
|
$primary: $jf-blue;
|
||||||
|
$secondary: $jf-gray-50;
|
||||||
|
$success: $jf-green-dark;
|
||||||
|
$danger: $jf-red-light;
|
||||||
|
$light: $jf-text-primary;
|
||||||
|
$dark: $jf-gray-90;
|
||||||
|
$info: $jf-yellow;
|
||||||
|
$warning: $jf-yellower;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$enable-gradients: false;
|
||||||
|
$enable-shadows: false;
|
||||||
|
|
||||||
|
$enable-rounded: false;
|
||||||
|
$body-bg: $jf-black;
|
||||||
|
$body-color: $jf-text-primary;
|
||||||
|
$border-color: $jf-gray-60;
|
||||||
|
$component-active-color: $jf-text-bold;
|
||||||
|
$component-active-bg: $jf-blue-focus;
|
||||||
|
$text-muted: $jf-text-secondary;
|
||||||
|
$link-color: $jf-blue-focus;
|
||||||
|
$btn-link-disabled-color: $jf-text-secondary;
|
||||||
|
$input-bg: $jf-gray-90;
|
||||||
|
$input-color: $jf-text-primary;
|
||||||
|
$input-focus-bg: $jf-gray-60;
|
||||||
|
$input-focus-border-color: $jf-blue-focus;
|
||||||
|
$input-disabled-bg: $jf-gray-70;
|
||||||
|
input:disabled {
|
||||||
|
color: $text-muted;
|
||||||
|
}
|
||||||
|
$input-border-color: $jf-gray-60;
|
||||||
|
$input-placeholder-color: $text-muted;
|
||||||
|
|
||||||
|
$form-check-input-bg: $jf-gray-60;
|
||||||
|
$form-check-input-border: $jf-gray-50;
|
||||||
|
$form-check-input-checked-color: $jf-blue-focus;
|
||||||
|
$form-check-input-checked-bg-color: $jf-blue-hover;
|
||||||
|
|
||||||
|
$input-group-addon-bg: $input-bg;
|
||||||
|
|
||||||
|
$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/bootstrap4/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;
|
||||||
|
}
|
||||||
|
|
|
@ -98,7 +98,7 @@ $list-group-action-active-bg: $jf-blue-focus;
|
||||||
background-color: $success;
|
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 {
|
.btn-primary, .btn-outline-primary:hover, .btn-outline-primary:active {
|
||||||
color: $jf-text-bold;
|
color: $jf-text-bold;
|
|
@ -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