fix: prevent OBS stream test service crash

This commit is contained in:
Franz Rolfsvaag 2026-07-25 00:04:03 +02:00
parent ea212c31b8
commit c321d2b1ff
19 changed files with 95 additions and 35 deletions

View File

@ -1,5 +1,10 @@
# Lumi changelog # 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 ## 0.3.1
- Fixed apparently inert Stream Testing starts by surfacing the server rejection immediately in the Stream Testing panel and local diagnostic log. - Fixed apparently inert Stream Testing starts by surfacing the server rejection immediately in the Stream Testing panel and local diagnostic log.

View File

@ -1,5 +1,5 @@
#ifndef AppVersion #ifndef AppVersion
#define AppVersion "0.2.1" #define AppVersion "0.2.2"
#endif #endif
#ifndef SourceRoot #ifndef SourceRoot
#error SourceRoot must point at the self-contained Companion publish directory. #error SourceRoot must point at the self-contained Companion publish directory.

View File

@ -2,8 +2,8 @@
# Copyright (c) 2026 OokamiKunTV # Copyright (c) 2026 OokamiKunTV
cmake_minimum_required(VERSION 3.28) cmake_minimum_required(VERSION 3.28)
project(lumi-obs-bridge VERSION 0.2.0 LANGUAGES CXX) project(lumi-obs-bridge VERSION 0.2.1 LANGUAGES CXX)
set(LUMI_BRIDGE_VERSION "0.2.0-development" CACHE STRING "Lumi Companion bridge release version") set(LUMI_BRIDGE_VERSION "0.2.1-development" CACHE STRING "Lumi Companion bridge release version")
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)

View File

@ -365,7 +365,9 @@ static void apply_stream_test_service(void *opaque)
context.snapshot_type = type ? type : ""; context.snapshot_type = type ? type : "";
context.snapshot_settings = settings ? obs_data_get_json(settings) : ""; context.snapshot_settings = settings ? obs_data_get_json(settings) : "";
if (settings) obs_data_release(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()) { if (context.snapshot_type.empty() || context.snapshot_settings.empty()) {
context.error = "The current OBS stream service could not be snapshotted safely."; context.error = "The current OBS stream service could not be snapshotted safely.";
return; return;
@ -485,7 +487,7 @@ static std::optional<json> handle_command(const json &message)
value.snapshot_type = type_id ? type_id : ""; value.snapshot_type = type_id ? type_id : "";
value.snapshot_settings = settings ? obs_data_get_json(settings) : ""; value.snapshot_settings = settings ? obs_data_get_json(settings) : "";
if (settings) obs_data_release(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(); 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."; if (!value.ok) value.error = "The current OBS stream service could not be snapshotted safely.";
}, &context, true); }, &context, true);

View File

@ -1,6 +1,6 @@
param( param(
[string]$ObsVersion = "31.1.1", [string]$ObsVersion = "31.1.1",
[string]$BridgeVersion = "0.2.0", [string]$BridgeVersion = "0.2.1",
[string]$CacheRoot = "$env:LOCALAPPDATA\LumiCompanionBuild" [string]$CacheRoot = "$env:LOCALAPPDATA\LumiCompanionBuild"
) )

View File

@ -1,6 +1,6 @@
param( param(
[string]$Version = "0.2.1", [string]$Version = "0.2.2",
[string]$BridgeVersion = "0.2.0", [string]$BridgeVersion = "0.2.1",
[string]$ObsVersion = "31.1.1" [string]$ObsVersion = "31.1.1"
) )

View File

@ -6,8 +6,8 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<ApplicationManifest>app.manifest</ApplicationManifest> <ApplicationManifest>app.manifest</ApplicationManifest>
<ApplicationIcon>Assets\Lumi.Companion.ico</ApplicationIcon> <ApplicationIcon>Assets\Lumi.Companion.ico</ApplicationIcon>
<Version>0.2.1</Version> <Version>0.2.2</Version>
<AssemblyVersion>0.2.1.0</AssemblyVersion> <AssemblyVersion>0.2.2.0</AssemblyVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<AvaloniaResource Include="Assets\Lumi.Companion.ico" /> <AvaloniaResource Include="Assets\Lumi.Companion.ico" />

View File

