Skip to content

feat(convert): per-track log, and our own art placeholder - #9

Merged
kolebayev merged 4 commits into
devfrom
kolebayev/fix-album-default-placeholder
Aug 21, 2026
Merged

kolebayev merged 4 commits into
devfrom
kolebayev/fix-album-default-placeholder

Conversation

@kolebayev

Copy link
Copy Markdown
Owner

Summary

Three unrelated fixes that came out of one session with a real iPod: a
cover the webview cannot decode no longer paints macOS's question mark,
the convert log now has a line per track instead of one per run, and the
batch runs two workers rather than half the machine's cores.

Changes

  • Broken cover art falls back to our placeholder. WebKit's
    broken-image icon on macOS is the system question-mark tile, which
    reads as neither "no cover" nor "bug". ArtworkThumb and the import
    dialog now catch the decode failure and paint the music-note tile;
    the artwork cache records it, so every later mount of that key agrees
    and invalidateArtwork still clears it when the cover is replaced.
  • Two convert workers instead of cores/2. Eight decode+resample
    chains starve CoreAudio's render thread — music playing on the machine
    stutters for as long as a batch lasts. Two still halve the wall clock
    against a plain queue.
  • A log line per track, with what the file turned out to be. Start
    and result lines per item, the result carrying codec, depth, rate,
    channels, size and average bitrate — read back off the finished file
    with ffprobe rather than derived from the request, so a passthrough or
    a VBR pass cannot be reported as whatever was asked for. The probe is
    gated on ConvertObserver::wants_stats, so the drag-and-drop import
    path does not pay for it.
  • Three log defects the per-track lines exposed: ffmpeg's lines
    carried the destination name while ours carried the source, so one
    track appeared twice under two names; swscale's "deprecated pixel
    format" warning fires once per track from cover normalization this app
    asks for itself; and the failure line repeated ffmpeg's whole stderr
    tail that had already been logged line by line above it.
  • Log colour by who is speaking, not one hue per level: the app
    narrating its own progress stays muted, ffmpeg keeps amber, and the
    two saturated colours go to the states a reader scans for. Both are
    the queue's existing tones, so a track that went green in the list is
    green in the log.

Test plan

  • cargo test — 47 unit + 12 round-trip, green. New tests cover the
    output-summary formatting (units, lossy output claiming no bit depth,
    an unknown bitrate omitted rather than printed as zero) and the
    swscale line being filtered.
  • npm test — 128 green, including four new cases for the undecodable
    mark: it holds, it does not refetch the same bytes, it does not leak
    to another size or track, and it clears when the cover is replaced.
  • npm run typecheck, cargo clippy --all-targets — clean.
  • Convert log verified against real files by driving prepare_batch
    with the same observer the job uses: 4 FLAC to ALAC and to MP3, plus a
    deliberately corrupt file for the failure path.
  • Release bundle built with npm run bundle and launched.

A cover the webview cannot decode is not a missing cover as far as the
<img> is concerned: WebKit swaps in its own broken-image icon, which on
macOS is the system question mark tile — foreign to this UI and readable
as neither "no cover" nor "bug".

The thumb now reports the failure to the artwork cache, so the key falls
back to the music-note placeholder for every later mount rather than only
the one that happened to paint it. The mark is an ordinary cache entry,
so invalidateArtwork drops it and a replaced cover is fetched again.
Half the cores left the UI responsive but not the audio: with eight
decode+resample chains running, CoreAudio's render thread loses the race
often enough that music playing on the machine stutters for as long as a
batch lasts.

Two workers still halve the wall clock against a plain queue, which is
where most of the win was.
A successful job used to leave a log holding a header, a footer and
whatever ffmpeg happened to complain about — no record of the tracks
themselves. Each one now writes a start line and a result line, and the
result carries what the file turned out to be:

  ✓ FKJ - Blessed.flac: converted in 0.7s · ALAC 16-bit 44.1 kHz stereo
    · 28.5 MB · 856 kbps

Those facts are read back off the finished file rather than derived from
the request: a passthrough that skipped the encode, or a VBR pass that
landed nowhere near its nominal rate, would otherwise be reported as
whatever was asked for. The extra ffprobe is gated on the observer
wanting it, so the drag-and-drop import path does not pay for it.

Three things the per-track lines exposed:

- ffmpeg's own lines carried the destination name while ours carried the
  source, so one track appeared in the log under two names.
- swscale's "deprecated pixel format" fires once per track of an album
  with art, from cover normalization this app asks for itself.
- the failure line repeated ffmpeg's whole stderr tail, which had already
  been logged line by line just above it.

Colour follows who is speaking rather than one hue per level: the app
narrating its own progress stays muted, ffmpeg keeps amber, and the only
two saturated colours are the states a reader scans for — landed and
failed. Both are the queue's existing tones, so a track that went green
in the list is green in the log too.
@kolebayev
kolebayev merged commit e10f054 into dev Aug 21, 2026
3 of 4 checks passed
@kolebayev
kolebayev deleted the kolebayev/fix-album-default-placeholder branch August 21, 2026 14:29
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