Adds TagWriter protocol, TagWriterFactory, MP4TagWriter (AVFoundation
passthrough export) and ID3TagWriter (ID3TagEditor 5.5.0 v2.3 builder),
with round-trip tests for both formats and fixture audio files.
Implements pure, UI-free, I/O-free logic for single- and multi-track
editing: field extraction, change detection, shared-vs-mixed across
multiple tracks, and selective field application. Enum named
EditableTrackField (not TrackField) to avoid collision with the
existing SmartPlaylistCondition.TrackField.
Extract shared `trackContextMenuConfig` computed property and pass it to
both TrackTableView and PlayerControlsView, so right-clicking the now-playing
area shows the same context menu as right-clicking a track row.
Also fixes two pre-existing call sites in PlaylistViewModel that still
used the old observeSmartPlaylistTracks(searchQuery:) label after it
was renamed to observeSmartPlaylistTracks(for:) in a prior task.
Adds the conditions column to smart_playlists via migration v5, removes the
temporary encode(to:) override now that GRDB Codable synthesis handles the JSON
column automatically, and wires up fetchTracks(conditions:), createSmartPlaylist
(name:conditions:), and updateSmartPlaylistConditions with parameterized WHERE
clause building. 17/17 SmartPlaylistTests pass.
AVPlayer treats a progressive HTTP stream as a seekable, finite asset only when
the server honors Range requests. /file always returned 200 with the whole body
and no Accept-Ranges, so AVPlayer never resolved a duration and reported
end-of-track as AVPlayerItem.status == .failed ("Operation Stopped") instead of
AVPlayerItemDidPlayToEndTime. Because the .failed path never calls
onTrackFinished, the queue stalled and the next track never played.
Honor single-range requests (bytes=N-M, bytes=N-, suffix bytes=-N) with 206 +
Content-Range, advertise Accept-Ranges: bytes on every response, and return 416
for out-of-range; partial reads stream only the requested slice via FileHandle.
Add an end-to-end test that plays a track to its natural end through the real
provider + server and asserts a clean finish (no "Operation Stopped").
Adds a standalone local Swift package (macOS 14, iOS 17) containing the
shared wire-protocol types (RemoteCommand, HostEvent, PlaybackStatePayload,
HandshakeMessage, RemoteProtocolVersion) with full public visibility and
explicit public initialisers, plus a matching test suite (15 tests, all
passing via swift test).
Artwork was stored inline in the tracks table (385MB / 98% of DB size for 8K tracks).
Now read directly from audio file metadata via AVAsset when needed, shrinking the DB to ~8MB.
Wrap breadcrumb + content area in a constrained VStack so search bar,
playlist bar, and player controls stay pinned when toggling Home view.
Refactor PlayerControlsView to use a full-width progress track above
transport buttons. Allow play/pause to start from first track when
nothing is queued.