Encoding: Profile, Queue-, Aufbewahrungs- und Dashboard-Entscheidungen dokumentiert.

Drei HandBrake-Presets (SVT-AV1 10-bit, MKV, Audio-Passthrough) versioniert
unter llm-server/encoding/profiles/; Profilwahl ueber gleichnamige Ordner
unter input/. Gemeinsamer NFS-Mount mit input/- und output/-Unterverzeichnissen.
Input-Loeschung nach Encoding und Validierung, Output bis Abholung, work/ pro
Lauf. Dashboard-Endpunkte fuer Fortschritt und Ressourcen als Statusanzeige.
Eigene Programme liegen kuenftig unter llm-server/encoding/scripts/.
GNENC-000/001 aktualisiert.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
2026-10-19 15:22:31 +02:00
parent aa5b37a191
commit 26fb86781f
5 changed files with 439 additions and 19 deletions
@@ -18,8 +18,8 @@
`docs/platform/llm-server/encoding/` und die Dokumentenkennung `GNENC`.
- Encoding ist nach Betreiberfestlegung CPU-basiert und darf parallel zur
jeweils aktiven KI-Umgebung laufen.
- Encoding soll ausschließlich außerhalb der Stoßzeiten, zunächst nachts,
automatisch ausgeführt werden.
- Encoding läuft automatisch nächtlich von 00:00 bis 06:00 Uhr Europe/Berlin
und darf parallel zur jeweils aktiven KI-Umgebung laufen.
- Das gesperrte Servicekonto `gn-encoding` mit UID/GID 982 und eigenem
rootless Podman-Store ist eingerichtet und verifiziert.
- Das Encoding-Ansible-Projekt unter `llm-server/encoding/ansible/` verwaltet
@@ -28,13 +28,24 @@
- Das Image `linuxserver/handbrake:1.11.2-ls48` ist mit dem amd64-Manifest-Digest
`sha256:fda92bd1b43a07a770b10ad793172b10451ba1690803112dbaf2b48a6148a3c7`
gepinnt und per Pull im rootless Store von `gn-encoding` verifiziert.
- Drei HandBrake-Presets (SVT-AV1 10-bit, MKV, Audio-Passthrough) liegen
versioniert unter `llm-server/encoding/profiles/`; die Profilwahl erfolgt
über gleichnamige Ordner unter `input/`.
- Aufbewahrung: Input-Dateien werden nach erfolgreichem Encoding und
Validierung gelöscht; Output bleibt bis zur Abholung; `work/` wird pro Lauf
geleert.
- Statusanzeige: Dashboard-Endpunkte für Arbeitsfortschritt und
System-Ressourcen des Encoding-Workloads.
- Es sind noch kein Container, keine Quadlet-Unit und kein Timer eingerichtet.
## Nächste Festlegungen
Am 19.10.2026 festgelegt: HandBrake im Image `linuxserver/handbrake:1.11.2-ls48`
(amd64-Manifest-Digest gepinnt), nächtliches Zeitfenster 00:00–06:00 Uhr
Europe/Berlin mit geordnetem Stopp und nächtlichem Retry sowie NFS-Mounts für
Ein- und Ausgabe. Noch offen: Codecs und Qualitätsprofile, NFS-Server- und
Exportdetails, Queue- und Fehlerverhalten, Aufbewahrung, Benachrichtigung sowie
Europe/Berlin mit geordnetem Stopp und nächtlichem Retry, gemeinsamer
NFS-Mount mit `input/`- und `output/`-Unterverzeichnissen, drei HandBrake-Presets
mit Auswahl über Input-Ordner, Aufbewahrung (Input-Löschung nach Encoding und
Validierung, Output bis Abholung, `work/` pro Lauf) sowie Dashboard-Endpunkte
für Fortschritt und Ressourcen. Noch offen: NFS-Server-Adresse und
Export-Pfad, Validierungsverfahren, technische Umsetzung der Endpunkte sowie
CPU-/I/O-Grenzen nach einem Lasttest unter Inferenzlast.
@@ -29,7 +29,11 @@ unabhängig konkurrierende Sollzustände auf denselben Host anwenden.
| Parallelität | darf parallel zu AI Production oder AI Testing laufen | Planungsentscheidung |
| Zeitsteuerung | nächtlich 00:00–06:00 Uhr Europe/Berlin; am Fensterende geordneter Stopp, unfertige Jobs starten nächtlich erneut | Betreiberentscheidung 19.10.2026 |
| Anwendung | HandBrake im Image `linuxserver/handbrake:1.11.2-ls48` mit gepinntem amd64-Manifest-Digest `sha256:fda92bd1b43a07a770b10ad793172b10451ba1690803112dbaf2b48a6148a3c7` | Betreiberentscheidung 19.10.2026; Digest aufgelöst und per Pull im gn-encoding-Store bestätigt |
| Ein-/Ausgabe | NFS-Mounts auf `/var/lib/ghostnet-encoding/input` und `/var/lib/ghostnet-encoding/output` | Betreiberentscheidung 19.10.2026; Server- und Exportdetails offen |
| Ein-/Ausgabe | ein NFS-Export, gemountet auf `/var/lib/ghostnet-encoding`; `input/` und `output/` als Unterverzeichnisse | Betreiberentscheidung 19.10.2026; Server-Adresse und Export-Pfad offen |
| Profile | drei versionierte HandBrake-Presets (SVT-AV1 10-bit, MKV, Audio-Passthrough) unter `llm-server/encoding/profiles/`; Auswahl über gleichnamige Ordner unter `input/` | Betreiberentscheidung 19.10.2026; Presets versioniert |
| Warteschlange | dateibasiert über `input/<Profilname>/`; genau ein gleichzeitiger Job; unfertige oder fehlerhafte Jobs werden nächtlich wiederholt; Teilresultate in `work/` | Betreiberentscheidung 19.10.2026 |
| Aufbewahrung | Input-Datei wird nach erfolgreichem Encoding und Validierung gelöscht; Output bleibt bis zur Abholung; `work/` wird pro Lauf geleert | Betreiberentscheidung 19.10.2026 |
| Statusanzeige | Dashboard-Endpunkte für Arbeitsfortschritt und System-Ressourcen des Encoding-Workloads | Betreiberentscheidung 19.10.2026; technische Umsetzung offen |
## 3. Zielbild
@@ -112,10 +116,12 @@ setzt die Host-Baseline voraus. Ein künftiges `ai.yml` im `llm/`-Projekt darf n
KI-Zustände verändern. Ein späteres `site.yml` kann die unterstützte
Gesamtkonfiguration in definierter Reihenfolge anwenden.
Ein separates Quellcode-Repository wird erst benötigt, wenn eigene
Encoding-Software, eine Oberfläche oder ein Queue-Dienst entwickelt wird. Ein
solches Repository erzeugt ein versioniertes Artefakt oder Image; ausschließlich
das Infrastructure-Repository legt fest, welche Version auf dem Server läuft.
Eigene Programme (z. B. das Queue-/Warteschlangen-Skript) liegen versioniert
unter `llm-server/encoding/scripts/` (Betreiberentscheidung 19.10.2026). Ein
separates Quellcode-Repository wird erst benötigt, wenn eine Oberfläche oder ein
umfangreicher Dienst eigenständig entwickelt wird. Ein solches Repository erzeugt
ein versioniertes Artefakt oder Image; ausschließlich das Infrastructure-
Repository legt fest, welche Version auf dem Server läuft.
## 6. Aktueller Implementierungsstand
@@ -155,16 +161,20 @@ gepinnte HandBrake-Image `linuxserver/handbrake:1.11.2-ls48`
Am 19.10.2026 wurden Encoder und Image (HandBrake, gepinnter Digest), das
nächtliche Zeitfenster (00:00–06:00 Uhr Europe/Berlin), das Abbruchverhalten am
Fensterende (geordneter Stopp, nächtlicher Retry) und das Ein-/Ausgabeverfahren
(NFS-Mounts) festgelegt. Offen bleiben:
Fensterende (geordneter Stopp, nächtlicher Retry), das Ein-/Ausgabeverfahren
(gemeinsamer NFS-Mount mit `input/`- und `output/`-Unterverzeichnissen), die
drei HandBrake-Presets mit profilbezogener Auswahl über Input-Ordner, die
Aufbewahrung (Input-Löschung nach Encoding und Validierung, Output bis zur
Abholung, `work/` pro Lauf) und die Statusanzeige (Dashboard-Endpunkte für
Arbeitsfortschritt und System-Ressourcen) festgelegt. Offen bleiben:
1. Welche Codecs, Qualitätsprofile und Hardwarekompatibilitäten sind erforderlich?
2. Welche NFS-Server-Adresse, Export-Pfade und Mount-Optionen gelten für Ein- und Ausgabe?
3. Wie werden Warteschlange, Wiederholung, Teilresultate und Fehler behandelt?
4. Welche Aufbewahrungs-, Backup- und Löschregeln gelten für Video- und
Arbeitsdateien?
5. Welche Statusanzeige oder Benachrichtigung benötigen Nutzer?
6. Welche CPU-, Speicher- und I/O-Grenzen schützen die KI-Dienste? Konkrete
1. Welche NFS-Server-Adresse, welcher Export-Pfad und welche Mount-Optionen
gelten für den gemeinsamen Mount auf `/var/lib/ghostnet-encoding`?
2. Wie wird ein fertiger Encode validiert, bevor die Input-Datei gelöscht wird
(Vorschlag: HandBrakeCLI-Exit 0, nicht-leere Zieldatei, fehlerfreies ffprobe)?
3. Wie werden die Dashboard-Endpunkte technisch umgesetzt (Metriken für
Arbeitsfortschritt und System-Ressourcen, Transport, Authentifizierung)?
4. Welche CPU-, Speicher- und I/O-Grenzen schützen die KI-Dienste? Konkrete
Grenzwerte werden erst nach einem repräsentativen Encoding-Test unter
gleichzeitiger Inferenzlast festgelegt (Abschnitt 4).
@@ -0,0 +1,133 @@
{
"PresetList": [
{
"AlignAVStart": false,
"AudioAutomaticNamingBehavior": "unnamed",
"AudioCopyMask": [],
"AudioEncoderFallback": "opus",
"AudioLanguageList": [
"deu",
"eng",
"jpn",
"kor"
],
"AudioList": [
{
"AudioBitrate": 192,
"AudioCompressionLevel": -1.0,
"AudioDitherMethod": "auto",
"AudioEncoder": "copy",
"AudioMixdown": "5point1",
"AudioNormalizeMixLevel": false,
"AudioSamplerate": "auto",
"AudioTrackDRCSlider": 0.0,
"AudioTrackGainSlider": 0.0,
"AudioTrackQuality": -1.0,
"AudioTrackQualityEnable": false
}
],
"AudioSecondaryEncoderMode": true,
"AudioTrackNamePassthru": true,
"AudioTrackSelectionBehavior": "first",
"ChapterMarkers": true,
"ChildrenArray": [],
"Default": false,
"FileFormat": "av_mkv",
"Folder": false,
"FolderOpen": false,
"InlineParameterSets": false,
"MetadataPassthru": true,
"Mp4iPodCompatible": false,
"Optimize": false,
"PictureAllowUpscaling": false,
"PictureAutoCrop": true,
"PictureBottomCrop": 0,
"PictureChromaSmoothCustom": "",
"PictureChromaSmoothPreset": "off",
"PictureChromaSmoothTune": "none",
"PictureColorspaceCustom": "",
"PictureColorspacePreset": "off",
"PictureCombDetectCustom": "",
"PictureCombDetectPreset": "default",
"PictureCropMode": 0,
"PictureDARWidth": 1920,
"PictureDeblockCustom": "strength=strong:thresh=20:blocksize=8",
"PictureDeblockPreset": "off",
"PictureDeblockTune": "medium",
"PictureDeinterlaceCustom": "",
"PictureDeinterlaceFilter": "decomb",
"PictureDeinterlacePreset": "default",
"PictureDenoiseCustom": "",
"PictureDenoiseFilter": "off",
"PictureDenoisePreset": "",
"PictureDenoiseTune": "none",
"PictureDetelecine": "off",
"PictureDetelecineCustom": "",
"PictureForceHeight": 0,
"PictureForceWidth": 0,
"PictureHeight": 1080,
"PictureItuPAR": false,
"PictureKeepRatio": true,
"PictureLeftCrop": 0,
"PictureModulus": 2,
"PicturePAR": "auto",
"PicturePARHeight": 1,
"PicturePARWidth": 1,
"PicturePadBottom": 0,
"PicturePadColor": "black",
"PicturePadLeft": 0,
"PicturePadMode": "none",
"PicturePadRight": 0,
"PicturePadTop": 0,
"PictureRightCrop": 0,
"PictureRotate": "angle=0:hflip=0",
"PictureSharpenCustom": "",
"PictureSharpenFilter": "off",
"PictureSharpenPreset": "",
"PictureSharpenTune": "",
"PictureTopCrop": 0,
"PictureUseMaximumSize": true,
"PictureWidth": 1920,
"PresetDescription": "AV1 video (up to 1080p60) and Opus audio optimized for Anime, in an MKV container.",
"PresetDisabled": false,
"PresetName": "AV1 MKV 1080p30 Anime RF34",
"SubtitleAddCC": false,
"SubtitleAddForeignAudioSearch": false,
"SubtitleAddForeignAudioSubtitle": false,
"SubtitleBurnBDSub": false,
"SubtitleBurnBehavior": "none",
"SubtitleBurnDVDSub": false,
"SubtitleLanguageList": [
"any"
],
"SubtitleTrackNamePassthru": true,
"SubtitleTrackSelectionBehavior": "all",
"Type": 1,
"UsesPictureFilters": true,
"VideoAvgBitrate": 10500,
"VideoColorMatrixCodeOverride": 0,
"VideoColorRange": "limited",
"VideoEncoder": "svt_av1_10bit",
"VideoFramerate": "30",
"VideoFramerateMode": "pfr",
"VideoGrayScale": false,
"VideoHWDecode": 0,
"VideoLevel": "auto",
"VideoMultiPass": true,
"VideoOptionExtra": "aq-mode=2:film-grain=0:tune=2:qm-min=6:qm-max=12:chroma-qm-min=8:chroma-qm-max=12:enable-qm=1",
"VideoPasshtruHDRDynamicMetadata": "all",
"VideoPreset": "5",
"VideoProfile": "main",
"VideoQualitySlider": 34.0,
"VideoQualityType": 2,
"VideoScaler": "swscale",
"VideoTune": "ssim",
"VideoTurboMultiPass": false,
"x264Option": "",
"x264UseAdvancedOptions": false
}
],
"VersionMajor": 67,
"VersionMicro": 0,
"VersionMinor": 0
}
@@ -0,0 +1,133 @@
{
"PresetList": [
{
"AlignAVStart": false,
"AudioAutomaticNamingBehavior": "unnamed",
"AudioCopyMask": [],
"AudioEncoderFallback": "opus",
"AudioLanguageList": [
"deu",
"eng",
"jpn",
"kor"
],
"AudioList": [
{
"AudioBitrate": 192,
"AudioCompressionLevel": -1.0,
"AudioDitherMethod": "auto",
"AudioEncoder": "copy",
"AudioMixdown": "5point1",
"AudioNormalizeMixLevel": false,
"AudioSamplerate": "auto",
"AudioTrackDRCSlider": 0.0,
"AudioTrackGainSlider": 0.0,
"AudioTrackQuality": -1.0,
"AudioTrackQualityEnable": false
}
],
"AudioSecondaryEncoderMode": true,
"AudioTrackNamePassthru": true,
"AudioTrackSelectionBehavior": "first",
"ChapterMarkers": true,
"ChildrenArray": [],
"Default": true,
"FileFormat": "av_mkv",
"Folder": false,
"FolderOpen": false,
"InlineParameterSets": false,
"MetadataPassthru": true,
"Mp4iPodCompatible": false,
"Optimize": false,
"PictureAllowUpscaling": false,
"PictureAutoCrop": true,
"PictureBottomCrop": 0,
"PictureChromaSmoothCustom": "",
"PictureChromaSmoothPreset": "off",
"PictureChromaSmoothTune": "none",
"PictureColorspaceCustom": "",
"PictureColorspacePreset": "off",
"PictureCombDetectCustom": "",
"PictureCombDetectPreset": "default",
"PictureCropMode": 0,
"PictureDARWidth": 1920,
"PictureDeblockCustom": "strength=strong:thresh=20:blocksize=8",
"PictureDeblockPreset": "off",
"PictureDeblockTune": "medium",
"PictureDeinterlaceCustom": "",
"PictureDeinterlaceFilter": "decomb",
"PictureDeinterlacePreset": "default",
"PictureDenoiseCustom": "",
"PictureDenoiseFilter": "off",
"PictureDenoisePreset": "",
"PictureDenoiseTune": "none",
"PictureDetelecine": "off",
"PictureDetelecineCustom": "",
"PictureForceHeight": 0,
"PictureForceWidth": 0,
"PictureHeight": 1080,
"PictureItuPAR": false,
"PictureKeepRatio": true,
"PictureLeftCrop": 0,
"PictureModulus": 2,
"PicturePAR": "auto",
"PicturePARHeight": 1,
"PicturePARWidth": 1,
"PicturePadBottom": 0,
"PicturePadColor": "black",
"PicturePadLeft": 0,
"PicturePadMode": "none",
"PicturePadRight": 0,
"PicturePadTop": 0,
"PictureRightCrop": 0,
"PictureRotate": "angle=0:hflip=0",
"PictureSharpenCustom": "",
"PictureSharpenFilter": "off",
"PictureSharpenPreset": "",
"PictureSharpenTune": "",
"PictureTopCrop": 0,
"PictureUseMaximumSize": true,
"PictureWidth": 1920,
"PresetDescription": "AV1 video (up to 2160p60) and Opus audio, in an MKV container.",
"PresetDisabled": false,
"PresetName": "AV1 MKV 1080p30 RF25",
"SubtitleAddCC": false,
"SubtitleAddForeignAudioSearch": false,
"SubtitleAddForeignAudioSubtitle": false,
"SubtitleBurnBDSub": false,
"SubtitleBurnBehavior": "none",
"SubtitleBurnDVDSub": false,
"SubtitleLanguageList": [
"any"
],
"SubtitleTrackNamePassthru": true,
"SubtitleTrackSelectionBehavior": "all",
"Type": 1,
"UsesPictureFilters": true,
"VideoAvgBitrate": 10500,
"VideoColorMatrixCodeOverride": 0,
"VideoColorRange": "limited",
"VideoEncoder": "svt_av1_10bit",
"VideoFramerate": "30",
"VideoFramerateMode": "pfr",
"VideoGrayScale": false,
"VideoHWDecode": 0,
"VideoLevel": "auto",
"VideoMultiPass": true,
"VideoOptionExtra": "aq-mode=1:enable-variance-boost=1:variance-boost-strength=2:variance-octile=4:film-grain-denoise=0:film-grain=8:tune=0",
"VideoPasshtruHDRDynamicMetadata": "all",
"VideoPreset": "4",
"VideoProfile": "main",
"VideoQualitySlider": 25.0,
"VideoQualityType": 2,
"VideoScaler": "swscale",
"VideoTune": "vq",
"VideoTurboMultiPass": false,
"x264Option": "",
"x264UseAdvancedOptions": false
}
],
"VersionMajor": 67,
"VersionMicro": 0,
"VersionMinor": 0
}
@@ -0,0 +1,133 @@
{
"PresetList": [
{
"AlignAVStart": false,
"AudioAutomaticNamingBehavior": "unnamed",
"AudioCopyMask": [],
"AudioEncoderFallback": "opus",
"AudioLanguageList": [
"deu",
"eng",
"jpn",
"kor"
],
"AudioList": [
{
"AudioBitrate": 256,
"AudioCompressionLevel": -1.0,
"AudioDitherMethod": "auto",
"AudioEncoder": "copy",
"AudioMixdown": "5point1",
"AudioNormalizeMixLevel": false,
"AudioSamplerate": "auto",
"AudioTrackDRCSlider": 0.0,
"AudioTrackGainSlider": 0.0,
"AudioTrackQuality": -1.0,
"AudioTrackQualityEnable": false
}
],
"AudioSecondaryEncoderMode": true,
"AudioTrackNamePassthru": true,
"AudioTrackSelectionBehavior": "first",
"ChapterMarkers": true,
"ChildrenArray": [],
"Default": false,
"FileFormat": "av_mkv",
"Folder": false,
"FolderOpen": false,
"InlineParameterSets": false,
"MetadataPassthru": true,
"Mp4iPodCompatible": false,
"Optimize": false,
"PictureAllowUpscaling": false,
"PictureAutoCrop": true,
"PictureBottomCrop": 0,
"PictureChromaSmoothCustom": "",
"PictureChromaSmoothPreset": "off",
"PictureChromaSmoothTune": "none",
"PictureColorspaceCustom": "",
"PictureColorspacePreset": "off",
"PictureCombDetectCustom": "",
"PictureCombDetectPreset": "default",
"PictureCropMode": 0,
"PictureDARWidth": 1920,
"PictureDeblockCustom": "strength=strong:thresh=20:blocksize=8",
"PictureDeblockPreset": "off",
"PictureDeblockTune": "medium",
"PictureDeinterlaceCustom": "",
"PictureDeinterlaceFilter": "decomb",
"PictureDeinterlacePreset": "default",
"PictureDenoiseCustom": "",
"PictureDenoiseFilter": "off",
"PictureDenoisePreset": "",
"PictureDenoiseTune": "none",
"PictureDetelecine": "off",
"PictureDetelecineCustom": "",
"PictureForceHeight": 0,
"PictureForceWidth": 0,
"PictureHeight": 2160,
"PictureItuPAR": false,
"PictureKeepRatio": true,
"PictureLeftCrop": 0,
"PictureModulus": 2,
"PicturePAR": "auto",
"PicturePARHeight": 1,
"PicturePARWidth": 1,
"PicturePadBottom": 0,
"PicturePadColor": "black",
"PicturePadLeft": 0,
"PicturePadMode": "none",
"PicturePadRight": 0,
"PicturePadTop": 0,
"PictureRightCrop": 0,
"PictureRotate": "angle=0:hflip=0",
"PictureSharpenCustom": "",
"PictureSharpenFilter": "off",
"PictureSharpenPreset": "",
"PictureSharpenTune": "",
"PictureTopCrop": 0,
"PictureUseMaximumSize": true,
"PictureWidth": 3840,
"PresetDescription": "AV1 video (up to 2160p60) and Opus audio, in an MKV container.",
"PresetDisabled": false,
"PresetName": "AV1 MKV 2160p60 4K RF23",
"SubtitleAddCC": false,
"SubtitleAddForeignAudioSearch": false,
"SubtitleAddForeignAudioSubtitle": false,
"SubtitleBurnBDSub": false,
"SubtitleBurnBehavior": "none",
"SubtitleBurnDVDSub": false,
"SubtitleLanguageList": [
"any"
],
"SubtitleTrackNamePassthru": true,
"SubtitleTrackSelectionBehavior": "all",
"Type": 1,
"UsesPictureFilters": true,
"VideoAvgBitrate": 10500,
"VideoColorMatrixCodeOverride": 0,
"VideoColorRange": "limited",
"VideoEncoder": "svt_av1_10bit",
"VideoFramerate": "60",
"VideoFramerateMode": "pfr",
"VideoGrayScale": false,
"VideoHWDecode": 0,
"VideoLevel": "auto",
"VideoMultiPass": true,
"VideoOptionExtra": "aq-mode=1:enable-variance-boost=1:variance-boost-strength=2:film-grain=12:film-grain-denoise=1",
"VideoPasshtruHDRDynamicMetadata": "all",
"VideoPreset": "4",
"VideoProfile": "main",
"VideoQualitySlider": 23.0,
"VideoQualityType": 2,
"VideoScaler": "swscale",
"VideoTune": "vq",
"VideoTurboMultiPass": false,
"x264Option": "",
"x264UseAdvancedOptions": false
}
],
"VersionMajor": 67,
"VersionMicro": 0,
"VersionMinor": 0
}