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

Configure Feed

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

fix: favourites matching multiple tracks inconsistency

+117 -39
+36
src/components/orchestrator/favourites/common.js
··· 1 + import * as TID from "@atcute/tid"; 2 + 3 + /** 4 + * @import {PlaylistItem, Track} from "@definitions/types.d.ts" 5 + */ 6 + 7 + /** 8 + * Create a favourites playlist item from a track. 9 + * @param {Track} track 10 + * @returns {PlaylistItem} 11 + */ 12 + export function createFavouriteItem(track) { 13 + const transformations = ["toLowerCase"]; 14 + const now = new Date().toISOString(); 15 + 16 + return /** @type {PlaylistItem} */ ({ 17 + $type: "sh.diffuse.output.playlistItem", 18 + id: TID.now(), 19 + playlist: "Favourites", 20 + criteria: [ 21 + { 22 + field: "tags.artist", 23 + value: /** @type {unknown} */ (track.tags?.artist), 24 + transformations, 25 + }, 26 + { 27 + field: "tags.title", 28 + value: /** @type {unknown} */ (track.tags?.title), 29 + transformations, 30 + }, 31 + ], 32 + createdAt: now, 33 + updatedAt: now, 34 + }); 35 + } 36 + 1 37 /** 2 38 * Filter playlist items that belong to the favourites playlist. 3 39 *
+3 -35
src/components/orchestrator/favourites/worker.js
··· 1 - import * as TID from "@atcute/tid"; 2 - 3 1 import { ostiary, rpc } from "@common/worker.js"; 4 - import { filterFavourites } from "./common.js"; 2 + import { createFavouriteItem, filterFavourites } from "./common.js"; 5 3 6 4 /** 7 5 * @import {PlaylistItem, Track} from "@definitions/types.d.ts" ··· 18 16 * @returns {string} 19 17 */ 20 18 function matchKey(track) { 21 - return `${track.tags?.artist ?? ""}.${track.tags?.title ?? ""}`; 19 + return `${track.tags?.artist ?? ""}.${track.tags?.title ?? ""}`.toLowerCase(); 22 20 } 23 21 24 22 /** ··· 31 29 ""; 32 30 const title = item.criteria.find((c) => c.field === "tags.title")?.value ?? 33 31 ""; 34 - return `${artist}.${title}`; 35 - } 36 - 37 - /** 38 - * Create a favourites playlist item from a track. 39 - * @param {Track} track 40 - * @returns {PlaylistItem} 41 - */ 42 - function createFavouriteItem(track) { 43 - const transformations = ["toLowerCase"]; 44 - const now = new Date().toISOString(); 45 - 46 - return /** @type {PlaylistItem} */ ({ 47 - $type: "sh.diffuse.output.playlistItem", 48 - id: TID.now(), 49 - playlist: "Favourites", 50 - criteria: [ 51 - { 52 - field: "tags.artist", 53 - value: /** @type {unknown} */ (track.tags?.artist), 54 - transformations, 55 - }, 56 - { 57 - field: "tags.title", 58 - value: /** @type {unknown} */ (track.tags?.title), 59 - transformations, 60 - }, 61 - ], 62 - createdAt: now, 63 - updatedAt: now, 64 - }); 32 + return `${artist}.${title}`.toLowerCase(); 65 33 } 66 34 67 35 ////////////////////////////////////////////
+4 -4
tests/components/engine/queue/test.ts
··· 14 14 15 15 const { tracks } = await import("@testing/sample/tracks.js"); 16 16 17 - await engine.add({ tracks }); 17 + await engine.add({ trackIds: tracks.map((t) => t.id) }); 18 18 return engine.future(); 19 19 }); 20 20 ··· 33 33 34 34 const { tracks } = await import("@testing/sample/tracks.js"); 35 35 36 - await engine.supply({ tracks }); 36 + await engine.supply({ trackIds: tracks.map((t) => t.id) }); 37 37 await engine.fill({ amount: 1, shuffled: false }); 38 38 await engine.shift(); 39 39 ··· 53 53 54 54 const { tracks } = await import("@testing/sample/tracks.js"); 55 55 56 - await engine.add({ tracks }); 56 + await engine.add({ trackIds: tracks.map((t) => t.id) }); 57 57 await engine.shift(); 58 58 await engine.shift(); 59 59 await engine.unshift(); ··· 74 74 75 75 const { tracks } = await import("@testing/sample/tracks.js"); 76 76 77 - await engine.add({ tracks }); 77 + await engine.add({ trackIds: tracks.map((t) => t.id) }); 78 78 await engine.shift(); 79 79 await engine.shift(); 80 80
+74
tests/components/orchestrator/favourites/test.ts
··· 99 99 expect(favourites.length).toBe(1); 100 100 }); 101 101 102 + it("does not include duplicate tracks with different casing", async () => { 103 + const favourites = await testWeb(async () => { 104 + const Output = await import( 105 + "@components/configurator/output/element.js" 106 + ); 107 + const Favourites = await import( 108 + "@components/orchestrator/favourites/element.js" 109 + ); 110 + const { tracks } = await import("@testing/sample/tracks.js"); 111 + 112 + const output = new Output.CLASS(); 113 + output.id = "test-output"; 114 + document.body.append(output); 115 + 116 + const fav = new Favourites.CLASS(); 117 + fav.setAttribute("output-selector", "#test-output"); 118 + document.body.append(fav); 119 + 120 + await customElements.whenDefined(output.localName); 121 + await customElements.whenDefined(fav.localName); 122 + 123 + await fav.include(tracks[0]); 124 + await fav.include({ 125 + ...tracks[0], 126 + tags: { 127 + ...tracks[0].tags, 128 + artist: tracks[0].tags?.artist?.toUpperCase(), 129 + title: tracks[0].tags?.title?.toUpperCase(), 130 + }, 131 + }); 132 + 133 + return fav.playlistItems(); 134 + }); 135 + 136 + expect(favourites.length).toBe(1); 137 + }); 138 + 102 139 it("expels tracks", async () => { 103 140 const favourites = await testWeb(async () => { 104 141 const Output = await import( ··· 129 166 expect(favourites.length).toBe(1); 130 167 expect(favourites[0].criteria[0].value).toBe(tracks[1].tags?.artist); 131 168 expect(favourites[0].criteria[1].value).toBe(tracks[1].tags?.title); 169 + }); 170 + 171 + it("expels tracks with different casing", async () => { 172 + const favourites = await testWeb(async () => { 173 + const Output = await import( 174 + "@components/configurator/output/element.js" 175 + ); 176 + const Favourites = await import( 177 + "@components/orchestrator/favourites/element.js" 178 + ); 179 + const { tracks } = await import("@testing/sample/tracks.js"); 180 + 181 + const output = new Output.CLASS(); 182 + output.id = "test-output"; 183 + document.body.append(output); 184 + 185 + const fav = new Favourites.CLASS(); 186 + fav.setAttribute("output-selector", "#test-output"); 187 + document.body.append(fav); 188 + 189 + await customElements.whenDefined(output.localName); 190 + await customElements.whenDefined(fav.localName); 191 + 192 + await fav.include(tracks[0]); 193 + await fav.expel({ 194 + ...tracks[0], 195 + tags: { 196 + ...tracks[0].tags, 197 + artist: tracks[0].tags?.artist?.toUpperCase(), 198 + title: tracks[0].tags?.title?.toUpperCase(), 199 + }, 200 + }); 201 + 202 + return fav.playlistItems(); 203 + }); 204 + 205 + expect(favourites.length).toBe(0); 132 206 }); 133 207 134 208 it("toggles tracks", async () => {