mirror of
https://github.com/hrfee/jellyfin-accounts.git
synced 2024-12-22 09:00:14 +00:00
Self-restarting for config changes
When changing settings that need restart, the option is now available to do it automatically. Functions on linux at least, might need testing on windows.
This commit is contained in:
parent
ef8ff531e3
commit
2e20466925
@ -1,7 +0,0 @@
|
|||||||
A user was created using code {{ code }}.
|
|
||||||
|
|
||||||
Name: {{ username }}
|
|
||||||
Address: {{ address }}
|
|
||||||
Time: {{ time }}
|
|
||||||
|
|
||||||
Note: Notification emails can be toggled on the admin dashboard.
|
|
@ -1,10 +0,0 @@
|
|||||||
Hi {{ username }},
|
|
||||||
|
|
||||||
Someone has recently requests a password reset on Jellyfin.
|
|
||||||
If this was you, enter the below pin into the prompt.
|
|
||||||
This code will expire on {{ expiry_date }}, at {{ expiry_time }} , which is in {{ expires_in }}.
|
|
||||||
If this wasn't you, please ignore this email.
|
|
||||||
|
|
||||||
PIN: {{ pin }}
|
|
||||||
|
|
||||||
{{ message }}
|
|
@ -1,5 +0,0 @@
|
|||||||
Invite expired.
|
|
||||||
|
|
||||||
Code {{ code }} expired at {{ expiry }}.
|
|
||||||
|
|
||||||
Note: Notification emails can be toggled on the admin dashboard.
|
|
@ -1,8 +0,0 @@
|
|||||||
Hi,
|
|
||||||
You've been invited to Jellyfin.
|
|
||||||
To join, follow the below link.
|
|
||||||
This invite will expire on {{ expiry_date }}, at {{ expiry_time }}, which is in {{ expires_in }}, so act quick.
|
|
||||||
|
|
||||||
{{ invite_link }}
|
|
||||||
|
|
||||||
{{ message }}
|
|
@ -214,18 +214,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal fade" id="restartModal" role="dialog" aria-labelledby"Restart Warning" aria-hidden="true">
|
<div class="modal fade" id="restartModal" role="dialog" aria-labelledby="Restart Warning" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title">Warning</h5>
|
<h5 class="modal-title">Warning</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p>A restart is needed to apply some settings. This must be done manually. Apply now?</p>
|
<p>A restart is needed to apply some settings. Restart now, later, or cancel?</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
|
<button type="button" class="btn btn-light" data-dismiss="modal">Cancel</button>
|
||||||
<button type="button" class="btn btn-primary" id="applyRestarts" data-dismiss="alert">Apply</button>
|
<button type="button" class="btn btn-secondary" id="applyRestarts" data-dismiss="modal">Apply, Restart later</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="applyAndRestart" data-dismiss="modal">Apply & Restart</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal fade" id="refreshModal" role="dialog" aria-labelledby="Refresh page notice" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Settings applied.</h5>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>Refresh the page in a few seconds.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -157,6 +157,7 @@ var settingsModal = createModal('settingsMenu');
|
|||||||
var userDefaultsModal = createModal('userDefaults');
|
var userDefaultsModal = createModal('userDefaults');
|
||||||
var usersModal = createModal('users');
|
var usersModal = createModal('users');
|
||||||
var restartModal = createModal('restartModal');
|
var restartModal = createModal('restartModal');
|
||||||
|
var refreshModal = createModal('refreshModal');
|
||||||
|
|
||||||
// Parsed invite: [<code>, <expires in _>, <1: Empty invite (no delete/link), 0: Actual invite>, <email address>, <remaining uses>, [<used-by>], <date created>, <notify on expiry>, <notify on creation>]
|
// Parsed invite: [<code>, <expires in _>, <1: Empty invite (no delete/link), 0: Actual invite>, <email address>, <remaining uses>, [<used-by>], <date created>, <notify on expiry>, <notify on creation>]
|
||||||
function parseInvite(invite, empty = false) {
|
function parseInvite(invite, empty = false) {
|
||||||
@ -961,8 +962,12 @@ document.getElementById('openSettings').onclick = function () {
|
|||||||
|
|
||||||
triggerTooltips();
|
triggerTooltips();
|
||||||
|
|
||||||
function sendConfig(modalId) {
|
function sendConfig(modalId, restart = false) {
|
||||||
let modal = document.getElementById(modalId);
|
let modal = document.getElementById(modalId);
|
||||||
|
modifiedConfig['restart-program'] = false;
|
||||||
|
if (restart) {
|
||||||
|
modifiedConfig['restart-program'] = true;
|
||||||
|
}
|
||||||
let send = JSON.stringify(modifiedConfig);
|
let send = JSON.stringify(modifiedConfig);
|
||||||
let req = new XMLHttpRequest();
|
let req = new XMLHttpRequest();
|
||||||
req.open("POST", "/modifyConfig", true);
|
req.open("POST", "/modifyConfig", true);
|
||||||
@ -975,6 +980,8 @@ function sendConfig(modalId) {
|
|||||||
if (modalId != 'settingsMenu') {
|
if (modalId != 'settingsMenu') {
|
||||||
settingsModal.hide();
|
settingsModal.hide();
|
||||||
}
|
}
|
||||||
|
} else if (restart) {
|
||||||
|
refreshModal.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -1010,7 +1017,8 @@ document.getElementById('settingsSave').onclick = function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (restart_setting_changed) {
|
if (restart_setting_changed) {
|
||||||
document.getElementById('applyRestarts').onclick = function(){sendConfig('restartModal');};
|
document.getElementById('applyRestarts').onclick = function(){ sendConfig('restartModal'); };
|
||||||
|
document.getElementById('applyAndRestart').onclick = function(){ sendConfig('restartModal', restart=true); };
|
||||||
settingsModal.hide();
|
settingsModal.hide();
|
||||||
restartModal.show();
|
restartModal.show();
|
||||||
} else if (settings_changed) {
|
} else if (settings_changed) {
|
||||||
|
@ -355,7 +355,7 @@
|
|||||||
<div class="card-body text-center">
|
<div class="card-body text-center">
|
||||||
<h5 class="card-title">Finished!</h5>
|
<h5 class="card-title">Finished!</h5>
|
||||||
<p class="card-text">
|
<p class="card-text">
|
||||||
Press the button below to submit your settings. The program will quit, so run it again, then refresh this page.
|
Press the button below to submit your settings. The program will restart. Once it's done, refresh this page.
|
||||||
</p>
|
</p>
|
||||||
<button id="submitButton" class="btn btn-primary">Submit</button>
|
<button id="submitButton" class="btn btn-primary">Submit</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -44,7 +44,7 @@ class Jellyfin:
|
|||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def __init__(self, server, client, version, device, deviceId):
|
def __init__(self, server, client, version, device, deviceId, cacheMinutes=30):
|
||||||
"""
|
"""
|
||||||
Initializes the Jellyfin object. All parameters except server
|
Initializes the Jellyfin object. All parameters except server
|
||||||
have no effect on the client's capability.
|
have no effect on the client's capability.
|
||||||
@ -61,7 +61,7 @@ class Jellyfin:
|
|||||||
self.version = version
|
self.version = version
|
||||||
self.device = device
|
self.device = device
|
||||||
self.deviceId = deviceId
|
self.deviceId = deviceId
|
||||||
self.timeout = 30 * 60
|
self.timeout = cacheMinutes * 60
|
||||||
self.userCacheAge = time.time() - self.timeout - 1
|
self.userCacheAge = time.time() - self.timeout - 1
|
||||||
self.userCachePublicAge = self.userCacheAge
|
self.userCachePublicAge = self.userCacheAge
|
||||||
self.useragent = f"{self.client}/{self.version}"
|
self.useragent = f"{self.client}/{self.version}"
|
||||||
@ -84,6 +84,16 @@ class Jellyfin:
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def reloadCache(self):
|
||||||
|
""" Forces a reload of the user caches """
|
||||||
|
self.userCachePublicAge = time.time() - self.timeout - 1
|
||||||
|
self.getUsers()
|
||||||
|
try:
|
||||||
|
self.userCacheAge = self.userCachePublicAge
|
||||||
|
self.getUsers(public=False)
|
||||||
|
except self.AuthenticationRequiredError:
|
||||||
|
pass
|
||||||
|
|
||||||
def getUsers(self, username: str = "all", userId: str = "all", public: bool = True):
|
def getUsers(self, username: str = "all", userId: str = "all", public: bool = True):
|
||||||
"""
|
"""
|
||||||
Returns details on user(s), such as ID, Name, Policy.
|
Returns details on user(s), such as ID, Name, Policy.
|
||||||
|
@ -5,6 +5,8 @@ from jellyfin_accounts.jf_api import Jellyfin
|
|||||||
from jellyfin_accounts import config, config_path, app, first_run, resp
|
from jellyfin_accounts import config, config_path, app, first_run, resp
|
||||||
from jellyfin_accounts import web_log as log
|
from jellyfin_accounts import web_log as log
|
||||||
import os
|
import os
|
||||||
|
import psutil
|
||||||
|
import sys
|
||||||
|
|
||||||
if first_run:
|
if first_run:
|
||||||
|
|
||||||
@ -51,8 +53,16 @@ if first_run:
|
|||||||
with open(config_path, "w") as config_file:
|
with open(config_path, "w") as config_file:
|
||||||
temp_config.write(config_file)
|
temp_config.write(config_file)
|
||||||
log.debug("Config written")
|
log.debug("Config written")
|
||||||
# ugly exit, sorry
|
log.info('Restarting...')
|
||||||
os._exit(1)
|
try:
|
||||||
|
p = psutil.Process(os.getpid())
|
||||||
|
for handler in p.open_files() + p.connections():
|
||||||
|
os.close(handler.fd)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
python = sys.executable
|
||||||
|
os.execl(python, python, *sys.argv)
|
||||||
return resp()
|
return resp()
|
||||||
|
|
||||||
@app.route("/testJF", methods=["GET", "POST"])
|
@app.route("/testJF", methods=["GET", "POST"])
|
||||||
|
@ -28,7 +28,6 @@ def page_not_found(e):
|
|||||||
|
|
||||||
@app.route("/", methods=["GET", "POST"])
|
@app.route("/", methods=["GET", "POST"])
|
||||||
def admin():
|
def admin():
|
||||||
# return app.send_static_file('admin.html')
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"admin.html",
|
"admin.html",
|
||||||
bs5=config.getboolean("ui", "bs5"),
|
bs5=config.getboolean("ui", "bs5"),
|
||||||
|
@ -6,6 +6,9 @@ import datetime
|
|||||||
import secrets
|
import secrets
|
||||||
import time
|
import time
|
||||||
import threading
|
import threading
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import psutil
|
||||||
from jellyfin_accounts import (
|
from jellyfin_accounts import (
|
||||||
config,
|
config,
|
||||||
config_path,
|
config_path,
|
||||||
@ -440,7 +443,7 @@ def modifyConfig():
|
|||||||
)
|
)
|
||||||
temp_config.read(config_path)
|
temp_config.read(config_path)
|
||||||
for section in data:
|
for section in data:
|
||||||
if section in temp_config:
|
if section in temp_config and 'restart-program' not in section:
|
||||||
for item in data[section]:
|
for item in data[section]:
|
||||||
temp_config[section][item] = data[section][item]
|
temp_config[section][item] = data[section][item]
|
||||||
data[section][item] = True
|
data[section][item] = True
|
||||||
@ -448,7 +451,18 @@ def modifyConfig():
|
|||||||
with open(config_path, "w") as config_file:
|
with open(config_path, "w") as config_file:
|
||||||
temp_config.write(config_file)
|
temp_config.write(config_file)
|
||||||
config.trigger_reload()
|
config.trigger_reload()
|
||||||
log.info("Config written. Restart may be needed to load settings.")
|
log.info("Config written.")
|
||||||
|
if 'restart-program' in data:
|
||||||
|
if data['restart-program']:
|
||||||
|
log.info('Restarting...')
|
||||||
|
try:
|
||||||
|
proc = psutil.Process(os.getpid())
|
||||||
|
for handler in proc.open_files() + proc.connections():
|
||||||
|
os.close(handler.fd)
|
||||||
|
except Exception as e:
|
||||||
|
log.error(f'Failed restart: {type(e).__name__}')
|
||||||
|
python = sys.executable
|
||||||
|
os.execl(python, python, *sys.argv)
|
||||||
return resp()
|
return resp()
|
||||||
|
|
||||||
|
|
||||||
|
26
poetry.lock
generated
26
poetry.lock
generated
@ -242,6 +242,17 @@ optional = false
|
|||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
category = "main"
|
||||||
|
description = "Cross-platform lib for process and system monitoring in Python."
|
||||||
|
name = "psutil"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||||
|
version = "5.7.2"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "main"
|
category = "main"
|
||||||
description = "C parser in Python"
|
description = "C parser in Python"
|
||||||
@ -422,7 +433,7 @@ dev = ["pytest", "pytest-timeout", "coverage", "tox", "sphinx", "pallets-sphinx-
|
|||||||
watchdog = ["watchdog"]
|
watchdog = ["watchdog"]
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
content-hash = "fa8b5fb1ded41b673b8062a2bfc6467e6a484ff62b578147bec001d7d9d8ca16"
|
content-hash = "1c2741c9be187d9d0be662509fb4a87f5978e5f44420e5049a20504824c29a59"
|
||||||
python-versions = "^3.6"
|
python-versions = "^3.6"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
@ -628,6 +639,19 @@ pathspec = [
|
|||||||
pathtools = [
|
pathtools = [
|
||||||
{file = "pathtools-0.1.2.tar.gz", hash = "sha256:7c35c5421a39bb82e58018febd90e3b6e5db34c5443aaaf742b3f33d4655f1c0"},
|
{file = "pathtools-0.1.2.tar.gz", hash = "sha256:7c35c5421a39bb82e58018febd90e3b6e5db34c5443aaaf742b3f33d4655f1c0"},
|
||||||
]
|
]
|
||||||
|
psutil = [
|
||||||
|
{file = "psutil-5.7.2-cp27-none-win32.whl", hash = "sha256:f2018461733b23f308c298653c8903d32aaad7873d25e1d228765e91ae42c3f2"},
|
||||||
|
{file = "psutil-5.7.2-cp27-none-win_amd64.whl", hash = "sha256:66c18ca7680a31bf16ee22b1d21b6397869dda8059dbdb57d9f27efa6615f195"},
|
||||||
|
{file = "psutil-5.7.2-cp35-cp35m-win32.whl", hash = "sha256:5e9d0f26d4194479a13d5f4b3798260c20cecf9ac9a461e718eb59ea520a360c"},
|
||||||
|
{file = "psutil-5.7.2-cp35-cp35m-win_amd64.whl", hash = "sha256:4080869ed93cce662905b029a1770fe89c98787e543fa7347f075ade761b19d6"},
|
||||||
|
{file = "psutil-5.7.2-cp36-cp36m-win32.whl", hash = "sha256:d8a82162f23c53b8525cf5f14a355f5d1eea86fa8edde27287dd3a98399e4fdf"},
|
||||||
|
{file = "psutil-5.7.2-cp36-cp36m-win_amd64.whl", hash = "sha256:0ee3c36428f160d2d8fce3c583a0353e848abb7de9732c50cf3356dd49ad63f8"},
|
||||||
|
{file = "psutil-5.7.2-cp37-cp37m-win32.whl", hash = "sha256:ff1977ba1a5f71f89166d5145c3da1cea89a0fdb044075a12c720ee9123ec818"},
|
||||||
|
{file = "psutil-5.7.2-cp37-cp37m-win_amd64.whl", hash = "sha256:a5b120bb3c0c71dfe27551f9da2f3209a8257a178ed6c628a819037a8df487f1"},
|
||||||
|
{file = "psutil-5.7.2-cp38-cp38-win32.whl", hash = "sha256:10512b46c95b02842c225f58fa00385c08fa00c68bac7da2d9a58ebe2c517498"},
|
||||||
|
{file = "psutil-5.7.2-cp38-cp38-win_amd64.whl", hash = "sha256:68d36986ded5dac7c2dcd42f2682af1db80d4bce3faa126a6145c1637e1b559f"},
|
||||||
|
{file = "psutil-5.7.2.tar.gz", hash = "sha256:90990af1c3c67195c44c9a889184f84f5b2320dce3ee3acbd054e3ba0b4a7beb"},
|
||||||
|
]
|
||||||
pycparser = [
|
pycparser = [
|
||||||
{file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"},
|
{file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"},
|
||||||
{file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
|
{file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
|
||||||
|
@ -29,6 +29,7 @@ python-dateutil = "^2.8.1"
|
|||||||
watchdog = "^0.10.2"
|
watchdog = "^0.10.2"
|
||||||
waitress = "^1.4.3"
|
waitress = "^1.4.3"
|
||||||
packaging = "^20.4"
|
packaging = "^20.4"
|
||||||
|
psutil = "^5.7.2"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
neovim = "^0.3.1"
|
neovim = "^0.3.1"
|
||||||
|
Loading…
Reference in New Issue
Block a user