Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,17 @@ phase plan these entries follow.
said the same thing every time whether or not anyone needed it. Hovering the row shows it
instead. The text is unchanged and still translated, and each control carries it as accessible
help text as well, because a tooltip is the one thing on the page a screen reader cannot see.
- **`build.ps1 -Clean` deletes `bin\` and `obj\` outright, because `dotnet clean` could never
reach what was actually stale.** It only removes what the configuration and target framework you
name would have produced, so everything orphaned survived every clean anyone ran: 485 MB of
`net10.0-windows` output stranded when the app retargeted to Windows 11, and 183 MB of
`obj\scratch*` trees left by builds that had redirected their output path to dodge a file lock.
All of it is git-ignored, so none of it ever appeared in `git status` — the repository quietly
carried two thirds of a gigabyte that no build would ever look at again. `-Clean` now removes
both folders beside every project and reports what it freed, and ignores `-Configuration`,
since the point is to take the configurations you are *not* building too. Deleting a folder the
running app holds open now says so and names the app, rather than passing on Windows' own
account of a file it cannot open.

### Removed

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ original assumptions and records what replaced it; then
<summary><b>Status — phases 0–7 complete, phase 8 (packaging) next</b></summary>

```powershell
.\build.ps1 -Clean -Test # 1,089 green; add -IncludeDesktop for the FlaUI suite
.\build.ps1 -Clean -Test # 1,128 green; add -IncludeDesktop for the FlaUI suite
dotnet run --project src/Offstream.App
```

Expand Down Expand Up @@ -384,7 +384,7 @@ fix rather than a compiler error.
.\build.ps1 -Configuration Release
.\build.ps1 -Test # build, then run the whole suite
.\build.ps1 -Test -Filter FileNameTemplate
.\build.ps1 -Clean -Test # rebuild from scratch, then test
.\build.ps1 -Clean -Test # delete every bin\ and obj\, rebuild, then test
.\build.ps1 -Format # apply .editorconfig
.\build.ps1 -VerifyFormat # what CI enforces
.\build.ps1 -Publish # self-contained win-x64 publish
Expand Down
35 changes: 31 additions & 4 deletions build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
.\build.ps1 -Test # build, then run the suite (desktop tests excluded)
.\build.ps1 -Test -IncludeDesktop # also run the FlaUI tests, needs a real session
.\build.ps1 -Test -Filter FileNameTemplate
.\build.ps1 -Clean -Test # rebuild from scratch, then test
.\build.ps1 -Clean -Test # delete every bin\ and obj\, rebuild, then test
.\build.ps1 -VerifyFormat # what CI enforces
.\build.ps1 -Publish # self-contained win-x64 publish
.\build.ps1 -Publish -BundleFfmpeg # ...with the ffmpeg a release ships (108 MB)
Expand Down Expand Up @@ -84,10 +84,37 @@ if ($Test -and -not $hasFfmpeg) {

# --- Clean ------------------------------------------------------------------

# `dotnet clean` only removes what the *current* configuration and target framework would
# have produced, so anything orphaned survives it: output stranded by a retarget (the
# pre-Win11 `net10.0-windows` folders sat there for weeks), and the `obj\scratch*` trees a
# build with a redirected BaseOutputPath leaves behind. Both are git-ignored, so they never
# show up in `git status` and accumulate unnoticed - 645 MB of them, once. Deleting bin\ and
# obj\ outright is the only clean that catches them, and it ignores -Configuration because
# the whole point is to take the configurations you are *not* building too.
if ($Clean) {
Step "Cleaning ($Configuration)..."
& dotnet clean $sln -c $Configuration --nologo -v minimal
Assert-ExitCode 'Clean'
Step 'Cleaning (every configuration and target framework)...'

$stale = @(
Get-ChildItem -Path $PSScriptRoot -Recurse -Filter '*.csproj' -File |
Where-Object { $_.FullName -notmatch '\\(bin|obj)\\' } |
ForEach-Object { (Join-Path $_.DirectoryName 'bin'), (Join-Path $_.DirectoryName 'obj') } |
Where-Object { Test-Path $_ }
)

$freed = 0
foreach ($dir in $stale) {
$freed += (Get-ChildItem $dir -Recurse -File -ErrorAction SilentlyContinue |
Measure-Object -Property Length -Sum).Sum
try {
Remove-Item $dir -Recurse -Force -ErrorAction Stop
}
catch {
# Much the likeliest cause, and the message Windows gives for it is unhelpful.
throw "Could not delete $dir - $($_.Exception.Message)`nIf Offstream is running it holds its own binaries open; quit it (the tray icon too) and re-run."
}
}

