Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions src/library/library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,35 +89,40 @@ export class Library {

async upsert(track: Track): Promise<void> {
this.index.tracks[track.id] = track;
this.notify();
this.version++;
this.sorted = null;
this.schedulePersist();
}

/** Apply many track updates with a single notify + persist. */
async upsertMany(tracks: Track[]): Promise<void> {
if (tracks.length === 0) return;
for (const track of tracks) this.index.tracks[track.id] = track;
this.notify();
this.version++;
this.sorted = null;
this.schedulePersist();
}

async remove(id: string): Promise<void> {
delete this.index.tracks[id];
this.notify();
this.version++;
this.sorted = null;
this.schedulePersist();
}

/** Remove many tracks with a single notify + persist. */
async removeMany(ids: string[]): Promise<void> {
if (ids.length === 0) return;
for (const id of ids) delete this.index.tracks[id];
this.notify();
this.version++;
this.sorted = null;
this.schedulePersist();
}

async clear(): Promise<void> {
this.index.tracks = {};
this.notify();
this.version++;
this.sorted = null;
this.schedulePersist();
}

Expand Down
6 changes: 4 additions & 2 deletions src/player/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ export class PlayHistory {
{ id, at: new Date().toISOString() },
...this.index.entries.filter((e) => e.id !== id),
].slice(0, CAP);
this.notify();
this.version++;
for (const fn of this.listeners) fn();
void this.persist();
}

Expand All @@ -88,7 +89,8 @@ export class PlayHistory {
const kept = this.index.entries.filter((e) => existing(e.id));
if (kept.length === this.index.entries.length) return;
this.index.entries = kept;
this.notify();
this.version++;
for (const fn of this.listeners) fn();
void this.persist();
}

Expand Down
5 changes: 5 additions & 0 deletions src/player/mpv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ export class MpvPlayer extends EventEmitter {
* after the last track, after stop), so transport commands are gated on this.
*/
private loaded = false;
/** Lock to prevent connection reset while ensureStarted() is in progress. */
private resetting = false;

constructor(mpvPath: string) {
super();
Expand Down Expand Up @@ -73,6 +75,8 @@ export class MpvPlayer extends EventEmitter {
* Used both on an unexpected exit (auto-respawn) and during teardown.
*/
private resetConnection(): void {
if (this.resetting) return;
this.resetting = true;
this.loaded = false;
if (this.sock) {
try {
Expand All @@ -91,6 +95,7 @@ export class MpvPlayer extends EventEmitter {
this.unlinkSocket();
// Each respawn gets a brand-new endpoint to avoid colliding with a stale one.
this.ipcPath = MpvPlayer.makeIpcPath();
this.resetting = false;
}

/** Best-effort removal of the unix socket file (no-op on Windows pipes). */
Expand Down
2 changes: 1 addition & 1 deletion src/sources/spotify/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export function clearSpotifyTokenCache(): void {
/** Fetch (or reuse) an anonymous web-player bearer token. */
export async function getWebPlayerToken(): Promise<WebPlayerToken> {
const hit = tokenCache;
if (hit && hit.expiresAtMs - Date.now() > TOKEN_SKEW_MS) {
if (hit && hit.expiresAtMs > Date.now()) {
return hit;
}

Expand Down