feat: add YAML settings management and admin settings page, add default_playlist_users
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
from logging.handlers import RotatingFileHandler
|
from logging.handlers import RotatingFileHandler
|
||||||
import os
|
import os
|
||||||
|
import threading
|
||||||
import time
|
import time
|
||||||
|
import yaml
|
||||||
from flask_socketio import SocketIO
|
from flask_socketio import SocketIO
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
@@ -97,9 +99,20 @@ app = Flask(__name__, template_folder="../templates", static_folder='../static')
|
|||||||
|
|
||||||
|
|
||||||
app.config.from_object(Config)
|
app.config.from_object(Config)
|
||||||
|
app.config['runtime_settings'] = {}
|
||||||
|
yaml_file = 'settings.yaml'
|
||||||
|
def load_yaml_settings():
|
||||||
|
with open(yaml_file, 'r') as f:
|
||||||
|
app.config['runtime_settings'] = yaml.safe_load(f)
|
||||||
|
def save_yaml_settings():
|
||||||
|
with open(yaml_file, 'w') as f:
|
||||||
|
yaml.dump(app.config['runtime_settings'], f)
|
||||||
|
|
||||||
|
|
||||||
for handler in app.logger.handlers:
|
for handler in app.logger.handlers:
|
||||||
app.logger.removeHandler(handler)
|
app.logger.removeHandler(handler)
|
||||||
|
|
||||||
|
|
||||||
log_level = getattr(logging, app.config['LOG_LEVEL'], logging.INFO) # Default to DEBUG if invalid
|
log_level = getattr(logging, app.config['LOG_LEVEL'], logging.INFO) # Default to DEBUG if invalid
|
||||||
app.logger.setLevel(log_level)
|
app.logger.setLevel(log_level)
|
||||||
|
|
||||||
@@ -197,3 +210,28 @@ if app.config['LIDARR_API_KEY'] and app.config['LIDARR_URL']:
|
|||||||
app.logger.info(f'Creating Lidarr Client with URL: {app.config["LIDARR_URL"]}')
|
app.logger.info(f'Creating Lidarr Client with URL: {app.config["LIDARR_URL"]}')
|
||||||
from lidarr.client import LidarrClient
|
from lidarr.client import LidarrClient
|
||||||
lidarr_client = LidarrClient(app.config['LIDARR_URL'], app.config['LIDARR_API_KEY'])
|
lidarr_client = LidarrClient(app.config['LIDARR_URL'], app.config['LIDARR_API_KEY'])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if os.path.exists(yaml_file):
|
||||||
|
app.logger.info('Loading runtime settings from settings.yaml')
|
||||||
|
load_yaml_settings()
|
||||||
|
# def watch_yaml_file(yaml_file, interval=30):
|
||||||
|
# last_mtime = os.path.getmtime(yaml_file)
|
||||||
|
# while True:
|
||||||
|
# time.sleep(interval)
|
||||||
|
# current_mtime = os.path.getmtime(yaml_file)
|
||||||
|
# if current_mtime != last_mtime:
|
||||||
|
# last_mtime = current_mtime
|
||||||
|
# yaml_settings = load_yaml_settings(yaml_file)
|
||||||
|
# app.config.update(yaml_settings)
|
||||||
|
# app.logger.info(f"Reloaded YAML settings from {yaml_file}")
|
||||||
|
|
||||||
|
# watcher_thread = threading.Thread(
|
||||||
|
# target=watch_yaml_file,
|
||||||
|
# args=('settings.yaml',),
|
||||||
|
# daemon=True
|
||||||
|
# )
|
||||||
|
# watcher_thread.start()
|
||||||
@@ -3,7 +3,7 @@ import json
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from flask import Flask, Response, jsonify, render_template, request, redirect, url_for, session, flash, Blueprint, g
|
from flask import Flask, Response, jsonify, render_template, request, redirect, url_for, session, flash, Blueprint, g
|
||||||
from app import app, db, functions, jellyfin, read_dev_build_file, tasks
|
from app import app, db, functions, jellyfin, read_dev_build_file, tasks, save_yaml_settings
|
||||||
from app.classes import AudioProfile, CombinedPlaylistData
|
from app.classes import AudioProfile, CombinedPlaylistData
|
||||||
from app.models import JellyfinUser,Playlist,Track
|
from app.models import JellyfinUser,Playlist,Track
|
||||||
from celery.result import AsyncResult
|
from celery.result import AsyncResult
|
||||||
@@ -75,7 +75,6 @@ def task_manager():
|
|||||||
lock_keys.append('full_update_jellyfin_ids_lock')
|
lock_keys.append('full_update_jellyfin_ids_lock')
|
||||||
return render_template('admin/tasks.html', tasks=statuses,lock_keys = lock_keys)
|
return render_template('admin/tasks.html', tasks=statuses,lock_keys = lock_keys)
|
||||||
|
|
||||||
@app.route('/admin')
|
|
||||||
@app.route('/admin/link_issues')
|
@app.route('/admin/link_issues')
|
||||||
@functions.jellyfin_admin_required
|
@functions.jellyfin_admin_required
|
||||||
def link_issues():
|
def link_issues():
|
||||||
@@ -168,6 +167,23 @@ def get_logs_for_issue():
|
|||||||
|
|
||||||
return jsonify({'logs': logs})
|
return jsonify({'logs': logs})
|
||||||
|
|
||||||
|
@app.route('/admin')
|
||||||
|
@app.route('/admin/settings')
|
||||||
|
@app.route('/admin/settings/save' , methods=['POST'])
|
||||||
|
@functions.jellyfin_admin_required
|
||||||
|
def admin_settings():
|
||||||
|
# if the request is a POST request, save the settings
|
||||||
|
if request.method == 'POST':
|
||||||
|
# from the form, get all values from default_playlist_users and join them to one array of strings
|
||||||
|
|
||||||
|
app.config['runtime_settings']['default_playlist_users'] = request.form.getlist('default_playlist_users')
|
||||||
|
save_yaml_settings()
|
||||||
|
flash('Settings saved', category='success')
|
||||||
|
return redirect('/admin/settings')
|
||||||
|
return render_template('admin/settings.html',jellyfin_users = jellyfin.get_users(session_token=functions._get_api_token()))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/run_task/<task_name>', methods=['POST'])
|
@app.route('/run_task/<task_name>', methods=['POST'])
|
||||||
@functions.jellyfin_admin_required
|
@functions.jellyfin_admin_required
|
||||||
def run_task(task_name):
|
def run_task(task_name):
|
||||||
|
|||||||
@@ -4,7 +4,11 @@
|
|||||||
<nav class="navbar navbar-expand-lg navbar-dark border-bottom mb-2">
|
<nav class="navbar navbar-expand-lg navbar-dark border-bottom mb-2">
|
||||||
<div class="collapse navbar-collapse" id="navbarNav">
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
<ul class="navbar-nav">
|
<ul class="navbar-nav">
|
||||||
<li class="nav-item active">
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/admin/settings">Settings
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/admin/link_issues">Link Issues
|
<a class="nav-link" href="/admin/link_issues">Link Issues
|
||||||
{% include 'partials/_unlinked_tracks_badge.html' %}</a>
|
{% include 'partials/_unlinked_tracks_badge.html' %}</a>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
20
templates/admin/settings.html
Normal file
20
templates/admin/settings.html
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{% extends "admin.html" %}
|
||||||
|
|
||||||
|
{% block admin_content %}
|
||||||
|
<h2>Settings</h2>
|
||||||
|
<form action="/admin/settings/save" method="post">
|
||||||
|
<div class="mb-3">
|
||||||
|
<h3>Default Playlist Users</h3>
|
||||||
|
<div id="defaultPlaylistUsers">
|
||||||
|
{% for user in jellyfin_users %}
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" name="default_playlist_users" value="{{ user.Id }}" id="user-{{ user.Id }}"
|
||||||
|
{% if user.Id in config['runtime_settings']['default_playlist_users'] %}checked{% endif %}>
|
||||||
|
<label class="form-check-label" for="user-{{ user.Id }}">{{ user.Name }}</label>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Save Settings</button>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
@@ -43,8 +43,9 @@ function loadAllUsers{{item['id']}}() {
|
|||||||
data.users.forEach(user => {
|
data.users.forEach(user => {
|
||||||
const checkbox = document.createElement('div');
|
const checkbox = document.createElement('div');
|
||||||
checkbox.classList.add('form-check');
|
checkbox.classList.add('form-check');
|
||||||
checkbox.innerHTML = `<input class="form-check-input" type="checkbox" value="${user.Id}" id="user-${user.Id}">
|
const isChecked = {{ config['runtime_settings']['default_playlist_users']|safe }}.includes(user.Id) ? 'checked' : '';
|
||||||
<label class="form-check-label" for="user-${user.Id}">${user.Name}</label>`;
|
checkbox.innerHTML = `<input class="form-check-input" type="checkbox" value="${user.Id}" id="user-${user.Id}" ${isChecked}>
|
||||||
|
<label class="form-check-label" for="user-${user.Id}">${user.Name}</label>`;
|
||||||
allUsersDiv.appendChild(checkbox);
|
allUsersDiv.appendChild(checkbox);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user