Skip to content

Foundation: typed API client, playback core with progress sync, auth fixes - #6

Merged
madeofpendletonwool merged 9 commits into
mainfrom
feat/foundation
Oct 17, 2026
Merged

madeofpendletonwool merged 9 commits into
mainfrom
feat/foundation

Conversation

@madeofpendletonwool

Copy link
Copy Markdown
Owner

Second step of the overhaul: builds on macOS again, resumes and syncs listening progress with the server, and fixes search, login and the crashes from the audit.

reqwest 0.13 (rustls with the platform verifier, no OpenSSL), ratatui 0.30
with its crossterm re-export instead of a second crossterm, rodio 0.23,
tracing in place of log/fern/env_logger. Drops crates nothing uses any
more: the old blocking client, mDNS, axum and the helpers they served.
It listened on 0.0.0.0 with no authentication and CORS open to any origin,
needed an unsound Send/Sync impl around the audio output (the reason main
didn't compile on macOS), and downloaded each episode twice to probe its
duration. The now-playing socket replaces it with control routed through
the PinePods server. config.rs held only its settings and an unused
leftover from the music player.
One reqwest client with timeouts, one error type, and models checked
against live responses. Columns the server can leave NULL no longer fail a
whole list, podcast_episodes' capitalized keys share the Episode type, and
search reads podcastdescription (every search with results used to fail).

Endpoints that drifted or never existed are fixed: progress goes to
record_listen_duration, clear_queue is a POST, reorders send
{episode_id, is_youtube} so a video and an episode with the same id don't
collide, and downloads carry is_youtube. About forty wrappers for routes
the server doesn't have are gone.
The device sink lives on a dedicated thread, so the handle everyone else
holds is Send + Sync without unsafe. A controller task owns what's
playing: it downloads off the UI path, resumes at the saved position
(it always started at 0:00), and keeps the server in step the way web and
mobile do: record_listen_duration every 15 s, on pause, seek, stop and
quit; increment_listen_time per minute played; history, play count and
queue_bump on start; mark_episode_completed at the end. Sync failures log
at warn instead of disappearing at debug.

History resets the saved position on the server, so it's sent with
position 0 (as web does) and followed by the real start position.
The key goes to the OS credential store, with session.json (now written
0600, atomically) holding the rest; where no store exists the key falls
back into that file. Sessions from the old location are moved over.

On start the key is checked with verify_key: a rejected key logs out, but
an unreachable server keeps the session instead of deleting it.

Also fixes first-run setup, which always failed with "Invalid state" and
never saved the session, and first-admin creation, which sent the
password unhashed so that account could never log in. AuthState no longer
carries the password or the hash the server returns, and its Debug output
leaves out the key.
- Global q, Space and digit shortcuts no longer swallow keys while the
  search box has focus, and the login screen quits on Ctrl+C only, so
  passwords and queries can contain those characters.
- Digit shortcuts and the tab header follow the tab order as displayed.
- Queue Play works (it was a TODO that printed into the TUI).
- Titles are cut by display width; byte slicing panicked on em-dashes and
  emoji and left the terminal in raw mode.
- A panic restores the terminal and points at the log.
- The skip interval and default volume settings take effect.
- Logs rotate daily under the local data dir and default to info;
  FIREWOOD_LOG overrides. Stray stderr from audio libraries goes to a file
  instead of over the UI.
- SIGTERM and closing the terminal save the position before exiting.
actions-rs is unmaintained; use dtolnay/rust-toolchain and rust-cache v2.
Clippy runs with -D warnings over all targets, and macOS now runs tests
too. The nightly schedule is dropped: it only re-ran unchanged code.
Formatting only. The existing code had never been through rustfmt, which
is why the fmt check always failed.
@madeofpendletonwool
madeofpendletonwool merged commit 86d14c2 into main Oct 17, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant