android: the repair sheet, browse, every long-press sheet, You's last sections, the lyrics tools — and the walk's audio check made honest
The playlist repair (Find a local match: the library searched by the row's name, a pick relinks it through playlist_relink_item; an unknown row takes the track's identity). A peer's sheet on the raw results (play or save the folder, browse their files, message, buddy) and the Browse screen — the hub's Browse tab: a user's whole share with sizes and totals, a folder played or saved whole; a share can hold thousands of folders, so it filters and renders 80 at a time. "Look up a user" on Community → Buddies. The sheets the phone lacked: a queue row's (Play now, Move to next, Remove from queue), a catalog song's, a catalog card's and the record page's own, a raw file's (in the results and in Browse), a room name's, a buddy's, the now-playing Save (the desktop's Save menu for a streamed track or record). The artwork viewer with pinch zoom, one layer the back gesture peels. Search's recent searches and its spelling suggestion from the names this phone knows. You → Streaming, Downloads, Sharing, Lyrics, Album artwork, and Clear play history; a record's Re-fetch the cover. The lyrics tools on the sheet's face: the offset nudged and kept per track, another LRCLIB candidate chosen, the lookup forgotten and done again (lyrics.svelte.js, shared by the face and the lyrics sheet). The harness: every CDP call times out and the reload step re-attaches (a WebView that stopped answering hung a walk for ten minutes); the audio check counts SoulStream's own tracks' underruns from the mixer dump instead of the thread-wide counters that any app's notification moves — the afternoon's "dropouts" were other apps' sounds, and the browse of a 49,544-file share measured at zero; __settle resets every tab to its root through the nav; an adb step kind; the walks cover all of the above on the mock and on the phone.
This commit is contained in:
1 parent
6e39dc43f3
commit
26928b706d
21 files changed
+1294
-92
No files matched your search
+12
-29
@@ -16,7 +16,8 @@
|
||||
import { art } from "./art.svelte.js";
|
||||
import { albumsBy } from "./lib.svelte.js";
|
||||
import { player, next, prev, toggle, seekFrac, seekSecs, playQueueAt, toggleShuffle, cycleRepeat } from "./player.svelte.js";
|
||||
import { nav, closeSheet, morphSource, openHalf, push } from "./nav.svelte.js";
|
||||
import { nav, closeSheet, morphSource, openHalf, openViewer, push } from "./nav.svelte.js";
|
||||
import { lyr, loadLyrics } from "./lyrics.svelte.js";
|
||||
import { mmss, drag } from "./util.js";
|
||||
|
||||
let frameEl = $state(null);
|
||||
@@ -106,33 +107,12 @@
|
||||
if (player.track) openHalf("track", { album: player.album, track: player.track });
|
||||
}
|
||||
|
||||
/* ── lyrics: the app's own pipeline, per track ───────────────────── */
|
||||
let lyr = $state({ key: null, status: "none", lines: [], plain: "", offset: 0 });
|
||||
/* ── lyrics: the store (lyrics.svelte.js), asked for the playing track
|
||||
whenever the face is up ───────────────────────────────────────── */
|
||||
$effect(() => {
|
||||
if (face !== "lyrics" || !player.track) return;
|
||||
const t = player.track;
|
||||
const key = t.path;
|
||||
if (lyr.key === key) return;
|
||||
lyr = { key, status: "loading", lines: [], plain: "", offset: 0 };
|
||||
api.lyricsGet({
|
||||
title: t.title,
|
||||
artist: t.artist || player.album?.artist || "",
|
||||
album: player.album?.title ?? null,
|
||||
durationSecs: t.duration_secs ?? null,
|
||||
path: t.path,
|
||||
}).then(
|
||||
(p) => {
|
||||
if (lyr.key !== key) return;
|
||||
lyr = {
|
||||
key,
|
||||
status: p?.status || "none",
|
||||
lines: p?.synced || [],
|
||||
plain: p?.plain || "",
|
||||
offset: (p?.offset_ms || 0) + (p?.auto_offset_ms || 0),
|
||||
};
|
||||
},
|
||||
() => { if (lyr.key === key) lyr = { key, status: "none", lines: [], plain: "", offset: 0 }; },
|
||||
);
|
||||
void player.track.path;
|
||||
void loadLyrics();
|
||||
});
|
||||
const nowMs = $derived(player.position * 1000);
|
||||
const activeIdx = $derived.by(() => {
|
||||
@@ -171,7 +151,7 @@
|
||||
<button class="iconbtn" onclick={more} aria-label="More"><Icon name="more" size={22} /></button>
|
||||
</div>
|
||||
|
||||
<div class="np-art" class:small>
|
||||
<div class="np-art" class:small onclick={() => openViewer(player.album?.art_path)} role="presentation">
|
||||
<div class="np-frame" bind:this={frameEl}><Cover path={player.album.art_path} eager size={64} /></div>
|
||||
</div>
|
||||
|
||||
@@ -188,7 +168,7 @@
|
||||
{:else}
|
||||
<div class="np-face" bind:this={faceEl}>
|
||||
{#if face === "lyrics"}
|
||||
<div class="lyr">
|
||||
<div class="lyr" oncontextmenu={(e) => { e.preventDefault(); openHalf("lyrics"); }} role="presentation">
|
||||
{#if lyr.status === "loading"}
|
||||
<div class="lyr-note">Looking for the words…</div>
|
||||
{:else if lyr.lines.length}
|
||||
@@ -205,11 +185,14 @@
|
||||
</button>
|
||||
{/if}
|
||||
{/each}
|
||||
<button class="lyr-tools" onclick={() => openHalf("lyrics")}>Out of time, or wrong? ⋯</button>
|
||||
{:else if lyr.plain}
|
||||
<div class="lyr-plain">{lyr.plain}</div>
|
||||
<div class="lyr-note">Plain lyrics — nothing timed for this recording.</div>
|
||||
<button class="lyr-tools" onclick={() => openHalf("lyrics")}>Wrong lyrics? ⋯</button>
|
||||
{:else if lyr.status === "instrumental"}
|
||||
<div class="lyr-note">Instrumental.</div>
|
||||
<button class="lyr-tools" onclick={() => openHalf("lyrics")}>Not so? ⋯</button>
|
||||
{:else}
|
||||
<div class="lyr-note">No lyrics found for this track.</div>
|
||||
{/if}
|
||||
@@ -225,7 +208,7 @@
|
||||
{#if e.album?.title !== prevAlbum}
|
||||
<div class="q-break">{e.album?.artist} · {e.album?.title}</div>
|
||||
{/if}
|
||||
<button class="row" onclick={() => playQueueAt(player.qi + 1 + k)} oncontextmenu={(ev) => { ev.preventDefault(); openHalf("track", { album: e.album, track: e.t }); }}>
|
||||
<button class="row" onclick={() => playQueueAt(player.qi + 1 + k)} oncontextmenu={(ev) => { ev.preventDefault(); openHalf("track", { album: e.album, track: e.t, queueIndex: player.qi + 1 + k }); }}>
|
||||
<span class="art"><Cover path={e.album?.art_path} size={18} /></span>
|
||||
<span class="b"><span class="t">{e.t.title}</span><span class="m">{e.t.artist || e.album?.artist}</span></span>
|
||||
<span class="r">{mmss(e.t.duration_secs)}</span>
|
||||
|
||||
Reference in new issue
Block a user