{#if yoursSrc}{/if}
{index + 1} of {total} {bucket} {#if choice?.deferred && !choice?.seen} skipped for now {/if}

{albumName ?? a.title}

{artist} · {a.tracks.length} tracks · {fmtMB(a.bytes)} · {ext} {a.avgBitrate ?? "—"}k
{gainLabel}
Artwork {#if !idn || idn.state === "loading"}looking the record up… {:else if idn.state === "error"}the lookup failed {:else if measuring}found it — measuring what each service has… {:else if allDead}found it, but no service has a cover for it {:else if measureFailed}found it, but the sizes could not be measured {:else if matchHasNoArt}the matching release carries no artwork {:else if !covers.length}no service has this record {:else}{@const best = Math.max(...covers.map((x) => x.px || 0))} {covers.length} service{covers.length === 1 ? "" : "s"} answered{#if !yours} — you have none{:else if (yours?.w ?? 0) >= best}, and none beats your {yours.w}px — nothing to gain here{:else} — yours is {yours.w}px, the best of these is {best}px{/if} {/if}
onChoose({ art: "yours" })} onkeydown={(e) => e.key === "Enter" && onChoose({ art: "yours" })}>
{ e.stopPropagation(); openFs(fsPick); }} role="presentation"> {#if yoursSrc} {:else}
no artwork in the folder,
and nothing embedded
{/if}
{yours ? `${yours.w} × ${yours.h}` : "none"} yours{yoursWins ? " · already the best" : ""}
{#if yours}
{yours.kind === "embedded" ? `embedded in ${yours.from}` : yours.from}
{/if} {#if yours?.kind === "derived"}
the only picture in the folder — nothing is embedded, so this thumbnail is all you have
{:else if yours?.derivedIgnored}
{yours.derivedIgnored} Media Player thumbnail{yours.derivedIgnored === 1 ? "" : "s"} in the folder, ignored
{/if}
{resolvedArt === "yours" ? "✓ using this" : "use this"}
{#each covers as c, n (c.url)} {@const dead = !!deadArt[c.url]} {@const smaller = yours && c.px && c.px < yours.w} {@const wrongTitle = c.title && norm2(c.title) !== norm2(a.title)}
!dead && onChoose({ art: c.provider })} onkeydown={(e) => !dead && e.key === "Enter" && onChoose({ art: c.provider })}>
{ if (!dead) { e.stopPropagation(); openFs(n + 1); } }} role="presentation"> {#if dead}
nothing on file here —
{c.provider} has no cover for this release
{:else} (deadArt[c.url] = true)} /> {/if}
{dead ? "no cover" : c.px ? `${c.px} × ${c.realH ?? c.px}` : measuring ? "measuring…" : "size unknown"} {c.provider}{smaller && !dead ? " · smaller than yours" : ""}
{#if wrongTitle}
“{c.title}”
{/if}
{dead ? "nothing to use" : resolvedArt === c.provider ? "✓ using this" : "use this"}
{/each} {#if idn?.state === "done" && !covers.length}
{matchHasNoArt ? "the matching release has no artwork on file" : "no service has this record,"}{#if !matchHasNoArt}
so there is nothing to compare{/if}
Deezer · Apple · Cover Art Archive
{:else if !idn || idn.state === "loading"}
looking this record up…
Deezer · Apple · Cover Art Archive
{/if}
Is this the same record?
{#if !idn || idn.state === "loading"}
Asking Deezer, then MusicBrainz when it has no answer…
{:else if idn.state === "error"}
The lookup failed — {idn.error}
{:else}
{#each ev.items as it} {it.cls === "ok" ? "✓" : it.cls === "no" ? "✕" : "!"} {it.text} {/each} {#if candidate}{candidate.source} {String(candidate.id).replace(/^mb:/, "#")}{/if}
{@html verdictLine[1]}
{/if}
Audio — the version you keep {#if !audition}whole album · one choice, like the artwork {:else if audition.status === "searching"}searching the network… {:else if audition.status === "none"}nothing on the network answered {:else if audition.status === "failed"}the transfer failed{audition.error ? ` — ${audition.error}` : ""} {:else if audition.status === "downloading"}{live?.stalled ? "the transfer went quiet" : "found a copy — fetching track 1 first, so the comparison can start"} {:else if audition.status === "complete"}the whole copy is cached — keeping it is instant {:else}{audition.arrived ?? 1} of {audition.expected ?? audition.ids?.length ?? "…"} tracks cached · the rest keep arriving {/if} the lit version is what gets imported
{#if !audition || audition.status === "none" || audition.status === "failed"}
onChoose({ audio: "yours" })} onkeydown={(e) => e.key === "Enter" && onChoose({ audio: "yours" })}>
Yours{ext} {a.avgBitrate ?? "—"}k · {a.tracks.length} tracks · {fmtMB(a.bytes)}
{#each (peaksYours ?? waveFloor().map((b) => 0)) as v, i (i)}{/each}
{a.tracks[0]?.seconds ? mmss(a.tracks[0].seconds) : "—"}
{audition?.status === "none" ? audition.silent ? "Soulseek answered nothing at all — the server refuses some queries outright, and quiet spells happen. Worth asking again." : "No peer offered this album just now — worth asking again another day." : audition?.status === "failed" ? "The peer stopped answering — try the search again." : "Searches Soulseek for this whole album; the best copy found downloads into the cache so you hear and compare the real thing before choosing."}
{:else if audition.status === "searching"}
Searching…asking the network for this album
{#each waveFloor() as b (b.i)}{/each}
—
{:else} {@const au = audition} {@const theirsReady = !!au.track1Path}
onChoose({ audio: "yours" })} onkeydown={(e) => e.key === "Enter" && onChoose({ audio: "yours" })}>
Yours{ext} {a.avgBitrate ?? "—"}k · {a.tracks.length} tracks · {fmtMB(a.bytes)}
{#each (peaksYours ?? []) as v, i (i)}{/each}
{a.tracks[0]?.seconds ? mmss(a.tracks[0].seconds) : "—"}
{resolvedAudio === "yours" ? "✓ keeping this" : "keep this"}
theirsReady && onChoose({ audio: "theirs" })} onkeydown={(e) => e.key === "Enter" && theirsReady && onChoose({ audio: "theirs" })}>
Found copy {au.probe ? `${(au.candidate.files[0]?.name ?? "").split(".").pop().toUpperCase()} ${au.probe.bitrate ?? "—"}k${au.probe.bitDepth ? ` · ${au.probe.bitDepth}-bit` : ""} · measured` : theirsReady ? `${au.candidate.avgBitrate ? au.candidate.avgBitrate + "k · " : ""}${au.candidate.trackCount} tracks · ${fmtMB(au.candidate.bytes)} · ${au.candidate.username}` : au.candidate.username}
{#if theirsReady && peaksTheirs}
{#each peaksTheirs as v, i (i)}{/each}
{:else if theirsReady}
{#each waveFloor() as b (b.i)}{/each}
{:else}
{arrivalLine(live, au)}
{/if}
{au.probe?.seconds ? mmss(au.probe.seconds) : "—"}
{resolvedAudio === "theirs" ? "✓ keeping this" : theirsReady ? "keep this" : live?.stalled ? "stalled" : "arriving…"}
{#if !theirsReady && live?.stalled} Nothing has arrived for {Math.round(live.quietMs / 1000)}s. The peer may have gone offline or dropped the queue. You can wait, try another peer, or keep your own copy — nothing is lost either way. {:else if !theirsReady} Track 1 is asked for on its own, so it is not queued behind the other {au.candidate.trackCount - 1} files of its own album.{#if au.switchedFrom}  · Moved on from {au.switchedFrom}{au.reason ? ` — ${au.reason}` : ""}. {/if} {:else if au.probe?.seconds && a.tracks[0]?.seconds} {@const d = Math.abs(a.tracks[0].seconds - au.probe.seconds)} {#if d <= 2} Ready to compare. Track 1 is the same length in both ({mmss(au.probe.seconds)}) — the strongest signal it is the same recording. The waveforms share one scale, so shape and loudness compare honestly.{#if au.status !== "complete"} The other {Math.max(0, (au.expected ?? au.ids?.length ?? 0) - (au.arrived ?? 0))} tracks are still coming; keeping this version waits for them.{/if} {:else} Track 1 differs by {d}s — different recording, different edit, or a different record. Listen before keeping it. {/if} {:else if au.candidate.countMatch === false} {au.candidate.trackCount} tracks against your {a.tracks.length} — this copy may be a different edition or incomplete. {:else} The whole album downloads into the cache — if you keep it, nothing is fetched twice; if you keep yours, the sweep reclaims it in a week. {/if}
{#if !theirsReady} {@const alts = au.alts?.length ?? 0}
{alts ? `${alts} more ${alts === 1 ? "copy" : "copies"} ranked behind this one${au.candidate.queue ? ` — this peer has ${au.candidate.queue} ahead of you` : ""}. Switching cancels this download; nothing already cached is lost.` : "No ranked alternates left for this copy — this asks the network again."}
{/if} {/if}
{#if candidate}
Track titles {a.tracks.length} yours · {candidate.tracklist.length} theirs pick a side per track
{#if a.tracks.length !== candidate.tracklist.length || slipped}
{#if a.tracks.length !== candidate.tracklist.length}Different track counts — {a.tracks.length} against {candidate.tracklist.length}.{/if} {#if slipped}{slipped} track{slipped === 1 ? "" : "s"} {slipped === 1 ? "does" : "do"} not match on length.{/if} These are paired by position because the provider sends no track numbers, so the rows below may be lined up against the wrong songs.
{:else}
Every track pairs with one of the same length, so the rows below line up.
{/if}
yours
{candidate.source}
{#each trackPairs as p (p.n)}
{p.row + 1}
{p.their ? (p.aligned ? mmss(p.dT) : `${p.drift ?? "?"}s off`) : ""}
{/each}
{#if trackChanges}{trackChanges} title{trackChanges === 1 ? "" : "s"} will be replaced. {:else}Keeping all of your titles.{/if}
Tags pick a side per field the lit one is what gets written
yours
{candidate.source}
{#each tagPairs as [k, mine, theirs] (k)} {@const same = mine && theirs && norm2(mine) === norm2(theirs)}
{k}
{/each}
{:else if idn?.state === "done"}
Tags
This record has not been matched, so there is nothing to choose between. What your files say is in the summary below.
{/if}
What gets imported {changeCount ? `${changeCount} change${changeCount === 1 ? "" : "s"} from what you have` : "everything of yours, unchanged"} {#if choice?.seen}{/if}
Artwork
{resolvedArt === "yours" ? (yours ? `yours · ${yours.w} × ${yours.h}` : "none — a cover will be generated") : `${resolvedArt} · ${covers.find((c) => c.provider === resolvedArt)?.px ?? "?"}px`}
{#each tagPairs as [k, mine, theirs] (k)} {@const side = resolvedTag(k)} {@const v = side === "theirs" ? theirs : mine} {#if v}
{k}
{v}
{/if} {/each}
Track titles
0}> {trackChanges ? `${trackChanges} of ${a.tracks.length} replaced` : `all ${a.tracks.length} kept as yours`}
Audio
{resolvedAudio === "theirs" && audition?.candidate ? `the found copy — ${audition.candidate.folderLeaf} (${audition.candidate.avgBitrate ? audition.candidate.avgBitrate + "k, " : ""}${audition.candidate.trackCount} tracks)` : `${ext} ${a.avgBitrate ?? "—"}k · unchanged`}
{#if fs} {@const right = fsRight}
{#if !right || !yoursSrc}
{a.title}{artist}
(fs = false)} onkeydown={(e) => e.key === "Enter" && (fs = false)}>⤡
{yoursSrc ? `yours · ${yours?.w} × ${yours?.h}${yours?.kind === "embedded" ? " · embedded" : ""}` : `${right?.provider} · ${right?.px ? `${right.px} × ${right.realH ?? right.px}` : "size not measured yet"}`} {#if yoursSrc && !covers.length} · no service has this record — nothing to compare against {:else if !yoursSrc} · you have no artwork for this record{/if}
{:else}
{a.title}{artist}
(fs = false)} onkeydown={(e) => e.key === "Enter" && (fs = false)}>⤡
{#if loupe && loupePos} {@const bs = `${loupePos.w * LOUPE_Z}px ${loupePos.h * LOUPE_Z}px`} {@const bx = loupeBg(loupePos.fx, loupePos.w, LOUPE / 2)} {@const by = loupeBg(loupePos.fy, loupePos.h, LOUPE)}
yours {right.provider}
{/if} {#if fsMode === "side"}
yours · {yours?.w}px
{right.provider} · {right.px}px
{/if}
yours {yours?.w} × {yours?.h}{yours?.kind === "embedded" ? " · embedded" : ""}  vs  {right.provider} {right.px} × {right.realH ?? right.px}
{#each covers.filter((c) => !deadArt[c.url]) as c, n (c.url)}
(fsPick = n + 1)} onkeydown={(e) => e.key === "Enter" && (fsPick = n + 1)}>
{c.provider}{c.px} × {c.realH ?? c.px}
{/each}
{/if}
{/if}