From 059f069ef4cd047a5a8974a84f0694a0ce9bf728 Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Thu, 21 May 2026 12:47:10 +0200 Subject: [PATCH] webapp: introduce Laravel app under webapp/ for scene overlays Replaces the per-scene HTML directories (landing/, loading/, game/, desktop/, goodbye/, music-box/, music/) with a single Laravel app serving every overlay over HTTP. Supervisord runs `php artisan serve` on 127.0.0.1:1118 and the OBS scene JSON now references HTTP routes instead of file:// URLs. Highlights: - public/css/hud.css consolidates the duplicated HUD chrome, scanlines/vignette/flicker, terminal styling, and pulse keyframes that were copy-pasted across all seven scenes. - Blade partials own hud-strip, crt-overlays, obs-ws-scripts, camera-frame, screen-frame; the seven scenes extend a shared overlay layout. - Artisan commands (`rig:setup`, `rig:telemetry`, `rig:loading`, `rig:playlist`, `rig:cmd`) replace the shell scripts that wrote per-rig JSON snapshots. TwitchHelix + HardwareSnapshot services handle the work the bash + Python helpers used to. - ObsWsClient + MusicCommandController kill the 250 ms cmd.js poll in the music daemon: POST /cmd/{skip|prev|pause|resume} opens a short-lived Pawl WS, authenticates, and broadcasts mpd:cmd. - AudioController streams files from the configured music dirs so CEF can load tracks under the HTTP origin (Chromium blocks HTTP-origin pages from loading file:// media). - DataController serves /data/playlist.js (with ETag mtime cache) and /cover.jpg (no-store) so the existing overlays' window.__PLAYLIST and cover.jpg cache-bust pattern keeps working. scripts/obs-webapp.supervisord.conf is the supervisord unit; install to /etc/supervisor.d/obs-webapp.conf. scripts/rewrite-scene-urls.py is a one-shot tool that rewrites basic/scenes/Default_Stream_HUD.json from file:// to HTTP URLs. basic/scenes/Default_Stream_HUD.json.pre-webapp is the rollback artifact (made with OBS closed; full pre-migration state). The seven old scene directories, vendor/, and the bash scripts are still on disk pending visual verification; the next commit will prune them. --- .gitignore | 46 +- basic/scenes/Default_Stream_HUD.json | 53 +- .../scenes/Default_Stream_HUD.json.pre-webapp | 3757 +++++++ scripts/obs-webapp.supervisord.conf | 31 + scripts/rewrite-scene-urls.py | 82 + webapp/.editorconfig | 18 + webapp/.gitattributes | 11 + webapp/.gitignore | 27 + webapp/README.md | 58 + webapp/app/Console/Commands/RigCmdCommand.php | 30 + .../Console/Commands/RigLoadingCommand.php | 101 + .../Console/Commands/RigPlaylistCommand.php | 125 + .../app/Console/Commands/RigSetupCommand.php | 84 + .../Console/Commands/RigTelemetryCommand.php | 42 + .../app/Http/Controllers/AudioController.php | 71 + webapp/app/Http/Controllers/Controller.php | 8 + .../app/Http/Controllers/DataController.php | 78 + .../Controllers/MusicCommandController.php | 20 + .../Overlays/DesktopController.php | 13 + .../Controllers/Overlays/GameController.php | 16 + .../Overlays/GoodbyeController.php | 16 + .../Overlays/LandingController.php | 16 + .../Overlays/LoadingController.php | 18 + .../Overlays/MusicBoxController.php | 33 + .../Overlays/MusicDaemonController.php | 13 + .../app/Providers/RigDataServiceProvider.php | 14 + webapp/app/Services/HardwareSnapshot.php | 186 + webapp/app/Services/ObsWsClient.php | 158 + webapp/app/Services/TwitchHelix.php | 92 + webapp/app/Support/RigData.php | 87 + webapp/artisan | 18 + webapp/bootstrap/app.php | 19 + webapp/bootstrap/cache/.gitignore | 2 + webapp/bootstrap/providers.php | 5 + webapp/composer.json | 49 + webapp/composer.lock | 8885 +++++++++++++++++ webapp/config/app.php | 126 + webapp/config/auth.php | 117 + webapp/config/cache.php | 136 + webapp/config/database.php | 184 + webapp/config/filesystems.php | 80 + webapp/config/logging.php | 132 + webapp/config/mail.php | 118 + webapp/config/queue.php | 129 + webapp/config/rig.php | 58 + webapp/config/services.php | 38 + webapp/config/session.php | 233 + webapp/phpunit.xml | 36 + webapp/public/.htaccess | 25 + .../public/audio}/static-hum.wav | Bin webapp/public/css/hud.css | 576 ++ webapp/public/favicon.ico | 0 webapp/public/index.php | 20 + webapp/public/js/crt-static.js | 28 + webapp/public/js/hud.js | 106 + webapp/public/js/obs-ws-bootstrap.js | 46 + webapp/public/js/obs-ws-mini.js | 237 + webapp/public/robots.txt | 2 + webapp/resources/css/app.css | 11 + webapp/resources/js/app.js | 1 + webapp/resources/views/index.blade.php | 34 + .../resources/views/layouts/overlay.blade.php | 25 + .../views/overlays/desktop.blade.php | 148 + .../resources/views/overlays/game.blade.php | 175 + .../views/overlays/goodbye.blade.php | 267 + .../views/overlays/landing.blade.php | 163 + .../views/overlays/loading.blade.php | 294 + .../views/overlays/music/box.blade.php | 195 + .../views/overlays/music/cover.blade.php | 186 + .../views/overlays/music/daemon.blade.php | 180 + .../views/overlays/music/nc.blade.php | 517 + .../views/overlays/music/widget.blade.php | 196 + .../views/partials/camera-frame.blade.php | 24 + .../views/partials/crt-overlays.blade.php | 4 + .../views/partials/hud-strip.blade.php | 27 + .../views/partials/obs-ws-scripts.blade.php | 2 + .../views/partials/screen-frame.blade.php | 11 + webapp/routes/web.php | 36 + webapp/storage/app/.gitignore | 4 + webapp/storage/app/private/.gitignore | 2 + webapp/storage/app/public/.gitignore | 2 + webapp/storage/framework/.gitignore | 9 + webapp/storage/framework/cache/.gitignore | 3 + .../storage/framework/cache/data/.gitignore | 2 + webapp/storage/framework/testing/.gitignore | 2 + 85 files changed, 19180 insertions(+), 49 deletions(-) create mode 100644 basic/scenes/Default_Stream_HUD.json.pre-webapp create mode 100644 scripts/obs-webapp.supervisord.conf create mode 100755 scripts/rewrite-scene-urls.py create mode 100644 webapp/.editorconfig create mode 100644 webapp/.gitattributes create mode 100644 webapp/.gitignore create mode 100644 webapp/README.md create mode 100644 webapp/app/Console/Commands/RigCmdCommand.php create mode 100644 webapp/app/Console/Commands/RigLoadingCommand.php create mode 100644 webapp/app/Console/Commands/RigPlaylistCommand.php create mode 100644 webapp/app/Console/Commands/RigSetupCommand.php create mode 100644 webapp/app/Console/Commands/RigTelemetryCommand.php create mode 100644 webapp/app/Http/Controllers/AudioController.php create mode 100644 webapp/app/Http/Controllers/Controller.php create mode 100644 webapp/app/Http/Controllers/DataController.php create mode 100644 webapp/app/Http/Controllers/MusicCommandController.php create mode 100644 webapp/app/Http/Controllers/Overlays/DesktopController.php create mode 100644 webapp/app/Http/Controllers/Overlays/GameController.php create mode 100644 webapp/app/Http/Controllers/Overlays/GoodbyeController.php create mode 100644 webapp/app/Http/Controllers/Overlays/LandingController.php create mode 100644 webapp/app/Http/Controllers/Overlays/LoadingController.php create mode 100644 webapp/app/Http/Controllers/Overlays/MusicBoxController.php create mode 100644 webapp/app/Http/Controllers/Overlays/MusicDaemonController.php create mode 100644 webapp/app/Providers/RigDataServiceProvider.php create mode 100644 webapp/app/Services/HardwareSnapshot.php create mode 100644 webapp/app/Services/ObsWsClient.php create mode 100644 webapp/app/Services/TwitchHelix.php create mode 100644 webapp/app/Support/RigData.php create mode 100755 webapp/artisan create mode 100644 webapp/bootstrap/app.php create mode 100644 webapp/bootstrap/cache/.gitignore create mode 100644 webapp/bootstrap/providers.php create mode 100644 webapp/composer.json create mode 100644 webapp/composer.lock create mode 100644 webapp/config/app.php create mode 100644 webapp/config/auth.php create mode 100644 webapp/config/cache.php create mode 100644 webapp/config/database.php create mode 100644 webapp/config/filesystems.php create mode 100644 webapp/config/logging.php create mode 100644 webapp/config/mail.php create mode 100644 webapp/config/queue.php create mode 100644 webapp/config/rig.php create mode 100644 webapp/config/services.php create mode 100644 webapp/config/session.php create mode 100644 webapp/phpunit.xml create mode 100644 webapp/public/.htaccess rename {landing => webapp/public/audio}/static-hum.wav (100%) create mode 100644 webapp/public/css/hud.css create mode 100644 webapp/public/favicon.ico create mode 100644 webapp/public/index.php create mode 100644 webapp/public/js/crt-static.js create mode 100644 webapp/public/js/hud.js create mode 100644 webapp/public/js/obs-ws-bootstrap.js create mode 100644 webapp/public/js/obs-ws-mini.js create mode 100644 webapp/public/robots.txt create mode 100644 webapp/resources/css/app.css create mode 100644 webapp/resources/js/app.js create mode 100644 webapp/resources/views/index.blade.php create mode 100644 webapp/resources/views/layouts/overlay.blade.php create mode 100644 webapp/resources/views/overlays/desktop.blade.php create mode 100644 webapp/resources/views/overlays/game.blade.php create mode 100644 webapp/resources/views/overlays/goodbye.blade.php create mode 100644 webapp/resources/views/overlays/landing.blade.php create mode 100644 webapp/resources/views/overlays/loading.blade.php create mode 100644 webapp/resources/views/overlays/music/box.blade.php create mode 100644 webapp/resources/views/overlays/music/cover.blade.php create mode 100644 webapp/resources/views/overlays/music/daemon.blade.php create mode 100644 webapp/resources/views/overlays/music/nc.blade.php create mode 100644 webapp/resources/views/overlays/music/widget.blade.php create mode 100644 webapp/resources/views/partials/camera-frame.blade.php create mode 100644 webapp/resources/views/partials/crt-overlays.blade.php create mode 100644 webapp/resources/views/partials/hud-strip.blade.php create mode 100644 webapp/resources/views/partials/obs-ws-scripts.blade.php create mode 100644 webapp/resources/views/partials/screen-frame.blade.php create mode 100644 webapp/routes/web.php create mode 100644 webapp/storage/app/.gitignore create mode 100644 webapp/storage/app/private/.gitignore create mode 100644 webapp/storage/app/public/.gitignore create mode 100644 webapp/storage/framework/.gitignore create mode 100644 webapp/storage/framework/cache/.gitignore create mode 100644 webapp/storage/framework/cache/data/.gitignore create mode 100644 webapp/storage/framework/testing/.gitignore diff --git a/.gitignore b/.gitignore index 5d3e303..abe0eab 100644 --- a/.gitignore +++ b/.gitignore @@ -24,36 +24,33 @@ plugin_config/obs-websocket/ # Machine-local generated data # ============================================================ -# Generated wrapper for telemetry.json — built by telemetry.sh, not edited. -# (The JSON itself is tracked: it's the hand-edited source for the wrapper.) -landing/telemetry.js - -# Per-session manifest written by loading.sh — ephemeral, not version-controlled. -loading/loading.json -loading/loading.js - -# Playlist manifest — generated from the audio in /playlist/, machine-local. -loading/playlist.json -loading/playlist.js - -# Music daemon command file — written by playlist.sh skip/prev/pause/resume. -loading/cmd.js - -# Audio files for the loading scene (any format). Kept out of git regardless -# of how they got there (yt-dlp, manual copy, etc.). -/playlist/ +# Per-session rig data written by `php artisan rig:loading`, `rig:playlist`, +# `rig:telemetry`. Re-derived on demand from the live system + Twitch API. +webapp/storage/data/*.json # OBS WebSocket connection details (port + password) — auto-generated by -# setup.sh from your existing plugin_config/obs-websocket/config.json. -vendor/obs-config.js +# `php artisan rig:setup` from plugin_config/obs-websocket/config.json. +webapp/public/js/obs-config.js # Album-art cache written by bridges/mpd-state.py on every track change — -# overlays load it as ``. Pure runtime state. +# the music-box/cover overlay loads it through `/cover.jpg?v=`. bridges/cover.jpg -# Local backups created during edits — keep out of git noise. -landing/*.manual -loading/*.manual +# Audio files for the loading/music-box scenes (any format). Kept out of git +# regardless of how they got there (yt-dlp, manual copy, etc.). +/playlist/ + +# Local backups created during rig:telemetry review. +webapp/storage/data/*.manual + +# Laravel-native ignores (composer deps, log + cache + session dirs). +/webapp/vendor/ +/webapp/node_modules/ +/webapp/storage/logs/*.log +/webapp/storage/framework/cache/data/ +/webapp/storage/framework/sessions/ +/webapp/storage/framework/views/ +/webapp/bootstrap/cache/*.php # ============================================================ # OBS runtime / generated state — no value in version control @@ -99,3 +96,4 @@ __pycache__/ *.pyc *.pyo temp +.claude/ diff --git a/basic/scenes/Default_Stream_HUD.json b/basic/scenes/Default_Stream_HUD.json index c134ca9..adcb693 100644 --- a/basic/scenes/Default_Stream_HUD.json +++ b/basic/scenes/Default_Stream_HUD.json @@ -269,7 +269,8 @@ "id": "browser_source", "versioned_id": "browser_source", "settings": { - "url": "file:///home/jin/.config/obs-studio/landing/index.html", + "is_local_file": false, + "url": "http://127.0.0.1:1118/landing", "width": 2560, "height": 1336, "fps": 30, @@ -309,7 +310,7 @@ "id": "ffmpeg_source", "versioned_id": "ffmpeg_source", "settings": { - "local_file": "/home/jin/.config/obs-studio/landing/static-hum.wav", + "local_file": "/home/jin/.config/obs-studio/webapp/public/audio/static-hum.wav", "close_when_inactive": false, "hw_decode": false, "is_local_file": true, @@ -350,9 +351,8 @@ "id": "browser_source", "versioned_id": "browser_source", "settings": { - "is_local_file": true, - "local_file": "/home/jin/.config/obs-studio/loading/index.html", - "url": "file:///home/jin/.config/obs-studio/loading/index.html", + "is_local_file": false, + "url": "http://127.0.0.1:1118/loading", "width": 2560, "height": 1336, "restart_when_active": true, @@ -419,8 +419,8 @@ "id": "browser_source", "versioned_id": "browser_source", "settings": { - "is_local_file": true, - "local_file": "/home/jin/.config/obs-studio/music/index.html", + "is_local_file": false, + "url": "http://127.0.0.1:1118/music-daemon", "width": 400, "height": 100, "shutdown": true, @@ -456,8 +456,8 @@ "id": "browser_source", "versioned_id": "browser_source", "settings": { - "is_local_file": true, - "local_file": "/home/jin/.config/obs-studio/game/index.html", + "is_local_file": false, + "url": "http://127.0.0.1:1118/game", "width": 2560, "height": 1336, "fps": 15, @@ -494,8 +494,8 @@ "id": "browser_source", "versioned_id": "browser_source", "settings": { - "is_local_file": true, - "local_file": "/home/jin/.config/obs-studio/goodbye/index.html", + "is_local_file": false, + "url": "http://127.0.0.1:1118/goodbye", "width": 2560, "height": 1336, "fps_custom": true, @@ -536,7 +536,7 @@ "mixers": 255, "sync": 0, "flags": 0, - "volume": 0.20938195288181305, + "volume": 0.19247779250144958, "balance": 0.5, "enabled": true, "muted": false, @@ -567,7 +567,7 @@ "mixers": 255, "sync": 0, "flags": 0, - "volume": 0.0053968820720911026, + "volume": 0.01389647088944912, "balance": 0.5, "enabled": true, "muted": false, @@ -593,8 +593,8 @@ "id": "browser_source", "versioned_id": "browser_source", "settings": { - "is_local_file": true, - "local_file": "/home/jin/.config/obs-studio/desktop/index.html", + "is_local_file": false, + "url": "http://127.0.0.1:1118/desktop", "width": 2560, "height": 1336, "fps": 15, @@ -631,8 +631,8 @@ "id": "browser_source", "versioned_id": "browser_source", "settings": { - "is_local_file": true, - "local_file": "/home/jin/.config/obs-studio/music-box/index.html", + "is_local_file": false, + "url": "http://127.0.0.1:1118/music-box", "width": 2560, "height": 1336, "fps": 30, @@ -670,9 +670,9 @@ "id": "browser_source", "versioned_id": "browser_source", "settings": { - "is_local_file": true, - "local_file": "/home/jin/.config/obs-studio/music-box/widget.html", + "is_local_file": false, "undo_uuid": "f662d513-6fe6-49fd-99f6-ea7f44e8de18", + "url": "http://127.0.0.1:1118/music-box/widget", "width": 549, "height": 880, "fps": 15, @@ -709,7 +709,8 @@ "id": "browser_source", "versioned_id": "browser_source", "settings": { - "url": "file:///home/jin/.config/obs-studio/music-box/cover.html" + "is_local_file": false, + "url": "http://127.0.0.1:1118/music-box/cover" }, "mixers": 255, "sync": 0, @@ -741,7 +742,8 @@ "id": "browser_source", "versioned_id": "browser_source", "settings": { - "url": "file:///home/jin/.config/obs-studio/music-box/cover.html?bars=0" + "is_local_file": false, + "url": "http://127.0.0.1:1118/music-box/cover?bars=0" }, "mixers": 255, "sync": 0, @@ -773,7 +775,8 @@ "id": "browser_source", "versioned_id": "browser_source", "settings": { - "url": "file:///home/jin/.config/obs-studio/music-box/nc-music-box.html", + "is_local_file": false, + "url": "http://127.0.0.1:1118/music-box/nc", "width": 2560, "height": 1331, "fps": 15, @@ -3683,8 +3686,8 @@ "name": "Good Bye" } ], - "current_scene": "Good Bye", - "current_program_scene": "Good Bye", + "current_scene": "Music Box", + "current_program_scene": "Music Box", "canvases": [], "current_transition": "Luma Wipe", "transition_duration": 1000, @@ -3716,7 +3719,7 @@ "saved_projectors": [], "preview_locked": false, "scaling_enabled": false, - "scaling_level": -12, + "scaling_level": -14, "scaling_off_x": 0.0, "scaling_off_y": 0.0, "modules": { diff --git a/basic/scenes/Default_Stream_HUD.json.pre-webapp b/basic/scenes/Default_Stream_HUD.json.pre-webapp new file mode 100644 index 0000000..e0ef678 --- /dev/null +++ b/basic/scenes/Default_Stream_HUD.json.pre-webapp @@ -0,0 +1,3757 @@ +{ + "name": "Default Stream HUD", + "DesktopAudioDevice1": { + "prev_ver": 536936450, + "name": "Desktop Audio", + "uuid": "5c879e8d-88e7-41d6-8371-474b9369d8a5", + "id": "pulse_output_capture", + "versioned_id": "pulse_output_capture", + "settings": { + "device_id": "alsa_output.pci-0000_2b_00.3.analog-stereo.monitor" + }, + "mixers": 255, + "sync": 0, + "flags": 0, + "volume": 0.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "libobs.mute": [], + "libobs.unmute": [], + "libobs.push-to-mute": [], + "libobs.push-to-talk": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + }, + "AuxAudioDevice1": { + "prev_ver": 536936450, + "name": "Mic/Aux", + "uuid": "fe24bff3-762e-416b-862a-6273326c13d0", + "id": "pulse_input_capture", + "versioned_id": "pulse_input_capture", + "settings": { + "device_id": "default" + }, + "mixers": 255, + "sync": 0, + "flags": 0, + "volume": 0.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "libobs.mute": [], + "libobs.unmute": [], + "libobs.push-to-mute": [], + "libobs.push-to-talk": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + }, + "sources": [ + { + "prev_ver": 536936450, + "name": "Starting Soon", + "uuid": "4b902d7f-9205-444c-8c8b-b559479d5bea", + "id": "image_source", + "versioned_id": "image_source", + "settings": { + "file": "/home/jin/HDD/Images/Gifs/stamd.png" + }, + "mixers": 0, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": {}, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "Doomguard Logo", + "uuid": "ee77caf1-a490-45fa-80d9-4072f7e63d9a", + "id": "image_source", + "versioned_id": "image_source", + "settings": { + "file": "/home/jin/cloud.jakubzych.com/_img/logos/dgw.png" + }, + "mixers": 0, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": {}, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "info", + "uuid": "cfcf2652-2777-4b92-a53b-54703f23deff", + "id": "text_ft2_source", + "versioned_id": "text_ft2_source_v2", + "settings": { + "text": "Helping twitch.tv/eisenhowler setup stream", + "font": { + "face": "Sansation", + "flags": 0, + "size": 14, + "style": "Regular" + } + }, + "mixers": 0, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": {}, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "Camera", + "uuid": "a8b362a1-91a6-491b-8751-26e2dad55ccd", + "id": "v4l2_input", + "versioned_id": "v4l2_input", + "settings": { + "device_id": "/dev/video0", + "Brightness": 0, + "Contrast": 35, + "Saturation": 64, + "Gamma": 99, + "Gain": 2, + "input": 0, + "pixelformat": 1196444237, + "resolution": -1 + }, + "mixers": 0, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": {}, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {}, + "filters": [ + { + "prev_ver": 536936450, + "name": "Crop/Pad", + "uuid": "8ea7885e-70c6-4dfd-b5f9-a7581f61b7c2", + "id": "crop_filter", + "versioned_id": "crop_filter", + "settings": { + "top": 360, + "right": 700 + }, + "mixers": 0, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": {}, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + } + ] + }, + { + "prev_ver": 536936450, + "name": "Screen Capture (PipeWire)", + "uuid": "8f6ef725-1eec-4fdf-930a-2aab93d84c06", + "id": "pipewire-screen-capture-source", + "versioned_id": "pipewire-screen-capture-source", + "settings": { + "RestoreToken": "3951c703-09cb-4e8a-9dfd-14b4090da608" + }, + "mixers": 0, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": {}, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "Screen Capture (PipeWire) 2", + "uuid": "1e23924b-8260-4246-b7ee-74456fb62e65", + "id": "pipewire-screen-capture-source", + "versioned_id": "pipewire-screen-capture-source", + "settings": { + "RestoreToken": "2b7b68cb-b5aa-4da2-b7b7-3c1e54e997e4" + }, + "mixers": 0, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": {}, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "Landing — Stand By (Browser)", + "uuid": "dd513385-f6d8-44da-a459-20acbe620823", + "id": "browser_source", + "versioned_id": "browser_source", + "settings": { + "url": "file:///home/jin/.config/obs-studio/landing/index.html", + "width": 2560, + "height": 1336, + "fps": 30, + "fps_custom": true, + "shutdown": false, + "restart_when_active": false, + "webpage_control_level": 1, + "reroute_audio": false + }, + "mixers": 0, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "libobs.mute": [], + "libobs.unmute": [], + "libobs.push-to-mute": [], + "libobs.push-to-talk": [], + "ObsBrowser.Refresh": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "Landing — Static Hum", + "uuid": "16cc92de-4b9b-4b1c-b405-8f089991ac86", + "id": "ffmpeg_source", + "versioned_id": "ffmpeg_source", + "settings": { + "local_file": "/home/jin/.config/obs-studio/landing/static-hum.wav", + "close_when_inactive": false, + "hw_decode": false, + "is_local_file": true, + "looping": true, + "clear_on_media_end": false, + "restart_on_activate": true + }, + "mixers": 255, + "sync": 0, + "flags": 0, + "volume": 0.35087639093399048, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "libobs.mute": [], + "libobs.unmute": [], + "libobs.push-to-mute": [], + "libobs.push-to-talk": [], + "MediaSource.Restart": [], + "MediaSource.Play": [], + "MediaSource.Pause": [], + "MediaSource.Stop": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "Loading Project", + "uuid": "6642f830-fbae-4020-8292-a9cb7991e6fc", + "id": "browser_source", + "versioned_id": "browser_source", + "settings": { + "is_local_file": true, + "local_file": "/home/jin/.config/obs-studio/loading/index.html", + "url": "file:///home/jin/.config/obs-studio/loading/index.html", + "width": 2560, + "height": 1336, + "restart_when_active": true, + "reroute_audio": false + }, + "mixers": 255, + "sync": 0, + "flags": 0, + "volume": 0.1156293973326683, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "libobs.mute": [], + "libobs.unmute": [], + "libobs.push-to-mute": [], + "libobs.push-to-talk": [], + "ObsBrowser.Refresh": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 2, + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "Audio", + "uuid": "fc5dfd34-bc55-4c92-8b5e-ef305afd1d8e", + "id": "pulse_input_capture", + "versioned_id": "pulse_input_capture", + "settings": { + "device_id": "alsa_input.usb-Creative_Technology_Ltd_Creative_Live__Cam_Sync_V3_SN0001-02.analog-stereo" + }, + "mixers": 255, + "sync": 0, + "flags": 0, + "volume": 0.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "libobs.mute": [], + "libobs.unmute": [], + "libobs.push-to-mute": [], + "libobs.push-to-talk": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "Audio Player", + "uuid": "b7b89406-53f4-45ba-aa1a-c20d17b1c905", + "id": "browser_source", + "versioned_id": "browser_source", + "settings": { + "is_local_file": true, + "local_file": "/home/jin/.config/obs-studio/music/index.html", + "width": 400, + "height": 100, + "shutdown": true, + "reroute_audio": true + }, + "mixers": 255, + "sync": 0, + "flags": 0, + "volume": 0.13031470775604248, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "libobs.mute": [], + "libobs.unmute": [], + "libobs.push-to-mute": [], + "libobs.push-to-talk": [], + "ObsBrowser.Refresh": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 2, + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "HUD", + "uuid": "f409a999-358f-488d-98db-c5c6a01d01b9", + "id": "browser_source", + "versioned_id": "browser_source", + "settings": { + "is_local_file": true, + "local_file": "/home/jin/.config/obs-studio/game/index.html", + "width": 2560, + "height": 1336, + "fps": 15, + "fps_custom": true, + "shutdown": true + }, + "mixers": 255, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "libobs.mute": [], + "libobs.unmute": [], + "libobs.push-to-mute": [], + "libobs.push-to-talk": [], + "ObsBrowser.Refresh": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "GB", + "uuid": "38306c90-5bb8-4603-a21c-09383c300097", + "id": "browser_source", + "versioned_id": "browser_source", + "settings": { + "is_local_file": true, + "local_file": "/home/jin/.config/obs-studio/goodbye/index.html", + "width": 2560, + "height": 1336, + "fps_custom": true, + "restart_when_active": true + }, + "mixers": 255, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "libobs.mute": [], + "libobs.unmute": [], + "libobs.push-to-mute": [], + "libobs.push-to-talk": [], + "ObsBrowser.Refresh": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "MPD", + "uuid": "25440930-0606-46d9-ba2c-be786ff4a3ae", + "id": "pulse_output_capture", + "versioned_id": "pulse_output_capture", + "settings": { + "device_id": "mpd_stream.monitor" + }, + "mixers": 255, + "sync": 0, + "flags": 0, + "volume": 0.19247779250144958, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "libobs.mute": [], + "libobs.unmute": [], + "libobs.push-to-mute": [], + "libobs.push-to-talk": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "MPD Silent", + "uuid": "41387cb0-801b-49df-92e6-75c7293bec3f", + "id": "pulse_output_capture", + "versioned_id": "pulse_output_capture", + "settings": { + "device_id": "mpd_stream.monitor" + }, + "mixers": 255, + "sync": 0, + "flags": 0, + "volume": 0.01389647088944912, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "libobs.mute": [], + "libobs.unmute": [], + "libobs.push-to-mute": [], + "libobs.push-to-talk": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "Desktop HUD", + "uuid": "8acfb832-cfb4-4310-8ebb-b58fad2b3509", + "id": "browser_source", + "versioned_id": "browser_source", + "settings": { + "is_local_file": true, + "local_file": "/home/jin/.config/obs-studio/desktop/index.html", + "width": 2560, + "height": 1336, + "fps": 15, + "fps_custom": true, + "shutdown": true + }, + "mixers": 255, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "libobs.mute": [], + "libobs.unmute": [], + "libobs.push-to-mute": [], + "libobs.push-to-talk": [], + "ObsBrowser.Refresh": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "Music Box HUD", + "uuid": "1b9f5436-864d-4f24-9f92-f45702a23953", + "id": "browser_source", + "versioned_id": "browser_source", + "settings": { + "is_local_file": true, + "local_file": "/home/jin/.config/obs-studio/music-box/index.html", + "width": 2560, + "height": 1336, + "fps": 30, + "fps_custom": true, + "shutdown": true, + "restart_when_active": true + }, + "mixers": 255, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "libobs.mute": [], + "libobs.unmute": [], + "libobs.push-to-mute": [], + "libobs.push-to-talk": [], + "ObsBrowser.Refresh": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "Music Box Widget", + "uuid": "f662d513-6fe6-49fd-99f6-ea7f44e8de18", + "id": "browser_source", + "versioned_id": "browser_source", + "settings": { + "is_local_file": true, + "local_file": "/home/jin/.config/obs-studio/music-box/widget.html", + "undo_uuid": "f662d513-6fe6-49fd-99f6-ea7f44e8de18", + "width": 549, + "height": 880, + "fps": 15, + "fps_custom": true, + "shutdown": true + }, + "mixers": 255, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "libobs.mute": [], + "libobs.unmute": [], + "libobs.push-to-mute": [], + "libobs.push-to-talk": [], + "ObsBrowser.Refresh": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "Cover", + "uuid": "54d4ac81-e354-431a-a2dc-a00e5555e8d9", + "id": "browser_source", + "versioned_id": "browser_source", + "settings": { + "url": "file:///home/jin/.config/obs-studio/music-box/cover.html" + }, + "mixers": 255, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "libobs.mute": [], + "libobs.unmute": [], + "libobs.push-to-mute": [], + "libobs.push-to-talk": [], + "ObsBrowser.Refresh": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "Cover Simple", + "uuid": "7062784f-7b1b-4728-9067-27128c0d5748", + "id": "browser_source", + "versioned_id": "browser_source", + "settings": { + "url": "file:///home/jin/.config/obs-studio/music-box/cover.html?bars=0" + }, + "mixers": 255, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "libobs.mute": [], + "libobs.unmute": [], + "libobs.push-to-mute": [], + "libobs.push-to-talk": [], + "ObsBrowser.Refresh": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "NC Music Box HUD", + "uuid": "edbd893d-ce53-4c91-bf6c-4d52fbf394d6", + "id": "browser_source", + "versioned_id": "browser_source", + "settings": { + "url": "file:///home/jin/.config/obs-studio/music-box/nc-music-box.html", + "width": 2560, + "height": 1331, + "fps": 15, + "fps_custom": true + }, + "mixers": 255, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "libobs.mute": [], + "libobs.unmute": [], + "libobs.push-to-mute": [], + "libobs.push-to-talk": [], + "ObsBrowser.Refresh": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "Waveform Visualizer", + "uuid": "f28cc995-6966-4afa-b831-ebb50d89ebe8", + "id": "phandasm_waveform_source", + "versioned_id": "phandasm_waveform_source", + "settings": { + "pulse_mode": "peak_magnitude", + "audio_source": "MPD", + "display_mode": "stepped_bars", + "channel_mode": "mono", + "render_mode": "solid", + "color_base": 4278216704, + "step_gap": 3 + }, + "mixers": 0, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": {}, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "Waveform Stereo", + "uuid": "bc63aaf3-2ae2-4de9-9c7a-c05e55fa2a11", + "id": "phandasm_waveform_source", + "versioned_id": "phandasm_waveform_source", + "settings": { + "audio_source": "MPD", + "display_mode": "stepped_bars", + "channel_mode": "single", + "temporal_smoothing": "exp_moving_avg", + "color_base": 4278216704, + "bar_width": 28 + }, + "mixers": 0, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": {}, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "Discord", + "uuid": "0d7d9693-0a0c-4a52-af3a-1ec742590358", + "id": "pulse_output_capture", + "versioned_id": "pulse_output_capture", + "settings": { + "device_id": "discord_stream.monitor" + }, + "mixers": 255, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "libobs.mute": [], + "libobs.unmute": [], + "libobs.push-to-mute": [], + "libobs.push-to-talk": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "Desktop", + "uuid": "af8849dc-ea94-40bf-b018-3297518b1b04", + "id": "scene", + "versioned_id": "scene", + "settings": { + "custom_size": false, + "id_counter": 18, + "items": [ + { + "name": "Screen Capture (PipeWire)", + "source_uuid": "8f6ef725-1eec-4fdf-930a-2aab93d84c06", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 1, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 13, + "group_item_backup": false, + "pos": { + "x": 534.0, + "y": 7.0 + }, + "pos_rel": { + "x": -1.1167664527893066, + "y": -0.98952096700668335 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "scale_rel": { + "x": 1.0, + "y": 1.0 + }, + "bounds": { + "x": 2017.0, + "y": 1213.0 + }, + "bounds_rel": { + "x": 3.0194611549377441, + "y": 1.8158682584762573 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 0 + }, + "hide_transition": { + "duration": 0 + }, + "private_settings": {} + }, + { + "name": "MPD", + "source_uuid": "25440930-0606-46d9-ba2c-be786ff4a3ae", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 14, + "group_item_backup": false, + "pos": { + "x": 0.0, + "y": 0.0 + }, + "pos_rel": { + "x": -1.9161676168441772, + "y": -1.0 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "scale_rel": { + "x": 1.0, + "y": 1.0 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 300 + }, + "hide_transition": { + "duration": 300 + }, + "private_settings": {} + }, + { + "name": "Desktop HUD", + "source_uuid": "8acfb832-cfb4-4310-8ebb-b58fad2b3509", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 1, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 15, + "group_item_backup": false, + "pos": { + "x": 0.0, + "y": 0.0 + }, + "pos_rel": { + "x": -1.9161676168441772, + "y": -1.0 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "scale_rel": { + "x": 1.0, + "y": 1.0 + }, + "bounds": { + "x": 2560.0, + "y": 1336.0 + }, + "bounds_rel": { + "x": 3.8323352336883545, + "y": 2.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 300 + }, + "hide_transition": { + "duration": 300 + }, + "private_settings": {} + }, + { + "name": "Camera", + "source_uuid": "a8b362a1-91a6-491b-8751-26e2dad55ccd", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 1920.0, + "y": 1080.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 16, + "group_item_backup": false, + "pos": { + "x": 22.0, + "y": 902.0 + }, + "pos_rel": { + "x": -1.8832335472106934, + "y": 0.35029935836791992 + }, + "scale": { + "x": 0.39754101634025574, + "y": 0.39722222089767456 + }, + "scale_rel": { + "x": 0.32136547565460205, + "y": 0.32110777497291565 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 0 + }, + "hide_transition": { + "duration": 0 + }, + "private_settings": {} + }, + { + "name": "Music Box Widget", + "source_uuid": "f662d513-6fe6-49fd-99f6-ea7f44e8de18", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 549.0, + "y": 880.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 17, + "group_item_backup": false, + "pos": { + "x": 12.0, + "y": 6.0 + }, + "pos_rel": { + "x": -1.8982036113739014, + "y": -0.99101793766021729 + }, + "scale": { + "x": 0.91985428333282471, + "y": 0.91931825876235962 + }, + "scale_rel": { + "x": 0.60589206218719482, + "y": 0.60553896427154541 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 300 + }, + "hide_transition": { + "duration": 300 + }, + "private_settings": {} + }, + { + "name": "Discord", + "source_uuid": "0d7d9693-0a0c-4a52-af3a-1ec742590358", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 18, + "group_item_backup": false, + "pos": { + "x": 0.0, + "y": 0.0 + }, + "pos_rel": { + "x": -1.9161676168441772, + "y": -1.0 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "scale_rel": { + "x": 1.0, + "y": 1.0 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 0 + }, + "hide_transition": { + "duration": 0 + }, + "private_settings": {} + } + ] + }, + "mixers": 0, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "OBSBasic.SelectScene": [ + { + "control": true, + "key": "OBS_KEY_COMMA" + } + ], + "libobs.show_scene_item.13": [], + "libobs.hide_scene_item.13": [], + "libobs.show_scene_item.14": [], + "libobs.hide_scene_item.14": [], + "libobs.show_scene_item.15": [], + "libobs.hide_scene_item.15": [], + "libobs.show_scene_item.16": [], + "libobs.hide_scene_item.16": [], + "libobs.show_scene_item.17": [], + "libobs.hide_scene_item.17": [], + "libobs.show_scene_item.18": [], + "libobs.hide_scene_item.18": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "canvas_uuid": "6c69626f-6273-4c00-9d88-c5136d61696e", + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "Game", + "uuid": "45b37004-1f57-4395-aeb1-06599415317d", + "id": "scene", + "versioned_id": "scene", + "settings": { + "custom_size": false, + "id_counter": 25, + "items": [ + { + "name": "Screen Capture (PipeWire) 2", + "source_uuid": "1e23924b-8260-4246-b7ee-74456fb62e65", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 1, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 21, + "group_item_backup": false, + "pos": { + "x": 0.0, + "y": 0.0 + }, + "pos_rel": { + "x": -1.9161676168441772, + "y": -1.0 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "scale_rel": { + "x": 1.0, + "y": 1.0 + }, + "bounds": { + "x": 2560.0, + "y": 1336.0 + }, + "bounds_rel": { + "x": 3.8323352336883545, + "y": 2.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 300 + }, + "hide_transition": { + "duration": 300 + }, + "private_settings": {} + }, + { + "name": "Doomguard Logo", + "source_uuid": "ee77caf1-a490-45fa-80d9-4072f7e63d9a", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 1920.0, + "y": 1080.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 6, + "group_item_backup": true, + "pos": { + "x": 2350.0, + "y": -0.99993896484375 + }, + "pos_rel": { + "x": 1.6017963886260986, + "y": -1.0014969110488892 + }, + "scale": { + "x": 0.35290849208831787, + "y": 0.35290849208831787 + }, + "scale_rel": { + "x": 0.28528529405593872, + "y": 0.28528529405593872 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 0 + }, + "hide_transition": { + "duration": 0 + }, + "private_settings": {} + }, + { + "name": "Starting Soon", + "source_uuid": "4b902d7f-9205-444c-8c8b-b559479d5bea", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 1920.0, + "y": 1080.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 10, + "group_item_backup": true, + "pos": { + "x": 835.35546875, + "y": 488.00006103515625 + }, + "pos_rel": { + "x": -0.66563552618026733, + "y": -0.26946097612380981 + }, + "scale": { + "x": 2.042553186416626, + "y": 2.0388350486755371 + }, + "scale_rel": { + "x": 1.6511658430099487, + "y": 1.6481600999832153 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "id": "wipe_transition", + "versioned_id": "wipe_transition", + "name": "Starting Soon Show Transition", + "transition": {}, + "duration": 1000 + }, + "hide_transition": { + "duration": 0 + }, + "private_settings": {} + }, + { + "name": "info", + "source_uuid": "cfcf2652-2777-4b92-a53b-54703f23deff", + "visible": false, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 1920.0, + "y": 1080.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 8, + "group_item_backup": true, + "pos": { + "x": 911.3555908203125, + "y": 1192.0 + }, + "pos_rel": { + "x": -0.55186289548873901, + "y": 0.7844310998916626 + }, + "scale": { + "x": 2.5, + "y": 2.5 + }, + "scale_rel": { + "x": 2.0209579467773438, + "y": 2.0209579467773438 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 0 + }, + "hide_transition": { + "duration": 0 + }, + "private_settings": {} + }, + { + "name": "STAND BY", + "source_uuid": "d730eeb6-8969-4265-b033-5f777686ce2d", + "visible": false, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 1920.0, + "y": 1080.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 12, + "group_item_backup": false, + "pos": { + "x": 835.35546875, + "y": -0.99993896484375 + }, + "pos_rel": { + "x": -0.66563552618026733, + "y": -1.0014969110488892 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "scale_rel": { + "x": 1.0, + "y": 1.0 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 0 + }, + "hide_transition": { + "duration": 0 + }, + "private_settings": { + "collapsed": false + } + }, + { + "name": "Audio Player", + "source_uuid": "b7b89406-53f4-45ba-aa1a-c20d17b1c905", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 22, + "group_item_backup": false, + "pos": { + "x": -315.0, + "y": 87.0 + }, + "pos_rel": { + "x": -2.3877246379852295, + "y": -0.86976051330566406 + }, + "scale": { + "x": 0.5975000262260437, + "y": 0.60000002384185791 + }, + "scale_rel": { + "x": 0.5975000262260437, + "y": 0.60000002384185791 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 300 + }, + "hide_transition": { + "duration": 300 + }, + "private_settings": {} + }, + { + "name": "HUD", + "source_uuid": "f409a999-358f-488d-98db-c5c6a01d01b9", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 1, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 23, + "group_item_backup": false, + "pos": { + "x": 0.0, + "y": 0.0 + }, + "pos_rel": { + "x": -1.9161676168441772, + "y": -1.0 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "scale_rel": { + "x": 1.0, + "y": 1.0 + }, + "bounds": { + "x": 2560.0, + "y": 1336.0 + }, + "bounds_rel": { + "x": 3.8323352336883545, + "y": 2.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 300 + }, + "hide_transition": { + "duration": 300 + }, + "private_settings": {} + }, + { + "name": "Camera", + "source_uuid": "a8b362a1-91a6-491b-8751-26e2dad55ccd", + "visible": false, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 1920.0, + "y": 1080.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 16, + "group_item_backup": false, + "pos": { + "x": 83.0, + "y": 88.0 + }, + "pos_rel": { + "x": -1.7919161319732666, + "y": -0.86826348304748535 + }, + "scale": { + "x": 0.24262295663356781, + "y": 0.24318182468414307 + }, + "scale_rel": { + "x": 0.19613233208656311, + "y": 0.19658410549163818 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "id": "wipe_transition", + "versioned_id": "wipe_transition", + "name": "Camera Show Transition", + "transition": { + "luma_image": "checkerboard-small.png" + }, + "duration": 1000 + }, + "hide_transition": { + "id": "wipe_transition", + "versioned_id": "wipe_transition", + "name": "Camera Hide Transition", + "transition": { + "luma_image": "checkerboard-small.png" + }, + "duration": 1000 + }, + "private_settings": {} + }, + { + "name": "MPD", + "source_uuid": "25440930-0606-46d9-ba2c-be786ff4a3ae", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 24, + "group_item_backup": false, + "pos": { + "x": 0.0, + "y": 0.0 + }, + "pos_rel": { + "x": -1.9161676168441772, + "y": -1.0 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "scale_rel": { + "x": 1.0, + "y": 1.0 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 300 + }, + "hide_transition": { + "duration": 300 + }, + "private_settings": {} + }, + { + "name": "Discord", + "source_uuid": "0d7d9693-0a0c-4a52-af3a-1ec742590358", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 25, + "group_item_backup": false, + "pos": { + "x": 0.0, + "y": 0.0 + }, + "pos_rel": { + "x": -1.9161676168441772, + "y": -1.0 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "scale_rel": { + "x": 1.0, + "y": 1.0 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 0 + }, + "hide_transition": { + "duration": 0 + }, + "private_settings": {} + } + ] + }, + "mixers": 0, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "OBSBasic.SelectScene": [ + { + "control": true, + "key": "OBS_KEY_PERIOD" + } + ], + "libobs.show_scene_item.21": [], + "libobs.hide_scene_item.21": [], + "libobs.show_scene_item.12": [], + "libobs.hide_scene_item.12": [], + "libobs.show_scene_item.22": [], + "libobs.hide_scene_item.22": [], + "libobs.show_scene_item.23": [], + "libobs.hide_scene_item.23": [], + "libobs.show_scene_item.16": [], + "libobs.hide_scene_item.16": [], + "libobs.show_scene_item.24": [], + "libobs.hide_scene_item.24": [], + "libobs.show_scene_item.25": [], + "libobs.hide_scene_item.25": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "canvas_uuid": "6c69626f-6273-4c00-9d88-c5136d61696e", + "private_settings": { + "transition": "" + } + }, + { + "prev_ver": 536936450, + "name": "Landing", + "uuid": "2b56b1b1-2f3e-44f0-ac0f-5d33cad38551", + "id": "scene", + "versioned_id": "scene", + "settings": { + "id_counter": 6, + "custom_size": false, + "items": [ + { + "name": "Landing — Stand By (Browser)", + "source_uuid": "dd513385-f6d8-44da-a459-20acbe620823", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 1, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 1, + "group_item_backup": false, + "pos": { + "x": 4.0, + "y": 0.0 + }, + "pos_rel": { + "x": -1.910179615020752, + "y": -1.0 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "scale_rel": { + "x": 1.0, + "y": 1.0 + }, + "bounds": { + "x": 2560.0, + "y": 1336.0 + }, + "bounds_rel": { + "x": 3.8323352336883545, + "y": 2.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 300 + }, + "hide_transition": { + "id": "wipe_transition", + "versioned_id": "wipe_transition", + "name": "Landing — Stand By (Browser) Hide Transition", + "transition": { + "luma_image": "cloud.png" + }, + "duration": 300 + }, + "private_settings": {} + }, + { + "name": "Landing — Static Hum", + "source_uuid": "16cc92de-4b9b-4b1c-b405-8f089991ac86", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 2, + "group_item_backup": false, + "pos": { + "x": 0.0, + "y": 0.0 + }, + "pos_rel": { + "x": -1.9161676168441772, + "y": -1.0 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "scale_rel": { + "x": 1.0, + "y": 1.0 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 300 + }, + "hide_transition": { + "duration": 300 + }, + "private_settings": {} + }, + { + "name": "Doomguard Logo", + "source_uuid": "ee77caf1-a490-45fa-80d9-4072f7e63d9a", + "visible": false, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 4, + "group_item_backup": false, + "pos": { + "x": 1213.0, + "y": 119.0 + }, + "pos_rel": { + "x": -0.1002994030714035, + "y": -0.82185626029968262 + }, + "scale": { + "x": 0.42642644047737122, + "y": 0.42642644047737122 + }, + "scale_rel": { + "x": 0.42642644047737122, + "y": 0.42642644047737122 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 300 + }, + "hide_transition": { + "duration": 300 + }, + "private_settings": {} + }, + { + "name": "MPD Silent", + "source_uuid": "41387cb0-801b-49df-92e6-75c7293bec3f", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 6, + "group_item_backup": false, + "pos": { + "x": 0.0, + "y": 0.0 + }, + "pos_rel": { + "x": -1.9161676168441772, + "y": -1.0 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "scale_rel": { + "x": 1.0, + "y": 1.0 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 300 + }, + "hide_transition": { + "duration": 300 + }, + "private_settings": {} + } + ] + }, + "mixers": 0, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "OBSBasic.SelectScene": [], + "libobs.show_scene_item.1": [], + "libobs.hide_scene_item.1": [], + "libobs.show_scene_item.2": [], + "libobs.hide_scene_item.2": [], + "libobs.show_scene_item.4": [], + "libobs.hide_scene_item.4": [], + "libobs.show_scene_item.6": [], + "libobs.hide_scene_item.6": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "canvas_uuid": "6c69626f-6273-4c00-9d88-c5136d61696e", + "private_settings": { + "transition": "" + } + }, + { + "prev_ver": 536936450, + "name": "Project Loading", + "uuid": "6b8e29a8-a66b-44f2-856e-bd41fb18720d", + "id": "scene", + "versioned_id": "scene", + "settings": { + "id_counter": 7, + "custom_size": false, + "items": [ + { + "name": "Loading Project", + "source_uuid": "6642f830-fbae-4020-8292-a9cb7991e6fc", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 1, + "group_item_backup": false, + "pos": { + "x": 0.0, + "y": 0.0 + }, + "pos_rel": { + "x": -1.9161676168441772, + "y": -1.0 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "scale_rel": { + "x": 1.0, + "y": 1.0 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 300 + }, + "hide_transition": { + "duration": 300 + }, + "private_settings": {} + }, + { + "name": "Audio", + "source_uuid": "fc5dfd34-bc55-4c92-8b5e-ef305afd1d8e", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 2, + "group_item_backup": false, + "pos": { + "x": 0.0, + "y": 0.0 + }, + "pos_rel": { + "x": -1.9161676168441772, + "y": -1.0 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "scale_rel": { + "x": 1.0, + "y": 1.0 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 300 + }, + "hide_transition": { + "duration": 300 + }, + "private_settings": {} + }, + { + "name": "MPD", + "source_uuid": "25440930-0606-46d9-ba2c-be786ff4a3ae", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 7, + "group_item_backup": false, + "pos": { + "x": 0.0, + "y": 0.0 + }, + "pos_rel": { + "x": -1.9161676168441772, + "y": -1.0 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "scale_rel": { + "x": 1.0, + "y": 1.0 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 300 + }, + "hide_transition": { + "duration": 300 + }, + "private_settings": {} + } + ] + }, + "mixers": 0, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "OBSBasic.SelectScene": [], + "libobs.show_scene_item.1": [], + "libobs.hide_scene_item.1": [], + "libobs.show_scene_item.2": [], + "libobs.hide_scene_item.2": [], + "libobs.show_scene_item.7": [], + "libobs.hide_scene_item.7": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "canvas_uuid": "6c69626f-6273-4c00-9d88-c5136d61696e", + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "Good Bye", + "uuid": "ff28214d-6519-4b4b-a00a-c68498c606ef", + "id": "scene", + "versioned_id": "scene", + "settings": { + "id_counter": 3, + "custom_size": false, + "items": [ + { + "name": "Audio Player", + "source_uuid": "b7b89406-53f4-45ba-aa1a-c20d17b1c905", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 1, + "group_item_backup": false, + "pos": { + "x": -101.0, + "y": 173.0 + }, + "pos_rel": { + "x": -2.0673651695251465, + "y": -0.74101793766021729 + }, + "scale": { + "x": 1.5, + "y": 1.5 + }, + "scale_rel": { + "x": 1.5, + "y": 1.5 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 300 + }, + "hide_transition": { + "duration": 300 + }, + "private_settings": {} + }, + { + "name": "GB", + "source_uuid": "38306c90-5bb8-4603-a21c-09383c300097", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 2, + "group_item_backup": false, + "pos": { + "x": 0.0, + "y": 0.0 + }, + "pos_rel": { + "x": -1.9161676168441772, + "y": -1.0 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "scale_rel": { + "x": 1.0, + "y": 1.0 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 300 + }, + "hide_transition": { + "duration": 300 + }, + "private_settings": {} + }, + { + "name": "MPD", + "source_uuid": "25440930-0606-46d9-ba2c-be786ff4a3ae", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 3, + "group_item_backup": false, + "pos": { + "x": 0.0, + "y": 0.0 + }, + "pos_rel": { + "x": -1.9161676168441772, + "y": -1.0 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "scale_rel": { + "x": 1.0, + "y": 1.0 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 300 + }, + "hide_transition": { + "duration": 300 + }, + "private_settings": {} + } + ] + }, + "mixers": 0, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "OBSBasic.SelectScene": [], + "libobs.show_scene_item.1": [], + "libobs.hide_scene_item.1": [], + "libobs.show_scene_item.2": [], + "libobs.hide_scene_item.2": [], + "libobs.show_scene_item.3": [], + "libobs.hide_scene_item.3": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "canvas_uuid": "6c69626f-6273-4c00-9d88-c5136d61696e", + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "Music Box", + "uuid": "747b2411-45a9-471a-b0dc-dfe39b0d6385", + "id": "scene", + "versioned_id": "scene", + "settings": { + "id_counter": 9, + "custom_size": false, + "items": [ + { + "name": "MPD", + "source_uuid": "25440930-0606-46d9-ba2c-be786ff4a3ae", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 1, + "group_item_backup": false, + "pos": { + "x": 0.0, + "y": 0.0 + }, + "pos_rel": { + "x": -1.9161676168441772, + "y": -1.0 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "scale_rel": { + "x": 1.0, + "y": 1.0 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 300 + }, + "hide_transition": { + "duration": 300 + }, + "private_settings": {} + }, + { + "name": "Music Box HUD", + "source_uuid": "1b9f5436-864d-4f24-9f92-f45702a23953", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 1, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 2, + "group_item_backup": false, + "pos": { + "x": 0.0, + "y": 0.0 + }, + "pos_rel": { + "x": -1.9161676168441772, + "y": -1.0 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "scale_rel": { + "x": 1.0, + "y": 1.0 + }, + "bounds": { + "x": 2560.0, + "y": 1336.0 + }, + "bounds_rel": { + "x": 3.8323352336883545, + "y": 2.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 300 + }, + "hide_transition": { + "duration": 300 + }, + "private_settings": {} + }, + { + "name": "Cover Simple", + "source_uuid": "7062784f-7b1b-4728-9067-27128c0d5748", + "visible": false, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 4, + "group_item_backup": false, + "pos": { + "x": 1711.0, + "y": 176.0 + }, + "pos_rel": { + "x": 0.64520961046218872, + "y": -0.7365269660949707 + }, + "scale": { + "x": 0.36875000596046448, + "y": 0.36833333969116211 + }, + "scale_rel": { + "x": 0.36875000596046448, + "y": 0.36833333969116211 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 300 + }, + "hide_transition": { + "duration": 300 + }, + "private_settings": {} + }, + { + "name": "NC Music Box HUD", + "source_uuid": "edbd893d-ce53-4c91-bf6c-4d52fbf394d6", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 5, + "group_item_backup": false, + "pos": { + "x": 0.0, + "y": 0.0 + }, + "pos_rel": { + "x": -1.9161676168441772, + "y": -1.0 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "scale_rel": { + "x": 1.0, + "y": 1.0 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "id": "wipe_transition", + "versioned_id": "wipe_transition", + "name": "NC Music Box HUD Show Transition", + "transition": { + "luma_image": "checkerboard-small.png" + }, + "duration": 300 + }, + "hide_transition": { + "id": "wipe_transition", + "versioned_id": "wipe_transition", + "name": "NC Music Box HUD Hide Transition", + "transition": { + "luma_image": "checkerboard-small.png" + }, + "duration": 300 + }, + "private_settings": {} + }, + { + "name": "Waveform Visualizer", + "source_uuid": "f28cc995-6966-4afa-b831-ebb50d89ebe8", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 6, + "group_item_backup": false, + "pos": { + "x": 2166.0, + "y": 1078.0 + }, + "pos_rel": { + "x": 1.3263473510742188, + "y": 0.61377251148223877 + }, + "scale": { + "x": 0.38249999284744263, + "y": 0.38222223520278931 + }, + "scale_rel": { + "x": 0.38249999284744263, + "y": 0.38222223520278931 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 300 + }, + "hide_transition": { + "duration": 300 + }, + "private_settings": {} + }, + { + "name": "Discord", + "source_uuid": "0d7d9693-0a0c-4a52-af3a-1ec742590358", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 7, + "group_item_backup": false, + "pos": { + "x": 0.0, + "y": 0.0 + }, + "pos_rel": { + "x": -1.9161676168441772, + "y": -1.0 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "scale_rel": { + "x": 1.0, + "y": 1.0 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 0 + }, + "hide_transition": { + "duration": 0 + }, + "private_settings": {} + } + ] + }, + "mixers": 0, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "OBSBasic.SelectScene": [ + { + "control": true, + "key": "OBS_KEY_M" + } + ], + "libobs.show_scene_item.1": [], + "libobs.hide_scene_item.1": [], + "libobs.show_scene_item.2": [], + "libobs.hide_scene_item.2": [], + "libobs.show_scene_item.4": [], + "libobs.hide_scene_item.4": [], + "libobs.show_scene_item.5": [], + "libobs.hide_scene_item.5": [], + "libobs.show_scene_item.6": [], + "libobs.hide_scene_item.6": [], + "libobs.show_scene_item.7": [], + "libobs.hide_scene_item.7": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "canvas_uuid": "6c69626f-6273-4c00-9d88-c5136d61696e", + "private_settings": {} + }, + { + "prev_ver": 536936450, + "name": "Desktop (No Cam)", + "uuid": "e68f67a3-f90b-4e81-a89b-308a0ea83e57", + "id": "scene", + "versioned_id": "scene", + "settings": { + "id_counter": 10, + "custom_size": false, + "items": [ + { + "name": "Screen Capture (PipeWire)", + "source_uuid": "8f6ef725-1eec-4fdf-930a-2aab93d84c06", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 1, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 1, + "group_item_backup": false, + "pos": { + "x": 10.0, + "y": 10.0 + }, + "pos_rel": { + "x": -1.9011975526809692, + "y": -0.98502993583679199 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "scale_rel": { + "x": 1.0, + "y": 1.0 + }, + "bounds": { + "x": 2127.0, + "y": 1289.0 + }, + "bounds_rel": { + "x": 3.1841316223144531, + "y": 1.9296407699584961 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 0 + }, + "hide_transition": { + "duration": 0 + }, + "private_settings": {} + }, + { + "name": "MPD", + "source_uuid": "25440930-0606-46d9-ba2c-be786ff4a3ae", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 2, + "group_item_backup": false, + "pos": { + "x": 0.0, + "y": 0.0 + }, + "pos_rel": { + "x": -1.9161676168441772, + "y": -1.0 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "scale_rel": { + "x": 1.0, + "y": 1.0 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 300 + }, + "hide_transition": { + "duration": 300 + }, + "private_settings": {} + }, + { + "name": "Desktop HUD", + "source_uuid": "8acfb832-cfb4-4310-8ebb-b58fad2b3509", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 1, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 3, + "group_item_backup": false, + "pos": { + "x": -1.0, + "y": 0.0 + }, + "pos_rel": { + "x": -1.917664647102356, + "y": -1.0 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "scale_rel": { + "x": 1.0, + "y": 1.0 + }, + "bounds": { + "x": 2560.0, + "y": 1336.0 + }, + "bounds_rel": { + "x": 3.8323352336883545, + "y": 2.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 300 + }, + "hide_transition": { + "duration": 300 + }, + "private_settings": {} + }, + { + "name": "Camera", + "source_uuid": "a8b362a1-91a6-491b-8751-26e2dad55ccd", + "visible": false, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 4, + "group_item_backup": false, + "pos": { + "x": 2019.0, + "y": 22.0 + }, + "pos_rel": { + "x": 1.1062874794006348, + "y": -0.96706587076187134 + }, + "scale": { + "x": 0.34521940350532532, + "y": 0.3454001247882843 + }, + "scale_rel": { + "x": 0.34521940350532532, + "y": 0.3454001247882843 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 0 + }, + "hide_transition": { + "duration": 0 + }, + "private_settings": {} + }, + { + "name": "Music Box Widget", + "source_uuid": "f662d513-6fe6-49fd-99f6-ea7f44e8de18", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 5, + "group_item_backup": false, + "pos": { + "x": 2156.0, + "y": 597.0 + }, + "pos_rel": { + "x": 1.3113772869110107, + "y": -0.10628741979598999 + }, + "scale": { + "x": 0.7012750506401062, + "y": 0.70113635063171387 + }, + "scale_rel": { + "x": 0.7012750506401062, + "y": 0.70113635063171387 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 300 + }, + "hide_transition": { + "duration": 300 + }, + "private_settings": {} + }, + { + "name": "Cover", + "source_uuid": "54d4ac81-e354-431a-a2dc-a00e5555e8d9", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 6, + "group_item_backup": false, + "pos": { + "x": 2158.0, + "y": 13.0 + }, + "pos_rel": { + "x": 1.3143712282180786, + "y": -0.98053890466690063 + }, + "scale": { + "x": 0.48124998807907104, + "y": 0.48166665434837341 + }, + "scale_rel": { + "x": 0.48124998807907104, + "y": 0.48166665434837341 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 300 + }, + "hide_transition": { + "duration": 300 + }, + "private_settings": {} + }, + { + "name": "Waveform Stereo", + "source_uuid": "bc63aaf3-2ae2-4de9-9c7a-c05e55fa2a11", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 8, + "group_item_backup": false, + "pos": { + "x": 2160.0, + "y": 470.0 + }, + "pos_rel": { + "x": 1.317365288734436, + "y": -0.29640716314315796 + }, + "scale": { + "x": 0.49625000357627869, + "y": 0.4977777898311615 + }, + "scale_rel": { + "x": 0.49625000357627869, + "y": 0.4977777898311615 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 300 + }, + "hide_transition": { + "duration": 300 + }, + "private_settings": {} + }, + { + "name": "Discord", + "source_uuid": "0d7d9693-0a0c-4a52-af3a-1ec742590358", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 2560.0, + "y": 1336.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 10, + "group_item_backup": false, + "pos": { + "x": 0.0, + "y": 0.0 + }, + "pos_rel": { + "x": -1.9161676168441772, + "y": -1.0 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "scale_rel": { + "x": 1.0, + "y": 1.0 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 0 + }, + "hide_transition": { + "duration": 0 + }, + "private_settings": {} + } + ] + }, + "mixers": 0, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "OBSBasic.SelectScene": [], + "libobs.show_scene_item.1": [], + "libobs.hide_scene_item.1": [], + "libobs.show_scene_item.2": [], + "libobs.hide_scene_item.2": [], + "libobs.show_scene_item.3": [], + "libobs.hide_scene_item.3": [], + "libobs.show_scene_item.4": [], + "libobs.hide_scene_item.4": [], + "libobs.show_scene_item.5": [], + "libobs.hide_scene_item.5": [], + "libobs.show_scene_item.6": [], + "libobs.hide_scene_item.6": [], + "libobs.show_scene_item.8": [], + "libobs.hide_scene_item.8": [], + "libobs.show_scene_item.10": [], + "libobs.hide_scene_item.10": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "canvas_uuid": "6c69626f-6273-4c00-9d88-c5136d61696e", + "private_settings": {} + } + ], + "groups": [ + { + "prev_ver": 536936450, + "name": "STAND BY", + "uuid": "d730eeb6-8969-4265-b033-5f777686ce2d", + "id": "group", + "versioned_id": "group", + "settings": { + "custom_size": true, + "cx": 1633, + "cy": 1233, + "id_counter": 0, + "items": [ + { + "name": "Doomguard Logo", + "source_uuid": "ee77caf1-a490-45fa-80d9-4072f7e63d9a", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 1920.0, + "y": 1080.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 6, + "group_item_backup": false, + "pos": { + "x": 1514.64453125, + "y": 0.0 + }, + "pos_rel": { + "x": 0.35126429796218872, + "y": -1.0 + }, + "scale": { + "x": 0.35290849208831787, + "y": 0.35290849208831787 + }, + "scale_rel": { + "x": 0.28528529405593872, + "y": 0.28528529405593872 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 0 + }, + "hide_transition": { + "duration": 0 + }, + "private_settings": {} + }, + { + "name": "Starting Soon", + "source_uuid": "4b902d7f-9205-444c-8c8b-b559479d5bea", + "visible": true, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 1920.0, + "y": 1080.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 10, + "group_item_backup": false, + "pos": { + "x": 0.0, + "y": 489.0 + }, + "pos_rel": { + "x": -1.9161676168441772, + "y": -0.26796406507492065 + }, + "scale": { + "x": 2.042553186416626, + "y": 2.0388350486755371 + }, + "scale_rel": { + "x": 1.6511657238006592, + "y": 1.6481600999832153 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "id": "wipe_transition", + "versioned_id": "wipe_transition", + "name": "Starting Soon Show Transition", + "transition": {}, + "duration": 1000 + }, + "hide_transition": { + "duration": 0 + }, + "private_settings": {} + }, + { + "name": "info", + "source_uuid": "cfcf2652-2777-4b92-a53b-54703f23deff", + "visible": false, + "locked": false, + "rot": 0.0, + "scale_ref": { + "x": 1920.0, + "y": 1080.0 + }, + "align": 5, + "bounds_type": 0, + "bounds_align": 0, + "bounds_crop": false, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 8, + "group_item_backup": false, + "pos": { + "x": 76.0001220703125, + "y": 1193.0 + }, + "pos_rel": { + "x": -1.8023951053619385, + "y": 0.78592813014984131 + }, + "scale": { + "x": 2.5, + "y": 2.5 + }, + "scale_rel": { + "x": 2.0209579467773438, + "y": 2.0209579467773438 + }, + "bounds": { + "x": 0.0, + "y": 0.0 + }, + "bounds_rel": { + "x": 0.0, + "y": 0.0 + }, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 0 + }, + "hide_transition": { + "duration": 0 + }, + "private_settings": {} + } + ] + }, + "mixers": 0, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "libobs.show_scene_item.6": [], + "libobs.hide_scene_item.6": [], + "libobs.show_scene_item.10": [], + "libobs.hide_scene_item.10": [], + "libobs.show_scene_item.8": [], + "libobs.hide_scene_item.8": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "canvas_uuid": "6c69626f-6273-4c00-9d88-c5136d61696e", + "private_settings": {} + } + ], + "scene_order": [ + { + "name": "Landing" + }, + { + "name": "Project Loading" + }, + { + "name": "Game" + }, + { + "name": "Desktop" + }, + { + "name": "Desktop (No Cam)" + }, + { + "name": "Music Box" + }, + { + "name": "Good Bye" + } + ], + "current_scene": "Good Bye", + "current_program_scene": "Good Bye", + "canvases": [], + "current_transition": "Luma Wipe", + "transition_duration": 1000, + "transitions": [ + { + "name": "Luma Wipe", + "id": "wipe_transition", + "settings": { + "luma_image": "checkerboard-small.png" + } + } + ], + "quick_transitions": [ + { + "name": "Cut", + "duration": 300, + "hotkeys": [], + "id": 1, + "fade_to_black": false + }, + { + "name": "Fade", + "duration": 300, + "hotkeys": [], + "id": 2, + "fade_to_black": false + } + ], + "saved_projectors": [], + "preview_locked": false, + "scaling_enabled": false, + "scaling_level": -14, + "scaling_off_x": 0.0, + "scaling_off_y": 0.0, + "modules": { + "scripts-tool": [], + "output-timer": { + "streamTimerHours": 0, + "streamTimerMinutes": 0, + "streamTimerSeconds": 30, + "recordTimerHours": 0, + "recordTimerMinutes": 0, + "recordTimerSeconds": 30, + "autoStartStreamTimer": false, + "autoStartRecordTimer": false, + "pauseRecordTimer": false + }, + "auto-scene-switcher": { + "interval": 300, + "non_matching_scene": "Nuclear", + "switch_if_not_matching": true, + "active": false, + "switches": [ + { + "scene": "Game", + "window_title": "Heroes of Might and Magic III: Horn of the Abyss" + } + ] + } + }, + "resolution": { + "x": 2560, + "y": 1336 + }, + "migration_resolution": { + "x": 1920, + "y": 1080 + }, + "version": 2 +} \ No newline at end of file diff --git a/scripts/obs-webapp.supervisord.conf b/scripts/obs-webapp.supervisord.conf new file mode 100644 index 0000000..cbdaa4d --- /dev/null +++ b/scripts/obs-webapp.supervisord.conf @@ -0,0 +1,31 @@ +; supervisord program file for the OPHI-118 scene overlay webapp. +; +; Listens on 127.0.0.1:1118 (CH 118.0 in the overlay lore, easy to remember). +; +; Install: +; sudo install -m 644 scripts/obs-webapp.supervisord.conf /etc/supervisor.d/obs-webapp.conf +; sudo supervisorctl reread && sudo supervisorctl update +; +; Restart after config / route / env changes: +; sudo supervisorctl restart obs-webapp +; +; Logs: +; tail -F webapp/storage/logs/supervisord.{out,err}.log + +[program:obs-webapp] +command=/usr/bin/php artisan serve --host=127.0.0.1 --port=1118 +directory=/home/jin/.config/obs-studio/webapp +user=jin +environment=HOME="/home/jin",USER="jin",PATH="/usr/local/sbin:/usr/local/bin:/usr/bin" +autostart=true +autorestart=true +startsecs=2 +startretries=3 +stopwaitsecs=10 +stopsignal=TERM +stdout_logfile=/home/jin/.config/obs-studio/webapp/storage/logs/supervisord.out.log +stderr_logfile=/home/jin/.config/obs-studio/webapp/storage/logs/supervisord.err.log +stdout_logfile_maxbytes=10MB +stdout_logfile_backups=3 +stderr_logfile_maxbytes=10MB +stderr_logfile_backups=3 diff --git a/scripts/rewrite-scene-urls.py b/scripts/rewrite-scene-urls.py new file mode 100755 index 0000000..0215ee9 --- /dev/null +++ b/scripts/rewrite-scene-urls.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +"""Rewrite OBS browser_source URLs in a scene-collection JSON from `file://` to +`http://127.0.0.1:1118/`. + +Usage: + python3 scripts/rewrite-scene-urls.py basic/scenes/Default_Stream_HUD.json + +Idempotent: runs against a partially-converted file are no-ops. + +Also moves the landing/static-hum.wav ffmpeg_source path to its new home under +webapp/public/audio/. + +OBS MUST be closed when this runs — OBS rewrites scene files on exit. +""" +import json +import re +import sys + + +BROWSER_MAPPING = { + 'landing/index.html': 'http://127.0.0.1:1118/landing', + 'loading/index.html': 'http://127.0.0.1:1118/loading', + 'game/index.html': 'http://127.0.0.1:1118/game', + 'desktop/index.html': 'http://127.0.0.1:1118/desktop', + 'goodbye/index.html': 'http://127.0.0.1:1118/goodbye', + 'music/index.html': 'http://127.0.0.1:1118/music-daemon', + 'music-box/index.html': 'http://127.0.0.1:1118/music-box', + 'music-box/widget.html': 'http://127.0.0.1:1118/music-box/widget', + 'music-box/cover.html': 'http://127.0.0.1:1118/music-box/cover', + 'music-box/nc-music-box.html': 'http://127.0.0.1:1118/music-box/nc', +} + +AUDIO_OLD = 'landing/static-hum.wav' +AUDIO_NEW = 'webapp/public/audio/static-hum.wav' + + +def rewrite_browser_source(src): + s = src.get('settings', {}) + for old, new in BROWSER_MAPPING.items(): + for k in ('url', 'local_file'): + v = s.get(k, '') + if old in v: + # Preserve query strings (e.g. ?bars=0). + m = re.search(r'\?[^"\s]*$', v) + qs = m.group(0) if m else '' + s['url'] = new + qs + s.pop('local_file', None) + s['is_local_file'] = False + return True + return False + + +def rewrite_audio_source(src): + s = src.get('settings', {}) + lf = s.get('local_file', '') + if lf.endswith(AUDIO_OLD): + s['local_file'] = lf.replace(AUDIO_OLD, AUDIO_NEW) + return True + return False + + +def main(): + if len(sys.argv) != 2: + print(__doc__, file=sys.stderr) + return 2 + path = sys.argv[1] + doc = json.load(open(path)) + rewrites = 0 + for src in doc.get('sources', []): + sid = src.get('id', '') + if sid == 'browser_source': + if rewrite_browser_source(src): + rewrites += 1 + elif sid == 'ffmpeg_source': + if rewrite_audio_source(src): + rewrites += 1 + json.dump(doc, open(path, 'w'), indent=4, ensure_ascii=False) + print(f'wrote {path} ({rewrites} sources rewritten)') + + +if __name__ == '__main__': + sys.exit(main() or 0) diff --git a/webapp/.editorconfig b/webapp/.editorconfig new file mode 100644 index 0000000..6df8428 --- /dev/null +++ b/webapp/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 + +[{compose,docker-compose}.{yml,yaml}] +indent_size = 4 diff --git a/webapp/.gitattributes b/webapp/.gitattributes new file mode 100644 index 0000000..fcb21d3 --- /dev/null +++ b/webapp/.gitattributes @@ -0,0 +1,11 @@ +* text=auto eol=lf + +*.blade.php diff=html +*.css diff=css +*.html diff=html +*.md diff=markdown +*.php diff=php + +/.github export-ignore +CHANGELOG.md export-ignore +.styleci.yml export-ignore diff --git a/webapp/.gitignore b/webapp/.gitignore new file mode 100644 index 0000000..7be55e2 --- /dev/null +++ b/webapp/.gitignore @@ -0,0 +1,27 @@ +*.log +.DS_Store +.env +.env.backup +.env.production +.phpactor.json +.phpunit.result.cache +/.codex +/.cursor/ +/.idea +/.nova +/.phpunit.cache +/.vscode +/.zed +/auth.json +/node_modules +/public/build +/public/fonts-manifest.dev.json +/public/hot +/public/storage +/storage/*.key +/storage/pail +/vendor +_ide_helper.php +Homestead.json +Homestead.yaml +Thumbs.db diff --git a/webapp/README.md b/webapp/README.md new file mode 100644 index 0000000..5ad1377 --- /dev/null +++ b/webapp/README.md @@ -0,0 +1,58 @@ +

