refactor: externalize app configuration to JSON and unify UI color scheme constants

This commit is contained in:
joel
2026-07-19 23:55:08 +02:00
parent d130dd267b
commit 293aa5a907
4 changed files with 97 additions and 55 deletions
+51
View File
@@ -7,8 +7,59 @@ Verwendet pathlib für alle Pfadoperationen.
import json
import platform
import sys
from pathlib import Path
# ---- Globale App-Informationen (aus config.json) ----
APP_INFO = {
"app_name": "fcco Converter",
"app_version": "2.0.0",
"repo_owner": "public",
"repo_name": "fcco-Converter",
"gitea_api_base": "https://git.joelunger.de/api/v1"
}
def _load_app_info():
try:
# Finde config.json relativ zum Skript oder im PyInstaller Bundle
if getattr(sys, "frozen", False):
base_dir = Path(sys.executable).parent
else:
base_dir = Path(__file__).parent.parent
config_path = base_dir / "config.json"
if config_path.exists():
with open(config_path, "r", encoding="utf-8") as f:
data = json.load(f)
APP_INFO.update(data)
except Exception:
pass
_load_app_info()
# =========================================================================
# Farbschema
# =========================================================================
T = {
"bg": "#000000",
"surface": "#111111",
"elevated": "#1a1a1a",
"card": "#222222",
"input": "#000000",
"accent": "#ffffff",
"accent_h": "#e6e6e6",
"success": "#ffffff",
"success_h": "#e6e6e6",
"warn": "#ffffff",
"error": "#ffffff",
"text": "#ffffff",
"text2": "#b3b3b3",
"muted": "#808080",
"border": "#333333",
"btn2": "#1a1a1a",
"btn2_h": "#333333",
}
# Standardmäßige Export-Kategorien
DEFAULT_EXPORT_PATHS = {
+33 -48
View File
@@ -29,6 +29,8 @@ from .config import (
load_settings,
save_settings,
save_export_history,
T,
APP_INFO,
)
from .converter import run_batch_conversion, _ensure_ffmpeg
from .metadata import (
@@ -50,25 +52,7 @@ IS_MACOS = platform.system() == "Darwin"
# Farbschema
# =========================================================================
T = {
"bg": "#000000",
"surface": "#111111",
"elevated": "#1a1a1a",
"card": "#222222",
"input": "#000000",
"accent": "#ffffff",
"accent_h": "#e6e6e6",
"success": "#ffffff",
"success_h": "#e6e6e6",
"warn": "#ffffff",
"error": "#ffffff",
"text": "#ffffff",
"text2": "#b3b3b3",
"muted": "#808080",
"border": "#333333",
"btn2": "#1a1a1a",
"btn2_h": "#333333",
}
# (Farben werden jetzt aus config.T importiert)
# =========================================================================
@@ -82,7 +66,7 @@ class App(ctk.CTk):
ctk.set_appearance_mode("dark")
ctk.set_default_color_theme("blue")
self.title(f"fcco Converter v{get_current_version()}")
self.title(f"{APP_INFO['app_name']} V{APP_INFO['app_version']}")
self.geometry("900x700")
self.minsize(700, 500)
self.configure(fg_color=T["bg"])
@@ -227,8 +211,8 @@ class App(ctk.CTk):
self.conv_btn = self._btn(
tb, text=" Konvertieren", command=self._convert,
image=self.icons.play, width=160, height=36,
fg_color=T["accent"], hover_color=T["accent_h"],
text_color="#000000", font=ctk.CTkFont(size=14, weight="bold"))
fg_color=T["btn2"], hover_color=T["btn2_h"],
text_color="#ffffff", font=ctk.CTkFont(size=14, weight="bold"))
self.conv_btn.grid(row=0, column=3, sticky="e")
def _make_filelist(self):
@@ -576,7 +560,7 @@ class App(ctk.CTk):
ctype_var = ctk.StringVar(value="Gottesdienst")
for i, opt in enumerate(["Gottesdienst", "Hochzeit", "Weissagung", "Jugend", "Sonstiges"]):
ctk.CTkRadioButton(mid, text=opt, variable=ctype_var, value=opt,
fg_color=T["accent"], hover_color=T["accent_h"],
fg_color=T["btn2"], hover_color=T["btn2_h"], checkmark_color="#ffffff",
border_color=T["border"], text_color=T["text"],
font=ctk.CTkFont(size=12)
).grid(row=i+1, column=0, sticky="w", pady=1, padx=(0, 24))
@@ -623,8 +607,8 @@ class App(ctk.CTk):
self._btn(bf, text=" Konvertieren starten", command=_ok,
image=self.icons.play, width=180, height=34,
fg_color=T["accent"], hover_color=T["accent_h"],
text_color="#000000", font=ctk.CTkFont(weight="bold")
fg_color=T["btn2"], hover_color=T["btn2_h"],
text_color="#ffffff", font=ctk.CTkFont(weight="bold")
).pack(side="right")
# Fokus (KEIN grab_set auf macOS!)
@@ -650,10 +634,11 @@ class App(ctk.CTk):
tabs = ctk.CTkTabview(w, fg_color=T["surface"],
segmented_button_fg_color=T["elevated"],
segmented_button_selected_color=T["accent"],
segmented_button_selected_hover_color=T["accent_h"],
segmented_button_selected_color=T["border"],
segmented_button_selected_hover_color=T["btn2_h"],
segmented_button_unselected_color=T["elevated"],
segmented_button_unselected_hover_color=T["btn2_h"])
segmented_button_unselected_hover_color=T["btn2_h"],
text_color=T["text"])
tabs.pack(fill="both", expand=True, padx=12, pady=(12, 4))
t1 = tabs.add("Allgemein")
@@ -681,8 +666,8 @@ class App(ctk.CTk):
font=ctk.CTkFont(size=12)).pack(anchor="w")
av = ctk.StringVar(value=self.settings.get("appearance_mode", "Dark"))
ctk.CTkOptionMenu(s1, values=["Dark", "Light", "System"], variable=av,
fg_color=T["input"], button_color=T["accent"],
button_hover_color=T["accent_h"], text_color=T["text"],
fg_color=T["input"], button_color=T["btn2"],
button_hover_color=T["btn2_h"], text_color=T["text"],
command=lambda v: ctk.set_appearance_mode(v)
).pack(anchor="w", pady=(2, 10))
wg["appearance"] = av
@@ -690,7 +675,7 @@ class App(ctk.CTk):
self._slabel(s1, "Updates")
uv = ctk.BooleanVar(value=self.settings.get("check_updates_on_start", True))
ctk.CTkCheckBox(s1, text="Beim Start auf Updates prüfen", variable=uv,
fg_color=T["accent"], hover_color=T["accent_h"],
fg_color=T["btn2"], hover_color=T["btn2_h"], checkmark_color="#ffffff",
border_color=T["border"], text_color=T["text"],
font=ctk.CTkFont(size=12)).pack(anchor="w", pady=4)
wg["auto_update"] = uv
@@ -702,16 +687,16 @@ class App(ctk.CTk):
self._slabel(s2, "Ausgabeformat")
fv = ctk.StringVar(value=self.settings.get("audio", {}).get("output_format", "mp3"))
ctk.CTkOptionMenu(s2, values=OUTPUT_FORMAT_OPTIONS, variable=fv,
fg_color=T["input"], button_color=T["accent"],
button_hover_color=T["accent_h"], text_color=T["text"]
fg_color=T["input"], button_color=T["btn2"],
button_hover_color=T["btn2_h"], text_color=T["text"]
).pack(anchor="w", pady=(2, 10))
wg["fmt"] = fv
self._slabel(s2, "Encoding")
mv = ctk.StringVar(value=self.settings.get("audio", {}).get("encoding_mode", "CBR"))
ctk.CTkOptionMenu(s2, values=ENCODING_MODE_OPTIONS, variable=mv,
fg_color=T["input"], button_color=T["accent"],
button_hover_color=T["accent_h"], text_color=T["text"]
fg_color=T["input"], button_color=T["btn2"],
button_hover_color=T["btn2_h"], text_color=T["text"]
).pack(anchor="w", pady=(2, 6))
wg["mode"] = mv
@@ -719,8 +704,8 @@ class App(ctk.CTk):
font=ctk.CTkFont(size=12)).pack(anchor="w", pady=(4, 0))
bv = ctk.StringVar(value=self.settings.get("audio", {}).get("bitrate", "192"))
ctk.CTkOptionMenu(s2, values=BITRATE_OPTIONS, variable=bv,
fg_color=T["input"], button_color=T["accent"],
button_hover_color=T["accent_h"], text_color=T["text"]
fg_color=T["input"], button_color=T["btn2"],
button_hover_color=T["btn2_h"], text_color=T["text"]
).pack(anchor="w", pady=(2, 6))
wg["br"] = bv
@@ -728,8 +713,8 @@ class App(ctk.CTk):
font=ctk.CTkFont(size=12)).pack(anchor="w", pady=(4, 0))
vv = ctk.StringVar(value=self.settings.get("audio", {}).get("vbr_quality", "2"))
ctk.CTkOptionMenu(s2, values=VBR_QUALITY_OPTIONS, variable=vv,
fg_color=T["input"], button_color=T["accent"],
button_hover_color=T["accent_h"], text_color=T["text"]
fg_color=T["input"], button_color=T["btn2"],
button_hover_color=T["btn2_h"], text_color=T["text"]
).pack(anchor="w", pady=(2, 10))
wg["vbr"] = vv
@@ -738,8 +723,8 @@ class App(ctk.CTk):
font=ctk.CTkFont(size=12)).pack(anchor="w")
sv = ctk.StringVar(value=self.settings.get("audio", {}).get("sample_rate", "Original"))
ctk.CTkOptionMenu(s2, values=SAMPLERATE_OPTIONS, variable=sv,
fg_color=T["input"], button_color=T["accent"],
button_hover_color=T["accent_h"], text_color=T["text"]
fg_color=T["input"], button_color=T["btn2"],
button_hover_color=T["btn2_h"], text_color=T["text"]
).pack(anchor="w", pady=(2, 6))
wg["sr"] = sv
@@ -747,15 +732,15 @@ class App(ctk.CTk):
font=ctk.CTkFont(size=12)).pack(anchor="w", pady=(4, 0))
cv = ctk.StringVar(value=self.settings.get("audio", {}).get("channels", "Original"))
ctk.CTkOptionMenu(s2, values=CHANNEL_OPTIONS, variable=cv,
fg_color=T["input"], button_color=T["accent"],
button_hover_color=T["accent_h"], text_color=T["text"]
fg_color=T["input"], button_color=T["btn2"],
button_hover_color=T["btn2_h"], text_color=T["text"]
).pack(anchor="w", pady=(2, 10))
wg["ch"] = cv
self._slabel(s2, "Optionen")
ov = ctk.BooleanVar(value=self.settings.get("audio", {}).get("overwrite_existing", True))
ctk.CTkCheckBox(s2, text="Bestehende Dateien überschreiben", variable=ov,
fg_color=T["accent"], hover_color=T["accent_h"],
fg_color=T["btn2"], hover_color=T["btn2_h"], checkmark_color="#ffffff",
border_color=T["border"], text_color=T["text"],
font=ctk.CTkFont(size=12)).pack(anchor="w", pady=3)
wg["ow"] = ov
@@ -796,14 +781,14 @@ class App(ctk.CTk):
self._slabel(s3, "Automatisierung")
atv = ctk.BooleanVar(value=self.settings.get("metadata", {}).get("auto_track_number", True))
ctk.CTkCheckBox(s3, text="Tracknummer automatisch vergeben", variable=atv,
fg_color=T["accent"], hover_color=T["accent_h"],
fg_color=T["btn2"], hover_color=T["btn2_h"], checkmark_color="#ffffff",
border_color=T["border"], text_color=T["text"],
font=ctk.CTkFont(size=12)).pack(anchor="w", pady=3)
wg["auto_track"] = atv
aav = ctk.BooleanVar(value=self.settings.get("metadata", {}).get("auto_album_from_folder", True))
ctk.CTkCheckBox(s3, text="Albumname aus Ordnername ableiten", variable=aav,
fg_color=T["accent"], hover_color=T["accent_h"],
fg_color=T["btn2"], hover_color=T["btn2_h"], checkmark_color="#ffffff",
border_color=T["border"], text_color=T["text"],
font=ctk.CTkFont(size=12)).pack(anchor="w", pady=3)
wg["auto_album"] = aav
@@ -848,8 +833,8 @@ class App(ctk.CTk):
self._btn(bf, text=" Speichern", command=lambda: self._save_s(wg, w),
image=self.icons.save, width=140, height=34,
fg_color=T["accent"], hover_color=T["accent_h"],
text_color="#000000", font=ctk.CTkFont(weight="bold")
fg_color=T["btn2"], hover_color=T["btn2_h"],
text_color="#ffffff", font=ctk.CTkFont(weight="bold")
).pack(side="right")
w.after(50, lambda: (w.lift(), w.focus_force()))
+6 -7
View File
@@ -17,14 +17,13 @@ from typing import Callable
from urllib.request import urlopen, Request
from urllib.error import URLError
import json
from .config import APP_INFO
# Aktuelle Version des Programms
CURRENT_VERSION = "2.0.0"
# Gitea API-URL für Releases
GITEA_API_BASE = "https://git.joelunger.de/api/v1"
REPO_OWNER = "public"
REPO_NAME = "fcco-Converter"
# Aktuelle Version des Programms und Repos (aus config.json)
CURRENT_VERSION = APP_INFO["app_version"]
GITEA_API_BASE = APP_INFO["gitea_api_base"]
REPO_OWNER = APP_INFO["repo_owner"]
REPO_NAME = APP_INFO["repo_name"]
RELEASES_URL = f"{GITEA_API_BASE}/repos/{REPO_OWNER}/{REPO_NAME}/releases"