@ -14,7 +14,7 @@ editable: false
Lumi is the core web UI and bot runtime. Lumi is the core web UI and bot runtime.
## Runtime ## Runtime
Package: lumi-bot Package: lumi-bot
Version: 0.3.1 Version: 0.3.2
## Routes ## Routes
- POST /api/diagnostics/v1/run - POST /api/diagnostics/v1/run
- GET /api/events - GET /api/events

View File

@ -14,7 +14,7 @@ editable: false
Server-hosted whisper.cpp transcription for Lumi Companion and OBS closed captions. Server-hosted whisper.cpp transcription for Lumi Companion and OBS closed captions.
## Metadata ## Metadata
Plugin ID: lumi_transcription Plugin ID: lumi_transcription
Version: 0.2.1 Version: 0.2.2
Default state: enabled Default state: enabled
## Web Routes ## Web Routes
- /plugins/lumi_transcription - /plugins/lumi_transcription

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "lumi-bot", "name": "lumi-bot",
"version": "0.3.1", "version": "0.3.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "lumi-bot", "name": "lumi-bot",
"version": "0.3.1", "version": "0.3.2",
"dependencies": { "dependencies": {
"adm-zip": "^0.6.0", "adm-zip": "^0.6.0",
"better-sqlite3": "^11.5.0", "better-sqlite3": "^11.5.0",

View File

@ -1,6 +1,6 @@
{ {
"name": "lumi-bot", "name": "lumi-bot",
"version": "0.3.1", "version": "0.3.2",
"private": true, "private": true,
"type": "commonjs", "type": "commonjs",
"scripts": { "scripts": {

View File

@ -1,5 +1,10 @@
# Lumi Transcription changelog # 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 ## 0.2.1
- Surface Stream Testing startup failures immediately instead of leaving the Companion action waiting with a stale status. - Surface Stream Testing startup failures immediately instead of leaving the Companion action waiting with a stale status.

View File

@ -1,17 +1,17 @@
{ {
"schema_version": 1, "schema_version": 1,
"version": "0.2.1", "version": "0.2.2",
"signed": false, "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": { "installer": {
"id": "windows-x64-installer", "id": "windows-x64-installer",
"platform": "win32", "platform": "win32",
"architecture": "x64", "architecture": "x64",
"label": "Windows x64 per-user installer", "label": "Windows x64 per-user installer",
"filename": "Lumi.Companion-Setup.exe", "filename": "Lumi.Companion-Setup.exe",
"url": "https://git.rolfsvaag.no/Rolfsvaag_Datateknikk/Lumi/releases/download/companion-v0.2.1/Lumi.Companion-Setup.exe", "url": "https://git.rolfsvaag.no/Rolfsvaag_Datateknikk/Lumi/releases/download/companion-v0.2.2/Lumi.Companion-Setup.exe",
"sha256": "9bf4d9308c31fe9933878ed784350236b0fd4098cbe838dc2dcb8387922c40cd", "sha256": "67a1dc04e3bd75649ba4191142a88ead406acf4338ec07e564e502215e82f636",
"bytes": 51883242 "bytes": 51864724
}, },
"artifacts": [ "artifacts": [
{ {
@ -20,9 +20,9 @@
"architecture": "x64", "architecture": "x64",
"label": "Windows x64 self-contained", "label": "Windows x64 self-contained",
"filename": "Lumi.Companion-win-x64.zip", "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", "url": "https://git.rolfsvaag.no/Rolfsvaag_Datateknikk/Lumi/releases/download/companion-v0.2.2/Lumi.Companion-win-x64.zip",
"sha256": "32ce845e7576f8cd65352df561998f60c00dfb6507ab96089790194446b799e6", "sha256": "5cd1f6e0d8a507db8e25b2d1db9608d4c5fcf20a9140d04bcb7150fb83a08a2f",
"bytes": 66773784, "bytes": 66773904,
"entrypoint": "Lumi.Companion.App.exe" "entrypoint": "Lumi.Companion.App.exe"
} }
] ]

View File

@ -1,7 +1,7 @@
{ {
"id": "lumi_transcription", "id": "lumi_transcription",
"name": "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.", "description": "Server-hosted whisper.cpp transcription for Lumi Companion and OBS closed captions.",
"main": "index.js", "main": "index.js",
"channel": "stable", "channel": "stable",

View File

@ -2,6 +2,38 @@
"schema_version": 1, "schema_version": 1,
"channel": "stable", "channel": "stable",
"releases": [ "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", "version": "0.3.1",
"ref": "refs/tags/v0.3.1", "ref": "refs/tags/v0.3.1",

View File

@ -4,12 +4,12 @@ const path = require("path");
const { findSafeTarget } = require("../src/services/versioning"); const { findSafeTarget } = require("../src/services/versioning");
const root = path.join(__dirname, ".."); const root = path.join(__dirname, "..");
const releaseVersion = "0.3.1"; const releaseVersion = "0.3.2";
const previousStableVersion = "0.3.0"; const previousStableVersion = "0.3.1";
const priorStableVersion = "0.2.27"; const priorStableVersion = "0.3.0";
const earliestCompatibleCoreVersion = "0.1.9"; const earliestCompatibleCoreVersion = "0.1.9";
const introducedPlugins = { 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" } 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(webSearch.minimum_lumi_ai_version, "0.8.2");
assert.equal(hasVersionHeading(readText("plugins/lumi_ai_web_search/CHANGELOG.md"), webSearch.version), true); 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.");

View File

@ -63,6 +63,9 @@ async function main() {
assert.match(nativeBridge, /stream_test_snapshot/); assert.match(nativeBridge, /stream_test_snapshot/);
assert.match(nativeBridge, /obs_frontend_save_streaming_service/); assert.match(nativeBridge, /obs_frontend_save_streaming_service/);
assert.match(nativeBridge, /stream_test_restore/); 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, /StreamTestRecoveryStore/);
assert.match(companion, /StartStreamTestAsync/); assert.match(companion, /StartStreamTestAsync/);
assert.match(companion, /RestoreObsAfterStreamTestAsync/); assert.match(companion, /RestoreObsAfterStreamTestAsync/);

View File

@ -24,7 +24,7 @@ function readJson(relativePath) {
const releaseIndex = readJson("release-index.json"); const releaseIndex = readJson("release-index.json");
const releaseVersions = releaseIndex.releases.map((release) => release.version); 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"); assert.equal(new Set(releaseVersions).size, releaseVersions.length, "release versions must be unique");
for (const release of releaseIndex.releases) { for (const release of releaseIndex.releases) {
assert.equal(normalizeRepositoryRef(release.ref), release.ref); assert.equal(normalizeRepositoryRef(release.ref), release.ref);
@ -37,8 +37,8 @@ const packageVersion = readJson("package.json").version;
const coreManifest = readJson("update-manifest.json"); const coreManifest = readJson("update-manifest.json");
assert.equal(packageVersion, coreManifest.version); assert.equal(packageVersion, coreManifest.version);
assert.equal(coreManifest.channel, "stable"); assert.equal(coreManifest.channel, "stable");
assert.equal(packageVersion, "0.3.1"); assert.equal(packageVersion, "0.3.2");
assert.equal(currentRelease.version, "0.3.1"); assert.equal(currentRelease.version, "0.3.2");
assert.deepEqual(currentRelease.replaces_versions, ["1.2.0"]); assert.deepEqual(currentRelease.replaces_versions, ["1.2.0"]);
for (const [pluginId, version] of Object.entries(currentRelease.plugins)) { for (const [pluginId, version] of Object.entries(currentRelease.plugins)) {
assert.equal(readJson(`plugins/${pluginId}/plugin.json`).version, version, `${pluginId} release catalog version`); 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 = { const baseTarget = {
current_version: "0.2.4", current_version: "0.2.4",
available_versions: [ 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.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.3.0", ref: "refs/tags/v0.3.0", rollback_safe: true },
{ version: "0.2.27", ref: "refs/tags/v0.2.27", 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" channel: "stable"
}); });
assert.equal(corrected.version_correction, true); 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.update_available, true);
assert.equal(corrected.blocked, false); assert.equal(corrected.blocked, false);

View File

@ -1,6 +1,6 @@
{ {
"name": "Lumi Core", "name": "Lumi Core",
"version": "0.3.1", "version": "0.3.2",
"channel": "stable", "channel": "stable",
"released_at": "2026-07-24", "released_at": "2026-07-24",
"compatible_from": "0.1.9", "compatible_from": "0.1.9",
@ -8,7 +8,7 @@
"replaces_versions": [ "replaces_versions": [
"1.2.0" "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, "rollback_safe": true,
"requirements": [ "requirements": [
"Node.js 18 or newer", "Node.js 18 or newer",
@ -362,6 +362,18 @@
], ],
"rollback_safe": true, "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." "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."
} }
] ]
} }