Laravel Logo

+ +

+Build Status +Total Downloads +Latest Stable Version +License +

+ +## About Laravel + +Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as: + +- [Simple, fast routing engine](https://laravel.com/docs/routing). +- [Powerful dependency injection container](https://laravel.com/docs/container). +- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage. +- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent). +- Database agnostic [schema migrations](https://laravel.com/docs/migrations). +- [Robust background job processing](https://laravel.com/docs/queues). +- [Real-time event broadcasting](https://laravel.com/docs/broadcasting). + +Laravel is accessible, powerful, and provides tools required for large, robust applications. + +## Learning Laravel + +Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework. + +In addition, [Laracasts](https://laracasts.com) contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library. + +You can also watch bite-sized lessons with real-world projects on [Laravel Learn](https://laravel.com/learn), where you will be guided through building a Laravel application from scratch while learning PHP fundamentals. + +## Agentic Development + +Laravel's predictable structure and conventions make it ideal for AI coding agents like Claude Code, Cursor, and GitHub Copilot. Install [Laravel Boost](https://laravel.com/docs/ai) to supercharge your AI workflow: + +```bash +composer require laravel/boost --dev + +php artisan boost:install +``` + +Boost provides your agent 15+ tools and skills that help agents build Laravel applications while following best practices. + +## Contributing + +Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions). + +## Code of Conduct + +In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct). + +## Security Vulnerabilities + +If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed. + +## License + +The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). diff --git a/webapp/app/Console/Commands/RigCmdCommand.php b/webapp/app/Console/Commands/RigCmdCommand.php new file mode 100644 index 0000000..a5e672c --- /dev/null +++ b/webapp/app/Console/Commands/RigCmdCommand.php @@ -0,0 +1,30 @@ +argument('type')); + if (!in_array($type, ['skip', 'prev', 'pause', 'resume'], true)) { + $this->error('type must be one of: skip, prev, pause, resume'); + return self::FAILURE; + } + try { + $ws->broadcast('mpd:cmd', ['type' => $type]); + } catch (Throwable $e) { + $this->error($e->getMessage()); + return self::FAILURE; + } + $this->info(" → {$type}"); + return self::SUCCESS; + } +} diff --git a/webapp/app/Console/Commands/RigLoadingCommand.php b/webapp/app/Console/Commands/RigLoadingCommand.php new file mode 100644 index 0000000..83b001c --- /dev/null +++ b/webapp/app/Console/Commands/RigLoadingCommand.php @@ -0,0 +1,101 @@ +loading(); + + $this->line('── Project Loading manifest ──'); + $this->line(' (press Enter to keep [defaults])'); + $this->line(''); + + $game = null; + $gameId = null; + $defaultGame = (string) ($prev['game'] ?? ''); + while (true) { + $query = (string) $this->ask('Game (search)', $defaultGame ?: null); + if ($query === '') { + $this->error(' ✗ Game is required'); + continue; + } + if ($twitch->enabled()) { + $cachedId = (string) ($prev['gameId'] ?? ''); + if ($query === $defaultGame && preg_match('/^\d+$/', $cachedId)) { + $game = $defaultGame; + $gameId = $cachedId; + $this->line(" ↻ reusing cached: {$game} (id={$gameId})"); + break; + } + try { + $hit = $twitch->searchCategory($query); + } catch (Throwable $e) { + $this->warn(' ! Twitch search failed: ' . $e->getMessage()); + if ($this->confirm('Use the raw input without resolving an id?', true)) { + $game = $query; + break; + } + continue; + } + if ($hit === null) { + $this->warn(' ! no Twitch matches for "' . $query . '"'); + continue; + } + $game = $hit['name']; + $gameId = $hit['id']; + $this->line(" ↳ matched: {$game} (id={$gameId})"); + break; + } + $game = $query; + break; + } + + $subtitle = (string) $this->ask('Subtitle (mode / episode / note)', $prev['subtitle'] ?? null); + $countdown = (int) $this->ask('Countdown (minutes)', (string) ($prev['countdownMin'] ?? 5)); + $camera = $this->confirm('Camera', (bool) ($prev['camera'] ?? true)); + $microphone = $this->confirm('Microphone', (bool) ($prev['microphone'] ?? true)); + + $data = [ + 'compiledAt' => gmdate('Y-m-d\TH:i:s\Z'), + 'game' => $game, + 'gameId' => $gameId, + 'subtitle' => $subtitle ?: null, + 'countdownMin' => $countdown, + 'camera' => $camera, + 'microphone' => $microphone, + ]; + $path = rtrim(config('rig.storage_data'), '/') . '/loading.json'; + @mkdir(dirname($path), 0775, true); + file_put_contents( + $path, + json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . "\n" + ); + $this->info(''); + $this->info(" ✓ wrote {$path}"); + + // Push to Twitch — soft-fail so a network blip doesn't block the local manifest. + if (!$this->option('no-twitch') && $twitch->enabled() && $gameId) { + $title = $subtitle ?: $game; + try { + $twitch->setChannel($title, $gameId); + $this->info(" ✓ Twitch channel updated → \"{$title}\" / category {$game}"); + } catch (Throwable $e) { + $this->warn(' ! Twitch sync failed (manifest still saved): ' . $e->getMessage()); + } + } + + $this->line(''); + $this->line(' → refresh the Project Loading browser source in OBS.'); + return self::SUCCESS; + } +} diff --git a/webapp/app/Console/Commands/RigPlaylistCommand.php b/webapp/app/Console/Commands/RigPlaylistCommand.php new file mode 100644 index 0000000..1add435 --- /dev/null +++ b/webapp/app/Console/Commands/RigPlaylistCommand.php @@ -0,0 +1,125 @@ +warn(' ! no music dirs configured (set MUSIC_DIR_1 in .env)'); + } + + $ffprobe = (string) config('rig.ffprobe', '/usr/bin/ffprobe'); + if (!is_executable($ffprobe)) { + $this->error(" ✗ ffprobe not found at {$ffprobe} (install ffmpeg or set FFPROBE_BIN)"); + return self::FAILURE; + } + + $this->line('── Indexing audio roots ──'); + foreach ($roots as $r) { + $this->line(is_dir($r) ? " + {$r}" : " ! {$r} (missing — skipped)"); + } + + $tracks = []; + foreach ($roots as $root) { + if (!is_dir($root)) continue; + $finder = (new Finder()) + ->files() + ->in($root) + ->name('/\.(' . implode('|', self::EXTS) . ')$/i') + ->ignoreUnreadableDirs() + ->sortByName(); + + foreach ($finder as $file) { + $name = $file->getFilename(); + $base = $file->getFilenameWithoutExtension(); + // Skip yt-dlp intermediates (e.g. foo.f140.m4a, foo.temp.opus). + if (preg_match('/\.(?:f\d+|temp|part)$/i', $base)) continue; + + $path = $file->getRealPath(); + $meta = $this->probe($ffprobe, $path); + $tracks[] = [ + 'title' => $this->cleanTitle($base), + 'file' => 'file://' . $this->urlEncodePath($path), + 'durationSec' => isset($meta['durationSec']) ? (int) $meta['durationSec'] : null, + ]; + } + } + + $doc = [ + 'syncedAt' => gmdate('Y-m-d\TH:i:s\Z'), + 'sourceDirs' => $roots, + 'trackCount' => count($tracks), + 'tracks' => $tracks, + ]; + $path = rtrim(config('rig.storage_data'), '/') . '/playlist.json'; + @mkdir(dirname($path), 0775, true); + file_put_contents( + $path, + json_encode($doc, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) + ); + + $this->line(''); + $this->info(" ✓ wrote {$path} ({$doc['trackCount']} tracks)"); + $this->line(' → refresh the Music Daemon browser source in OBS to reload the queue.'); + return self::SUCCESS; + } + + private function probe(string $ffprobe, string $path): array + { + try { + $proc = new Process([ + $ffprobe, '-v', 'error', + '-show_entries', 'format=duration:format_tags=title,artist', + '-of', 'json', + $path, + ]); + $proc->setTimeout(10); + $proc->run(); + if (!$proc->isSuccessful()) return []; + $json = json_decode($proc->getOutput(), true) ?: []; + $fmt = $json['format'] ?? []; + return [ + 'durationSec' => isset($fmt['duration']) ? (float) $fmt['duration'] : null, + ]; + } catch (Throwable) { + return []; + } + } + + /** + * Mirrors scripts/playlist.sh's clean_title(): + * - drop "| ..." (or fullwidth |) tails (genre/label noise) + * - strip trailing YouTube IDs in brackets like "[-XxZTgMWKV0]" + * - strip "[NCS Release]" / "(NCS10 Release)" suffixes + */ + private function cleanTitle(string $t): string + { + $t = preg_replace('/\s*[||].*$/u', '', $t) ?? $t; + $t = preg_replace('/\s*\[[A-Za-z0-9_-]{11}\]\s*$/', '', $t) ?? $t; + $t = preg_replace( + '/\s*[\[(](?:NCS\d*|No Copyright Sounds)(?:\s+Release)?[\])]\s*$/i', + '', + $t + ) ?? $t; + return trim($t); + } + + private function urlEncodePath(string $path): string + { + // Same as Python's urllib.parse.quote(..., safe='/') — keep separators, encode the rest. + return implode('/', array_map('rawurlencode', explode('/', $path))); + } +} diff --git a/webapp/app/Console/Commands/RigSetupCommand.php b/webapp/app/Console/Commands/RigSetupCommand.php new file mode 100644 index 0000000..433f123 --- /dev/null +++ b/webapp/app/Console/Commands/RigSetupCommand.php @@ -0,0 +1,84 @@ +error(" ✗ {$src} not found."); + $this->line(' Open OBS once with obs-websocket loaded so it generates the config.'); + return self::FAILURE; + } + $cfg = json_decode((string) file_get_contents($src), true); + if (!is_array($cfg)) { + $this->error(" ✗ {$src} did not parse as JSON."); + return self::FAILURE; + } + $port = (int) ($cfg['server_port'] ?? 4455); + $pass = (string) ($cfg['server_password'] ?? ''); + $url = "ws://localhost:{$port}"; + + $out = (string) config('rig.obs_config_js'); + @mkdir(dirname($out), 0775, true); + $js = <<jsString($url)}, + password: {$this->jsString($pass)}, +}; + +JS; + file_put_contents($out, $js); + $this->info(" ✓ wrote {$out}"); + + // Mirror into .env so MusicCommandController / ObsWsClient can authenticate. + $this->writeEnv('OBS_WS_URL', $url); + $this->writeEnv('OBS_WS_PASSWORD', $pass); + $this->info(' ✓ updated .env OBS_WS_URL / OBS_WS_PASSWORD'); + + $this->line(''); + $this->line(" url = {$url}"); + $this->line(' password = ' . ($pass === '' ? '(none)' : '(set, ' . strlen($pass) . ' chars)')); + $this->line(''); + $this->line(' → if obs-webapp is running under supervisord, restart it so the new env takes effect:'); + $this->line(' sudo supervisorctl restart obs-webapp'); + + return self::SUCCESS; + } + + private function jsString(string $s): string + { + return json_encode($s, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + } + + private function writeEnv(string $key, string $value): void + { + $env = base_path('.env'); + $raw = is_file($env) ? file_get_contents($env) : ''; + $line = $key . '=' . $this->escapeEnvValue($value); + if (preg_match("/^{$key}=.*$/m", $raw)) { + $raw = preg_replace("/^{$key}=.*$/m", $line, $raw); + } else { + $raw = rtrim($raw, "\n") . "\n" . $line . "\n"; + } + file_put_contents($env, $raw); + } + + private function escapeEnvValue(string $v): string + { + if ($v === '') return ''; + if (preg_match('/[\s"#=]/', $v)) { + return '"' . str_replace('"', '\"', $v) . '"'; + } + return $v; + } +} diff --git a/webapp/app/Console/Commands/RigTelemetryCommand.php b/webapp/app/Console/Commands/RigTelemetryCommand.php new file mode 100644 index 0000000..3183619 --- /dev/null +++ b/webapp/app/Console/Commands/RigTelemetryCommand.php @@ -0,0 +1,42 @@ +option('collect') || !is_file($path)) { + $data = $snap->collect(); + @mkdir(dirname($path), 0775, true); + file_put_contents( + $path, + json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . "\n" + ); + $this->info(" ✓ wrote {$path}"); + } else { + $this->line(" · {$path} already exists — use --collect to re-read hardware."); + } + + $current = $rig->telemetry(); + $this->line(''); + $this->line(" rig = " . ($current['rig'] ?? '?')); + $this->line(" cpu = " . ($current['cpu']['model'] ?? '?')); + if (!empty($current['gpu']['name'])) { + $this->line(" gpu = " . $current['gpu']['name']); + } + if (!empty($current['obs']['output']['w'])) { + $this->line(" output = {$current['obs']['output']['w']}×{$current['obs']['output']['h']} @ {$current['obs']['fps']} fps"); + } + return self::SUCCESS; + } +} diff --git a/webapp/app/Http/Controllers/AudioController.php b/webapp/app/Http/Controllers/AudioController.php new file mode 100644 index 0000000..3a94d6f --- /dev/null +++ b/webapp/app/Http/Controllers/AudioController.php @@ -0,0 +1,71 @@ +query('p', ''); + if ($b64 === '') abort(400, 'missing p'); + + // URL-safe base64; pad back to a multiple of 4 for strict decoding. + $padded = $b64 . str_repeat('=', (4 - strlen($b64) % 4) % 4); + $path = base64_decode(strtr($padded, '-_', '+/'), true); + if ($path === false || $path === '') abort(400, 'bad encoding'); + + $real = realpath($path); + if ($real === false || !is_file($real)) abort(404, 'file not found: ' . $path); + + $allowed = false; + foreach ((array) config('rig.music_dirs') as $root) { + $rootReal = realpath($root); + if ($rootReal && str_starts_with($real . '/', rtrim($rootReal, '/') . '/')) { + $allowed = true; + break; + } + } + if (!$allowed) abort(403, 'outside allowed roots'); + + $ext = strtolower(pathinfo($real, PATHINFO_EXTENSION)); + $mime = match ($ext) { + 'm4a', 'aac' => 'audio/mp4', + 'mp3' => 'audio/mpeg', + 'opus', 'ogg'=> 'audio/ogg', + 'flac' => 'audio/flac', + 'webm' => 'audio/webm', + 'wav' => 'audio/wav', + default => 'application/octet-stream', + }; + + // BinaryFileResponse handles Range requests automatically, which the + // HTML5