Skip to content

feat: generate cover art thumbnail and preview derivatives in library root (#91)#107

Open
thedavidweng wants to merge 142 commits into
mainfrom
feat/artwork-derivatives-91
Open

feat: generate cover art thumbnail and preview derivatives in library root (#91)#107
thedavidweng wants to merge 142 commits into
mainfrom
feat/artwork-derivatives-91

Conversation

@thedavidweng

@thedavidweng thedavidweng commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Summary

  • Keep songs.cover_art as the authoritative original bytes and generate square lossless WebP derivatives at 80×80 (thumb) and 256×256 (preview) in <library>/artwork/
  • Derivative identity is the SHA-256 of the original cover bytes, not the song hash, so replacing/extracting cover art produces new filenames and cannot serve stale imagery
  • Lazy repair regenerates missing/corrupt derivatives from the original cover art, updating paths only when the cover_art BLOB still matches (concurrent-replacement safe)
  • Deletion is reference-counted so two songs sharing the same cover digest never remove a shared file
  • Remote mirror sync and publish propagate derivatives to the cloud working copy and reference-count deletions
  • The get_cover_art command is now async with an optional size parameter and runs disk/decode work in spawn_blocking
  • The frontend Blob URL cache is keyed by ${songHash}:${size} with byte identity for replacement detection
  • CoverArtThumbnail fetches the thumb derivative first, falling back to full cover art; PlaybackStage uses the preview for the ambience backdrop

Test plan

  • cargo test -q — 398 Rust tests pass (16 new artwork tests)
  • pnpm test — 1312 frontend tests pass (9 new cover-art cache + fetch tests)
  • pnpm lint / pnpm build / cargo fmt / cargo deny check — all clean
  • pnpm tauri build — full release build succeeds
  • Patch coverage 93.1% (target 80%)

Open in Devin Review

@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 79.06% (🎯 65%) 4040 / 5110
🔵 Statements 78.52% (🎯 65%) 4292 / 5466
🔵 Functions 69.87% (🎯 60%) 1083 / 1550
🔵 Branches 71.09% (🎯 60%) 2233 / 3141
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
src/components/Player/FullscreenControls.tsx 19.23% 20% 12.5% 20% 19-27, 32-55
src/components/Player/PeakMeter.tsx 96.66% 88.09% 100% 100% 50, 53, 107
src/components/Player/SeekBar.tsx 91.19% 75.8% 92.1% 96.26% 85, 98, 107, 116-117, 158-160, 173, 188, 209, 241
src/components/Settings/SettingsCrossfadeSection.tsx 100% 87.5% 100% 100%
src/components/Settings/SettingsEqSection.tsx 96.15% 81.81% 93.33% 96.15% 158-159
src/components/Settings/SettingsOverlay.context.ts 100% 100% 22.22% 100%
src/components/Settings/SettingsOverlay.state.ts 89.05% 77.14% 88% 88.72% 178-179, 184-197, 202-208, 219, 287
src/components/Settings/SettingsOverlay.tsx 100% 100% 100% 100%
src/components/Shared/CoverArtThumbnail.tsx 87.5% 86.66% 80% 95.23% 32, 38, 60
src/hooks/use-playback-runtime.ts 67.28% 64.28% 61.76% 67.13% 41-54, 139, 143, 159-161, 187-218, 233-235, 250-252, 325-338, 368-387, 410-430
src/lib/cover-art.ts 96.22% 87.91% 100% 96.19% 54, 253-257, 299
src/lib/waveform-cache.ts 90.62% 72.22% 100% 90.62% 70-72
src/locales/en.json 100% 100% 100% 100%
src/locales/zh-CN.json 100% 100% 100% 100%
src/playback/session.ts 96.36% 89.58% 95.83% 96.15% 259-265
src/stores/player-store.ts 87.87% 82.14% 90.47% 90.62% 220, 228, 286, 294, 302, 326, 332, 340, 348, 403-404
src/stores/queue-store.ts 98.8% 97.05% 100% 98.68% 53
src/stores/settings-store.ts 100% 77.77% 100% 100%
Generated in workflow #892 for commit a2dc845 by the Vitest Coverage Report Action

@codecov

codecov Bot commented Jul 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.78511% with 19 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.9%. Comparing base (ec54066) to head (a2dc845).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/components/Player/SeekBar.tsx 94.6% 5 Missing ⚠️
src/lib/cover-art.ts 94.7% 4 Missing ⚠️
src/playback/session.ts 80.0% 4 Missing ⚠️
src/lib/waveform-cache.ts 92.8% 3 Missing ⚠️
src/components/Settings/SettingsEqSection.tsx 96.5% 2 Missing ⚠️
src/stores/player-store.ts 80.0% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##            main    #107     +/-   ##
=======================================
+ Coverage   77.3%   78.9%   +1.6%     
=======================================
  Files        126     131      +5     
  Lines       6266    6964    +698     
  Branches    1984    2140    +156     
=======================================
+ Hits        4846    5501    +655     
- Misses      1376    1420     +44     
+ Partials      44      43      -1     
Flag Coverage Δ
frontend 78.9% <96.7%> (+1.6%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
frontend 78.9% <96.7%> (+1.6%) ⬆️
rust ∅ <ø> (∅)
Files with missing lines Coverage Δ
src/components/Player/FullscreenControls.tsx 27.5% <ø> (ø)
src/components/Player/PeakMeter.tsx 100.0% <100.0%> (ø)
...c/components/Settings/SettingsCrossfadeSection.tsx 100.0% <100.0%> (ø)
src/components/Settings/SettingsOverlay.context.ts 39.1% <100.0%> (+7.4%) ⬆️
src/components/Settings/SettingsOverlay.state.ts 89.1% <100.0%> (+8.4%) ⬆️
src/components/Settings/SettingsOverlay.tsx 100.0% <ø> (ø)
src/components/Shared/CoverArtThumbnail.tsx 96.1% <100.0%> (+41.1%) ⬆️
src/hooks/use-playback-runtime.ts 66.8% <100.0%> (ø)
src/locales/en.json 100.0% <ø> (ø)
src/locales/zh-CN.json 100.0% <ø> (ø)
... and 8 more

... and 4 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@greptile-apps

greptile-apps Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds generated cover-art derivatives for faster thumbnail and backdrop rendering. The main changes are:

  • Generates 80×80 thumbnail and 256×256 preview WebP files from original cover art bytes.
  • Stores derivative paths under <library>/artwork/ using the cover byte SHA-256 digest.
  • Adds lazy repair for missing or invalid derivatives while keeping songs.cover_art authoritative.
  • Propagates artwork derivatives through remote publish and mirror sync with reference-counted cleanup.
  • Updates frontend cover-art fetching and blob URL caching for thumb, preview, and original sizes.

Confidence Score: 5/5

This PR appears safe to merge.

The reviewed paths keep original cover art authoritative, validate derivative files before serving or publishing, guard lazy repair writes with cover-art byte matching, and handle shared derivative deletion by database reference counts.

No files require special attention.

T-Rex T-Rex Logs

What T-Rex did

  • The test command completed and returned EXIT_CODE 0.
  • From the captured log, the test run shows 6 test files passed and 201 tests passed.
  • The full verbose command transcript was saved for review.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
src-tauri/src/library/artwork.rs Introduces bounded image decoding, deterministic lossless WebP derivative generation, path validation, and cleanup helpers.
src-tauri/src/commands/import/mod.rs Makes get_cover_art async, supports size-specific derivatives, and performs lazy repair off the IPC thread.
src-tauri/src/cache/mod.rs Adds artwork path migrations, atomic cover-art/path updates, lazy repair helpers, and reference counting.
src-tauri/src/remote/sync/publish.rs Publishes validated derivatives, regenerating missing or corrupt local files before updating the remote DB.
src-tauri/src/remote/sync/mirror.rs Reference-counts derivative deletion during mirror sync so shared artwork remains available.
src/lib/cover-art.ts Keys blob URL cache by song and size, uses byte digests for replacement, and preserves orphaned active URLs.
src/components/Shared/CoverArtThumbnail.tsx Fetches thumbnail derivatives first and falls back to original cover art for placeholders.
src/components/Playback/PlaybackStage.tsx Fetches preview cover art for ambience backdrop when the library row omits original bytes.
src/lib/tauri/library.ts Adds typed helpers for original, thumbnail, and preview get_cover_art IPC calls.
src/types/ipc.ts Adds the CoverArtSize union matching the Rust IPC enum.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Import as Import / Extract
participant DB as songs table
participant Art as artwork module
participant FS as <library>/artwork
participant UI as Frontend
participant Remote as Remote sync/publish

Import->>Art: generate derivatives from cover_art bytes
Art->>FS: "write thumb_*.webp and preview_*.webp"
Import->>DB: persist cover_art + derivative paths
UI->>DB: get_cover_art(hash, size)
DB-->>UI: recorded path or original bytes
UI->>Art: validate/read requested derivative
Art-->>UI: derivative bytes or fallback original
Remote->>Art: validate/regenerate local derivatives
Remote->>FS: copy/upload artwork files
Remote->>DB: persist remote derivative paths
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Import as Import / Extract
participant DB as songs table
participant Art as artwork module
participant FS as <library>/artwork
participant UI as Frontend
participant Remote as Remote sync/publish

Import->>Art: generate derivatives from cover_art bytes
Art->>FS: "write thumb_*.webp and preview_*.webp"
Import->>DB: persist cover_art + derivative paths
UI->>DB: get_cover_art(hash, size)
DB-->>UI: recorded path or original bytes
UI->>Art: validate/read requested derivative
Art-->>UI: derivative bytes or fallback original
Remote->>Art: validate/regenerate local derivatives
Remote->>FS: copy/upload artwork files
Remote->>DB: persist remote derivative paths
Loading

Reviews (3): Last reviewed commit: "fix(cover-art): use stable content-key d..." | Re-trigger Greptile

greptile-apps[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Owner Author

Maintainer acceptance — merge slot 11, strictly after #106

BLOCKED — changes required.

Blocking Blob-URL lifecycle leak

useCoverArtUrl acquires a ref-counted cache entry by calling retainCoverArtUrl from useMemo during render. If a consumer rerenders with a fresh-but-byte-identical array, retainCoverArtUrl increments refs but returns the same URL; the effect dependencies do not change, so only one release happens at unmount. Abandoned/StrictMode render paths can also acquire without a committed cleanup. This leaks cache refs/object URLs and fails #91's per-size ref-count lifecycle guarantee.

Move acquisition/release into a committed effect or make acquisition idempotent per mounted consumer/digest, and add a test that rerenders with equal bytes in a new array (plus StrictMode/aborted-render-safe behavior).

After #106 lands, rebase onto its migration/audio result before merging #107.

Copy link
Copy Markdown
Owner Author

Re-review: blocking Blob-URL ownership/lifetime fix required

This PR is stacked after #106. Its current cover-art URL hook performs cache acquisition in useMemo during render and releases in an effect cleanup. That is not a balanced lifetime: rerendering with a fresh but byte-identical Uint8Array can increment the refcount while the effect dependency does not produce a matching cleanup. Strict Mode and abandoned renders can also leak.

Implement exactly this ownership model:

  1. Make render pure. Do not call retainCoverArtUrl, URL.createObjectURL, or any refcount-mutating helper from useMemo or any render-time path.
  2. Move acquisition to a committed effect. One mounted consumer must acquire exactly one reference and its cleanup must release exactly that same reference once.
  3. Use a stable content identity for the effect/cache key, not merely the JavaScript byte-array object identity. Byte-identical replacement arrays for the same song/size must not accumulate references; genuinely changed bytes must replace the displayed URL safely.
  4. Guard asynchronous fetch/update cleanup so an unmounted/replaced consumer cannot set a stale URL. Keep the existing thumbnail/preview fallback behavior and cache sharing across consumers.
  5. Preserve correct ownership when two visible consumers share the same image and when they unmount in either order.

Add deterministic tests (mock URL.createObjectURL/revoke and the cache) for:

  • repeated renders with fresh, byte-identical arrays;
  • replacement with changed bytes;
  • two concurrent consumers, then separate unmounts;
  • React Strict Mode/double-mount behavior;
  • no URL/refcount mutation from a discarded render path.

After #106 merges, rebase onto its resulting main and ensure this PR's diff contains no waveform work. Run frontend and Rust suites, then reply with the new SHA and test evidence.

@thedavidweng

Copy link
Copy Markdown
Owner Author

@greptile review

1 similar comment
@thedavidweng

Copy link
Copy Markdown
Owner Author

@greptile review

Add a five-band peaking EQ (60 Hz, 230 Hz, 910 Hz, 3.6 kHz, 14 kHz;
±12 dB per band) with automatic preamp reduction to compensate for
positive boosts and a tanh-based soft limiter that engages above 0.85
amplitude. The EqProcessor is wired into the realtime output callback
after the source/stem mix and before the play/pause fade, with linear
smoothing for gain, preamp, and bypass dry/wet transitions.

New set_eq_enabled and set_eq_gains Tauri commands persist to
AppConfig and push updates to the active PlaybackController via
PlaybackCoordinator. The settings overlay exposes a new
SettingsEqSection with per-band sliders, enable toggle, and
reset-to-flat. AppSettings now includes eq_enabled and eq_gains_db
fields.

Closes #86
The soft limiter engages above 0.95 amplitude (LIMITER_THRESHOLD),
not 0.85 as previously stated in the CHANGELOG entry.
The EQ feature added an `eq_processor` parameter to `render_output_buffer`
but the integration tests in `tests/phase2_output.rs` and
`tests/phase3_playback_mode.rs` were not updated, causing `cargo test`
to fail with E0061 (wrong argument count). Also fixes a clippy
`needless_range_loop` warning in the EQ Nyquist guard test.
Rust 1.97 promotes the f32 fallback lint to a hard error under
`-D warnings`. Annotate the four `Hertz::from_hz(1.0)` fallback
literals in `eq.rs` with `1.0_f32` so CI clippy stays green.
…ndant clamp

Settings EQ sliders now keep a local draft for immediate visual
feedback and commit the full five-value array through a single
`setEqGains` IPC call after a 75 ms trailing debounce (or immediately
on pointer/key release). This replaces the per-band `setEqBandGain`
overlay action, which fired one IPC call per change tick.

Both the settings store and the overlay actions now revert to the
previous authoritative `eqGainsDb` when `set_eq_gains` fails, so a
rejected command no longer leaves the UI showing values the backend
did not accept. `set_eq_gains` now reports validation failures as
`invalid_playback_state` (with `keep_current_state` fallback) instead
of a generic internal error, matching the existing error contract.

The post-mix `[-1, 1]` clamp in `render_output_buffer` is removed:
the EQ soft limiter already bounds the output, and the clamp masked
legitimate headroom during stem mixing. Adds `@testing-library/react`
as a dev dependency (already imported by the EQ section tests) and
regenerates the Flatpak node sources so the clean-room Flatpak build
can resolve it. Adds config-hydration tests covering default, clamped,
and non-finite persisted EQ gains.
Add rollback to setEqEnabled and resetEqGains in settings-overlay
library-actions: when the backend command fails, revert both the
overlay state and the shared settings store to their previous
authoritative values instead of leaving stale optimistic updates.

Also strengthen the setEqGains rollback test to assert the settings
store is reverted, and add a new resetEqGains failure test.
Missing eq_gains_db in the Playwright Tauri mock left SettingsEqSection
with undefined draft gains and crashed the tree (Preferences not found).
Recomputing remaining/smooth_samples every callback made the 50 ms
linear gain ramp buffer-size dependent. Steps are now fixed when
targets change and advanced per rendered frame.
set_eq_enabled and set_eq_gains were synchronous Tauri commands that
used rx.blocking_recv() to wait for the playback coordinator's reply.
Blocking the Tauri command thread freezes the UI while the coordinator
processes the command. Both commands are now async fn and use rx.await,
matching the pattern already used by the playback commands.

Also sync flatpak node sources with the pnpm lockfile to fix CI.
set_eq_enabled and set_eq_gains previously persisted the new EQ value
to config before pushing it to the playback coordinator. If the
coordinator rejected the update or the channel was disconnected, the
command returned an error after the new value was already on disk,
leaving persisted settings divergent from the running audio engine.

The commands now apply through the coordinator first and persist only
on success. If persistence then fails, the coordinator is reverted to
the old value so the engine and config stay consistent.

Added focused tests for coordinator-send/reply/apply failure and
persistence failure (with coordinator revert).
- Auto preamp now only considers bands that are active at the current
  sample rate, fixing a volume drop when boosting the 14 kHz band on
  low-sample-rate audio where that band is skipped by the Nyquist guard.
- EqProcessor::process skips per-frame coefficient updates and biquad
  runs when the dry/wet mix is fully bypassed, eliminating CPU burn
  while EQ is off. Scalar smoothing still advances so enable transitions
  resume from the latest target.
- Add regression tests for both behaviors.
Advance the incoming stem with a dedicated source-frame cursor and give
it its own ResamplerCache so device-frame progress and shared rubato
state cannot corrupt rate conversion during overlap.
render_output_buffer gained a dedicated incoming ResamplerCache; update
phase2/phase3 integration call sites so the full crate test build compiles.
… promotion

Convert source-frame quantities to device (output) frames before calling
effective_overlap_frames so the overlap timing clamp operates in a single
frame domain. Use incoming_source_frame (not rendered_frames) for
promote_crossfade_track so the promoted track render_frame matches the
incoming source cursor. Swap the incoming resampler cache into the primary
lane on promotion to preserve sinc delay history, and clear both lanes on
cancellation/seek/device-recreation.
…n tail

The gapless swap path in the realtime callback applied EQ to the
post-swap remaining buffer but skipped the soft_limit pass that the
normal render path and the crossfade fallback gapless path both apply.
A positive EQ band gain on the transition tail could exceed full-scale
and clip. Add the limiter pass after EQ, matching the other two paths,
with a regression test that feeds above-threshold samples through the
gapless swap and asserts the tail is bounded.
Verify the PeakRing captures post-limiter values (after soft_limit),
not pre-limiter values clamped by sanitize_peak. A track with samples
at 1.0 (above the 0.95 threshold) is rendered; the output and peak ring
must both show the compressed value (< 1.0, > threshold), proving the
peak accumulator runs after the soft limiter in the render pipeline.
promote_crossfade_track replaced the current track but did not bump
transport_generation, unlike perform_gapless_swap. Without the bump, a
delayed playback-position event from the old song (same generation)
could arrive after the new-song snapshot and be accepted by the
frontend generation filter, reverting the clock and queue
reconciliation back to the old song.

Add the bump and a regression test mirroring the gapless swap test.
Update the playback contract doc to list crossfade promotion alongside
gapless swap as a transport_generation-bumping event, and correct the
race-protection note that previously claimed gapless swap does not bump
the generation.
Finding 1 (EQ action shadow): Remove duplicate setEqEnabled/setEqGains/
resetEqGains from settings-overlay.library-actions.ts that shadowed the
state.ts versions. Re-add the Math.max(-12, Math.min(12, g)) clamp and
current.every() no-op guard to setEqGains in SettingsOverlay.state.ts,
plus the optimistic settingsStore.patchAppSettings update with rollback
on failure for all three EQ actions. Move EQ tests from
library-actions.test.ts to state.test.ts with assertions for the new
clamping, no-op guard, and patchAppSettings behavior.

Finding 2 (duplicate test): Remove the duplicate 'cleans up timer and
cancels polling on unmount' test block from PeakMeter.test.tsx so the
test appears only once.

Findings 3 and 4 (output.rs frame/sample mismatch and soft limiter
regression) were already fixed in prior commits on this branch.

Update CHANGELOG.md with the fixes.
The crossfade branch added a duplicate "eq" section in both en.json and
zh-CN.json (at line 360) while the same key already existed later in each
file (at line 430) with more complete content including band labels.
JSON parsers keep only the last occurrence, so the earlier duplicate was
silently ignored. Removing the duplicate ensures the complete EQ locale
section is the only one present.
cancel_crossfade_and_prepared now also clears pending_transition_out so
that a stale gapless/crossfade transition (A->B) is never emitted to the
frontend after the user manually loads an unrelated song C. Previously,
start_track_loading and clear_track cancelled the active crossfade and
prepared track but left the pending transition in place, allowing the
position emitter to drain it with a snapshot of the newly-loaded song
at the new transport_generation — bypassing the frontend staleness
guard and corrupting the queue/history reconciliation.

Also corrects the inaccurate rationale comment in session.ts that
claimed the gapless swap does not bump transport_generation.
The soft limiter is now gated on EQ activity (is_fully_bypassed). The
peak_ring_captures_post_limiter_values test used a default-disabled
EqProcessor, so the limiter was skipped and the 1.0 samples were not
clipped. Enable the EQ in the test so the limiter is active.
…imiter on EQ

Move the preload_request_generation fetch_add inside the preload_shutdown
lock so the generation assignment and CancelPreparedNext send are ordered
consistently for concurrent callers. Without this, two overlapping
set_preload_candidate invocations could obtain generations in one order
but acquire the lock in the opposite order, causing the coordinator to
accept an older stale preload candidate while rejecting the newer one.

Gate the gapless transition tail soft limiter on EQ activity
(!eq_processor.is_fully_bypassed()) to match the main render path. The
unconditional soft_limit attenuated loud-but-unclipped audio during the
brief song-to-song transition for users who never enable the EQ.

Add set_preload_candidate handler to the e2e Tauri mock so the always-on
preload effect does not hit the unhandled-command rejection path.
Update use-playback-runtime.test.tsx to use the current TrackTransitionedEvent
(requires transition_serial) and PlaybackPositionEvent (requires ms,
transport_generation, snapshot) shapes instead of the stale song_id/position_ms
fields. Resolves the Frontend quality / build / test TS2741 and TS2353 failures.
)

Rebased onto feat/crossfade-playback-89 head. Includes all waveform-specific
fixes: LRU cache, width buckets, request generation, DPR-aware canvas,
RAII completion guard, zero-channel panic guard, and transition snapshot
capture at switch time.
… feat/crossfade-playback-89

# Conflicts:
#	src-tauri/src/audio/output.rs
Merge brought in the gapless branch's test (snake_case fields, two-arg
onTrackTransitioned) but the crossfade branch's implementation uses
camelCase (via serde rename_all) and passes the whole event object.
Update the test to match the crossfade event shape.
The test used `instrumental` which doesn't exist on the StemVolumes
interface (vocals/drums/bass/other). This caused a tsc build failure in CI.
The soft limiter is gated on EQ activity (is_fully_bypassed). The
gapless_swap_tail_applies_soft_limiter test used a default-disabled
EqProcessor, so the limiter was skipped and the 1.0 tail samples were
not compressed. Enable the EQ in the test so the limiter is active,
matching the fix applied to peak_ring_captures_post_limiter_values in
ce496d5.
…eat/artwork-derivatives-91

# Conflicts:
#	CHANGELOG.md
#	docs/references/generated/db-schema.md
#	src-tauri/migrations/011_waveforms.sql
#	src-tauri/src/audio/output.rs
#	src/components/Settings/SettingsOverlay.state.test.ts
#	src/components/Settings/SettingsOverlay.state.ts
#	src/locales/en.json
#	src/locales/zh-CN.json

Copy link
Copy Markdown
Owner Author

Re-review — no LGTM: scope is not isolated and branch conflicts

Reviewed head a2dc845c0b7db43e4021e1e8dcd429748f0bca9c.

The artwork-specific fixes are directionally complete and CI/Packaging are green, but the current PR is conflicting and shows 142 commits / 106 changed files against main, carrying the entire audio feature stack rather than an auditable artwork-only change.

After #104/#106 are repaired and merged in order:

  1. rebase this branch onto current main;
  2. ensure the final diff contains only artwork derivative schema/cache/import/delete/remote-sync/frontend work;
  3. preserve the current audio engine unchanged;
  4. rerun CI + Packaging and the shared-file reference-count, corrupt/missing derivative repair, Blob URL lifetime, and remote publish/mirror tests;
  5. attach packaged/manual evidence for thumbnail, preview backdrop, replacement, deletion, and remote synchronization.

The current history/scope is not safe to merge.

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