diff --git a/docs/platform/llm-server/encoding/GNENC-000-DOC_Index.md b/docs/platform/llm-server/encoding/GNENC-000-DOC_Index.md index 5fff2f3..ecb7139 100644 --- a/docs/platform/llm-server/encoding/GNENC-000-DOC_Index.md +++ b/docs/platform/llm-server/encoding/GNENC-000-DOC_Index.md @@ -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. diff --git a/docs/platform/llm-server/encoding/GNENC-001-PLAN_Architecture.md b/docs/platform/llm-server/encoding/GNENC-001-PLAN_Architecture.md index b9d4ccf..37f420f 100644 --- a/docs/platform/llm-server/encoding/GNENC-001-PLAN_Architecture.md +++ b/docs/platform/llm-server/encoding/GNENC-001-PLAN_Architecture.md @@ -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//`; 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). diff --git a/llm-server/encoding/profiles/AV1 MKV 1080p30 Anime RF34.json b/llm-server/encoding/profiles/AV1 MKV 1080p30 Anime RF34.json new file mode 100644 index 0000000..88cee13 --- /dev/null +++ b/llm-server/encoding/profiles/AV1 MKV 1080p30 Anime RF34.json @@ -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 +} \ No newline at end of file diff --git a/llm-server/encoding/profiles/AV1 MKV 1080p30 RF25.json b/llm-server/encoding/profiles/AV1 MKV 1080p30 RF25.json new file mode 100644 index 0000000..51d9d7a --- /dev/null +++ b/llm-server/encoding/profiles/AV1 MKV 1080p30 RF25.json @@ -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 +} \ No newline at end of file diff --git a/llm-server/encoding/profiles/AV1 MKV 2160p60 4K RF23.json b/llm-server/encoding/profiles/AV1 MKV 2160p60 4K RF23.json new file mode 100644 index 0000000..b9b4eb3 --- /dev/null +++ b/llm-server/encoding/profiles/AV1 MKV 2160p60 4K RF23.json @@ -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 +} \ No newline at end of file