konfigurierbare farben und texte
This commit is contained in:
@@ -4,11 +4,12 @@ import threading
|
||||
import time
|
||||
import uuid
|
||||
import json
|
||||
import re
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
from flask import Flask, abort, jsonify, render_template_string, request, send_from_directory, url_for, Response, stream_with_context
|
||||
from flask import Flask, abort, jsonify, render_template_string, request, send_from_directory, url_for, Response, stream_with_context, send_file
|
||||
|
||||
from watchdog.events import FileSystemEventHandler
|
||||
from watchdog.observers import Observer
|
||||
@@ -24,6 +25,56 @@ INDEX_FILE = Path(os.environ.get("INDEX_FILE", str(FINISHED_DIR / ".event-audio-
|
||||
WAVEFORMS_DIR = Path(os.environ.get("WAVEFORMS_DIR", "/waveforms"))
|
||||
WEB_PORT = int(os.environ.get("WEB_PORT", "8080"))
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Branding / theme configuration
|
||||
|
||||
SITE_NAME = os.environ.get("SITE_NAME", "Event Audio")
|
||||
SITE_SUBTITLE = os.environ.get("SITE_SUBTITLE", "Audiostream-Aufzeichnungen")
|
||||
PAGE_TITLE = os.environ.get("PAGE_TITLE", "Event Audio – Editor")
|
||||
PAGE_HEADING = os.environ.get("PAGE_HEADING", "Event Audio – Editor")
|
||||
EDIT_PAGE_TITLE = os.environ.get("EDIT_PAGE_TITLE", "Event Audio – Aufnahme bearbeiten")
|
||||
EDIT_PAGE_HEADING = os.environ.get("EDIT_PAGE_HEADING", "Aufnahme bearbeiten")
|
||||
THEME_FILE = Path(os.environ.get("THEME_FILE", "/config/theme.json"))
|
||||
LOGO_FILE = Path(os.environ["LOGO_FILE"]) if os.environ.get("LOGO_FILE") else None
|
||||
FAVICON_FILE = Path(os.environ["FAVICON_FILE"]) if os.environ.get("FAVICON_FILE") else None
|
||||
|
||||
DEFAULT_THEME = {'background': '#111', 'surface': '#181818', 'surface_alt': '#222', 'surface_dark': '#0b0b0b', 'text': '#eee', 'text_secondary': '#ccc', 'text_muted': '#aaa', 'text_faint': '#777', 'border': '#333', 'border_subtle': '#2b2b2b', 'control': '#333', 'control_hover': '#444', 'primary': '#1769aa', 'primary_hover': '#1f7fc2', 'accent': '#ffcc33', 'success': '#173d24', 'success_text': '#b8f5c9', 'warning': '#6b4e00', 'danger': '#8b0000', 'danger_soft': '#4a2020', 'danger_soft_hover': '#6a2b2b', 'error': '#4a1b1b', 'error_text': '#ffd0d0', 'recording': '#b00000', 'recording_border': '#3a2222', 'selection': 'rgba(80,150,255,.16)', 'shadow': '#000', 'scroll_thumb': '#777', 'scroll_thumb_hover': '#999', 'scroll_thumb_active': '#aaa', 'live_surface': '#1d1111', 'table_header': '#252525', 'footer': '#666', 'white': '#fff','surface_modal':'#202020','overlay':'rgba(0,0,0,.78)'}
|
||||
|
||||
def load_theme():
|
||||
"""Load semantic theme colors from JSON, falling back to built-in defaults."""
|
||||
theme = dict(DEFAULT_THEME)
|
||||
if THEME_FILE.is_file():
|
||||
try:
|
||||
data = json.loads(THEME_FILE.read_text(encoding="utf-8"))
|
||||
colors = data.get("colors", data) if isinstance(data, dict) else {}
|
||||
if isinstance(colors, dict):
|
||||
for key in theme:
|
||||
value = colors.get(key)
|
||||
if isinstance(value, str) and re.fullmatch(r"(?:#[0-9a-fA-F]{3,8}|rgba?\([^)]*\))", value.strip()):
|
||||
theme[key] = value.strip()
|
||||
except Exception as exc:
|
||||
print(f"[branding] Theme konnte nicht geladen werden: {exc}", flush=True)
|
||||
return theme
|
||||
|
||||
def theme_css():
|
||||
theme = load_theme()
|
||||
return ":root {\n" + "\n".join(
|
||||
f" --color-{key.replace('_', '-')}: {value};" for key, value in theme.items()
|
||||
) + "\n}"
|
||||
|
||||
def branding_context():
|
||||
return {
|
||||
"site_name": SITE_NAME,
|
||||
"site_subtitle": SITE_SUBTITLE,
|
||||
"page_title": PAGE_TITLE,
|
||||
"page_heading": PAGE_HEADING,
|
||||
"edit_page_title": EDIT_PAGE_TITLE,
|
||||
"edit_page_heading": EDIT_PAGE_HEADING,
|
||||
"theme_css": theme_css(),
|
||||
"logo_url": "/branding/logo" if LOGO_FILE and LOGO_FILE.is_file() else None,
|
||||
"favicon_url": "/branding/favicon" if FAVICON_FILE and FAVICON_FILE.is_file() else None,
|
||||
}
|
||||
|
||||
NORMALIZE = os.environ.get("NORMALIZE", "true").lower() not in ("0", "false", "no")
|
||||
NORMALIZE_TARGET = os.environ.get("NORMALIZE_TARGET", "-16")
|
||||
NORMALIZE_TRUE_PEAK = os.environ.get("NORMALIZE_TRUE_PEAK", "-1.5")
|
||||
@@ -801,59 +852,62 @@ EDITOR_HTML = """
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Event Audio – Aufnahme bearbeiten</title>
|
||||
<title>{{ page_title }}</title>
|
||||
{% if favicon_url %}<link rel="icon" href="{{ favicon_url }}">{% endif %}
|
||||
<style>
|
||||
{{ theme_css|safe }}
|
||||
* { box-sizing: border-box; }
|
||||
body { margin:0; padding:24px; background:#111; color:#eee; font-family:Arial,Helvetica,sans-serif; }
|
||||
.site-logo { max-width:220px; max-height:72px; object-fit:contain; display:block; margin-bottom:12px; }
|
||||
body { margin:0; padding:24px; background:var(--color-background); color:var(--color-text); font-family:Arial,Helvetica,sans-serif; }
|
||||
.container { max-width:1400px; margin:0 auto; }
|
||||
h1 { margin:0 0 6px; font-size:28px; }
|
||||
.subtitle { color:#aaa; margin-bottom:22px; }
|
||||
.editor { background:#181818; border:1px solid #333; border-radius:10px; padding:20px; }
|
||||
.wave-wrap { position:relative; width:100%; height:240px; background:#0b0b0b; border-radius:6px; overflow:hidden; user-select:none; touch-action:none; }
|
||||
.subtitle { color:var(--color-text-muted); margin-bottom:22px; }
|
||||
.editor { background:var(--color-surface); border:1px solid var(--color-border); border-radius:10px; padding:20px; }
|
||||
.wave-wrap { position:relative; width:100%; height:240px; background:var(--color-surface-dark); border-radius:6px; overflow:hidden; user-select:none; touch-action:none; }
|
||||
.wave-image { position:absolute; top:0; left:0; width:100%; height:100%; object-fit:fill; display:block; opacity:.9; pointer-events:none; }
|
||||
.selection { position:absolute; top:0; bottom:0; background:rgba(80,150,255,.16); border-left:2px solid #fff; border-right:2px solid #fff; pointer-events:none; }
|
||||
.playhead { position:absolute; top:0; bottom:0; width:2px; margin-left:-1px; background:#ffcc33; box-shadow:0 0 5px #000; z-index:4; pointer-events:none; }
|
||||
.selection { position:absolute; top:0; bottom:0; background:var(--color-selection); border-left:2px solid var(--color-white); border-right:2px solid var(--color-white); pointer-events:none; }
|
||||
.playhead { position:absolute; top:0; bottom:0; width:2px; margin-left:-1px; background:var(--color-accent); box-shadow:0 0 5px var(--color-shadow); z-index:4; pointer-events:none; }
|
||||
.handle { position:absolute; top:0; bottom:0; width:18px; margin-left:-9px; cursor:ew-resize; z-index:3; touch-action:none; }
|
||||
.handle::after { content:""; position:absolute; left:7px; top:0; bottom:0; width:4px; background:#fff; border-radius:2px; box-shadow:0 0 5px #000; }
|
||||
.view-scroll { position:relative; width:100%; height:14px; margin-top:5px; background:#292929; border-radius:7px; overflow:hidden; touch-action:none; cursor:grab; }
|
||||
.view-scroll-thumb { position:absolute; top:2px; bottom:2px; left:0; min-width:14px; background:#777; border-radius:5px; cursor:grab; touch-action:none; }
|
||||
.view-scroll-thumb:hover { background:#999; }
|
||||
.view-scroll-thumb.dragging { background:#aaa; cursor:grabbing; }
|
||||
.time-row { display:flex; justify-content:space-between; margin-top:8px; color:#aaa; font-size:14px; }
|
||||
.handle::after { content:""; position:absolute; left:7px; top:0; bottom:0; width:4px; background:var(--color-white); border-radius:2px; box-shadow:0 0 5px var(--color-shadow); }
|
||||
.view-scroll { position:relative; width:100%; height:14px; margin-top:5px; background:var(--color-border-subtle); border-radius:7px; overflow:hidden; touch-action:none; cursor:grab; }
|
||||
.view-scroll-thumb { position:absolute; top:2px; bottom:2px; left:0; min-width:14px; background:var(--color-text-faint); border-radius:5px; cursor:grab; touch-action:none; }
|
||||
.view-scroll-thumb:hover { background:var(--color-text-muted); }
|
||||
.view-scroll-thumb.dragging { background:var(--color-text-muted); cursor:grabbing; }
|
||||
.time-row { display:flex; justify-content:space-between; margin-top:8px; color:var(--color-text-muted); font-size:14px; }
|
||||
.controls { display:flex; flex-wrap:wrap; gap:8px; align-items:center; margin-top:18px; }
|
||||
button, a.button { border:0; border-radius:6px; padding:11px 16px; background:#333; color:#fff; cursor:pointer; text-decoration:none; font-size:15px; }
|
||||
button:hover, a.button:hover { background:#444; }
|
||||
.button.danger { background:#4a2020; }
|
||||
.button.danger:hover { background:#6a2b2b; }
|
||||
button, a.button { border:0; border-radius:6px; padding:11px 16px; background:var(--color-border); color:var(--color-white); cursor:pointer; text-decoration:none; font-size:15px; }
|
||||
button:hover, a.button:hover { background:var(--color-control-hover); }
|
||||
.button.danger { background:var(--color-danger-soft); }
|
||||
.button.danger:hover { background:var(--color-danger-soft-hover); }
|
||||
.actions { display:flex; gap:6px; flex-wrap:wrap; }
|
||||
button.primary { background:#1769aa; }
|
||||
button.danger { background:#8b0000; }
|
||||
button.primary { background:var(--color-primary); }
|
||||
button.danger { background:var(--color-danger); }
|
||||
button:disabled { opacity:.45; cursor:not-allowed; }
|
||||
.position { margin-left:auto; color:#ccc; font-variant-numeric:tabular-nums; }
|
||||
.zoom { color:#aaa; font-variant-numeric:tabular-nums; }
|
||||
.position { margin-left:auto; color:var(--color-text-secondary); font-variant-numeric:tabular-nums; }
|
||||
.zoom { color:var(--color-text-muted); font-variant-numeric:tabular-nums; }
|
||||
.values { display:grid; grid-template-columns:repeat(3,1fr); gap:12px; margin-top:20px; }
|
||||
.value { background:#222; border-radius:6px; padding:12px; }
|
||||
.value span { display:block; color:#999; font-size:13px; margin-bottom:5px; }
|
||||
.value { background:var(--color-surface-alt); border-radius:6px; padding:12px; }
|
||||
.value span { display:block; color:var(--color-text-muted); font-size:13px; margin-bottom:5px; }
|
||||
.value strong { font-size:20px; font-variant-numeric:tabular-nums; }
|
||||
.actions { display:flex; gap:10px; flex-wrap:wrap; margin-top:20px; }
|
||||
.info { margin-top:16px; color:#999; font-size:14px; }
|
||||
.info { margin-top:16px; color:var(--color-text-muted); font-size:14px; }
|
||||
.message { margin-top:15px; padding:10px 12px; border-radius:6px; display:none; }
|
||||
.message.ok { display:block; background:#173d24; color:#b8f5c9; }
|
||||
.message.error { display:block; background:#4a1b1b; color:#ffd0d0; }
|
||||
.meta-dialog { position:fixed; inset:0; background:rgba(0,0,0,.78); display:none; align-items:center; justify-content:center; z-index:900; padding:20px; }
|
||||
.message.ok { display:block; background:var(--color-success); color:var(--color-success-text); }
|
||||
.message.error { display:block; background:var(--color-error); color:var(--color-error-text); }
|
||||
.meta-dialog { position:fixed; inset:0; background:var(--color-overlay); display:none; align-items:center; justify-content:center; z-index:900; padding:20px; }
|
||||
.meta-dialog.active { display:flex; }
|
||||
.meta-card { width:min(560px,100%); background:#202020; border:1px solid #444; border-radius:10px; padding:22px; box-shadow:0 10px 40px #000; }
|
||||
.meta-card { width:min(560px,100%); background:var(--color-surface-modal); border:1px solid var(--color-control-hover); border-radius:10px; padding:22px; box-shadow:0 10px 40px var(--color-shadow); }
|
||||
.meta-card h2 { margin-top:0; }
|
||||
.meta-card label { display:block; margin:14px 0; color:#ccc; }
|
||||
.meta-card input { display:block; width:100%; margin-top:6px; padding:10px; background:#111; color:#eee; border:1px solid #555; border-radius:5px; font-size:16px; }
|
||||
.meta-card label { display:block; margin:14px 0; color:var(--color-text-secondary); }
|
||||
.meta-card input { display:block; width:100%; margin-top:6px; padding:10px; background:var(--color-background); color:var(--color-text); border:1px solid var(--color-control-hover); border-radius:5px; font-size:16px; }
|
||||
.meta-actions { display:flex; gap:10px; justify-content:flex-end; margin-top:20px; }
|
||||
.meta-actions button { margin:0; }
|
||||
.progress-box { display:none; margin-top:16px; }
|
||||
.progress-box.active { display:block; }
|
||||
.progress-track { height:18px; background:#292929; border-radius:9px; overflow:hidden; }
|
||||
.progress-bar { height:100%; width:0; background:#1769aa; transition:width .2s ease; }
|
||||
.progress-info { display:flex; justify-content:space-between; margin-top:7px; color:#aaa; font-size:14px; font-variant-numeric:tabular-nums; }
|
||||
.back { display:inline-block; margin-bottom:18px; color:#bbb; text-decoration:none; }
|
||||
.progress-track { height:18px; background:var(--color-border-subtle); border-radius:9px; overflow:hidden; }
|
||||
.progress-bar { height:100%; width:0; background:var(--color-primary); transition:width .2s ease; }
|
||||
.progress-info { display:flex; justify-content:space-between; margin-top:7px; color:var(--color-text-muted); font-size:14px; font-variant-numeric:tabular-nums; }
|
||||
.back { display:inline-block; margin-bottom:18px; color:var(--color-text-secondary); text-decoration:none; }
|
||||
@media(max-width:700px) {
|
||||
body { padding:12px; }
|
||||
.editor { padding:12px; }
|
||||
@@ -870,8 +924,9 @@ button:disabled { opacity:.45; cursor:not-allowed; }
|
||||
<body>
|
||||
<div class="container">
|
||||
<a class="back" href="/">← Zurück zu den Aufnahmen</a>
|
||||
<h1>Aufnahme bearbeiten</h1>
|
||||
<div class="subtitle">{{ filename }} · Originaldauer {{ duration }}</div>
|
||||
{% if logo_url %}<img class="site-logo" src="{{ logo_url }}" alt="{{ site_name }}">{% endif %}
|
||||
<h1>{{ edit_page_heading }}</h1>
|
||||
<div class="subtitle">{{ site_name }} · {{ filename }} · Originaldauer {{ duration }}</div>
|
||||
|
||||
<div class="editor">
|
||||
<div class="wave-wrap" id="wave">
|
||||
@@ -1247,51 +1302,54 @@ INDEX_HTML = """
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Event Audio – Editor</title>
|
||||
<title>{{ page_title }}</title>
|
||||
{% if favicon_url %}<link rel="icon" href="{{ favicon_url }}">{% endif %}
|
||||
<style>
|
||||
{{ theme_css|safe }}
|
||||
* { box-sizing:border-box; }
|
||||
body { margin:0; padding:24px; background:#111; color:#eee; font-family:Arial,Helvetica,sans-serif; }
|
||||
body { margin:0; padding:24px; background:var(--color-background); color:var(--color-text); font-family:Arial,Helvetica,sans-serif; }
|
||||
.container { max-width:1400px; margin:0 auto; }
|
||||
h1 { margin:0 0 6px; }
|
||||
.subtitle { color:#aaa; margin-bottom:22px; }
|
||||
.section { background:#181818; border:1px solid #333; border-radius:10px; padding:18px; margin-bottom:18px; }
|
||||
.subtitle { color:var(--color-text-muted); margin-bottom:22px; }
|
||||
.section { background:var(--color-surface); border:1px solid var(--color-border); border-radius:10px; padding:18px; margin-bottom:18px; }
|
||||
.filter-bar { display:grid; grid-template-columns:repeat(4,minmax(150px,1fr)); gap:10px; margin-bottom:14px; }
|
||||
.filter-bar label { color:#aaa; font-size:13px; }
|
||||
.filter-bar input, .filter-bar select { width:100%; margin-top:5px; padding:9px; background:#111; color:#eee; border:1px solid #555; border-radius:5px; font-size:15px; }
|
||||
.filter-bar label { color:var(--color-text-muted); font-size:13px; }
|
||||
.filter-bar input, .filter-bar select { width:100%; margin-top:5px; padding:9px; background:var(--color-background); color:var(--color-text); border:1px solid var(--color-control-hover); border-radius:5px; font-size:15px; }
|
||||
.filter-bar .search { grid-column:auto; }
|
||||
.filter-info { color:#999; font-size:13px; margin-bottom:10px; }
|
||||
.filter-info { color:var(--color-text-muted); font-size:13px; margin-bottom:10px; }
|
||||
.upload-bar { display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-bottom:16px; }
|
||||
.upload-info { color:#999; font-size:13px; }
|
||||
.upload-progress { display:none; flex:1 1 240px; min-width:180px; height:18px; background:#292929; border-radius:9px; overflow:hidden; }
|
||||
.upload-info { color:var(--color-text-muted); font-size:13px; }
|
||||
.upload-progress { display:none; flex:1 1 240px; min-width:180px; height:18px; background:var(--color-border-subtle); border-radius:9px; overflow:hidden; }
|
||||
.upload-progress.active { display:block; }
|
||||
.upload-progress-bar { width:0; height:100%; background:#1769aa; transition:width .15s ease; }
|
||||
.upload-progress-bar { width:0; height:100%; background:var(--color-primary); transition:width .15s ease; }
|
||||
|
||||
table { width:100%; border-collapse:collapse; }
|
||||
th,td { padding:10px 8px; border-bottom:1px solid #333; text-align:left; }
|
||||
th { color:#aaa; font-weight:normal; }
|
||||
.button { display:inline-block; border:0; border-radius:6px; padding:9px 13px; background:#333; color:#fff; cursor:pointer; text-decoration:none; }
|
||||
.button:hover { background:#444; }
|
||||
.empty { color:#999; padding:15px 0; }
|
||||
.meta-dialog { position:fixed; inset:0; background:rgba(0,0,0,.78); display:none; align-items:center; justify-content:center; z-index:900; padding:20px; }
|
||||
th,td { padding:10px 8px; border-bottom:1px solid var(--color-border); text-align:left; }
|
||||
th { color:var(--color-text-muted); font-weight:normal; }
|
||||
.button { display:inline-block; border:0; border-radius:6px; padding:9px 13px; background:var(--color-border); color:var(--color-white); cursor:pointer; text-decoration:none; }
|
||||
.button:hover { background:var(--color-control-hover); }
|
||||
.empty { color:var(--color-text-muted); padding:15px 0; }
|
||||
.meta-dialog { position:fixed; inset:0; background:var(--color-overlay); display:none; align-items:center; justify-content:center; z-index:900; padding:20px; }
|
||||
.meta-dialog.active { display:flex; }
|
||||
.meta-card { width:min(560px,100%); background:#202020; border:1px solid #444; border-radius:10px; padding:22px; box-shadow:0 10px 40px #000; }
|
||||
.meta-card { width:min(560px,100%); background:var(--color-surface-modal); border:1px solid var(--color-control-hover); border-radius:10px; padding:22px; box-shadow:0 10px 40px var(--color-shadow); }
|
||||
.meta-card h2 { margin-top:0; }
|
||||
.meta-card label { display:block; margin:14px 0; color:#ccc; }
|
||||
.meta-card input { display:block; width:100%; margin-top:6px; padding:10px; background:#111; color:#eee; border:1px solid #555; border-radius:5px; font-size:16px; }
|
||||
.meta-card label { display:block; margin:14px 0; color:var(--color-text-secondary); }
|
||||
.meta-card input { display:block; width:100%; margin-top:6px; padding:10px; background:var(--color-background); color:var(--color-text); border:1px solid var(--color-control-hover); border-radius:5px; font-size:16px; }
|
||||
.meta-actions { display:flex; gap:10px; justify-content:flex-end; margin-top:20px; }
|
||||
.meta-actions button { margin:0; }
|
||||
.loading-overlay { position:fixed; inset:0; background:rgba(0,0,0,.78); display:none; align-items:center; justify-content:center; z-index:1000; }
|
||||
.loading-overlay { position:fixed; inset:0; background:var(--color-overlay); display:none; align-items:center; justify-content:center; z-index:1000; }
|
||||
.loading-overlay.active { display:flex; }
|
||||
.spinner { width:54px; height:54px; border:5px solid #444; border-top-color:#fff; border-radius:50%; animation:spin .8s linear infinite; }
|
||||
.spinner { width:54px; height:54px; border:5px solid var(--color-control-hover); border-top-color:var(--color-white); border-radius:50%; animation:spin .8s linear infinite; }
|
||||
@keyframes spin { to { transform:rotate(360deg); } }
|
||||
@media(max-width:900px) { .filter-bar { grid-template-columns:1fr 1fr; } .filter-bar .search { grid-column:auto; } }
|
||||
@media(max-width:800px) { body { padding:12px; } table,thead,tbody,tr,th,td { display:block; } thead { display:none; } tr { padding:10px 0; border-bottom:1px solid #333; } td { border:0; padding:5px 0; } }
|
||||
@media(max-width:800px) { body { padding:12px; } table,thead,tbody,tr,th,td { display:block; } thead { display:none; } tr { padding:10px 0; border-bottom:1px solid var(--color-border); } td { border:0; padding:5px 0; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="loading-overlay" id="loadingOverlay"><div class="spinner" aria-label="Laden"></div></div>
|
||||
<div class="container">
|
||||
<h1>Event Audio – Editor</h1>
|
||||
{% if logo_url %}<img class="site-logo" src="{{ logo_url }}" alt="{{ site_name }}">{% endif %}
|
||||
<h1>{{ page_heading }}</h1>
|
||||
<div class="subtitle">Fertige Aufnahmen aus {{ recordings_dir }}</div>
|
||||
|
||||
<div class="section">
|
||||
@@ -1628,6 +1686,20 @@ async function deleteFile(kind, filename) {
|
||||
# Routes
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@app.route("/branding/logo")
|
||||
def branding_logo():
|
||||
if LOGO_FILE is None or not LOGO_FILE.is_file():
|
||||
abort(404)
|
||||
return send_file(LOGO_FILE)
|
||||
|
||||
|
||||
@app.route("/branding/favicon")
|
||||
def branding_favicon():
|
||||
if FAVICON_FILE is None or not FAVICON_FILE.is_file():
|
||||
abort(404)
|
||||
return send_file(FAVICON_FILE)
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
with INDEX_LOCK:
|
||||
@@ -1683,6 +1755,7 @@ def index():
|
||||
artists=artists,
|
||||
albums=albums,
|
||||
recordings_dir=str(RECORDINGS_DIR),
|
||||
**branding_context(),
|
||||
)
|
||||
|
||||
|
||||
@@ -1722,6 +1795,7 @@ def edit_recording(filename):
|
||||
metadata_datetime=metadata_form_value(path),
|
||||
waveform_url=url_for("waveform", filename=filename),
|
||||
audio_url=url_for("recording", filename=filename),
|
||||
**branding_context(),
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user