Write-Host (" removed {0} folder(s), {1:N0} MB" -f $stale.Count, ($freed / 1MB)) -ForegroundColor DarkGray
}

# --- Format -----------------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions docs/MODERNIZATION-PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,7 @@ error paths and the scope list.
- **The TFM raise was mechanical, and the routing warning was unfounded — verified rather than assumed.** `net10.0-windows10.0.22621.0`, one property. The plan flagged that CsWinRT projections change what the compiler generates around WinRT types and that `IAudioPolicyConfig` should be re-checked. It is unaffected *by construction*: the routing code names `Windows.Media.Internal.AudioPolicyConfig` only as a runtime-class **string** and otherwise uses `ComImport` and raw P/Invoke, so there is no WinRT type in the type system for the projections to claim. Confirmed at runtime anyway — still binds the 21H2 IID and completes a COM round-trip on build 26200.
- **22621, not the 22000 floor.** It is the oldest build still in support, so targeting lower gains nothing, and Windows 11 only (decided 2026-08-11) means it costs no supported user.
- **`build.ps1` held a second copy of the TFM** in its publish path and pointed at a directory that stopped existing the moment this changed. It reads the value from `Directory.Build.props` now. Worth remembering that the TFM appears in more places than the props file.
- **The TFM raise also stranded every build output under the old one, and nothing cleaned it up (found 2026-08-29).** `dotnet clean` only removes what the configuration and target framework you name would have produced, so the `net10.0-windows` folders left behind by the raise above were unreachable by every clean anyone ran - 485 MB of them, still there a fortnight later. Another 183 MB was `obj\scratch*` trees from builds that had redirected their output path to get around a locked DLL, which is what happens when the app is running while the solution rebuilds; quitting it is the fix, and redirecting the output just moves the lock and leaves the copy behind. All of it is git-ignored, so none of it ever showed up in `git status` and the working tree carried two thirds of a gigabyte nobody could see. `build.ps1 -Clean` deletes `bin\` and `obj\` outright now rather than delegating to `dotnet clean`. The general point is the one the finding above makes from the other direction: **changing the TFM invalidates paths that no longer appear anywhere in the build**, and the ones already written to disk have nothing left that knows their name.
- **SMTC is the primary source and the window title is the fallback, not the reverse.** The title is only readable while Spotify has a window — minimised to the tray it has none, which is the gap this closes. It is also *better* information: SMTC hands over separate artist, title and album fields, where the title is one string that has to be split on a separator that can legitimately appear inside either half. Confirmed live: the media session reported an album (`… (Expanded Edition)`) that no window title carries.
- **"Prefers" means "answers", nothing cleverer.** The preferred source wins whenever it returns a track at all — not by being newer or more detailed. An *idle* answer is still an answer and is not second-guessed; only a null, meaning "I cannot see Spotify", reaches the fallback. Anything more sophisticated means two detectors disagreeing mid-track and a recording whose tags change halfway through.
- **A failing SMTC is treated as a silent one.** It is a system service Offstream does not control, so a fault costs the better metadata and never the recording — logged once per transition rather than per poll, since this runs several times a second. Cancellation is exempt: that is the session stopping, and it propagates.
Expand Down
Loading