From c321d2b1ff61f15948804e96f62e4816d46bdbe2 Mon Sep 17 00:00:00 2001 From: Franz Rolfsvaag Date: Sat, 25 Jul 2026 00:04:03 +0200 Subject: [PATCH] fix: prevent OBS stream test service crash --- CHANGELOG.md | 5 +++ companion/installer/Lumi.Companion.iss | 2 +- companion/native/obs-bridge/CMakeLists.txt | 4 +-- companion/native/obs-bridge/src/plugin.cpp | 6 ++-- companion/scripts/build-obs-bridge.ps1 | 2 +- companion/scripts/publish-companion.ps1 | 4 +-- .../Lumi.Companion.App.csproj | 4 +-- knowledge/core/lumi-core.md | 2 +- knowledge/plugins/lumi-transcription.md | 2 +- package-lock.json | 4 +-- package.json | 2 +- plugins/lumi_transcription/CHANGELOG.md | 5 +++ .../companion_manifest.json | 16 +++++----- plugins/lumi_transcription/plugin.json | 2 +- release-index.json | 32 +++++++++++++++++++ scripts/verify-release-metadata.js | 10 +++--- scripts/verify-stream-testing.js | 3 ++ scripts/verify-update-system.js | 9 +++--- update-manifest.json | 16 ++++++++-- 19 files changed, 95 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bcbea8f..45c781b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Lumi changelog +## 0.3.2 + +- Fixed an OBS crash at private Stream Test startup by respecting the OBS Frontend API's borrowed streaming-service reference instead of releasing OBS-owned memory. +- Rebuilt the managed OBS Bridge with a new version so Companion clearly requires and installs the corrected integration before another Stream Test can start. + ## 0.3.1 - Fixed apparently inert Stream Testing starts by surfacing the server rejection immediately in the Stream Testing panel and local diagnostic log. diff --git a/companion/installer/Lumi.Companion.iss b/companion/installer/Lumi.Companion.iss index aec1f9c..afef090 100644 --- a/companion/installer/Lumi.Companion.iss +++ b/companion/installer/Lumi.Companion.iss @@ -1,5 +1,5 @@ #ifndef AppVersion - #define AppVersion "0.2.1" + #define AppVersion "0.2.2" #endif #ifndef SourceRoot #error SourceRoot must point at the self-contained Companion publish directory. diff --git a/companion/native/obs-bridge/CMakeLists.txt b/companion/native/obs-bridge/CMakeLists.txt index bba5a52..b3cf9f4 100644 --- a/companion/native/obs-bridge/CMakeLists.txt +++ b/companion/native/obs-bridge/CMakeLists.txt @@ -2,8 +2,8 @@ # Copyright (c) 2026 OokamiKunTV cmake_minimum_required(VERSION 3.28) -project(lumi-obs-bridge VERSION 0.2.0 LANGUAGES CXX) -set(LUMI_BRIDGE_VERSION "0.2.0-development" CACHE STRING "Lumi Companion bridge release version") +project(lumi-obs-bridge VERSION 0.2.1 LANGUAGES CXX) +set(LUMI_BRIDGE_VERSION "0.2.1-development" CACHE STRING "Lumi Companion bridge release version") set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/companion/native/obs-bridge/src/plugin.cpp b/companion/native/obs-bridge/src/plugin.cpp index b5d1665..c06fe96 100644 --- a/companion/native/obs-bridge/src/plugin.cpp +++ b/companion/native/obs-bridge/src/plugin.cpp @@ -365,7 +365,9 @@ static void apply_stream_test_service(void *opaque) context.snapshot_type = type ? type : ""; context.snapshot_settings = settings ? obs_data_get_json(settings) : ""; if (settings) obs_data_release(settings); - obs_service_release(existing); + // obs_frontend_get_streaming_service returns a borrowed reference. + // Releasing it here destroys OBS's current service; SetService then + // releases the same object again while replacing it. if (context.snapshot_type.empty() || context.snapshot_settings.empty()) { context.error = "The current OBS stream service could not be snapshotted safely."; return; @@ -485,7 +487,7 @@ static std::optional handle_command(const json &message) value.snapshot_type = type_id ? type_id : ""; value.snapshot_settings = settings ? obs_data_get_json(settings) : ""; if (settings) obs_data_release(settings); - obs_service_release(existing); + // The frontend owns this borrowed service reference. value.ok = !value.snapshot_type.empty() && !value.snapshot_settings.empty(); if (!value.ok) value.error = "The current OBS stream service could not be snapshotted safely."; }, &context, true); diff --git a/companion/scripts/build-obs-bridge.ps1 b/companion/scripts/build-obs-bridge.ps1 index 2a23d62..def1bbe 100644 --- a/companion/scripts/build-obs-bridge.ps1 +++ b/companion/scripts/build-obs-bridge.ps1 @@ -1,6 +1,6 @@ param( [string]$ObsVersion = "31.1.1", - [string]$BridgeVersion = "0.2.0", + [string]$BridgeVersion = "0.2.1", [string]$CacheRoot = "$env:LOCALAPPDATA\LumiCompanionBuild" ) diff --git a/companion/scripts/publish-companion.ps1 b/companion/scripts/publish-companion.ps1 index a11f3ba..6524440 100644 --- a/companion/scripts/publish-companion.ps1 +++ b/companion/scripts/publish-companion.ps1 @@ -1,6 +1,6 @@ param( - [string]$Version = "0.2.1", - [string]$BridgeVersion = "0.2.0", + [string]$Version = "0.2.2", + [string]$BridgeVersion = "0.2.1", [string]$ObsVersion = "31.1.1" ) diff --git a/companion/src/Lumi.Companion.App/Lumi.Companion.App.csproj b/companion/src/Lumi.Companion.App/Lumi.Companion.App.csproj index 45a8818..1b62fa7 100644 --- a/companion/src/Lumi.Companion.App/Lumi.Companion.App.csproj +++ b/companion/src/Lumi.Companion.App/Lumi.Companion.App.csproj @@ -6,8 +6,8 @@ enable app.manifest Assets\Lumi.Companion.ico - 0.2.1 - 0.2.1.0 + 0.2.2 + 0.2.2.0 diff --git a/knowledge/core/lumi-core.md b/knowledge/core/lumi-core.md index e5e8c3e..6b7a117 100644 --- a/knowledge/core/lumi-core.md +++ b/knowledge/core/lumi-core.md @@ -14,7 +14,7 @@ editable: false Lumi is the core web UI and bot runtime. ## Runtime Package: lumi-bot -Version: 0.3.1 +Version: 0.3.2 ## Routes - POST /api/diagnostics/v1/run - GET /api/events diff --git a/knowledge/plugins/lumi-transcription.md b/knowledge/plugins/lumi-transcription.md index a002bee..25df1ac 100644 --- a/knowledge/plugins/lumi-transcription.md +++ b/knowledge/plugins/lumi-transcription.md @@ -14,7 +14,7 @@ editable: false Server-hosted whisper.cpp transcription for Lumi Companion and OBS closed captions. ## Metadata Plugin ID: lumi_transcription -Version: 0.2.1 +Version: 0.2.2 Default state: enabled ## Web Routes - /plugins/lumi_transcription diff --git a/package-lock.json b/package-lock.json index cb63bd3..958c82c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "lumi-bot", - "version": "0.3.1", + "version": "0.3.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "lumi-bot", - "version": "0.3.1", + "version": "0.3.2", "dependencies": { "adm-zip": "^0.6.0", "better-sqlite3": "^11.5.0", diff --git a/package.json b/package.json index b2d7cc6..74256d7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lumi-bot", - "version": "0.3.1", + "version": "0.3.2", "private": true, "type": "commonjs", "scripts": { diff --git a/plugins/lumi_transcription/CHANGELOG.md b/plugins/lumi_transcription/CHANGELOG.md index cb527fd..e07a03f 100644 --- a/plugins/lumi_transcription/CHANGELOG.md +++ b/plugins/lumi_transcription/CHANGELOG.md @@ -1,5 +1,10 @@ # Lumi Transcription changelog +## 0.2.2 + +- Fixed the managed OBS Bridge's streaming-service ownership so starting a private Stream Test no longer releases OBS's current service and crashes OBS. +- Require the corrected bridge version before Stream Testing and reuse the existing Update OBS Bridge action to install it safely while OBS is closed. + ## 0.2.1 - Surface Stream Testing startup failures immediately instead of leaving the Companion action waiting with a stale status. diff --git a/plugins/lumi_transcription/companion_manifest.json b/plugins/lumi_transcription/companion_manifest.json index cf98716..6282f2a 100644 --- a/plugins/lumi_transcription/companion_manifest.json +++ b/plugins/lumi_transcription/companion_manifest.json @@ -1,17 +1,17 @@ { "schema_version": 1, - "version": "0.2.1", + "version": "0.2.2", "signed": false, - "release_notes": "Fixes Stream Testing startup feedback, discovers local FFmpeg installations, and adds explicit outdated OBS Bridge update actions.", + "release_notes": "Fixes the OBS Bridge streaming-service ownership crash and requires the corrected bridge before another private Stream Test.", "installer": { "id": "windows-x64-installer", "platform": "win32", "architecture": "x64", "label": "Windows x64 per-user installer", "filename": "Lumi.Companion-Setup.exe", - "url": "https://git.rolfsvaag.no/Rolfsvaag_Datateknikk/Lumi/releases/download/companion-v0.2.1/Lumi.Companion-Setup.exe", - "sha256": "9bf4d9308c31fe9933878ed784350236b0fd4098cbe838dc2dcb8387922c40cd", - "bytes": 51883242 + "url": "https://git.rolfsvaag.no/Rolfsvaag_Datateknikk/Lumi/releases/download/companion-v0.2.2/Lumi.Companion-Setup.exe", + "sha256": "67a1dc04e3bd75649ba4191142a88ead406acf4338ec07e564e502215e82f636", + "bytes": 51864724 }, "artifacts": [ { @@ -20,9 +20,9 @@ "architecture": "x64", "label": "Windows x64 self-contained", "filename": "Lumi.Companion-win-x64.zip", - "url": "https://git.rolfsvaag.no/Rolfsvaag_Datateknikk/Lumi/releases/download/companion-v0.2.1/Lumi.Companion-win-x64.zip", - "sha256": "32ce845e7576f8cd65352df561998f60c00dfb6507ab96089790194446b799e6", - "bytes": 66773784, + "url": "https://git.rolfsvaag.no/Rolfsvaag_Datateknikk/Lumi/releases/download/companion-v0.2.2/Lumi.Companion-win-x64.zip", + "sha256": "5cd1f6e0d8a507db8e25b2d1db9608d4c5fcf20a9140d04bcb7150fb83a08a2f", + "bytes": 66773904, "entrypoint": "Lumi.Companion.App.exe" } ] diff --git a/plugins/lumi_transcription/plugin.json b/plugins/lumi_transcription/plugin.json index 2bbc295..923f910 100644 --- a/plugins/lumi_transcription/plugin.json +++ b/plugins/lumi_transcription/plugin.json @@ -1,7 +1,7 @@ { "id": "lumi_transcription", "name": "Lumi Transcription", - "version": "0.2.1", + "version": "0.2.2", "description": "Server-hosted whisper.cpp transcription for Lumi Companion and OBS closed captions.", "main": "index.js", "channel": "stable", diff --git a/release-index.json b/release-index.json index 5cd2124..f7d1cfb 100644 --- a/release-index.json +++ b/release-index.json @@ -2,6 +2,38 @@ "schema_version": 1, "channel": "stable", "releases": [ + { + "version": "0.3.2", + "ref": "refs/tags/v0.3.2", + "released_at": "2026-07-24", + "installable": true, + "rollback_safe": true, + "replaces_versions": [ + "1.2.0" + ], + "data_policy": "preserve", + "dependency_policy": "sync_on_restart", + "migration_notes": "Fixes the managed OBS Bridge streaming-service ownership crash and requires the corrected bridge before another private Stream Test. Existing local and plugin data remains preserved.", + "plugins": { + "auto-vc": "0.1.6", + "birthday": "0.1.3", + "economy-framework": "0.2.10", + "economy-games": "0.1.7", + "expression-interaction": "0.2.1", + "lumi_ai": "0.8.5", + "lumi_transcription": "0.2.2", + "moderation": "0.1.5", + "now_playing": "0.1.2", + "okf": "0.1.2", + "quotes": "0.1.2", + "sample-plugin": "0.1.0", + "throne_wishlist": "0.1.2", + "welcome_messages": "0.1.1" + }, + "tools": { + "lumi_ai_web_search": "0.1.1" + } + }, { "version": "0.3.1", "ref": "refs/tags/v0.3.1", diff --git a/scripts/verify-release-metadata.js b/scripts/verify-release-metadata.js index 1ba797b..2ed3ef3 100644 --- a/scripts/verify-release-metadata.js +++ b/scripts/verify-release-metadata.js @@ -4,12 +4,12 @@ const path = require("path"); const { findSafeTarget } = require("../src/services/versioning"); const root = path.join(__dirname, ".."); -const releaseVersion = "0.3.1"; -const previousStableVersion = "0.3.0"; -const priorStableVersion = "0.2.27"; +const releaseVersion = "0.3.2"; +const previousStableVersion = "0.3.1"; +const priorStableVersion = "0.3.0"; const earliestCompatibleCoreVersion = "0.1.9"; const introducedPlugins = { - lumi_transcription: { version: "0.2.1", knowledge: "lumi-transcription" }, + lumi_transcription: { version: "0.2.2", knowledge: "lumi-transcription" }, now_playing: { version: "0.1.2", knowledge: "now-playing" } }; @@ -82,4 +82,4 @@ assert.equal(webSearch.minimum_lumi_version, "0.2.0"); assert.equal(webSearch.minimum_lumi_ai_version, "0.8.2"); assert.equal(hasVersionHeading(readText("plugins/lumi_ai_web_search/CHANGELOG.md"), webSearch.version), true); -console.log("Release metadata verification passed: stable core 0.3.1 after 0.3.0 with synchronized Companion plugin metadata."); +console.log("Release metadata verification passed: stable core 0.3.2 after 0.3.1 with synchronized Companion plugin metadata."); diff --git a/scripts/verify-stream-testing.js b/scripts/verify-stream-testing.js index fd23e09..b750550 100644 --- a/scripts/verify-stream-testing.js +++ b/scripts/verify-stream-testing.js @@ -63,6 +63,9 @@ async function main() { assert.match(nativeBridge, /stream_test_snapshot/); assert.match(nativeBridge, /obs_frontend_save_streaming_service/); assert.match(nativeBridge, /stream_test_restore/); + assert(!nativeBridge.includes("obs_service_release(existing)"), "OBS frontend service getters return borrowed references that must not be released"); + assert.match(nativeBridge, /obs_frontend_set_streaming_service\(test\)[\s\S]*obs_service_release\(test\)/); + assert.match(nativeBridge, /obs_frontend_set_streaming_service\(restored\)[\s\S]*obs_service_release\(restored\)/); assert.match(companion, /StreamTestRecoveryStore/); assert.match(companion, /StartStreamTestAsync/); assert.match(companion, /RestoreObsAfterStreamTestAsync/); diff --git a/scripts/verify-update-system.js b/scripts/verify-update-system.js index d0972bd..6c47e87 100644 --- a/scripts/verify-update-system.js +++ b/scripts/verify-update-system.js @@ -24,7 +24,7 @@ function readJson(relativePath) { const releaseIndex = readJson("release-index.json"); const releaseVersions = releaseIndex.releases.map((release) => release.version); -assert.deepEqual(releaseVersions, ["0.3.1", "0.3.0", "0.2.27", "0.2.26", "0.2.25", "0.2.24", "0.2.23", "0.2.22", "0.2.21", "0.2.20", "0.2.19", "0.2.18", "0.2.17", "0.2.16", "0.2.15", "0.2.14", "0.2.13", "0.2.12", "0.2.11", "0.2.10", "0.2.9", "0.2.8", "0.2.7", "0.2.6", "0.2.5", "0.2.4", "0.2.3", "0.2.2", "0.2.1", "0.2.0", "0.1.9"]); +assert.deepEqual(releaseVersions, ["0.3.2", "0.3.1", "0.3.0", "0.2.27", "0.2.26", "0.2.25", "0.2.24", "0.2.23", "0.2.22", "0.2.21", "0.2.20", "0.2.19", "0.2.18", "0.2.17", "0.2.16", "0.2.15", "0.2.14", "0.2.13", "0.2.12", "0.2.11", "0.2.10", "0.2.9", "0.2.8", "0.2.7", "0.2.6", "0.2.5", "0.2.4", "0.2.3", "0.2.2", "0.2.1", "0.2.0", "0.1.9"]); assert.equal(new Set(releaseVersions).size, releaseVersions.length, "release versions must be unique"); for (const release of releaseIndex.releases) { assert.equal(normalizeRepositoryRef(release.ref), release.ref); @@ -37,8 +37,8 @@ const packageVersion = readJson("package.json").version; const coreManifest = readJson("update-manifest.json"); assert.equal(packageVersion, coreManifest.version); assert.equal(coreManifest.channel, "stable"); -assert.equal(packageVersion, "0.3.1"); -assert.equal(currentRelease.version, "0.3.1"); +assert.equal(packageVersion, "0.3.2"); +assert.equal(currentRelease.version, "0.3.2"); assert.deepEqual(currentRelease.replaces_versions, ["1.2.0"]); for (const [pluginId, version] of Object.entries(currentRelease.plugins)) { assert.equal(readJson(`plugins/${pluginId}/plugin.json`).version, version, `${pluginId} release catalog version`); @@ -50,6 +50,7 @@ for (const [toolId, version] of Object.entries(currentRelease.tools)) { const baseTarget = { current_version: "0.2.4", available_versions: [ + { version: "0.3.2", ref: "refs/tags/v0.3.2", rollback_safe: true }, { version: "0.3.1", ref: "refs/tags/v0.3.1", rollback_safe: true }, { version: "0.3.0", ref: "refs/tags/v0.3.0", rollback_safe: true }, { version: "0.2.27", ref: "refs/tags/v0.2.27", rollback_safe: true }, @@ -150,7 +151,7 @@ const corrected = buildStatus({ channel: "stable" }); assert.equal(corrected.version_correction, true); -assert.equal(corrected.safe_target_version, "0.3.1"); +assert.equal(corrected.safe_target_version, "0.3.2"); assert.equal(corrected.update_available, true); assert.equal(corrected.blocked, false); diff --git a/update-manifest.json b/update-manifest.json index c629ab4..3574c3e 100644 --- a/update-manifest.json +++ b/update-manifest.json @@ -1,6 +1,6 @@ { "name": "Lumi Core", - "version": "0.3.1", + "version": "0.3.2", "channel": "stable", "released_at": "2026-07-24", "compatible_from": "0.1.9", @@ -8,7 +8,7 @@ "replaces_versions": [ "1.2.0" ], - "migration_notes": "Fixes Stream Testing startup feedback and Windows FFmpeg discovery, and adds explicit outdated OBS Bridge update guidance and actions. Existing settings, databases, pairing records, OBS settings, credentials, overlays, uploads, models, secrets, plugin data, and local-only plugins are preserved.", + "migration_notes": "Fixes the managed OBS Bridge streaming-service ownership crash and requires the corrected bridge before Stream Testing. Existing settings, databases, pairing records, OBS settings, credentials, overlays, uploads, models, secrets, plugin data, and local-only plugins are preserved.", "rollback_safe": true, "requirements": [ "Node.js 18 or newer", @@ -362,6 +362,18 @@ ], "rollback_safe": true, "migration_notes": "Adds private OBS stream testing, crash-safe exact-service restoration, corrected overlay media boundaries, and repeatable Companion update checks while preserving local data." + }, + { + "version": "0.3.1", + "channel": "stable", + "released_at": "2026-07-24", + "compatible_from": "0.1.9", + "migration_kind": "patch", + "replaces_versions": [ + "1.2.0" + ], + "rollback_safe": true, + "migration_notes": "Fixes Stream Testing startup feedback and Windows FFmpeg discovery, and adds explicit loaded OBS Bridge version checks with update actions while preserving local data." } ] }