diff --git a/HANDOFF.md b/HANDOFF.md index 0253886..2816eaa 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -35,6 +35,6 @@ - Pack URIs for bundled assets must be assembly-qualified (`/LlamaCasty;component/Assets/…`): the unqualified form resolves against the entry assembly, which is the test host under `dotnet test`. - Media sessions are keyed by **path + look** (`MainViewModel.MediaSessionKey`); never acquire by raw path. -- **The audio pipe is one instance per machine.** DEBUG appends its id (`InstanceProfile.AudioPipeName`). +- **The audio pipe allows ONE server per name**; DEBUG suffixes it (id, else pid), Release does not. - Integration tests constructing `MainWindow` must set a temporary `LayoutPathOverride` and clear it in `finally`. - Bundled FFmpeg stays outside the single-file exe (`ExcludeFromSingleFile="true"`). diff --git a/Helpers/InstanceProfile.cs b/Helpers/InstanceProfile.cs index d066915..d44ee96 100644 --- a/Helpers/InstanceProfile.cs +++ b/Helpers/InstanceProfile.cs @@ -70,13 +70,13 @@ public static class InstanceProfile #endif /// The audio pipe name for this process. The pipe server allows exactly ONE instance, so a - /// shared name meant the second app on the machine could never go live — it threw - /// "All pipe instances are busy" out of the Go Live click and ended the process (2026-11-06: two - /// instances, one recording the other). A dev instance appends its id; Release, and therefore the - /// first instance, keeps the plain name. + /// shared name means the second app on the machine can never go live — it throws "All pipe instances + /// are busy" (2026-11-06: Release recording, Debug trying to go live). Release keeps the plain name; + /// a DEBUG build ALWAYS appends a suffix — its claimed id, or the process id when it has none — so a + /// debug run launched straight from the IDE can never collide with the Release build beside it. public static string AudioPipeName(string baseName) #if DEBUG - => Id is { } id ? $"{baseName}_{id}" : baseName; + => Id is { } id ? $"{baseName}_{id}" : $"{baseName}_pid{Environment.ProcessId}"; #else => baseName; #endif diff --git a/ViewModels/MainViewModel.Streaming.Operations.cs b/ViewModels/MainViewModel.Streaming.Operations.cs index 7332e0b..ac1ca12 100644 --- a/ViewModels/MainViewModel.Streaming.Operations.cs +++ b/ViewModels/MainViewModel.Streaming.Operations.cs @@ -79,9 +79,11 @@ public partial class MainViewModel : ViewModelBase /// private bool TryStartAudioPipe() { + var pipeName = InstanceProfile.AudioPipeName(EncoderOptions.DefaultAudioPipeName); try { - _audioMixer.StartLive(InstanceProfile.AudioPipeName(EncoderOptions.DefaultAudioPipeName)); + _audioMixer.StartLive(pipeName); + AppLog.Write($"Audio pipe: {pipeName}"); return true; } catch (Exception ex) diff --git a/ytLive.Tests/AudioPipeInstanceTests.cs b/ytLive.Tests/AudioPipeInstanceTests.cs index c864351..8598cb9 100644 --- a/ytLive.Tests/AudioPipeInstanceTests.cs +++ b/ytLive.Tests/AudioPipeInstanceTests.cs @@ -36,7 +36,14 @@ public sealed class AudioPipeInstanceTests : IDisposable public void ADevInstanceServesItsOwnPipeName() { Environment.SetEnvironmentVariable(InstanceProfile.InstanceVariable, null); +#if DEBUG + // No claimed id (launched from the IDE) must still never collide with the Release build's pipe. + var unnamed = InstanceProfile.AudioPipeName("ytllive_audio"); + Assert.StartsWith("ytllive_audio_pid", unnamed); + Assert.Equal(unnamed, InstanceProfile.AudioPipeName("ytllive_audio")); +#else Assert.Equal("ytllive_audio", InstanceProfile.AudioPipeName("ytllive_audio")); +#endif Environment.SetEnvironmentVariable(InstanceProfile.InstanceVariable, "2"); Assert.Equal("ytllive_audio_2", InstanceProfile.AudioPipeName("ytllive_audio"));