A music player that connects to your cloud/distributed storage.
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor: search + queue supply

+58 -50
+5 -5
src/components/engine/queue/element.js
··· 26 26 27 27 this.add = this.proxy.add; 28 28 this.fill = this.proxy.fill; 29 - this.pool = this.proxy.pool; 30 29 this.shift = this.proxy.shift; 30 + this.supply = this.proxy.supply; 31 31 this.unshift = this.proxy.unshift; 32 32 } 33 33 ··· 36 36 #future = signal(/** @type {Array<Item>} */ ([])); 37 37 #now = signal(/** @type {Item | null} */ (null)); 38 38 #past = signal(/** @type {Array<Item>} */ ([])); 39 - #poolHash = signal(/** @type {string | undefined} */ (undefined)); 39 + #supplyFingerprint = signal(/** @type {string | undefined} */ (undefined)); 40 40 41 41 // STATE 42 42 43 43 future = this.#future.get; 44 44 now = this.#now.get; 45 45 past = this.#past.get; 46 - poolHash = this.#poolHash.get; 46 + supplyFingerprint = this.#supplyFingerprint.get; 47 47 48 48 // LIFECYCLE 49 49 ··· 60 60 listen("future", this.#future.set, link); 61 61 listen("now", this.#now.set, link); 62 62 listen("past", this.#past.set, link); 63 - listen("poolHash", this.#poolHash.set, link); 63 + listen("supplyFingerprint", this.#supplyFingerprint.set, link); 64 64 65 65 // Fetch current data state 66 66 this.proxy.future().then(this.#future.set); 67 67 this.proxy.now().then(this.#now.set); 68 68 this.proxy.past().then(this.#past.set); 69 - this.proxy.poolHash().then(this.#poolHash.set); 69 + this.proxy.supplyFingerprint().then(this.#supplyFingerprint.set); 70 70 } 71 71 } 72 72
+6 -2
src/components/engine/queue/types.d.ts
··· 11 11 shuffled: boolean; 12 12 }, 13 13 ) => void; 14 - pool: (tracks: Track[]) => void; 15 14 shift: () => void; 15 + supply: (args: { tracks: Track[] }) => void; 16 16 unshift: () => void; 17 17 }; 18 18 ··· 24 24 future: SignalReader<Item[]>; 25 25 now: SignalReader<Item | null>; 26 26 past: SignalReader<Item[]>; 27 - poolHash: SignalReader<string | undefined>; 27 + 28 + /** 29 + * Initially this is set to `undefined`, but whenever the cache is changed afterwards this will be the hash of the items in the supply. 30 + */ 31 + supplyFingerprint: SignalReader<string | undefined>; 28 32 };
+15 -11
src/components/engine/queue/worker.js
··· 17 17 export const $future = signal(/** @type {Item[]} */ ([])); 18 18 export const $now = signal(/** @type {Item | null} */ (null)); 19 19 export const $past = signal(/** @type {Item[]} */ ([])); 20 - export const $poolHash = signal(/** @type {string | undefined} */ (undefined)); 20 + export const $supplyFingerprint = signal( 21 + /** @type {string | undefined} */ (undefined), 22 + ); 21 23 22 24 //////////////////////////////////////////// 23 25 // ACTIONS ··· 51 53 } 52 54 53 55 /** 54 - * @type {Actions['pool']} 56 + * @type {Actions['shift']} 55 57 */ 56 - export function pool(tracks) { 57 - $lake.value = tracks; 58 - $poolHash.value = tracks.length ? hash(tracks) : undefined; 58 + export function shift() { 59 + return _shift(); 59 60 } 60 61 61 62 /** 62 - * @type {Actions['shift']} 63 + * @type {Actions['supply']} 63 64 */ 64 - export function shift() { 65 - return _shift(); 65 + export function supply({ tracks }) { 66 + $lake.value = tracks; 67 + $supplyFingerprint.value = tracks.length ? hash(tracks) : undefined; 66 68 } 67 69 68 70 /** ··· 89 91 rpc(context, { 90 92 add, 91 93 fill, 92 - pool, 93 94 shift, 95 + supply, 94 96 unshift, 95 97 96 98 // State 97 99 future: $future.get, 98 100 now: $now.get, 99 101 past: $past.get, 100 - poolHash: $poolHash.get, 102 + supplyFingerprint: $supplyFingerprint.get, 101 103 }); 102 104 103 105 // Effects ··· 106 108 effect(() => announce("future", $future.value, context)); 107 109 effect(() => announce("now", $now.value, context)); 108 110 effect(() => announce("past", $past.value, context)); 109 - effect(() => announce("poolHash", $poolHash.value, context)); 111 + effect(() => 112 + announce("supplyFingerprint", $supplyFingerprint.value, context) 113 + ); 110 114 }); 111 115 112 116 ////////////////////////////////////////////
+1 -1
src/components/orchestrator/queue-tracks/worker.js
··· 39 39 }, []); 40 40 41 41 // Set pool 42 - await queue.pool(availableTracks); 42 + await queue.supply({ tracks: availableTracks }); 43 43 } 44 44 45 45 ////////////////////////////////////////////
+1 -1
src/components/orchestrator/repeat-shuffle/element.js
··· 53 53 // Effects 54 54 this.effect(() => { 55 55 const trigger = queue.now(); 56 - const _other_trigger = queue.poolHash(); 56 + const _other_trigger = queue.supplyFingerprint(); 57 57 58 58 this.isLeader().then((isLeader) => { 59 59 if (!isLeader) return;
+4 -4
src/components/processor/search/element.js
··· 30 30 31 31 // SIGNALS 32 32 33 - #cacheId = signal(/** @type {string | undefined} */ (undefined)); 33 + #supplyFingerprint = signal(/** @type {string | undefined} */ (undefined)); 34 34 35 35 // STATE 36 36 37 - cacheId = this.#cacheId.get; 37 + supplyFingerprint = this.#supplyFingerprint.get; 38 38 39 39 // LIFECYCLE 40 40 ··· 48 48 const link = this.workerLink(); 49 49 50 50 // Listen for remote data changes 51 - listen("cacheId", this.#cacheId.set, link); 51 + listen("supplyFingerprint", this.#supplyFingerprint.set, link); 52 52 53 53 // Fetch current data state 54 - this.proxy.cacheId().then(this.#cacheId.set); 54 + this.proxy.supplyFingerprint().then(this.#supplyFingerprint.set); 55 55 } 56 56 } 57 57
+4 -1
src/components/processor/search/types.d.ts
··· 15 15 export type Schema = Orama<typeof SCHEMA>; 16 16 17 17 export type State = { 18 - cacheId: SignalReader<string | undefined>; 18 + /** 19 + * Initially this is set to `undefined`, but whenever the cache is changed afterwards this will be the hash of the items in the supply. 20 + */ 21 + supplyFingerprint: SignalReader<string | undefined>; 19 22 };
+8 -6
src/components/processor/search/worker.js
··· 22 22 }); 23 23 24 24 // Communicated state 25 - export const $cacheId = signal(/** @type {string | undefined} */ (undefined)); 25 + export const $supplyFingerprint = signal( 26 + /** @type {string | undefined} */ (undefined), 27 + ); 26 28 27 29 //////////////////////////////////////////// 28 30 // DATABASE ··· 93 95 await Orama.removeMultiple(db, Array.from(removedIds)); 94 96 await Orama.insertMultiple(db, newTracks); 95 97 96 - $cacheId.value = ids.length === 0 97 - ? undefined 98 - : xxh32(ids.sort().join("")).toString(); 98 + $supplyFingerprint.value = xxh32(ids.sort().join("")).toString(); 99 99 } 100 100 101 101 //////////////////////////////////////////// ··· 108 108 supply, 109 109 110 110 // State 111 - cacheId: $cacheId.get, 111 + supplyFingerprint: $supplyFingerprint.get, 112 112 }); 113 113 114 114 // Effects 115 115 116 116 // Communicate state 117 - effect(() => announce("cacheId", $cacheId.value, context)); 117 + effect(() => 118 + announce("supplyFingerprint", $supplyFingerprint.value, context) 119 + ); 118 120 }); 119 121 120 122 ////////////////////////////////////////////
+2 -2
src/themes/blur/index.js
··· 34 34 }); 35 35 36 36 effect(() => { 37 - console.log("Queue pool hash:", queue.poolHash()); 37 + console.log("Queue pool hash:", queue.supplyFingerprint()); 38 38 }); 39 39 40 40 /** ··· 42 42 */ 43 43 effect(() => { 44 44 const trigger = queue.now(); 45 - const _other_trigger = queue.poolHash(); 45 + const _other_trigger = queue.supplyFingerprint(); 46 46 47 47 isLeader().then((bool) => { 48 48 if (bool) {
+3 -2
src/themes/webamp/browser/element.js
··· 66 66 // Wait for the above dependencies to be defined, then render again. 67 67 whenElementsDefined({ input, output, search }).then(() => { 68 68 this.effect(() => { 69 - const _cacheId = search.cacheId(); 69 + const _ = search.supplyFingerprint(); 70 70 this.performSearch(); 71 71 }); 72 72 ··· 118 118 render({ html }) { 119 119 const isLoading = this.$output.value?.tracks?.state() !== "loaded" || 120 120 (this.#collectionSize.value > 0 && 121 - this.$search.value?.cacheId() === undefined); 121 + this.$search.value?.supplyFingerprint() === undefined); 122 122 123 123 const tracks = this.#searchResults.value; 124 124 ··· 174 174 } 175 175 176 176 table tbody tr { 177 + cursor: pointer; 177 178 content-visibility: auto; 178 179 } 179 180
+8 -12
src/themes/webamp/index.js
··· 85 85 * Whenever the queue changes update the playlist. 86 86 */ 87 87 effect(() => { 88 - const now = untracked(queue.now); 89 - const past = untracked(queue.past); 90 88 const future = queue.future(); 91 89 92 90 const playlist = [ 93 - ...past, 94 - ...(now ? [now] : []), 95 91 ...future, 96 92 ]; 97 93 98 94 const lengthLastPlaylist = untracked($playlist.get).length; 99 - const tracksToAdd = playlist.slice(lengthLastPlaylist); 95 + const tracksToAdd = playlist.slice( 96 + 0, 97 + Math.max(0, playlist.length - lengthLastPlaylist), 98 + ); 100 99 101 100 $playlist.value = playlist; 102 101 ··· 116 115 const state = output.tracks.state(); 117 116 if (state !== "loaded") return; 118 117 119 - const cacheId = search.cacheId(); 120 - if (cacheId === undefined) return; 118 + const fingerprintSearch = search.supplyFingerprint(); 119 + if (fingerprintSearch === undefined) return; 121 120 122 - const poolHash = queue.poolHash(); 123 - if (poolHash === undefined) return; 121 + const fingerprintQueue = queue.supplyFingerprint(); 122 + if (fingerprintQueue === undefined) return; 124 123 125 124 tracksPromise.resolve("loaded"); 126 125 }); ··· 172 171 173 172 async function addBatch() { 174 173 await queue.fill({ augment: true, amount: 50, shuffled: true }); 175 - 176 - // Automatically insert track if there isn't any 177 - if (!queue.now()) await queue.shift(); 178 174 } 179 175 180 176 function windowManager() {
+1 -3
tests/components/engine/queue/test.ts
··· 4 4 import { testWeb } from "@tests/common/index.ts"; 5 5 import { tracks } from "@src/testing/sample/tracks.js"; 6 6 7 - import type { Item } from "@components/engine/queue/types.d.ts"; 8 - 9 7 describe("components/engine/queue", () => { 10 8 it("adds tracks", async () => { 11 9 const items = await testWeb(async () => { ··· 35 33 36 34 const { tracks } = await import("@src/testing/sample/tracks.js"); 37 35 38 - await engine.pool(tracks); 36 + await engine.supply({ tracks }); 39 37 await engine.fill({ amount: 1, shuffled: false }); 40 38 await engine.shift(); 41 39