Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fca6637f50 | ||
|
|
cdf6073cba | ||
|
|
c486339f8d | ||
|
|
87b5ba91b1 |
@@ -1,6 +1,6 @@
|
|||||||
# fcco Converter V2
|
# fcco Converter V2
|
||||||
|
|
||||||
WAV-zu-MP3-Konverter mit grafischer Benutzeroberfläche. Unterstützt automatische Metadaten-Erstellung (Künstler, Album, Titel, Tracknummer), Cover-Bilder und alphabetische Sortierung. Läuft auf **Windows** und **macOS**.
|
WAV-zu-MP3-Konverter mit grafischer Benutzeroberfläche. Unterstützt automatische Metadaten-Erstellung (Künstler, Album, Titel, Tracknummer), Cover-Bilder und alphabetische Sortierung. Läuft auf Windows und macOS.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -13,16 +13,33 @@ WAV-zu-MP3-Konverter mit grafischer Benutzeroberfläche. Unterstützt automatisc
|
|||||||
- Einstellungen werden dauerhaft gespeichert
|
- Einstellungen werden dauerhaft gespeichert
|
||||||
- Fortschrittsanzeige pro Datei und gesamt
|
- Fortschrittsanzeige pro Datei und gesamt
|
||||||
- Automatisches Update direkt aus der App
|
- Automatisches Update direkt aus der App
|
||||||
- Moderne, dunkle Benutzeroberfläche (CustomTkinter)
|
- Moderne, dunkle Benutzeroberfläche
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Automatische Titel-Extrahierung (Neu in V2.0.2)
|
||||||
|
|
||||||
|
Das Programm erkennt Dateinamen im typischen fcco-Format und erstellt daraus automatisch saubere Songtitel (ID3-Tags).
|
||||||
|
Das Muster wird nach dem Unterstrich-Format extrahiert. Hier sind Beispiele, wie aus Dateinamen automatisch MP3-Titel gebildet werden:
|
||||||
|
|
||||||
|
- 260719So_N_10_Predigt_D Vorname Nachname.wav -> "Predigt Vorname Nachname"
|
||||||
|
- 260719So_N_07_Vorwort_D Vorname Nachname.wav -> "Vorwort Vorname Nachname"
|
||||||
|
- 260719So_N_09_Gedicht_D Vorname Nachname.wav -> "Gedicht Vorname Nachname"
|
||||||
|
- 260719So_N_10_Predigt_D.wav -> "Predigt D"
|
||||||
|
- 260719So_N_10_Predigt_D Gast.wav -> "Predigt Gast"
|
||||||
|
- 260719So_N_06_Lied_R О пройденном я подвожу итог.wav -> "О пройденном я подвожу итог"
|
||||||
|
- 260719So_N_04_Lied_D Zum blauen Himmel.wav -> "Zum blauen Himmel"
|
||||||
|
- 260719So_N_04_Chor_D Zum blauen Himmel.wav -> "Zum blauen Himmel (Chor)"
|
||||||
|
- 260719So_N_04_Orchester_D Zum blauen Himmel.wav -> "Zum blauen Himmel (Orchester)"
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Installation (Endnutzer)
|
## Installation (Endnutzer)
|
||||||
|
|
||||||
Lade die neueste Version von der [Releases-Seite](https://git.joelunger.de/public/fcco-Converter/releases) herunter:
|
Lade die neueste Version von der Releases-Seite (Gitea) herunter:
|
||||||
|
|
||||||
- **Windows**: `fcco-Converter-windows.zip` entpacken und `fcco-Converter.exe` starten.
|
- Windows: fcco-Converter-windows.zip entpacken und fcco-Converter.exe starten.
|
||||||
- **macOS**: `fcco-Converter-macos.zip` entpacken und `fcco-Converter` starten.
|
- macOS: fcco-Converter-macos.zip entpacken und fcco-Converter starten.
|
||||||
|
|
||||||
FFmpeg wird automatisch beim ersten Start heruntergeladen. Keine weitere Installation nötig.
|
FFmpeg wird automatisch beim ersten Start heruntergeladen. Keine weitere Installation nötig.
|
||||||
|
|
||||||
@@ -30,66 +47,27 @@ FFmpeg wird automatisch beim ersten Start heruntergeladen. Keine weitere Install
|
|||||||
|
|
||||||
## Updates
|
## Updates
|
||||||
|
|
||||||
In der App auf **"Update prüfen"** klicken. Wenn eine neue Version verfügbar ist, wird sie direkt heruntergeladen und installiert. Danach muss die App einmal neu gestartet werden.
|
In der App auf "Update prüfen" klicken. Wenn eine neue Version verfügbar ist, wird sie direkt heruntergeladen und installiert. Danach muss die App einmal neu gestartet werden.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Entwicklung
|
## Entwicklung
|
||||||
|
|
||||||
### Voraussetzungen
|
### Voraussetzungen
|
||||||
|
|
||||||
- Python 3.11 oder neuer
|
- Python 3.11 oder neuer
|
||||||
- Virtuelle Umgebung empfohlen
|
- Virtuelle Umgebung empfohlen
|
||||||
|
|
||||||
### Setup
|
### Setup
|
||||||
|
|
||||||
```bash
|
|
||||||
python -m venv .venv
|
python -m venv .venv
|
||||||
source .venv/bin/activate # macOS / Linux
|
source .venv/bin/activate # macOS / Linux
|
||||||
# .venv\Scripts\activate # Windows
|
# .venv\Scripts\activate # Windows
|
||||||
|
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
```
|
|
||||||
|
|
||||||
### Starten
|
### Starten
|
||||||
|
|
||||||
```bash
|
|
||||||
python main.py
|
python main.py
|
||||||
```
|
|
||||||
|
|
||||||
### Build (Standalone-Executable)
|
### Build (Standalone-Executable & ZIP-Erstellung)
|
||||||
|
|
||||||
```bash
|
|
||||||
python build.py
|
python build.py
|
||||||
```
|
|
||||||
|
|
||||||
Die fertige Datei liegt anschließend im `dist/`-Verzeichnis.
|
Die fertige App und die Release-ZIP-Datei liegen anschließend im dist/ Verzeichnis.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Projektstruktur
|
|
||||||
|
|
||||||
```
|
|
||||||
fcco-Converter/
|
|
||||||
├── main.py # Einstiegspunkt
|
|
||||||
├── build.py # Build-Skript (PyInstaller)
|
|
||||||
├── requirements.txt # Abhängigkeiten
|
|
||||||
├── icon.ico # Programm-Icon
|
|
||||||
├── fcco_converter/
|
|
||||||
│ ├── __init__.py
|
|
||||||
│ ├── config.py # Einstellungen & Konfiguration
|
|
||||||
│ ├── converter.py # FFmpeg-Konvertierung
|
|
||||||
│ ├── metadata.py # ID3-Tags & Cover
|
|
||||||
│ ├── updater.py # Auto-Update von Gitea Releases
|
|
||||||
│ └── gui.py # CustomTkinter GUI
|
|
||||||
└── README.md
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Hinweise
|
|
||||||
|
|
||||||
- Nur `.wav`-Dateien werden als Eingabe akzeptiert.
|
|
||||||
- Cover-Bilder müssen `.jpg`, `.jpeg` oder `.png` sein.
|
|
||||||
- Export erfolgt im `.mp3`-Format (192 kbit/s).
|
|
||||||
- Einstellungen werden in `~/.config/fcco-Converter/` (Linux), `~/Library/Application Support/fcco-Converter/` (macOS) bzw. `%APPDATA%/fcco-Converter/` (Windows) gespeichert.
|
|
||||||
@@ -70,6 +70,21 @@ def build():
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
print("WARNUNG: customtkinter nicht installiert. Bitte 'pip install -r requirements.txt' ausführen.")
|
print("WARNUNG: customtkinter nicht installiert. Bitte 'pip install -r requirements.txt' ausführen.")
|
||||||
|
|
||||||
|
# static_ffmpeg-Binaries einbinden (FFmpeg komplett offline bündeln)
|
||||||
|
try:
|
||||||
|
import static_ffmpeg
|
||||||
|
print("Lade FFmpeg-Binaries herunter (falls noch nicht vorhanden)...")
|
||||||
|
# Zwingt static_ffmpeg dazu, FFmpeg herunterzuladen, falls es noch nicht im venv/bin ist
|
||||||
|
static_ffmpeg.run.get_or_fetch_platform_executables_else_raise()
|
||||||
|
|
||||||
|
sf_path = Path(static_ffmpeg.__file__).parent
|
||||||
|
if system == "Windows":
|
||||||
|
args.extend(["--add-data", f"{sf_path};static_ffmpeg"])
|
||||||
|
else:
|
||||||
|
args.extend(["--add-data", f"{sf_path}:static_ffmpeg"])
|
||||||
|
except Exception as e:
|
||||||
|
print(f"WARNUNG: static_ffmpeg konnte nicht gebündelt werden: {e}")
|
||||||
|
|
||||||
# Hauptdatei
|
# Hauptdatei
|
||||||
args.append(str(project_dir / "main.py"))
|
args.append(str(project_dir / "main.py"))
|
||||||
|
|
||||||
@@ -86,7 +101,8 @@ def build():
|
|||||||
|
|
||||||
# ZIP-Archiv für das Release erstellen
|
# ZIP-Archiv für das Release erstellen
|
||||||
import tempfile
|
import tempfile
|
||||||
zip_name = f"fcco-Converter-{system.lower()}"
|
sys_name = "macos" if system == "Darwin" else system.lower()
|
||||||
|
zip_name = f"fcco-Converter-{sys_name}"
|
||||||
zip_path = dist_dir / zip_name
|
zip_path = dist_dir / zip_name
|
||||||
|
|
||||||
with tempfile.TemporaryDirectory() as tmpdir:
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"app_name": "fcco Converter",
|
"app_name": "fcco Converter",
|
||||||
"app_version": "2.0.1",
|
"app_version": "2.0.2",
|
||||||
"repo_owner": "public",
|
"repo_owner": "public",
|
||||||
"repo_name": "fcco-Converter",
|
"repo_name": "fcco-Converter",
|
||||||
"gitea_api_base": "https://git.joelunger.de/api/v1"
|
"gitea_api_base": "https://git.joelunger.de/api/v1"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from pathlib import Path
|
|||||||
# ---- Globale App-Informationen (aus config.json) ----
|
# ---- Globale App-Informationen (aus config.json) ----
|
||||||
APP_INFO = {
|
APP_INFO = {
|
||||||
"app_name": "fcco Converter",
|
"app_name": "fcco Converter",
|
||||||
"app_version": "2.0.0",
|
"app_version": "2.0.2",
|
||||||
"repo_owner": "public",
|
"repo_owner": "public",
|
||||||
"repo_name": "fcco-Converter",
|
"repo_name": "fcco-Converter",
|
||||||
"gitea_api_base": "https://git.joelunger.de/api/v1"
|
"gitea_api_base": "https://git.joelunger.de/api/v1"
|
||||||
|
|||||||
@@ -16,14 +16,36 @@ import static_ffmpeg
|
|||||||
from .metadata import apply_id3_tags
|
from .metadata import apply_id3_tags
|
||||||
|
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import io
|
||||||
|
import shutil
|
||||||
|
|
||||||
def _ensure_ffmpeg() -> tuple[str, str]:
|
def _ensure_ffmpeg() -> tuple[str, str]:
|
||||||
"""Stellt sicher, dass ffmpeg und ffprobe verfügbar sind.
|
"""Stellt sicher, dass ffmpeg und ffprobe verfügbar sind.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(ffmpeg_path, ffprobe_path)
|
(ffmpeg_path, ffprobe_path)
|
||||||
"""
|
"""
|
||||||
|
# 1. Behebe NoneType 'write' Fehler, wenn App im windowed-Modus gestartet wurde
|
||||||
|
if sys.stdout is None:
|
||||||
|
sys.stdout = io.StringIO()
|
||||||
|
if sys.stderr is None:
|
||||||
|
sys.stderr = io.StringIO()
|
||||||
|
|
||||||
|
# 2. Versuch 1: static_ffmpeg (jetzt direkt in der App gebündelt)
|
||||||
|
try:
|
||||||
ffmpeg_path, ffprobe_path = static_ffmpeg.run.get_or_fetch_platform_executables_else_raise()
|
ffmpeg_path, ffprobe_path = static_ffmpeg.run.get_or_fetch_platform_executables_else_raise()
|
||||||
return ffmpeg_path, ffprobe_path
|
return ffmpeg_path, ffprobe_path
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 3. Versuch 2: System-FFmpeg (z.B. über winget, apt, brew) als Fallback
|
||||||
|
sys_ffmpeg = shutil.which("ffmpeg")
|
||||||
|
sys_ffprobe = shutil.which("ffprobe")
|
||||||
|
if sys_ffmpeg and sys_ffprobe:
|
||||||
|
return sys_ffmpeg, sys_ffprobe
|
||||||
|
|
||||||
|
raise RuntimeError("FFmpeg konnte weder im Bundle noch im System gefunden werden.")
|
||||||
|
|
||||||
|
|
||||||
def get_duration(file_path: Path, ffprobe_path: str) -> float:
|
def get_duration(file_path: Path, ffprobe_path: str) -> float:
|
||||||
@@ -205,7 +227,8 @@ def convert_file_entry(
|
|||||||
audio_settings = {"output_format": "mp3", "bitrate": "192", "encoding_mode": "CBR"}
|
audio_settings = {"output_format": "mp3", "bitrate": "192", "encoding_mode": "CBR"}
|
||||||
|
|
||||||
file_path = Path(entry["full_path"])
|
file_path = Path(entry["full_path"])
|
||||||
title = file_path.stem
|
from .metadata import extract_title_from_filename
|
||||||
|
title = extract_title_from_filename(file_path.stem)
|
||||||
output_format = audio_settings.get("output_format", "mp3")
|
output_format = audio_settings.get("output_format", "mp3")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ fcco-Converter V2 – Metadaten-Modul
|
|||||||
Hilfsfunktionen für ID3-Tags, Cover-Bilder und Albumname-Generierung.
|
Hilfsfunktionen für ID3-Tags, Cover-Bilder und Albumname-Generierung.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -51,6 +52,47 @@ def extract_event_from_folder(file_path: Path) -> tuple[str, str]:
|
|||||||
return "", ""
|
return "", ""
|
||||||
|
|
||||||
|
|
||||||
|
def extract_title_from_filename(filename: str) -> str:
|
||||||
|
"""Extrahiert einen sauberen Titel aus dem komplexen Dateinamen-Format.
|
||||||
|
|
||||||
|
Format-Erwartung: [Prefix]_[Track]_[Type]_[Lang][ Remainder]
|
||||||
|
Beispiele:
|
||||||
|
260719So_N_10_Predigt_D Vorname Nachname -> "Predigt Vorname Nachname"
|
||||||
|
260719So_N_04_Lied_D Zum blauen Himmel -> "Zum blauen Himmel"
|
||||||
|
"""
|
||||||
|
# Regulärer Ausdruck, um die Komponenten zu extrahieren
|
||||||
|
match = re.match(r"^.*?_\d+_(?P<type>[a-zA-Z]+)_(?P<lang>[a-zA-Z]+)(?: (?P<remainder>.*))?$", filename)
|
||||||
|
if not match:
|
||||||
|
return filename
|
||||||
|
|
||||||
|
typ = match.group("type")
|
||||||
|
lang = match.group("lang")
|
||||||
|
remainder = match.group("remainder") or ""
|
||||||
|
|
||||||
|
typ_lower = typ.lower()
|
||||||
|
|
||||||
|
if typ_lower in ["predigt", "vorwort", "gedicht"]:
|
||||||
|
if remainder:
|
||||||
|
return f"{typ} {remainder}"
|
||||||
|
return f"{typ} {lang}"
|
||||||
|
|
||||||
|
elif typ_lower == "lied":
|
||||||
|
if remainder:
|
||||||
|
return remainder
|
||||||
|
return f"{typ} {lang}"
|
||||||
|
|
||||||
|
elif typ_lower in ["chor", "orchester"]:
|
||||||
|
if remainder:
|
||||||
|
return f"{remainder} ({typ})"
|
||||||
|
return f"{typ} {lang}"
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Fallback
|
||||||
|
if remainder:
|
||||||
|
return f"{typ} {remainder}"
|
||||||
|
return f"{typ} {lang}"
|
||||||
|
|
||||||
|
|
||||||
from mutagen.id3 import ID3, APIC, TIT2, TPE1, TALB, TRCK, TDRC
|
from mutagen.id3 import ID3, APIC, TIT2, TPE1, TALB, TRCK, TDRC
|
||||||
|
|
||||||
def apply_id3_tags(
|
def apply_id3_tags(
|
||||||
|
|||||||
@@ -7,6 +7,13 @@ Startet die grafische Benutzeroberfläche.
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
import platform
|
import platform
|
||||||
|
import io
|
||||||
|
|
||||||
|
# Behebe NoneType 'write' Fehler in windowed GUI-Modus (PyInstaller --windowed auf Windows/macOS)
|
||||||
|
if sys.stdout is None:
|
||||||
|
sys.stdout = io.StringIO()
|
||||||
|
if sys.stderr is None:
|
||||||
|
sys.stderr = io.StringIO()
|
||||||
|
|
||||||
# DPI-Awareness auf Windows setzen (vor dem GUI-Import)
|
# DPI-Awareness auf Windows setzen (vor dem GUI-Import)
|
||||||
if platform.system() == "Windows":
|
if platform.system() == "Windows":
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"export_paths": {
|
|
||||||
"Gottesdienst": "D:\\Tonaufnahmen\\3MP3-192kbit Gottesdienste\\2026",
|
|
||||||
"Hochzeit": "D:\\Tonaufnahmen\\4MP3-192kbit Hochzeiten",
|
|
||||||
"Weissagung": "D:\\Tonaufnahmen\\5Sonstiges\\Weissagungen",
|
|
||||||
"Jugend": "D:\\Tonaufnahmen\\3MP3-192kbit Gottesdienste\\2026"
|
|
||||||
},
|
|
||||||
"metadata": {
|
|
||||||
"artist": "FECG Rottweil-Neukirch",
|
|
||||||
"cover": "D:\\Portable Software\\fcco_Converter\\Cover\\cover_fecg_rottweil.jpg"
|
|
||||||
},
|
|
||||||
"last_export_dir": "D:\\Tonaufnahmen\\3MP3-192kbit Gottesdienste\\2026\\260406Mo Ostermontag",
|
|
||||||
"threads": 0
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user