Initial commit: OBS scene collection, profile, landing overlay

Tracked: scene JSON (Untitled + pre-migration backups), landing/
overlay (HTML + static-hum.wav), profile encoder/basic settings,
rtmp-services metadata, modules list, ini files, CLAUDE.md.

Ignored via .gitignore: stream keys (**/service.json), obs-websocket
password (plugin_config/obs-websocket/), logs/, profiler_data/,
.sentinel/, OBS-managed *.bak and *.json.v1 sidecars.
This commit is contained in:
Jakub Zych
2026-04-25 23:19:57 +02:00
commit 6decec66cc
17 changed files with 6204 additions and 0 deletions

58
.gitignore vendored Normal file
View File

@@ -0,0 +1,58 @@
# ============================================================
# SECRETS — never commit these. OBS stores live credentials here.
# ============================================================
# Twitch (and other RTMP) stream keys — `settings.key` field.
# Path: basic/profiles/<profile>/service.json
**/service.json
**/service.json.bak
# obs-websocket server password (plugin_config/obs-websocket/config.json
# contains `server_password`). Block the whole dir defensively.
plugin_config/obs-websocket/
# Defensive blanket — anything obviously credential-shaped.
*.key
*.pem
*.secret
*.token
*credentials*
*.env
.env*
# ============================================================
# OBS runtime / generated state — no value in version control
# ============================================================
# One log per launch; OBS rotates these itself (MaxLogs=10).
logs/
# Performance traces.
profiler_data/
# Running-instance lockfile.
.sentinel/
# Crash/safe-mode artefacts.
crashes/
safe_mode/
# ============================================================
# OBS-managed sidecars — OBS rewrites these on every save.
# Tracking them produces noisy diffs and they're not real history.
# Manual migration backups (.pre-*) are intentionally NOT ignored —
# they were created by hand and capture meaningful pre-change state.
# ============================================================
*.bak
*.json.v1
# ============================================================
# Editor / OS noise
# ============================================================
.DS_Store
Thumbs.db
*.swp
*.swo
*~
.idea/
.vscode/

113
CLAUDE.md Normal file
View File

