Author SHA1 Message Date
shadowdaoandClaude Opus 5 d9f73926e4 feat(release): publish releases directly, and fix the install path in the notes
Build / macOS (macos-latest) (push) Successful in 27s
Build / macOS (macos-latest) (pull_request) Successful in 26s
Build / Linux (ubuntu-24.04) (push) Successful in 56s
Build / Linux (ubuntu-24.04) (pull_request) Successful in 54s
Build / Windows (windows-latest) (push) Successful in 4m20s
Build / Windows (windows-latest) (pull_request) Successful in 3m55s
Releases were created as drafts for one stated reason: nobody had run the
plugin in the OBS GUI on any platform, so a human had to look before anything
became visible. The first confirmed GUI load (Windows, OBS 32.2.2 on Windows
11, 2026-09-09) retired that gate, so `publish-release.sh` now posts
`"draft": False` and the workflow no longer needs a human click.

The caveats did not go away, they moved: the generated release notes now lead
with what is actually confirmed (module loads and registers its source type,
Windows only) and what is not (video rendering, A/V sync, latency, mid-show
publisher restart, Linux and macOS in the GUI at all), and the per-platform
table carries the rest.

Also fixes the third and last copy of the wrong Windows install path. The
release notes template told every downloader to extract into
`%APPDATA%\obs-studio\plugins\`, which on Windows is OBS's config directory
and is never scanned for plugins -- that is what stopped a director's
correctly-shaped install from loading. The notes now carry a per-platform
table (`C:\ProgramData\obs-studio\plugins\` on Windows), the exact finished
path, a warning about Explorer's "Extract All..." wrapper folder, and how to
confirm the load in the OBS log. Both failure modes are silent, which is
precisely why they belong in the notes.

Note the tradeoff now that nothing is held back: assets upload after the
release row is created, so a release is briefly visible with no files
attached. Called out in the script header.

Verified by rendering the heredoc with a stub tag: backslash escaping survives
into correct markdown, and the YAML parses.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AzGnvQ6wfD7bw7PZN35ft9
2026-09-09 16:50:44 -07:00
jknapp ee44fb6a73 Merge pull request 'docs: record the first confirmed OBS GUI load (Windows)' (#3) from docs/first-gui-load into main
Build / macOS (macos-latest) (push) Successful in 28s
Build / Linux (ubuntu-24.04) (push) Successful in 58s
Build / Windows (windows-latest) (push) Successful in 3m45s
2026-09-09 23:47:05 +00:00
shadowdaoandClaude Opus 5 abd4dc9aca docs: record the first confirmed OBS GUI load (Windows)
Build / macOS (macos-latest) (pull_request) Successful in 26s
Build / Linux (ubuntu-24.04) (pull_request) Successful in 59s
Build / macOS (macos-latest) (push) Successful in 37s
Build / Linux (ubuntu-24.04) (push) Successful in 55s
Build / Windows (windows-latest) (pull_request) Successful in 3m45s
Build / Windows (windows-latest) (push) Successful in 3m48s
The v0.1.0 Windows artifact loaded into OBS 32.2.2 on Windows 11 (build
26200) on a director's machine on 2026-09-09, from
C:\ProgramData\obs-studio\plugins\streamer-tools-camera\bin\64bit\. That
retires "has not been run in the OBS GUI on any platform", which this README
asserted in four places.

Scoped deliberately to what was actually observed: the module loads and
registers its source type. Whether video renders, the colours, A/V sync,
latency, and mid-show publisher restart are all still unverified, and Linux
and macOS have still never been opened in the GUI. The "Not verified
anywhere" list now says so explicitly rather than being deleted.

Also drops the release-draft rationale that pointed at the no-GUI-load state,
and carries the ProgramData install-path warning up into the status section,
since that is what stopped the first GUI load from happening sooner.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AzGnvQ6wfD7bw7PZN35ft9
2026-09-09 16:47:00 -07:00
jknapp 1571d7ecea Merge pull request 'docs: the Windows plugin dir is ProgramData, not the config dir' (#2) from docs/windows-plugin-path into main
Build / macOS (macos-latest) (push) Successful in 29s
Build / Linux (ubuntu-24.04) (push) Successful in 45s
Build / Windows (windows-latest) (push) Successful in 4m3s
2026-09-09 23:44:20 +00:00
shadowdaoandClaude Opus 5 bfc38f45ca docs: the Windows plugin dir is ProgramData, not the config dir
Build / macOS (macos-latest) (push) Successful in 36s
Build / Linux (ubuntu-24.04) (push) Successful in 55s
Build / macOS (macos-latest) (pull_request) Successful in 33s
Build / Linux (ubuntu-24.04) (pull_request) Successful in 1m9s
Build / Windows (windows-latest) (push) Successful in 10m51s
Build / Windows (windows-latest) (pull_request) Successful in 10m27s
The packaging section claimed `<config>/obs-studio/plugins/<name>/bin/64bit`
is "exactly the layout OBS searches on Linux and Windows". The layout is
right; the base directory is not the same on both. obs-studio's
`AddExtraModulePaths()` uses `GetAppConfigPath` on Linux/macOS but
`GetProgramDataPath` (`CSIDL_COMMON_APPDATA`) on Windows, so Windows scans
`C:\ProgramData\obs-studio\plugins\`, never `%APPDATA%\obs-studio\`
(`CSIDL_APPDATA`) — which on Windows is where OBS keeps its config.

This bit a director on 2026-09-09: a correctly-shaped install under
`AppData\Roaming` produced an OBS log with zero mention of the module. Noted
here because the failure is silent — OBS names any module it finds but cannot
use, so an absence of any line means the file is not on a scanned path at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AzGnvQ6wfD7bw7PZN35ft9
2026-09-09 16:44:06 -07:00
3 changed files with 97 additions and 42 deletions
+43 -19
View File
@@ -1,9 +1,15 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Creates a (draft) Gitea Release for the tag that triggered # Creates a published Gitea Release for the tag that triggered
# .gitea/workflows/release.yml, and uploads every archive in $DIST_DIR as a # .gitea/workflows/release.yml, and uploads every archive in $DIST_DIR as a
# release asset. Draft because nobody has run this plugin in the OBS GUI on # release asset.
# any platform yet -- a human still opens it and clicks Publish once that's #
# no longer true (or once they're satisfied regardless). # This used to create a DRAFT, on the grounds that nobody had run the plugin
# in the OBS GUI on any platform. That stopped being true on 2026-09-09, when
# the v0.1.0 Windows artifact loaded into OBS 32.2.2 on Windows 11 -- so the
# release publishes directly and the per-platform table below carries the
# remaining caveats instead. Assets upload AFTER the release row is created
# either way, so a release is briefly visible with no files attached; that is
# the tradeoff for not needing a human click.
# #
# Required env: GITEA_TOKEN, SERVER, OWNER, REPO, TAG, SHA, DIST_DIR # Required env: GITEA_TOKEN, SERVER, OWNER, REPO, TAG, SHA, DIST_DIR
# Optional env: MACOS_BUNDLE_FOUND ("true"/"false", default "false") # Optional env: MACOS_BUNDLE_FOUND ("true"/"false", default "false")
@@ -30,26 +36,44 @@ cat > "${NOTES_FILE}" <<EOF
Built from commit \`${SHA}\`. Built from commit \`${SHA}\`.
**Nobody has yet run this plugin in the OBS GUI, on any platform.** See "What **Read the per-platform notes below before relying on this.** The module has
is verified, and how" in \`README.md\` for exactly what has and has not been been loaded in the OBS GUI exactly once -- Windows, OBS 32.2.2 on Windows 11,
checked, including which claims are backed by automated tests versus a human 2026-09-09 -- and only "it loads and registers its source type" is confirmed
watching OBS. This is why the release is a draft -- open it and click Publish there. Whether video renders correctly, A/V sync, end-to-end latency and
once you're satisfied. mid-show publisher restart are all still unverified on every platform. See
"What is verified, and how" in \`README.md\` for exactly what is backed by
automated tests versus a human watching OBS.
| Platform | Archive | Notes | | Platform | Archive | Notes |
|---|---|---| |---|---|---|
| Linux (x64) | \`streamer-tools-camera-${TAG}-linux-x64.zip\` | Functionally complete and verified end to end against a real LiveKit server and a real libobs (see README); OBS GUI itself still unverified | | Linux (x64) | \`streamer-tools-camera-${TAG}-linux-x64.zip\` | Functionally complete and verified end to end against a real LiveKit server and a real libobs (see README); OBS GUI itself still unverified |
| Windows (x64) | \`streamer-tools-camera-${TAG}-windows-x64.zip\` | Built and tested by this workflow's Windows job; the WinHTTP backend has never been exercised against a real streamer-tools server, only a loopback test server -- see README's Windows CI section | | Windows (x64) | \`streamer-tools-camera-${TAG}-windows-x64.zip\` | Built and tested by this workflow's Windows job. Confirmed to load in the OBS GUI (32.2.2 / Windows 11, 2026-09-09); nothing past module load is verified. The WinHTTP backend has never been exercised against a real streamer-tools server, only a loopback test server -- see README's Windows CI section |
| macOS | \`streamer-tools-camera-${TAG}-macos.zip\` | Built and tested by this workflow's macOS job. ${MACOS_NOTE} | | macOS | \`streamer-tools-camera-${TAG}-macos.zip\` | Built and tested by this workflow's macOS job. ${MACOS_NOTE} |
## Installing ## Installing
Extract the archive into your OBS plugins folder for your platform (the Extract the archive into your OBS plugins folder. **The directory is not the
default locations are easy to find online -- typically same shape on every platform, and picking the wrong one fails silently -- OBS
\`~/.config/obs-studio/plugins/\` on Linux, \`%APPDATA%\\obs-studio\\plugins\\\` logs nothing at all for a plugin it never finds:**
on Windows, \`~/Library/Application Support/obs-studio/plugins/\` on macOS).
Each archive's top-level folder already matches the shape OBS expects there, | Platform | Extract into |
so extracting is the whole install step. Then in OBS: Sources -> \`+\` -> |---|---|
| Windows | \`C:\\ProgramData\\obs-studio\\plugins\\\` -- **not** \`%APPDATA%\\obs-studio\\\`, which is where OBS keeps its config and is never scanned for plugins |
| macOS | \`~/Library/Application Support/obs-studio/plugins/\` |
| Linux | \`~/.config/obs-studio/plugins/\` |
Each archive's top-level folder already matches the shape OBS expects, so
extracting is the whole install step -- but check the result is exactly one
folder deep. Windows Explorer's "Extract All..." adds a folder named after the
zip unless you clear it from the destination box, which nests it one level too
far and is equally silent. On Windows the finished path must be:
\`\`\`
C:\\ProgramData\\obs-studio\\plugins\\streamer-tools-camera\\bin\\64bit\\streamer-tools-camera.dll
\`\`\`
To confirm it loaded, restart OBS and check Help -> Log Files -> View Current
Log for \`streamer-tools-camera\` under "Loaded Modules". Then in OBS: Sources -> \`+\` ->
"streamer-tools Camera" -> fill in the server URL, room slug and read key "streamer-tools Camera" -> fill in the server URL, room slug and read key
from the room's settings page -> "Refresh camera list" -> pick a camera. from the room's settings page -> "Refresh camera list" -> pick a camera.
@@ -73,7 +97,7 @@ print(json.dumps({
"tag_name": tag, "tag_name": tag,
"name": tag, "name": tag,
"body": notes, "body": notes,
"draft": True, "draft": False,
"prerelease": False, "prerelease": False,
})) }))
PYEOF PYEOF
@@ -87,7 +111,7 @@ RESP="$(curl -sS -f -X POST \
"${SERVER}/api/v1/repos/${OWNER}/${REPO}/releases")" "${SERVER}/api/v1/repos/${OWNER}/${REPO}/releases")"
RELEASE_ID="$(python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])' <<<"${RESP}")" RELEASE_ID="$(python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])' <<<"${RESP}")"
echo "Created release id ${RELEASE_ID} (draft)." echo "Created release id ${RELEASE_ID} (published; assets upload next)."
shopt -s nullglob shopt -s nullglob
ASSETS=("${DIST_DIR}"/*) ASSETS=("${DIST_DIR}"/*)
@@ -106,4 +130,4 @@ for f in "${ASSETS[@]}"; do
> /dev/null > /dev/null
done done
echo "Done. Draft release: ${SERVER}/${OWNER}/${REPO}/releases/${RELEASE_ID}" echo "Done. Release: ${SERVER}/${OWNER}/${REPO}/releases/${RELEASE_ID}"
+9 -7
View File
@@ -1,14 +1,16 @@
name: Release name: Release
# Packages a build of each platform into a downloadable archive and creates # Packages a build of each platform into a downloadable archive and creates
# a (draft) Gitea Release for it, so the project owner and other directors # a published Gitea Release for it, so the project owner and other directors
# can grab a ready-to-use build instead of compiling from source. # can grab a ready-to-use build instead of compiling from source.
# #
# Runs only on a pushed version tag (see `on.push.tags` below) -- never on an # Runs only on a pushed version tag (see `on.push.tags` below) -- never on an
# ordinary push or PR, unlike build.yml. The release it creates is a DRAFT: # ordinary push or PR, unlike build.yml. The release it creates is PUBLISHED
# it stays invisible to anyone without write access until a human explicitly # immediately. It used to be a draft, gated on a human clicking Publish
# opens it and clicks Publish, since nobody has run this plugin in the OBS # because nobody had run the plugin in the OBS GUI on any platform; the first
# GUI on any platform yet. # confirmed GUI load (Windows, 2026-09-09) retired that. The caveats that
# remain live in the generated release notes, not in the draft flag -- see
# .gitea/scripts/publish-release.sh.
# #
# The actual per-platform build commands live in .gitea/scripts/ and are the # The actual per-platform build commands live in .gitea/scripts/ and are the
# same scripts .gitea/workflows/build.yml uses, so this workflow can't drift # same scripts .gitea/workflows/build.yml uses, so this workflow can't drift
@@ -183,7 +185,7 @@ jobs:
path: dist path: dist
release: release:
name: Create Gitea Release (draft) name: Create Gitea Release
needs: [linux, macos, windows] needs: [linux, macos, windows]
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
permissions: permissions:
@@ -214,7 +216,7 @@ jobs:
name: release-archive-windows-x64 name: release-archive-windows-x64
path: dist path: dist
- name: Create draft release and upload assets - name: Create release and upload assets
run: .gitea/scripts/publish-release.sh run: .gitea/scripts/publish-release.sh
env: env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
+45 -16
View File
@@ -14,18 +14,32 @@ match the vendored LiveKit binaries, which are also Apache-2.0 — see
`.gitea/workflows/build.yml` builds, tests, and uploads CI-internal build `.gitea/workflows/build.yml` builds, tests, and uploads CI-internal build
artifacts on every push. `.gitea/workflows/release.yml` packages a tagged artifacts on every push. `.gitea/workflows/release.yml` packages a tagged
build (`v*`) into a **draft** Gitea Release — draft because nobody has run build (`v*`) into a **published** Gitea Release. It created drafts until
this in the OBS GUI yet (see below), not because of anything else; a human 2026-09-09, gated on a human clicking Publish because nobody had run the
still needs to open it and click Publish. plugin in the OBS GUI; the first confirmed GUI load retired that gate, and the
remaining caveats live in the generated release notes instead.
The plugin is **functionally complete on Linux and verified end to end there** The plugin is **functionally complete on Linux and verified end to end there**
(module loads into real libobs, connects to a real LiveKit server through the (module loads into real libobs, connects to a real LiveKit server through the
real streamer-tools API shape, and pushes decoded frames into real streamer-tools API shape, and pushes decoded frames into
`obs_source_output_video`/`_audio`). `obs_source_output_video`/`_audio`).
It has **not been run in the OBS GUI on any platform.** macOS builds the real **First confirmed OBS GUI load: Windows, 2026-09-09** — the v0.1.0 release
module in CI but its artifact is not yet loadable (see the macOS packaging gap artifact loaded into OBS 32.2.2 on Windows 11 (build 26200) on a director's
under CI). machine, from
`C:\ProgramData\obs-studio\plugins\streamer-tools-camera\bin\64bit\`.
That retires "the module will not even load in a real OBS" for Windows. It
does **not** yet cover whether video renders correctly, colours, A/V sync or
latency — see "Not verified anywhere" below for what is still open. Linux and
macOS have still never been opened in the GUI; macOS builds the real module in
CI but its artifact is not yet loadable (see the macOS packaging gap under CI).
⚠️ **The install directory is not the same on every platform, and getting it
wrong fails silently.** On Windows it is
`C:\ProgramData\obs-studio\plugins\` (`GetProgramDataPath`
`CSIDL_COMMON_APPDATA`), **not** `%APPDATA%\obs-studio\` — see the packaging
section. That mistake cost the director above an evening: OBS logs nothing at
all for a plugin it never finds.
**Windows CI is now green.** The run at `f27b1c0` is the first completed **Windows CI is now green.** The run at `f27b1c0` is the first completed
green Windows job on this repository: the from-source libobs bootstrap green Windows job on this repository: the from-source libobs bootstrap
@@ -35,8 +49,8 @@ suites pass, and `build\package\bin\64bit\streamer-tools-camera.dll`
out of the job's own log body, not inferred from the job status. That also out of the job's own log body, not inferred from the job status. That also
retires three previously-unproven items in one go: the `-A x64` argument fix, retires three previously-unproven items in one go: the `-A x64` argument fix,
the PowerShell rewrite of the Windows steps, and the `add_subdirectory` the PowerShell rewrite of the Windows steps, and the `add_subdirectory`
patch for `OBS::w32-pthreads`. Windows is still **unverified in the OBS GUI**, patch for `OBS::w32-pthreads`. Windows has since been **loaded in the real OBS
exactly like the other two platforms. See "Where the Windows bootstrap got GUI** (see above); Linux and macOS have not. See "Where the Windows bootstrap got
to" under CI below for the whole trace, and check current CI status rather to" under CI below for the whole trace, and check current CI status rather
than trusting this paragraph's age. than trusting this paragraph's age.
@@ -66,7 +80,7 @@ scripts/livekit-dev-room.py - mints tokens for the integration test
third_party/livekit/ - redistribution notices for the LiveKit binaries third_party/livekit/ - redistribution notices for the LiveKit binaries
.gitea/scripts/ - the actual per-platform build commands, shared by build.yml and release.yml .gitea/scripts/ - the actual per-platform build commands, shared by build.yml and release.yml
.gitea/workflows/build.yml - 3-platform CI matrix (every push/PR; never publishes) .gitea/workflows/build.yml - 3-platform CI matrix (every push/PR; never publishes)
.gitea/workflows/release.yml - packages + creates a draft Gitea Release (only on a `v*` tag push; see Status above) .gitea/workflows/release.yml - packages + publishes a Gitea Release (only on a `v*` tag push; see Status above)
``` ```
## How it works ## How it works
@@ -142,17 +156,28 @@ build/package/licenses/...
``` ```
That is exactly the layout OBS searches on Linux and Windows — That is exactly the layout OBS searches on Linux and Windows —
`<config>/obs-studio/plugins/<name>/bin/64bit` plus a sibling `data/`, per `<base>/obs-studio/plugins/<name>/bin/64bit` plus a sibling `data/`, per
`AddExtraModulePaths()` in obs-studio's `UI/window-basic-main.cpp` — so `AddExtraModulePaths()` in obs-studio (`UI/window-basic-main.cpp` in 30.x,
`build/package/` is a straight drop-in. The module resolves the LiveKit `frontend/widgets/OBSBasic.cpp` in 32.x) — so `build/package/` is a straight
libraries from `$ORIGIN` (verified: `ldd` on the staged copy resolves both drop-in. **`<base>` is NOT the same directory on every platform**, and getting
this wrong is silent: OBS logs nothing at all for a plugin it never finds.
Linux uses the user config dir (`GetAppConfigPath``~/.config`), but Windows
uses `GetProgramDataPath` (`CSIDL_COMMON_APPDATA`) — i.e.
`C:\ProgramData\obs-studio\plugins\`, **not** `%APPDATA%\obs-studio\`
(`CSIDL_APPDATA`), which on Windows holds OBS's config and is never scanned for
plugins. This bit a director on 2026-09-09: a correctly-shaped install under
`AppData\Roaming` produced a log with zero mention of the module.
The module resolves the LiveKit libraries from `$ORIGIN` (verified: `ldd` on the staged copy resolves both
to `bin/64bit/`), not from the build tree. macOS is not this shape; see the to `bin/64bit/`), not from the build tree. macOS is not this shape; see the
macOS packaging gap under CI. macOS packaging gap under CI.
## Testing this by hand ## Testing this by hand
**Nobody has yet run this in the OBS GUI. That test is still outstanding on **The module has been loaded in the OBS GUI on Windows once (2026-09-09, OBS
all three platforms.** To do it on Linux: 32.2.2 / Windows 11 26200) — nothing beyond "it loads and registers its source"
is confirmed there, and Linux and macOS have never been opened in the GUI at
all.** To do it on Linux:
``` ```
mkdir -p ~/.config/obs-studio/plugins/streamer-tools-camera mkdir -p ~/.config/obs-studio/plugins/streamer-tools-camera
@@ -208,7 +233,11 @@ livekit-server 1.13.6 in dev mode):
| Two sources in one OBS process | same harness with a second source added: both connect with distinct nonce identities, both receive frames, both tear down cleanly | | Two sources in one OBS process | same harness with a second source added: both connect with distinct nonce identities, both receive frames, both tear down cleanly |
**Not verified anywhere:** **Not verified anywhere:**
- The OBS GUI, on any platform. No human has looked at this in OBS. - Anything past module load in the OBS GUI. Windows 2026-09-09 confirms the
module loads and its source type appears; whether video actually renders
(right way up, right colours), what the A/V sync and latency look like, and
whether a publisher restarting mid-show recovers on screen are all still
unanswered. Linux and macOS have not been opened in the GUI at all.
- macOS beyond "CI builds and links the real module and the core tests pass". - macOS beyond "CI builds and links the real module and the core tests pass".
Its artifact is a bare `.so` with a relative libobs install name and will Its artifact is a bare `.so` with a relative libobs install name and will
not load in OBS.app — see the macOS packaging gap under CI. not load in OBS.app — see the macOS packaging gap under CI.