Skip to content

Commit 067f8bc

Browse files
authored
Merge pull request #2 from ChocoMeow/beta
Vocard Dashboard v2.0.1 Update: bug fixes, and code clean-up
2 parents a3987d7 + 36c8faf commit 067f8bc

File tree

6 files changed

+255
-122
lines changed

6 files changed

+255
-122
lines changed

assets/js/action.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ function buildPlaylistHtml(dataId, playlist, type) {
172172
if (decodedTracks.length > 0) {
173173
let trackImagesHtml = `<div class="image">`;
174174
decodedTracks.slice(0, 4).forEach((track) => {
175-
trackImagesHtml += `<img src="${track.imageUrl}" alt="">`;
175+
trackImagesHtml += `<img src="${track.artworkUrl}" alt="">`;
176176
});
177177
trackImagesHtml += `</div>`;
178178
return trackImagesHtml;
@@ -254,7 +254,7 @@ function buildTrackRowHtml(index, track) {
254254
<div class="left">
255255
<span>${index}</span>
256256
<img src="${
257-
track.imageUrl
257+
track.artworkUrl
258258
}" onerror="this.src='/static/img/notFound.png'" alt="">
259259
<div class="track-info">
260260
<p class="title">${track.title}</p>
@@ -271,7 +271,7 @@ function buildTrackRowHtml(index, track) {
271271
function buildTrackCardHtml(track) {
272272
return `<div class="card" data-id="${track.trackId}" data-type="track">
273273
<div class="thumbnail">
274-
<img src="${track.imageUrl}" onerror="this.src='/static/img/notFound.png'" alt="">
274+
<img src="${track.artworkUrl}" onerror="this.src='/static/img/notFound.png'" alt="">
275275
<span class="material-symbols-outlined filled">play_circle</span>
276276
</div>
277277
<div class="track-info">
@@ -298,7 +298,7 @@ function buildQueueTrackHtml(track) {
298298
return `<div class="track" data-id="${track.trackId}">
299299
<div class="left">
300300
<div class="thumbnail">
301-
<img class="track-img" src="${track.imageUrl}" onerror="this.src='/static/img/notFound.png'" alt="">
301+
<img class="track-img" src="${track.artworkUrl}" onerror="this.src='/static/img/notFound.png'" alt="">
302302
<img class="requester-img" src="${track?.requester?.avatarUrl}" alt="">
303303
</div>
304304
<div class="track-info">
@@ -2065,7 +2065,7 @@ $(document).ready(function () {
20652065
<div class="track-header">
20662066
<div class="left">
20672067
<img src="${
2068-
decodedTrack.imageUrl
2068+
decodedTrack.artworkUrl
20692069
}" onerror="this.src='/static/img/notFound.png'" alt="">
20702070
<div class="track-info">
20712071
<p class="small">${localeTexts.track.track}</p>

assets/js/objects.js

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -172,15 +172,15 @@ class Track {
172172
this.author = object.author;
173173
this.source = object.source;
174174
this.identifier = object.identifier;
175-
if (object.thumbnail == undefined) {
175+
if (object.artworkUrl == undefined) {
176176
if (this.source == "youtube") {
177-
this.imageUrl = `https://img.youtube.com/vi/${this.identifier}/hqdefault.jpg`;
177+
this.artworkUrl = `https://img.youtube.com/vi/${this.identifier}/hqdefault.jpg`;
178178
} else {
179-
this.imageUrl =
179+
this.artworkUrl =
180180
"https://cdn.discordapp.com/attachments/674788144931012638/823086668445384704/eq-dribbble.gif";
181181
}
182182
} else {
183-
this.imageUrl = object.thumbnail;
183+
this.artworkUrl = object.artworkUrl;
184184
}
185185
this.isStream = object.isStream;
186186
this.length = Number(object.length);
@@ -443,7 +443,7 @@ const methods = {
443443
<div class="track-row">
444444
<div class="left">
445445
<img src="${
446-
track.imageUrl
446+
track.artworkUrl
447447
}" onerror="this.src='/static/img/notFound.png'" alt="">
448448
<div class="track-info">
449449
<p class="title">${track.title}</p>
@@ -1264,9 +1264,9 @@ class Player {
12641264
"disabled",
12651265
this.currentTrack?.isStream ? "disabled" : false
12661266
);
1267-
1268-
this.updateImage("#controller-img", this.currentTrack?.imageUrl);
1269-
this.updateImage("#now-playing-img", this.currentTrack?.imageUrl);
1267+
1268+
this.updateImage("#controller-img", this.currentTrack?.artworkUrl);
1269+
this.updateImage("#now-playing-img", this.currentTrack?.artworkUrl);
12701270
this.updateImage(
12711271
"#now-playing-requester-img",
12721272
this.currentTrack?.requester.avatarUrl
@@ -1433,10 +1433,10 @@ class Player {
14331433
: $("#repeat-btn").addClass("active");
14341434
}
14351435

1436-
updateImage(selector, imageUrl) {
1437-
if (imageUrl) {
1436+
updateImage(selector, artworkUrl) {
1437+
if (artworkUrl) {
14381438
$(selector).fadeIn(200, function () {
1439-
$(this).attr("src", imageUrl);
1439+
$(this).attr("src", artworkUrl);
14401440
});
14411441
} else {
14421442
$(selector).fadeOut(200, function () {

assets/js/transformer.js

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
class DataReader {
2+
constructor(base64Str) {
3+
const binaryStr = atob(base64Str);
4+
const byteArray = Uint8Array.from(binaryStr, (c) => c.charCodeAt(0));
5+
this.buffer = byteArray.buffer;
6+
this.view = new DataView(this.buffer);
7+
this.position = 0;
8+
this.mark = null;
9+
}
10+
11+
get remaining() {
12+
return this.buffer.byteLength - this.position;
13+
}
14+
15+
mark() {
16+
this.mark = this.position;
17+
}
18+
19+
rewind() {
20+
if (this.mark === null) {
21+
throw new Error("Cannot rewind buffer without a marker!");
22+
}
23+
if (this.mark < 0) {
24+
throw new Error("Cannot rewind buffer to a negative position!");
25+
}
26+
this.position = this.mark;
27+
this.mark = null;
28+
}
29+
30+
readByte() {
31+
if (this.position >= this.buffer.byteLength) {
32+
throw new Error("End of buffer");
33+
}
34+
const byte = this.view.getUint8(this.position);
35+
this.position += 1;
36+
return byte;
37+
}
38+
39+
readBoolean() {
40+
return this.readByte() !== 0;
41+
}
42+
43+
readUnsignedShort() {
44+
if (this.position + 2 > this.buffer.byteLength) {
45+
throw new Error("End of buffer");
46+
}
47+
const value = this.view.getUint16(this.position, false); // big-endian
48+
this.position += 2;
49+
return value;
50+
}
51+
52+
readInt() {
53+
if (this.position + 4 > this.buffer.byteLength) {
54+
throw new Error("End of buffer");
55+
}
56+
const value = this.view.getInt32(this.position, false); // big-endian
57+
this.position += 4;
58+
return value;
59+
}
60+
61+
readLong() {
62+
if (this.position + 8 > this.buffer.byteLength) {
63+
throw new Error("End of buffer");
64+
}
65+
const value = this.view.getBigUint64(this.position, false); // big-endian
66+
this.position += 8;
67+
return value;
68+
}
69+
70+
readNullableUtf(utfm = false) {
71+
const exists = this.readBoolean();
72+
if (!exists) {
73+
return null;
74+
}
75+
return utfm ? this.readUtfm() : this.readUtf();
76+
}
77+
78+
readUtf() {
79+
const length = this.readUnsignedShort();
80+
if (this.position + length > this.buffer.byteLength) {
81+
throw new Error("End of buffer");
82+
}
83+
const bytes = new Uint8Array(this.buffer, this.position, length);
84+
this.position += length;
85+
const decoder = new TextDecoder("utf-8");
86+
return decoder.decode(bytes);
87+
}
88+
89+
readUtfm() {
90+
const length = this.readUnsignedShort();
91+
if (this.position + length > this.buffer.byteLength) {
92+
throw new Error("End of buffer");
93+
}
94+
const bytes = new Uint8Array(this.buffer, this.position, length);
95+
this.position += length;
96+
return readUtfm(length, bytes);
97+
}
98+
}
99+
100+
function decodeProbeInfo(reader) {
101+
const probeInfo = reader.readUtf();
102+
return { probe_info: probeInfo };
103+
}
104+
105+
function decodeLavasrcFields(reader) {
106+
if (reader.remaining <= 8) {
107+
return {};
108+
}
109+
110+
const albumName = reader.readNullableUtf();
111+
const albumUrl = reader.readNullableUtf();
112+
const artistUrl = reader.readNullableUtf();
113+
const artistArtworkUrl = reader.readNullableUtf();
114+
const previewUrl = reader.readNullableUtf();
115+
const isPreview = reader.readBoolean();
116+
117+
return {
118+
album_name: albumName,
119+
album_url: albumUrl,
120+
artist_url: artistUrl,
121+
artist_artwork_url: artistArtworkUrl,
122+
preview_url: previewUrl,
123+
is_preview: isPreview,
124+
};
125+
}
126+
127+
const DEFAULT_DECODER_MAPPING = {
128+
http: decodeProbeInfo,
129+
local: decodeProbeInfo,
130+
deezer: decodeLavasrcFields,
131+
spotify: decodeLavasrcFields,
132+
applemusic: decodeLavasrcFields,
133+
};
134+
135+
function readUtfm(utfLen, utfBytes) {
136+
const chars = [];
137+
let count = 0;
138+
139+
while (count < utfLen) {
140+
const char = utfBytes[count] & 0xff;
141+
if (char > 127) {
142+
break;
143+
}
144+
count += 1;
145+
chars.push(String.fromCharCode(char));
146+
}
147+
148+
while (count < utfLen) {
149+
const char = utfBytes[count] & 0xff;
150+
const shift = char >> 4;
151+
152+
if (shift >= 0 && shift <= 7) {
153+
count += 1;
154+
chars.push(String.fromCharCode(char));
155+
} else if (shift >= 12 && shift <= 13) {
156+
if (count + 2 > utfLen) {
157+
throw new Error("malformed input: partial character at end");
158+
}
159+
const char2 = utfBytes[count + 1];
160+
if ((char2 & 0xc0) !== 0x80) {
161+
throw new Error(`malformed input around byte ${count + 1}`);
162+
}
163+
const charShift = ((char & 0x1f) << 6) | (char2 & 0x3f);
164+
chars.push(String.fromCharCode(charShift));
165+
count += 2;
166+
} else if (shift === 14) {
167+
if (count + 3 > utfLen) {
168+
throw new Error("malformed input: partial character at end");
169+
}
170+
const char2 = utfBytes[count + 1];
171+
const char3 = utfBytes[count + 2];
172+
if ((char2 & 0xc0) !== 0x80 || (char3 & 0xc0) !== 0x80) {
173+
throw new Error(`malformed input around byte ${count + 1}`);
174+
}
175+
const charShift =
176+
((char & 0x0f) << 12) | ((char2 & 0x3f) << 6) | (char3 & 0x3f);
177+
chars.push(String.fromCharCode(charShift));
178+
count += 3;
179+
} else {
180+
throw new Error(`malformed input around byte ${count}`);
181+
}
182+
}
183+
return chars.join("");
184+
}
185+
186+
function readTrackCommon(reader) {
187+
const title = reader.readUtfm();
188+
const author = reader.readUtfm();
189+
const length = reader.readLong();
190+
const identifier = reader.readUtf();
191+
const isStream = reader.readBoolean();
192+
const uri = reader.readNullableUtf();
193+
return [title, author, length, identifier, isStream, uri];
194+
}
195+
196+
function decode(trackId, requester, sourceDecoders = {}) {
197+
const decoders = { ...DEFAULT_DECODER_MAPPING, ...sourceDecoders };
198+
const reader = new DataReader(trackId);
199+
200+
const flags = (reader.readInt() & 0xc0000000) >>> 30;
201+
const version = (flags & 1) !== 0 ? reader.readByte() : 1;
202+
203+
const [title, author, length, identifier, isStream, uri] =
204+
readTrackCommon(reader);
205+
206+
let extraFields = {};
207+
if (version === 3) {
208+
extraFields.artworkUrl = reader.readNullableUtf();
209+
extraFields.isrc = reader.readNullableUtf();
210+
}
211+
212+
const source = reader.readUtf();
213+
214+
let sourceSpecificFields = {};
215+
if (source in decoders) {
216+
sourceSpecificFields = decoders[source](reader);
217+
}
218+
219+
const position = reader.readLong();
220+
221+
return new Track({
222+
trackId,
223+
title,
224+
author,
225+
length: parseInt(length),
226+
identifier,
227+
isStream,
228+
uri,
229+
isSeekable: !isStream,
230+
sourceName: source,
231+
position: parseInt(position),
232+
...extraFields,
233+
}, requester);
234+
}

0 commit comments

Comments
 (0)