tangled
alpha
login
or
join now
tokono.ma
/
diffuse-applets
0
fork
atom
Experiment to rebuild Diffuse using web applets.
0
fork
atom
overview
issues
pulls
pipelines
feat: various artwork improvements
Steven Vandevelde
9 months ago
81cceb1d
a801d137
+61
-26
4 changed files
expand all
collapse all
unified
split
src
pages
constituent
blur
artwork-controller
_applet.astro
orchestrator
queue-audio
_applet.astro
scripts
processor
artwork
types.d.ts
worker.ts
+4
-16
src/pages/constituent/blur/artwork-controller/_applet.astro
reviewed
···
63
63
left: 0;
64
64
object-fit: cover;
65
65
opacity: 0;
66
66
-
pointer-events: none;
67
66
position: absolute;
68
67
top: 0;
69
68
transition-duration: var(--transition-durition);
···
150
149
cite {
151
150
display: block;
152
151
font-style: normal;
153
153
-
line-height: var(--leading-snug);
154
152
text-shadow: var(--text-shadow-sm);
155
153
}
156
154
···
482
480
483
481
reactive(
484
482
engine.queue,
485
485
-
(data) => comparable(data.future),
486
486
-
() => {
487
487
-
const track = engine.queue.data.now;
488
488
-
489
489
-
if (!track) {
490
490
-
setActiveTrack(undefined);
491
491
-
return;
492
492
-
}
493
493
-
494
494
-
setActiveTrack(track);
495
495
-
},
483
483
+
(data) => data.now,
484
484
+
(track) => setActiveTrack(track || undefined),
496
485
);
497
486
498
487
// Changed artwork based on active queue item.
499
488
// (debounced)
500
489
501
501
-
reactive(engine.queue, (data) => comparable(data.future), debounce(2000, changeArtwork));
490
490
+
reactive(engine.queue, (data) => data.now, debounce(2000, changeArtwork));
502
491
503
492
async function changeArtwork() {
504
493
const track = engine.queue.data.now;
···
595
584
const color = fac.getColor(img as HTMLImageElement);
596
585
const rgb = color.value;
597
586
const o = Math.round((rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000);
598
598
-
console.log(o);
599
587
600
588
setArtworkColor(color.rgba);
601
589
setArtworkLightMode(o > 165);
···
657
645
computed(
658
646
() =>
659
647
activeTrack()?.tags?.artist ||
660
660
-
(isMainGroup() && !activeTrack() ? "Waiting for tracks to be queued ..." : ""),
648
648
+
(isMainGroup() && !activeTrack() ? "Waiting on queue ..." : ""),
661
649
),
662
650
),
663
651
),
+10
-2
src/pages/orchestrator/queue-audio/_applet.astro
reviewed
···
60
60
const activeTrack = engine.queue.data.now;
61
61
const isPlaying = engine.audio.data.isPlaying;
62
62
63
63
+
// Resolve URIs
64
64
+
const url = activeTrack
65
65
+
? await inputUrl(configurator.input, activeTrack.uri).then((a) => a?.url)
66
66
+
: undefined;
67
67
+
68
68
+
// Check if we still need to render
69
69
+
if (engine.queue.data.now?.id !== activeTrack?.id) return;
70
70
+
63
71
// Play new active queue item
64
72
// TODO: Take URL expiration timestamp into account
65
73
// TODO: Preload next queue item
···
71
79
{
72
80
id: activeTrack.id,
73
81
isPreload: false,
74
74
-
url: await inputUrl(configurator.input, activeTrack.uri).then((a) => a?.url),
82
82
+
url,
75
83
},
76
84
]
77
77
-
: // TODO: This probably isn't correct, keep preloads?
85
85
+
: // TODO: Keep preloads
78
86
[],
79
87
play: activeTrack && isPlaying ? { audioId: activeTrack.id } : undefined,
80
88
},
+1
src/scripts/processor/artwork/types.d.ts
reviewed
···
11
11
stream?: ReadableStream;
12
12
tags?: Tags;
13
13
urls?: Urls;
14
14
+
variousArtists?: boolean;
14
15
};
15
16
16
17
// export type State = {
+46
-8
src/scripts/processor/artwork/worker.ts
reviewed
···
35
35
////////////////////////////////////////////
36
36
// 🛠️
37
37
////////////////////////////////////////////
38
38
+
function escapeLucene(str: string) {
39
39
+
return [].map
40
40
+
.call(str, (char) => {
41
41
+
if (
42
42
+
char === "+" ||
43
43
+
char === "-" ||
44
44
+
char === "&" ||
45
45
+
char === "|" ||
46
46
+
char === "!" ||
47
47
+
char === "(" ||
48
48
+
char === ")" ||
49
49
+
char === "{" ||
50
50
+
char === "}" ||
51
51
+
char === "[" ||
52
52
+
char === "]" ||
53
53
+
char === "^" ||
54
54
+
char === '"' ||
55
55
+
char === "~" ||
56
56
+
char === "*" ||
57
57
+
char === "?" ||
58
58
+
char === ":" ||
59
59
+
char === "\\" ||
60
60
+
char === "/"
61
61
+
)
62
62
+
return "\\" + char;
63
63
+
else return char;
64
64
+
})
65
65
+
.join("");
66
66
+
}
67
67
+
38
68
async function lastFm(req: ArtworkRequest): Promise<Artwork[]> {
39
69
if (!navigator.onLine) return [];
40
70
···
77
107
if (!navigator.onLine) return [];
78
108
if (!album && !artist) return [];
79
109
80
80
-
// TODO
81
81
-
const variousArtists = false;
82
82
-
83
83
-
const query = `release:"${album}"` + (variousArtists ? `` : ` AND artist:"${artist}"`);
110
110
+
const query =
111
111
+
`release:"${escapeLucene(album || "")}"` +
112
112
+
(req.variousArtists ? `` : ` AND artistname:"${escapeLucene(artist || "")}"`);
84
113
const encodedQuery = encodeURIComponent(query);
85
114
86
115
return await fetch(`https://musicbrainz.org/ws/2/release/?query=${encodedQuery}&fmt=json`)
87
116
.then((r) => r.json())
88
88
-
.then((r) => musicBrainzCover(r.releases))
117
117
+
.then((r) => {
118
118
+
if (r.releases.length === 0 && !req.variousArtists) {
119
119
+
return musicBrainz({ ...req, variousArtists: true });
120
120
+
} else {
121
121
+
return musicBrainzCover(r.releases, req);
122
122
+
}
123
123
+
})
89
124
.catch((err) => {
90
125
console.error(err);
91
126
return [];
92
127
});
93
128
}
94
129
95
95
-
async function musicBrainzCover(remainingReleases: any[]): Promise<Artwork[]> {
130
130
+
async function musicBrainzCover(remainingReleases: any[], req: ArtworkRequest): Promise<Artwork[]> {
96
131
const release = remainingReleases[0];
97
132
if (!release) return [];
98
133
134
134
+
const credit = release?.["artist-credit"]?.[0]?.name;
135
135
+
if (req.variousArtists && credit !== "Various Artists" && credit !== req.tags?.artist) return [];
136
136
+
99
137
return await fetch(`https://coverartarchive.org/release/${release.id}/front-500`)
100
138
.then((r) => r.blob())
101
139
.then(async (b) => {
102
140
if (b.type.startsWith("image/")) {
103
141
return [{ bytes: await b.arrayBuffer().then((buf) => new Uint8Array(buf)), mime: b.type }];
104
142
} else {
105
105
-
return musicBrainzCover(remainingReleases.slice(1));
143
143
+
return musicBrainzCover(remainingReleases.slice(1), req);
106
144
}
107
145
})
108
146
.catch((err) => {
109
147
console.error(err);
110
110
-
return musicBrainzCover(remainingReleases.slice(1));
148
148
+
return musicBrainzCover(remainingReleases.slice(1), req);
111
149
});
112
150
}
113
151