From 594b1a8fad2096b18a4d70de880f45f250383c7f Mon Sep 17 00:00:00 2001 From: revtex Date: Sat, 15 Aug 2026 00:00:03 -0400 Subject: [PATCH] Close the changelog for 0.1.0 Step one of the two-step release. Everything that was under [Unreleased] becomes the 0.1.0 section, dated, with an empty [Unreleased] opened above it; the release workflow refuses a tag whose section does not exist, and does so before it builds. Two things fixed on the way, because this section becomes the release notes and both would have shipped inside them: [Unreleased] carried two separate "### Added" headings, so the same category appeared twice in one release. The stray entry - the Settings page naming the signed-in Spotify account - moves into the first block, and loses its note about existing installs needing to sign in again, which cannot apply to a first release. CLAUDE.md still said Offstream requests exactly one scope. It requests two: GET /me needs user-read-private, and that is what puts the account name on the Settings page. The code and SpotifyAuthOptionsTests have said so since that feature landed; the rules file had not caught up, which is the worst place for a stale claim about the app's permission footprint. README stops saying there is nothing to download, and stops telling people they need ffmpeg when a release brings its own. --- CHANGELOG.md | 28 +++++++++++++++------------- CLAUDE.md | 2 +- README.md | 18 ++++++++++-------- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5cfbd9..da48717 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,6 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -Offstream has not shipped a release yet; everything below is work towards the first one. Because Offstream succeeds **Spytify** (.NET Framework 4.6.1 / WinForms) rather than starting from nothing, the `Changed`, `Removed` and `Fixed` entries are written against that predecessor — they say how Offstream differs from the app it replaces. `docs/MODERNIZATION-PLAN.md` is the @@ -13,6 +12,12 @@ phase plan these entries follow. ## [Unreleased] +## [0.1.0] - 2026-08-15 + +The first release. Everything below is the whole of Offstream rather than a change to it: +phases 0–8 of `docs/MODERNIZATION-PLAN.md`, from the .NET 10 retarget through to a per-user +installer. + ### Added - **The licences are readable from inside the app, not just present as files in the folder.** @@ -28,6 +33,15 @@ phase plan these entries follow. Last.fm now says whose data is being written into the recordings — an attribution Spotify's Developer Terms require and that the app had never carried anywhere. It is empty when no provider is selected, because crediting a service the app is not calling would be a false statement. +- **The Settings page names which Spotify account is signed in**, as display name and account id + — not just that one exists. Recordings go untagged when the signed-in account is not the one + the music plays on, and nothing on screen says which it is, so the only way to find out was to + read the logs. Costs one scope, `user-read-private`. The id is there because a display name is + not an identifier: two accounts can share one, and telling exactly those apart is what this is + for. `user-read-email` would be the obvious way to do that and is deliberately not requested — + Spotify removed the `email` field in its late-2024 cull, so that permission now covers data the + endpoint no longer returns. + - **A release pipeline, with the git tag as the only place a version number lives.** Pushing `v1.2.3` builds, tests, publishes, signs and attaches a self-contained `win-x64` zip and its SHA-256 to a GitHub release; the changelog becomes the release notes. Nothing in the repository records a @@ -329,18 +343,6 @@ phase plan these entries follow. harness nothing built on, carried in the solution and the layout docs as though it were part of the app. `docs/decisions/0001-phase-0-retarget-spike.md` keeps the findings. -### Added - -- **The Settings page names which Spotify account is signed in**, as display name and account id - — not just that one exists. Recordings go untagged when the signed-in account is not the one - the music plays on, and nothing on screen said which it was, so the only way to find out was - to read the logs. Costs one scope, `user-read-private`, and existing installs show nothing - here until they sign in again, since a refresh token predating the scope cannot read the - profile. The id is there because a display name is not an identifier: two accounts can share - one, and telling exactly those apart is what this is for. `user-read-email` would be the - obvious way to do that and is deliberately not requested — Spotify removed the `email` field - in its late-2024 cull, so that permission now covers data the endpoint no longer returns. - ### Fixed - **Pressing record with Spotify paused, then pressing play, recorded nothing.** The level meter diff --git a/CLAUDE.md b/CLAUDE.md index 54d7736..4d5055d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -63,7 +63,7 @@ Set by the user on 2026-08-13 and binding like the constraints above. Most were - **Endpoints, parameters and field names come from the [OpenAPI schema](https://developer.spotify.com/reference/web-api/open-api-schema.yaml).** Never guessed. In practice `SpotifyAPI.Web` is the typed SDK and nothing here hand-rolls HTTP; check the schema before adding a call, not after. - **[Authorization Code with PKCE](https://developer.spotify.com/documentation/web-api/tutorials/code-pkce-flow) for user data.** Client Credentials only for public non-user data. **Implicit Grant is banned** — it is deprecated. A desktop app is a public client, so there is no Client Secret to protect and none may be introduced. - **Redirect URIs are HTTPS**, except `http://127.0.0.1` for local development. Never bare `localhost` (Spotify rejects it), never wildcards. `SpotifyAuthOptions.DefaultRedirectUri` is the one in use and a test pins its shape. -- **Request the minimum [scopes](https://developer.spotify.com/documentation/web-api/concepts/scopes) the shipped features need, never a scope for a feature that might arrive later.** Offstream makes exactly two calls: `/me/player/currently-playing`, which needs `user-read-currently-playing`, and `/albums/{id}`, which needs no user scope. That is the whole list, and `SpotifyAuthOptionsTests` fails the build if it grows. A scope requested ahead of its feature is a permission the user grants for nothing, on a consent screen where the spare lines look identical to the load-bearing one. +- **Request the minimum [scopes](https://developer.spotify.com/documentation/web-api/concepts/scopes) the shipped features need, never a scope for a feature that might arrive later.** Offstream makes exactly three calls: `/me/player/currently-playing`, which needs `user-read-currently-playing`; `/albums/{id}`, which needs no user scope; and `/me`, which puts the signed-in account's name on the Settings page and needs `user-read-private`. Two scopes, and `SpotifyAuthOptionsTests` fails the build if the list grows. `user-read-email` would identify an account unambiguously and is deliberately **not** requested: Spotify removed the `email` field in its late-2024 cull, so the permission now covers data the endpoint no longer returns. `SpotifyAuthOptions.DefaultScopes` carries the full reasoning and is the place to change it. A scope requested ahead of its feature is a permission the user grants for nothing, on a consent screen where the spare lines look identical to the load-bearing one. - **Tokens are stored protected, refreshed, and abandoned when dead.** DPAPI covers the refresh token; the access token never reaches disk. Spotify rotates the refresh token on every renewal, so the replacement must be persisted or a long-running install silently stops working. A **401** means the refresh token itself is gone — clear it and put the user back through sign-in rather than retrying it forever. Only 401: treating a rate limit or an outage that way would sign the user out over a transient fault. - **On 429, honour `Retry-After` exactly; back off exponentially only where there is no such instruction.** Guessing shorter is what gets an application throttled harder. Never retry in a tight loop. `SpotifyRetryHandler` is the implementation — note that `SpotifyClientConfig.CreateDefault()` attaches **no** retry handler, so a client built without one treats rate limiting as fatal. - **Log throttling at `Warning`.** The Record page's activity log shows Information and above, so anything quieter is invisible to everyone who has not gone looking. Transient 5xx stays at `Information` — it usually clears, and promoting it makes the Problems filter too noisy to read. diff --git a/README.md b/README.md index 8e767ce..7e5744b 100644 --- a/README.md +++ b/README.md @@ -34,10 +34,11 @@ drops into the list below as it is completed.* Three things worth knowing up front, so nothing is a surprise: -**There's no release yet.** Offstream is still in development, so there is nothing to download — you -build it from source, which is a few downloads and one build, with every step below. The installer -and the release pipeline are built and waiting on a version tag; check -[Releases](https://github.com/revtex/offstream/releases) before assuming this is still true. +**Download it from [Releases](https://github.com/revtex/offstream/releases).** The installer is +per-user and never asks for administrator rights; the zip is the same build if you would rather not +install anything. **Neither is code-signed**, so Windows SmartScreen warns the first time you run +it — choose **More info**, then **Run anyway**, after checking the download against the SHA-256 in +the release notes. Building from source is the other option, and every step is below. **It records your PC's sound, not Spotify specifically.** By default Offstream captures whatever your speakers are playing, so a Windows notification chime or a YouTube tab in the background lands @@ -46,10 +47,11 @@ in the recording too. Either keep the machine quiet while it records, or install settings, and record that instead — then only Spotify is captured. Offstream tells you on the Settings page whether VB-CABLE is installed. -**You need ffmpeg — for now.** It's the free tool Offstream uses to turn the captured audio into -MP3s. One command installs it; see [Getting Offstream running](#getting-offstream-running). -Releases bundle their own copy, so this is only true while building from source. If you have one -installed anyway, or point Offstream at a particular build on the Settings page, yours wins. +**You need ffmpeg only if you build from source.** It's the free tool Offstream uses to turn the +captured audio into MP3s, and releases bundle their own copy in an `ffmpeg` folder beside the +executable. One command installs it otherwise; see +[Getting Offstream running](#getting-offstream-running). If you have one installed anyway, or point +Offstream at a particular build on the Settings page, yours wins over the bundled one. ## Getting Offstream running