@@ -0,0 +1,113 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## What this directory is
Active OBS Studio user config for the **Flatpak** install (`com.obsproject.Studio` from Flathub). Path: `~/.config/obs-studio/`.
**Why `~/.config/` and not `~/.var/app/com.obsproject.Studio/config/obs-studio/`?** The Flatpak ships with `filesystems=host` (full host access, see `flatpak info --show-permissions com.obsproject.Studio`). With that permission, Flatpak does **not** indirect XDG paths into the per-app private dir — OBS sees the real `$HOME/.config/` and uses `~/.config/obs-studio/` directly, like a native install would.
A ghost copy lives at `~/.var/app/com.obsproject.Studio/config/obs-studio/` — that's the path Flatpak *would* use if `filesystems=host` weren't set. **OBS does not read or write there.** Some files (older CLAUDE.md, an aborted 2026-04 source-id migration, a `landing/` asset duplicate) accumulated there from work that targeted the wrong path. Treat it as orphaned; don't edit it expecting OBS to pick changes up. The 2026-04 migration script described below ran against the ghost dir and left this active dir un-migrated until 2026-04-25 23:09.
The user works here to (a) edit scene collections directly as JSON and (b) build/iterate stream overlays as local HTML/CSS/JS rendered via the browser source.
## How OBS is launched
```
flatpak run com.obsproject.Studio
```
Or via the app menu ("OBS Studio"). The native `obs` binary still exists from `pacman` but should be considered dead — `obs-studio` and `obs-pipewire-audio-capture-bin{,-debug}` are pending removal once the user is fully comfortable with Flatpak.
## Migration history (matters when reading old scene JSON or AI-written files)
- **Was:** native Arch `obs-studio` (no browser support) + AUR `obs-linuxbrowser-source` (id: `linuxbrowser-source`).
- **Is:** Flatpak `com.obsproject.Studio` with bundled CEF browser source (id: `browser_source`).
- **2026-04 attempt (misdirected):** A scripted migration rewrote four sources (`Tip4Stream Scena Domyślna`, `Twitch Viewers`, `StreamLabs Jar`, `Landing — Stand By (Browser)`) from `linuxbrowser-source``browser_source` — but ran against the **ghost** `~/.var/app/.../obs-studio/` directory. OBS never read those edits.
- **2026-04-25 23:09 (correct):** Same migration applied to the **active** `~/.config/obs-studio/basic/scenes/Untitled.json`. Backup at `basic/scenes/Untitled.json.pre-cef-migration`. Settings keys: `url`, `width`, `height`, `fps` map directly; CEF requires `fps_custom: true`, `reroute_audio: false`, `restart_when_active: false`, `shutdown: false`, `webpage_control_level: 1`. Stale `linuxbrowser.reloadpage` hotkey keys were also dropped.
- If you encounter `linuxbrowser-source` anywhere — in a backup, a doc, a paste — assume it's pre-migration and migrate the same way.
- Older backup of pre-rebuild scene JSON: `basic/scenes/Untitled.json.pre-landing-rebuild`.
## Critical gotcha: OBS overwrites on exit
OBS rewrites `basic/scenes/<collection>.json`, `global.ini`, and `user.ini` on shutdown (and periodically). **Close OBS before editing these files**, or the next OBS exit will clobber the edit. After editing, re-launch OBS to load the change.
OBS itself maintains `.bak` (previous save) and `.json.v1` (pre-version-migration) sidecars in `basic/scenes/`. Those are not git history — leave them alone unless explicitly recovering.
## Flatpak permissions — what the sandbox allows
OBS flatpak ships with `filesystems=host` by default — full host access. Asset paths like `/home/jin/HDD/Images/Gifs/stamd.png` and `/home/jin/cloud.jakubzych.com/_img/logos/dgw.png` work without `flatpak override`. PipeWire audio works via `xdg-run/pipewire-0`. NVIDIA acceleration works via `org.freedesktop.Platform.GL.nvidia-595-58-03` (auto-installed). If you ever need to widen permissions: `flatpak override --user com.obsproject.Studio --<flag>=<value>`.
`filesystems=host` is also the reason this config lives at `~/.config/obs-studio/` instead of the per-app sandboxed path — see "What this directory is" above.
## Layout — what lives where
- `user.ini` / `global.ini` — selects active scene collection and profile, plus window state. Currently both `Untitled`. Don't touch geometry/dock-state lines (huge base64 blobs OBS owns).
- `basic/scenes/<name>.json`**the scene collection**. One file = one full set of scenes + sources + transitions + audio routing. Active: `Untitled.json`.
- `basic/profiles/<name>/` — encoder, streaming service, recording settings (separate from scenes).
- `plugin_config/<plugin-id>/` — per-plugin settings. `obs-websocket/config.json` holds the WebSocket password — secret.
- `plugin_manager/modules.json` — third-party plugins OBS loads. Currently lists only `linux-pipewire-audio` (the bundled-into-flatpak version, same source ids as the AUR plugin it replaced).
- `logs/YYYY-MM-DD HH-MM-SS.txt` — one per launch. Most recent is the source of truth for "why did source X fail to load" or "what plugin crashed". `MaxLogs=10`.
- `landing/` — overlays for the Landing scene (custom; not an OBS convention):
- `index.html` — Fallout-style "PLEASE STAND BY" rendered by browser source. Self-contained, no external deps. Edit, then click "Refresh cache" on the browser source to see changes.
- `static-hum.wav` — 12s seamless CRT static loop, generated via `ffmpeg -filter_complex` (anoisesrc white+brown+60Hz hum, filtered + crossfaded). Loaded by a Media source.
- `profiler_data/` — perf traces. Ignore unless profiling.
- `.sentinel/run_<uuid>` — running-instance lockfile; safe to delete only if no OBS process is alive.
## Scene collection JSON — structure to know before editing
Top-level keys in `basic/scenes/Untitled.json`:
- `name` — collection name (matches filename stem).
- `sources`**flat array of every source, scene, and group**. Scenes are entries here with `"id": "scene"`; their `settings.items[]` references child sources **by name string**, not uuid. Renaming a source means updating every scene that references it.
- `scene_order` — array of `{name}` defining sidebar order.
- `current_scene` / `current_program_scene` — strings matching scene names.
- `transitions`, `quick_transitions`, `current_transition`, `transition_duration` — transition setup.
- `groups` — group definitions.
- `modules` — per-source-plugin state.
- `version`, `migration_resolution`, `resolution`, `canvases` — collection schema + canvas sizing. Canvas: **2560×1336**.
Each source entry has: `name`, `uuid`, `id`, `versioned_id`, `settings` (plugin-specific), plus filters/transform/audio fields. Scene items inside `settings.items[]` reference sources via `source_uuid`. **Use uuids when adding new items programmatically; use names when reading.**
Source `id` values currently in use:
- `scene` — a scene
- `pipewire-screen-capture-source` — Wayland/PipeWire screen/window capture
- `pipewire-camera-source` — PipeWire camera
- `v4l2_input` — V4L2 webcam (legacy path)
- `xshm_input` — X11 screen capture (legacy path)
- `browser_source` — CEF browser (post-migration; was `linuxbrowser-source`)
- `image_source` — static image
- `text_ft2_source` — FreeType2 text
- `ffmpeg_source` — media file (audio/video) via ffmpeg
Active scenes: `Landing` (current), `Game`, `Desktop`.
When editing scene JSON: keep `uuid` values stable across edits, preserve 4-space indentation, and validate with `python3 -m json.tool` before re-launching OBS.
## Landing scene — current state
Composition (full-bleed at 2560×1336):
1. `Landing — Stand By (Browser)` (`browser_source`) — points at `file:///home/jin/.config/obs-studio/landing/index.html`. Renders: top HUD (`TRANSMISSION OPHI-118 / SIGNAL bars / UTC clock`), centered cyan-ring + red-orange triangle mark, big `PLEASE STAND BY`, footer (`CH 118.0 MHz · NTSC · AUDIO ACTIVE`), animated static + scanlines + vignette + occasional flicker.
2. `Landing — Static Hum` (`ffmpeg_source`) — looped 12s WAV at volume 0.55.
Palette (in CSS vars at top of `landing/index.html`): `--bg #07080d`, `--hud #4fd2ff`, `--accent #e63a2e`, `--warn #ffd000` (matches the user's ophi118 Twitch identity).
To iterate: edit HTML → in OBS, right-click the browser source → "Refresh cache". No restart needed.
## Working with this directory
- **Confirm OBS is using THIS dir, not the ghost** before assuming an edit will land. While OBS is running: `ls -la /proc/$(pgrep -fx obs)/cwd` and check `lsof -p $(pgrep -fx obs) | grep scenes`. Or compare mtimes after an OBS session — the file OBS rewrites is the live one.
- **Inspecting a scene collection:** `python3 -m json.tool basic/scenes/Untitled.json | less`. List scenes: `python3 -c "import json; d=json.load(open('basic/scenes/Untitled.json')); print([s['name'] for s in d['scene_order']])"`.
- **Diffing after an OBS session:** OBS rewrites the file even when nothing visibly changed. Expect noisy diffs; focus on `sources[]` and `scene_order` for meaningful changes.
- **Backing up before edits:** copy the active `.json` somewhere outside this dir — OBS's own `.bak` will be overwritten on the next save.
- **Plugin development:** Flatpak OBS plugins live inside the runtime; you can't drop `.so` files into the host filesystem and have them load. Plugin extensions for the OBS flatpak come as `com.obsproject.Studio.Plugin.<name>` flatpak extensions (search Flathub). If a plugin only has source/AUR distribution, it won't work in Flatpak — that's the tradeoff of this distribution choice.
- **WebSocket control:** `plugin_config/obs-websocket/config.json` holds connection settings. Programmatic scene edits while OBS is running should go through obs-websocket — file edits get clobbered on save.
## User-specific conventions (from global instructions)
- Never add co-author tags to commit messages.
- On any path error, immediately run `pwd` to confirm location.
- `ssu` is available for git submodule management.

View File

@@ -0,0 +1,126 @@
[General]
Name=Untitled
[Video]
BaseCX=2560
BaseCY=1336
OutputCX=1920
OutputCY=1080
FPSType=0
FPSCommon=30
FPSInt=30
FPSNum=30
FPSDen=1
ScaleType=bicubic
ColorFormat=NV12
ColorSpace=709
ColorRange=Partial
SdrWhiteLevel=300
HdrNominalPeakLevel=1000
[Panels]
CookieId=48546E60C6A25B1C
[Output]
Mode=Advanced
FilenameFormatting=%CCYY-%MM-%DD %hh-%mm-%ss
DelayEnable=false
DelaySec=20
DelayPreserve=true
Reconnect=true
RetryDelay=2
MaxRetries=25
BindIP=default
IPFamily=IPv4+IPv6
NewSocketLoopEnable=false
LowLatencyEnable=false
[AdvOut]
RescaleRes=1280x720
TrackIndex=1
RecType=Standard
RecRescaleRes=1280x1024
RecTracks=1
FFOutputToFile=true
FFFormat=mpegts
FFFormatMimeType=video/MP2T
FFRescaleRes=1280x1024
FFVEncoderId=27
FFVEncoder=libx264
FFAEncoderId=86018
FFAEncoder=aac
FFAudioMixes=1
FFURL=
FFExtension=ts
FFMCustom=content_type=video/mp2ts ice_genre=fun
FFIgnoreCompat=true
RecFilePath=/home/jin/Videos/QS
RecFileNameWithoutSpace=true
RecFormat=mkv
RecEncoder=ffmpeg_nvenc
Rescale=false
FLVTrack=1
FFVBitrate=2400
FFVGOPSize=249
FFFilePath=/run/media/jin/NTFS/recordings
VodTrackIndex=2
RecMuxerCustom=
Encoder=obs_x264
RecSplitFileType=Time
RecFormat2=mkv
ApplyServiceSettings=true
UseRescale=false
RecUseRescale=false
FFUseRescale=false
FFABitrate=160
Track1Bitrate=160
Track2Bitrate=160
Track3Bitrate=160
Track4Bitrate=160
Track5Bitrate=160
Track6Bitrate=160
RecSplitFileTime=15
RecSplitFileSize=2048
RecRB=false
RecRBTime=20
RecRBSize=512
AudioEncoder=libfdk_aac
RecAudioEncoder=libfdk_aac
StreamMultiTrackAudioMixes=1
[SimpleOutput]
UseAdvanced=false
StreamEncoder=x264
Preset=veryfast
FilePath=/home/jin/screencasts
RecFormat=mkv
RecFormat2=mkv
VBitrate=2500
ABitrate=160
NVENCPreset2=p5
RecQuality=Stream
RecRB=false
RecRBTime=20
RecRBSize=512
RecRBPrefix=Replay
StreamAudioEncoder=aac
RecAudioEncoder=aac
RecTracks=1
RecEncoder=nvenc
[Twitch]
AddonChoice=3
[Stream1]
IgnoreRecommended=false
EnableMultitrackVideo=false
MultitrackVideoMaximumAggregateBitrateAuto=true
MultitrackVideoMaximumVideoTracksAuto=true
[Audio]
MonitoringDeviceId=default
MonitoringDeviceName=Default
SampleRate=48000
ChannelSetup=Stereo
MeterDecayRate=23.53
PeakMeterType=0

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{"rate_control":"CBR","keyint_sec":2,"profile":"high"}

1688
basic/scenes/Untitled.json Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

90
global.ini Normal file
View File

@@ -0,0 +1,90 @@
[General]
Pre19Defaults=false
LicenseAccepted=true
FirstRun=true
Pre21Defaults=false
Pre23Defaults=false
LastVersion=536936449
Pre24.1Defaults=true
MaxLogs=10
InfoIncrement=-1
ProcessPriority=Normal
EnableAutoUpdates=true
ConfirmOnExit=true
HotkeyFocusType=NeverDisableHotkeys
Pre31Migrated=true
BrowserHWAccel=true
[Basic]
SceneCollection=Untitled
SceneCollectionFile=Untitled
Profile=Untitled
ProfileDir=Untitled
ConfigOnNewProfile=true
[PropertiesWindow]
cx=720
cy=580
[BasicWindow]
geometry=AdnQywADAAAAAAeAAAAAKAAADv0AAAQ3AAAHgAAAAFMAAA79AAAENwAAAAEAAAAAB4AAAAeAAAAAUwAADv0AAAQ3
DockState=AAAA/wAAAAD9AAAAAQAAAAMAAAd+AAABWfwBAAAABvsAAAAUAHMAYwBlAG4AZQBzAEQAbwBjAGsBAAAAAAAAAV4AAACYAP////sAAAAWAHMAbwB1AHIAYwBlAHMARABvAGMAawEAAAFiAAABRQAAAJgA////+wAAABIAbQBpAHgAZQByAEQAbwBjAGsBAAACqwAAAdkAAADeAP////sAAAAeAHQAcgBhAG4AcwBpAHQAaQBvAG4AcwBEAG8AYwBrAQAABIgAAAFfAAAAoQD////7AAAAGABjAG8AbgB0AHIAbwBsAHMARABvAGMAawEAAAXrAAABkwAAAKIA////+wAAABIAcwB0AGEAdABzAEQAbwBjAGsCAAAExAAAAXAAAAK8AAAAyAAAB34AAAJPAAAABAAAAAQAAAAIAAAACPwAAAAA
PreviewEnabled=true
AlwaysOnTop=false
SceneDuplicationMode=true
SwapScenesMode=true
EditPropertiesMode=false
PreviewProgramMode=false
DocksLocked=false
gridMode=false
SnappingEnabled=false
ScreenSnapping=false
SourceSnapping=false
WarnBeforeStartingStream=false
WarnBeforeStoppingStream=false
WarnBeforeStoppingRecord=false
HideProjectorCursor=false
ProjectorAlwaysOnTop=false
CenterSnapping=false
SnapDistance=10
SpacingHelpersEnabled=true
RecordWhenStreaming=false
KeepRecordingWhenStreamStops=false
SysTrayEnabled=true
SysTrayWhenStarted=false
SaveProjectors=false
ShowTransitions=true
ShowListboxToolbars=true
ShowStatusBar=true
ShowSourceIcons=true
ShowContextToolbars=true
StudioModeLabels=true
VerticalVolControl=false
MultiviewMouseSwitch=true
MultiviewDrawNames=true
MultiviewDrawAreas=true
MediaControlsCountdownTimer=true
SideDocks=false
[ScriptLogWindow]
geometry=AdnQywADAAAAAAABAAAAQQAAAlgAAAHQAAAAAQAAAEEAAAJYAAAB0AAAAAAAAAAAB4AAAAABAAAAQQAAAlgAAAHQ
[Video]
Renderer=OpenGL
[Accessibility]
SelectRed=255
SelectGreen=65280
SelectBlue=16744192
MixerGreen=2522918
MixerYellow=2523007
MixerRed=2500223
MixerGreenActive=5046092
MixerYellowActive=5046271
MixerRedActive=5000447
[Locations]
Configuration=/home/jin/.config
SceneCollections=/home/jin/.config
Profiles=/home/jin/.config
PluginManagerSettings=/home/jin/.config

274
landing/index.html Normal file
View File

@@ -0,0 +1,274 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>OPHI-118 / STAND BY</title>
<style>
:root {
--bg: #07080d;
--ink: #e8e8e0;
--hud: #4fd2ff;
--hud-dim: #2a7fa6;
--accent: #e63a2e;
--warn: #ffd000;
--mono: 'DejaVu Sans Mono', 'Liberation Mono', 'Consolas', monospace;
--display: 'DejaVu Sans', 'Liberation Sans', 'Helvetica', 'Arial', sans-serif;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
width: 100%; height: 100%;
background: var(--bg);
color: var(--ink);
font-family: var(--display);
overflow: hidden;
user-select: none;
}
body {
position: relative;
display: grid;
grid-template-rows: auto 1fr auto auto;
padding: 40px 72px;
gap: 24px;
}
/* ───────── HUD top strip ───────── */
.hud {
display: flex;
justify-content: space-between;
align-items: center;
font-family: var(--mono);
font-size: 22px;
font-weight: 700;
color: var(--hud);
letter-spacing: 0.08em;
}
.hud .group { display: flex; gap: 28px; align-items: center; }
.hud .dim { color: var(--hud-dim); }
.hud .led {
display: inline-block;
width: 11px; height: 11px;
border-radius: 50%;
background: var(--accent);
box-shadow: 0 0 10px var(--accent);
margin-right: 12px;
vertical-align: middle;
animation: rec-blink 1.6s ease-in-out infinite;
}
@keyframes rec-blink {
0%, 55% { opacity: 1; }
65%, 100% { opacity: 0.2; }
}
/* ───────── Centered mark ───────── */
.stage {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 36px;
}
.mark {
width: 360px; height: 360px;
filter: drop-shadow(0 0 22px rgba(58, 209, 255, 0.35));
animation: mark-breathe 6s ease-in-out infinite;
}
@keyframes mark-breathe {
0%, 100% { filter: drop-shadow(0 0 18px rgba(58, 209, 255, 0.30)); }
50% { filter: drop-shadow(0 0 30px rgba(58, 209, 255, 0.50)); }
}
.bars {
display: flex;
width: 720px;
height: 14px;
opacity: 0.32;
}
.bars i { flex: 1; display: block; }
.bars i:nth-child(1) { background: #c7c7c7; }
.bars i:nth-child(2) { background: #c7c700; }
.bars i:nth-child(3) { background: #00c7c7; }
.bars i:nth-child(4) { background: #00c700; }
.bars i:nth-child(5) { background: #c700c7; }
.bars i:nth-child(6) { background: #c70000; }
.bars i:nth-child(7) { background: #0000c7; }
/* ───────── Standby block ───────── */
.standby {
text-align: center;
}
.standby .big {
font-size: 172px;
font-weight: 900;
letter-spacing: 0.20em;
line-height: 1;
text-shadow: 0 0 18px rgba(79, 210, 255, 0.18);
animation: standby-pulse 3s ease-in-out infinite;
}
.standby .sub {
margin-top: 30px;
font-family: var(--mono);
font-size: 26px;
letter-spacing: 0.45em;
color: var(--hud-dim);
}
@keyframes standby-pulse {
0%, 100% { opacity: 0.90; }
50% { opacity: 1.00; }
}
/* ───────── Bottom HUD ───────── */
.foot {
display: flex;
justify-content: space-between;
align-items: center;
font-family: var(--mono);
font-size: 19px;
color: var(--hud-dim);
letter-spacing: 0.12em;
}
.foot .warn { color: var(--warn); }
/* ───────── Overlays ───────── */
#static {
position: absolute; inset: 0;
width: 100%; height: 100%;
pointer-events: none;
opacity: 0.045;
mix-blend-mode: screen;
image-rendering: pixelated;
}
.scanlines {
position: absolute; inset: 0;
pointer-events: none;
background: repeating-linear-gradient(
to bottom,
rgba(0,0,0,0) 0px,
rgba(0,0,0,0) 2px,
rgba(0,0,0,0.20) 3px,
rgba(0,0,0,0.20) 4px
);
opacity: 0.5;
mix-blend-mode: multiply;
}
.vignette {
position: absolute; inset: 0;
pointer-events: none;
background: radial-gradient(
ellipse at center,
rgba(0,0,0,0) 45%,
rgba(0,0,0,0.60) 100%
);
}
.flicker {
position: absolute; inset: 0;
pointer-events: none;
background: rgba(0,0,0,0);
animation: flicker 11s ease-in-out infinite;
}
@keyframes flicker {
0%, 100% { background: rgba(0,0,0,0); }
18% { background: rgba(0,0,0,0); }
18.3% { background: rgba(0,0,0,0.06); }
18.5% { background: rgba(0,0,0,0); }
47% { background: rgba(0,0,0,0); }
47.3% { background: rgba(0,0,0,0.08); }
47.5% { background: rgba(0,0,0,0); }
78% { background: rgba(0,0,0,0); }
78.4% { background: rgba(0,0,0,0.05); }
78.7% { background: rgba(0,0,0,0); }
}
</style>
</head>
<body>
<header class="hud">
<div class="group">
<span><span class="led"></span>TRANSMISSION</span>
<span>OPHI-118</span>
</div>
<div class="group">
<span class="dim">SIGNAL</span>
<span id="signal">&#x25AE;&#x25AE;&#x25AE;&#x25AF;&#x25AF;</span>
</div>
<div class="group">
<span class="dim">UTC</span>
<span id="clock">--:--:--</span>
</div>
</header>
<main class="stage">
<svg class="mark" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<circle cx="100" cy="100" r="92" fill="none" stroke="#3ad1ff" stroke-width="1.2" opacity="0.35"/>
<circle cx="100" cy="100" r="80" fill="none" stroke="#3ad1ff" stroke-width="2.5"/>
<g stroke="#3ad1ff" stroke-width="2" opacity="0.55">
<line x1="100" y1="8" x2="100" y2="22"/>
<line x1="100" y1="178" x2="100" y2="192"/>
<line x1="8" y1="100" x2="22" y2="100"/>
<line x1="178" y1="100" x2="192" y2="100"/>
</g>
<polygon points="100,52 148,140 52,140"
fill="none" stroke="#e63a2e" stroke-width="3.5" stroke-linejoin="miter"/>
<circle cx="100" cy="116" r="4" fill="#ffd000"/>
</svg>
<div class="bars"><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div>
</main>
<section class="standby">
<div class="big">PLEASE STAND BY</div>
<div class="sub">&mdash; DO NOT ADJUST YOUR RECEIVER &mdash;</div>
</section>
<footer class="foot">
<span>CH 118.0 MHz</span>
<span>NTSC &middot; 525i &middot; MONO</span>
<span class="warn">&#x25B2; AUDIO ACTIVE</span>
</footer>
<canvas id="static"></canvas>
<div class="scanlines"></div>
<div class="vignette"></div>
<div class="flicker"></div>
<script>
// Live UTC clock
const clockEl = document.getElementById('clock');
const pad = n => String(n).padStart(2, '0');
const tickClock = () => {
const d = new Date();
clockEl.textContent = `${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())}`;
};
tickClock();
setInterval(tickClock, 1000);
// Signal bar fluctuation — 3 bars typical, occasional drop or spike
const sigEl = document.getElementById('signal');
const FILL = '▮', EMPTY = '▯';
const drawSig = n => FILL.repeat(n) + EMPTY.repeat(5 - n);
setInterval(() => {
const r = Math.random();
const n = r < 0.06 ? 2 : r < 0.70 ? 3 : r < 0.95 ? 4 : 5;
sigEl.textContent = drawSig(n);
}, 1500);
// Animated CRT static (small buffer, scaled by browser)
const cv = document.getElementById('static');
const ctx = cv.getContext('2d');
const W = 320, H = 180;
cv.width = W; cv.height = H;
const img = ctx.createImageData(W, H);
const drawNoise = () => {
const d = img.data;
for (let i = 0; i < d.length; i += 4) {
const v = (Math.random() * 255) | 0;
d[i] = d[i+1] = d[i+2] = v;
d[i+3] = 255;
}
ctx.putImageData(img, 0, 0);
};
drawNoise();
setInterval(drawNoise, 1000 / 12);
</script>
</body>
</html>

BIN
landing/static-hum.wav Normal file

Binary file not shown.

View File

@@ -0,0 +1 @@
{"etag":"W/\"6254aa76-7a\""}

View File

@@ -0,0 +1 @@
{"$schema":"schema/package-schema.json","url":"https://obsproject.com/obs2_update/rtmp-services/v5","version":285,"files":[{"name":"services.json","version":285}]}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,130 @@
{
"ingests": [
{
"_id": 0,
"availability": 1.0,
"default": false,
"name": "Default",
"url_template": "rtmp://ingest.global-contribute.live-video.net/app/{stream_key}",
"url_template_secure": "rtmps://ingest.global-contribute.live-video.net/app/{stream_key}",
"priority": 0
},
{
"_id": 1,
"availability": 1.0,
"default": false,
"name": "Europe (Frankfurt)",
"url_template": "rtmp://euc10.contribute.live-video.net/app/{stream_key}",
"url_template_secure": "rtmps://euc10.contribute.live-video.net/app/{stream_key}",
"priority": 1
},
{
"_id": 2,
"availability": 1.0,
"default": false,
"name": "Europe (Stockholm)",
"url_template": "rtmp://eun10.contribute.live-video.net/app/{stream_key}",
"url_template_secure": "rtmps://eun10.contribute.live-video.net/app/{stream_key}",
"priority": 2
},
{
"_id": 3,
"availability": 1.0,
"default": false,
"name": "Europe (Paris)",
"url_template": "rtmp://euw30.contribute.live-video.net/app/{stream_key}",
"url_template_secure": "rtmps://euw30.contribute.live-video.net/app/{stream_key}",
"priority": 3
},
{
"_id": 4,
"availability": 1.0,
"default": false,
"name": "Europe (Ireland)",
"url_template": "rtmp://euw10.contribute.live-video.net/app/{stream_key}",
"url_template_secure": "rtmps://euw10.contribute.live-video.net/app/{stream_key}",
"priority": 4
},
{
"_id": 5,
"availability": 1.0,
"default": false,
"name": "Asia Pacific (Mumbai)",
"url_template": "rtmp://aps30.contribute.live-video.net/app/{stream_key}",
"url_template_secure": "rtmps://aps30.contribute.live-video.net/app/{stream_key}",
"priority": 5
},
{
"_id": 6,
"availability": 1.0,
"default": false,
"name": "US East (N. Virginia)",
"url_template": "rtmp://use10.contribute.live-video.net/app/{stream_key}",
"url_template_secure": "rtmps://use10.contribute.live-video.net/app/{stream_key}",
"priority": 6
},
{
"_id": 7,
"availability": 1.0,
"default": false,
"name": "US East (Ohio)",
"url_template": "rtmp://use20.contribute.live-video.net/app/{stream_key}",
"url_template_secure": "rtmps://use20.contribute.live-video.net/app/{stream_key}",
"priority": 7
},
{
"_id": 8,
"availability": 1.0,
"default": false,
"name": "Asia Pacific (Seoul)",
"url_template": "rtmp://apn20.contribute.live-video.net/app/{stream_key}",
"url_template_secure": "rtmps://apn20.contribute.live-video.net/app/{stream_key}",
"priority": 8
},
{
"_id": 9,
"availability": 1.0,
"default": false,
"name": "US West (Oregon)",
"url_template": "rtmp://usw20.contribute.live-video.net/app/{stream_key}",
"url_template_secure": "rtmps://usw20.contribute.live-video.net/app/{stream_key}",
"priority": 9
},
{
"_id": 10,
"availability": 1.0,
"default": false,
"name": "Asia Pacific (Tokyo)",
"url_template": "rtmp://apn10.contribute.live-video.net/app/{stream_key}",
"url_template_secure": "rtmps://apn10.contribute.live-video.net/app/{stream_key}",
"priority": 10
},
{
"_id": 11,
"availability": 1.0,
"default": false,
"name": "Asia Pacific (Singapore)",
"url_template": "rtmp://aps10.contribute.live-video.net/app/{stream_key}",
"url_template_secure": "rtmps://aps10.contribute.live-video.net/app/{stream_key}",
"priority": 11
},
{
"_id": 12,
"availability": 1.0,
"default": false,
"name": "South America (São Paulo)",
"url_template": "rtmp://sae10.contribute.live-video.net/app/{stream_key}",
"url_template_secure": "rtmps://sae10.contribute.live-video.net/app/{stream_key}",
"priority": 12
},
{
"_id": 13,
"availability": 1.0,
"default": false,
"name": "Asia Pacific (Sydney)",
"url_template": "rtmp://aps20.contribute.live-video.net/app/{stream_key}",
"url_template_secure": "rtmps://aps20.contribute.live-video.net/app/{stream_key}",
"priority": 13
}
]
}

View File

@@ -0,0 +1,13 @@
[
{
"display_name": "",
"enabled": true,
"encoders": [],
"id": "",
"module_name": "linux-pipewire-audio",
"outputs": [],
"services": [],
"sources": [],
"version": ""
}
]

91
user.ini Normal file
View File

@@ -0,0 +1,91 @@
[General]
Pre19Defaults=false
LicenseAccepted=true
FirstRun=true
Pre21Defaults=false
Pre23Defaults=false
LastVersion=503447555
Pre24.1Defaults=true
MaxLogs=10
InfoIncrement=-1
ProcessPriority=Normal
EnableAutoUpdates=true
ConfirmOnExit=true
HotkeyFocusType=NeverDisableHotkeys
[Basic]
SceneCollection=Untitled
SceneCollectionFile=Untitled.json
Profile=Untitled
ProfileDir=Untitled
ConfigOnNewProfile=true
[PropertiesWindow]
cx=720
cy=580
[BasicWindow]
geometry=AdnQywADAAAAAAAAAAAAAAAACf8AAAVDAAACgQAAABkAAAn+AAAEHAAAAAECAAAACgAAAAAAAAAAAAAACf8AAAVD
DockState=AAAA/wAAAAD9AAAAAQAAAAMAAAoAAAABLPwBAAAABvsAAAAUAHMAYwBlAG4AZQBzAEQAbwBjAGsBAAAAAAAAAe8AAACYAP////sAAAAWAHMAbwB1AHIAYwBlAHMARABvAGMAawEAAAHzAAAB6QAAAJgA////+wAAABIAbQBpAHgAZQByAEQAbwBjAGsBAAAD4AAAAn4AAAECAP////sAAAAeAHQAcgBhAG4AcwBpAHQAaQBvAG4AcwBEAG8AYwBrAQAABmIAAAHRAAAArQD////7AAAAGABjAG8AbgB0AHIAbwBsAHMARABvAGMAawEAAAg3AAAByQAAAKIA////+wAAABIAcwB0AGEAdABzAEQAbwBjAGsCAAAExAAAAXAAAAK8AAAAyAAACgAAAAPYAAAABAAAAAQAAAAIAAAACPwAAAAA
PreviewEnabled=true
AlwaysOnTop=false
SceneDuplicationMode=true
SwapScenesMode=true
EditPropertiesMode=false
PreviewProgramMode=false
DocksLocked=false
gridMode=false
SnappingEnabled=false
ScreenSnapping=false
SourceSnapping=false
WarnBeforeStartingStream=false
WarnBeforeStoppingStream=false
WarnBeforeStoppingRecord=false
HideProjectorCursor=false
ProjectorAlwaysOnTop=false
CenterSnapping=false
SnapDistance=10
SpacingHelpersEnabled=true
RecordWhenStreaming=false
KeepRecordingWhenStreamStops=false
SysTrayEnabled=true
SysTrayWhenStarted=false
SaveProjectors=false
ShowTransitions=true
ShowListboxToolbars=true
ShowStatusBar=true
ShowSourceIcons=true
ShowContextToolbars=true
StudioModeLabels=true
VerticalVolControl=false
MultiviewMouseSwitch=true
MultiviewDrawNames=true
MultiviewDrawAreas=true
MediaControlsCountdownTimer=true
SideDocks=false
VerticalVolumeControl=true
MixerShowInactive=false
MixerKeepInactiveLast=false
MixerShowHidden=false
MixerKeepHiddenLast=false
[ScriptLogWindow]
geometry=AdnQywADAAAAAAAAAAAAAAAAAlcAAAGPAAAAAAAAAAAAAAJXAAABjwAAAAEAAAAACgAAAAAAAAAAAAAAAlcAAAGP
[Video]
Renderer=OpenGL
[Accessibility]
SelectRed=255
SelectGreen=65280
SelectBlue=16744192
MixerGreen=2522918
MixerYellow=2523007
MixerRed=2500223
MixerGreenActive=5046092
MixerYellowActive=5046271
MixerRedActive=5000447
[Appearance]
FontScale=10
Density=1