@lucaismyname/ginger 0.0.13 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -0
- package/dist/analyzer/liveAudioGraph.d.ts +14 -0
- package/dist/analyzer/liveAudioGraph.d.ts.map +1 -0
- package/dist/analyzer/useGingerLiveAnalyzer.d.ts +21 -0
- package/dist/analyzer/useGingerLiveAnalyzer.d.ts.map +1 -0
- package/dist/client.cjs +1 -1
- package/dist/client.js +21 -18
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -18
- package/dist/internal/fft.d.ts +7 -0
- package/dist/internal/fft.d.ts.map +1 -0
- package/dist/internal/fft.test.d.ts +2 -0
- package/dist/internal/fft.test.d.ts.map +1 -0
- package/dist/useSeekDrag-Cc0wpC70.cjs +2 -0
- package/dist/useSeekDrag-Cc0wpC70.cjs.map +1 -0
- package/dist/useSeekDrag-DkAJvf8H.js +289 -0
- package/dist/useSeekDrag-DkAJvf8H.js.map +1 -0
- package/dist/waveform/analyzeAudioFile.d.ts +31 -0
- package/dist/waveform/analyzeAudioFile.d.ts.map +1 -0
- package/dist/waveform/analyzeAudioFile.test.d.ts +2 -0
- package/dist/waveform/analyzeAudioFile.test.d.ts.map +1 -0
- package/dist/waveform/index.cjs +1 -1
- package/dist/waveform/index.cjs.map +1 -1
- package/dist/waveform/index.d.ts +4 -0
- package/dist/waveform/index.d.ts.map +1 -1
- package/dist/waveform/index.js +207 -23
- package/dist/waveform/index.js.map +1 -1
- package/dist/waveform/useAudioFileAnalysis.d.ts +8 -0
- package/dist/waveform/useAudioFileAnalysis.d.ts.map +1 -0
- package/package.json +1 -1
- package/dist/useSeekDrag-DBzoym0-.cjs +0 -2
- package/dist/useSeekDrag-DBzoym0-.cjs.map +0 -1
- package/dist/useSeekDrag-jLsYA-uG.js +0 -174
- package/dist/useSeekDrag-jLsYA-uG.js.map +0 -1
package/dist/waveform/index.js
CHANGED
|
@@ -1,42 +1,226 @@
|
|
|
1
|
-
import { useState as
|
|
2
|
-
function
|
|
3
|
-
const [
|
|
1
|
+
import { useState as x, useEffect as y } from "react";
|
|
2
|
+
function D(e, t = 64) {
|
|
3
|
+
const [r, a] = x({
|
|
4
4
|
peaks: [],
|
|
5
5
|
isLoading: !1,
|
|
6
6
|
error: null
|
|
7
7
|
});
|
|
8
|
-
return
|
|
9
|
-
if (!
|
|
10
|
-
|
|
8
|
+
return y(() => {
|
|
9
|
+
if (!e) {
|
|
10
|
+
a({ peaks: [], isLoading: !1, error: null });
|
|
11
11
|
return;
|
|
12
12
|
}
|
|
13
|
-
let
|
|
14
|
-
return
|
|
13
|
+
let o = !1;
|
|
14
|
+
return a((n) => ({ ...n, isLoading: !0, error: null })), (async () => {
|
|
15
15
|
try {
|
|
16
|
-
const
|
|
17
|
-
if (!
|
|
18
|
-
const
|
|
19
|
-
for (let
|
|
16
|
+
const n = await fetch(e);
|
|
17
|
+
if (!n.ok) throw new Error(`Fetch failed: ${n.status} ${n.statusText}`);
|
|
18
|
+
const l = await n.arrayBuffer(), s = new AudioContext(), f = (await s.decodeAudioData(l)).getChannelData(0), w = Math.max(1, Math.floor(f.length / t)), u = [];
|
|
19
|
+
for (let h = 0; h < t; h += 1) {
|
|
20
20
|
let i = 0;
|
|
21
|
-
const d =
|
|
22
|
-
for (let
|
|
23
|
-
i = Math.max(i, Math.abs(
|
|
24
|
-
|
|
21
|
+
const d = h * w, m = Math.min(f.length, d + w);
|
|
22
|
+
for (let c = d; c < m; c += 1)
|
|
23
|
+
i = Math.max(i, Math.abs(f[c] ?? 0));
|
|
24
|
+
u.push(i);
|
|
25
25
|
}
|
|
26
|
-
await
|
|
27
|
-
} catch (
|
|
28
|
-
|
|
26
|
+
await s.close(), o || a({ peaks: u, isLoading: !1, error: null });
|
|
27
|
+
} catch (n) {
|
|
28
|
+
o || a({
|
|
29
29
|
peaks: [],
|
|
30
30
|
isLoading: !1,
|
|
31
|
-
error:
|
|
31
|
+
error: n instanceof Error ? n.message : "Failed to decode peaks"
|
|
32
32
|
});
|
|
33
33
|
}
|
|
34
34
|
})(), () => {
|
|
35
|
-
|
|
35
|
+
o = !0;
|
|
36
36
|
};
|
|
37
|
-
}, [
|
|
37
|
+
}, [t, e]), r;
|
|
38
|
+
}
|
|
39
|
+
function A(e, t) {
|
|
40
|
+
const r = e.length;
|
|
41
|
+
if (r !== t.length || r < 2 || r & r - 1)
|
|
42
|
+
throw new Error("fftInPlace: length must be equal powers of 2 >= 2");
|
|
43
|
+
let a = 0;
|
|
44
|
+
for (let o = 0; o < r - 1; o += 1) {
|
|
45
|
+
if (o < a) {
|
|
46
|
+
const l = e[o], s = t[o];
|
|
47
|
+
e[o] = e[a], t[o] = t[a], e[a] = l, t[a] = s;
|
|
48
|
+
}
|
|
49
|
+
let n = r >> 1;
|
|
50
|
+
for (; n <= a; )
|
|
51
|
+
a -= n, n >>= 1;
|
|
52
|
+
a += n;
|
|
53
|
+
}
|
|
54
|
+
for (let o = 2; o <= r; o <<= 1) {
|
|
55
|
+
const n = -2 * Math.PI / o, l = Math.cos(n), s = Math.sin(n);
|
|
56
|
+
for (let g = 0; g < r; g += o) {
|
|
57
|
+
let f = 1, w = 0;
|
|
58
|
+
const u = o >> 1;
|
|
59
|
+
for (let h = 0; h < u; h += 1) {
|
|
60
|
+
const i = g + h, d = i + u, m = f * e[d] - w * t[d], c = f * t[d] + w * e[d];
|
|
61
|
+
e[d] = e[i] - m, t[d] = t[i] - c, e[i] = e[i] + m, t[i] = t[i] + c;
|
|
62
|
+
const M = f * l - w * s, p = f * s + w * l;
|
|
63
|
+
f = M, w = p;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function F(e) {
|
|
69
|
+
const t = e.length;
|
|
70
|
+
if (t < 2 || t & t - 1)
|
|
71
|
+
throw new Error("realFftMagnitudes: length must be a power of 2 >= 2");
|
|
72
|
+
const r = new Float64Array(t), a = new Float64Array(t);
|
|
73
|
+
for (let n = 0; n < t; n += 1) r[n] = e[n];
|
|
74
|
+
A(r, a);
|
|
75
|
+
const o = new Float64Array(t >> 1);
|
|
76
|
+
for (let n = 0; n < t >> 1; n += 1)
|
|
77
|
+
o[n] = Math.hypot(r[n], a[n]);
|
|
78
|
+
return o;
|
|
79
|
+
}
|
|
80
|
+
function L(e) {
|
|
81
|
+
const t = new Float64Array(e);
|
|
82
|
+
if (e === 1)
|
|
83
|
+
return t[0] = 1, t;
|
|
84
|
+
const r = e - 1;
|
|
85
|
+
for (let a = 0; a < e; a += 1)
|
|
86
|
+
t[a] = 0.5 * (1 - Math.cos(2 * Math.PI * a / r));
|
|
87
|
+
return t;
|
|
88
|
+
}
|
|
89
|
+
function C(e) {
|
|
90
|
+
const t = 2 ** Math.round(Math.log2(e));
|
|
91
|
+
return Math.min(8192, Math.max(32, t));
|
|
92
|
+
}
|
|
93
|
+
function k(e) {
|
|
94
|
+
const { numberOfChannels: t, length: r } = e;
|
|
95
|
+
if (t === 1)
|
|
96
|
+
return e.getChannelData(0);
|
|
97
|
+
const a = new Float32Array(r), o = 1 / t;
|
|
98
|
+
for (let n = 0; n < t; n += 1) {
|
|
99
|
+
const l = e.getChannelData(n);
|
|
100
|
+
for (let s = 0; s < r; s += 1)
|
|
101
|
+
a[s] += l[s] * o;
|
|
102
|
+
}
|
|
103
|
+
return a;
|
|
104
|
+
}
|
|
105
|
+
function B(e, t) {
|
|
106
|
+
if (t === "mix") return k(e);
|
|
107
|
+
const r = Math.max(0, Math.min(e.numberOfChannels - 1, t));
|
|
108
|
+
return e.getChannelData(r);
|
|
109
|
+
}
|
|
110
|
+
function E(e, t, r) {
|
|
111
|
+
const a = [], o = e.length;
|
|
112
|
+
if (o === 0) {
|
|
113
|
+
for (let l = 0; l < t; l += 1)
|
|
114
|
+
a.push(Array.from({ length: r }, () => 0));
|
|
115
|
+
return a;
|
|
116
|
+
}
|
|
117
|
+
const n = o / t;
|
|
118
|
+
for (let l = 0; l < t; l += 1) {
|
|
119
|
+
const s = [], g = Math.floor(l * n), f = Math.floor((l + 1) * n), u = Math.max(1, f - g) / r;
|
|
120
|
+
for (let h = 0; h < r; h += 1) {
|
|
121
|
+
const i = Math.floor(g + h * u), d = Math.min(f, Math.floor(g + (h + 1) * u));
|
|
122
|
+
let m = 0;
|
|
123
|
+
for (let c = i; c < d; c += 1)
|
|
124
|
+
m = Math.max(m, Math.abs(e[c] ?? 0));
|
|
125
|
+
s.push(m);
|
|
126
|
+
}
|
|
127
|
+
a.push(s);
|
|
128
|
+
}
|
|
129
|
+
return a;
|
|
130
|
+
}
|
|
131
|
+
function S(e, t, r, a) {
|
|
132
|
+
const o = [];
|
|
133
|
+
let n = 1e-12;
|
|
134
|
+
const l = e.length, s = C(r), g = s >> 1, f = Math.min(a, g), w = L(s);
|
|
135
|
+
if (l < s) {
|
|
136
|
+
for (let u = 0; u < t; u += 1)
|
|
137
|
+
o.push(Array.from({ length: f }, () => 0));
|
|
138
|
+
return { rows: o, maxMag: 1 };
|
|
139
|
+
}
|
|
140
|
+
for (let u = 0; u < t; u += 1) {
|
|
141
|
+
const h = t <= 1 ? 0 : Math.min(Math.floor(u * (l - s) / (t - 1)), l - s), i = new Float64Array(s);
|
|
142
|
+
for (let c = 0; c < s; c += 1)
|
|
143
|
+
i[c] = (e[h + c] ?? 0) * (w[c] ?? 0);
|
|
144
|
+
const d = F(i), m = [];
|
|
145
|
+
for (let c = 0; c < f; c += 1) {
|
|
146
|
+
const M = d[c] ?? 0;
|
|
147
|
+
m.push(M), n = Math.max(n, M);
|
|
148
|
+
}
|
|
149
|
+
o.push(m);
|
|
150
|
+
}
|
|
151
|
+
return { rows: o, maxMag: n };
|
|
152
|
+
}
|
|
153
|
+
function b(e, t = {}) {
|
|
154
|
+
const r = Math.max(1, t.timeSlices ?? 128), a = Math.max(1, t.samplesPerSlice ?? 8), o = !!t.spectrogram, n = t.fftSize ?? 1024, l = Math.max(1, t.frequencyBins ?? 256), s = t.channel ?? 0, g = B(e, s), f = E(g, r, a), w = {
|
|
155
|
+
duration: e.duration,
|
|
156
|
+
sampleRate: e.sampleRate,
|
|
157
|
+
amplitudeGrid: f
|
|
158
|
+
};
|
|
159
|
+
if (o) {
|
|
160
|
+
const { rows: u, maxMag: h } = S(g, r, n, l), i = h > 0 ? 1 / h : 1;
|
|
161
|
+
w.spectrogram = u.map((d) => d.map((m) => m * i));
|
|
162
|
+
}
|
|
163
|
+
return w;
|
|
164
|
+
}
|
|
165
|
+
async function z(e, t = {}) {
|
|
166
|
+
const r = await fetch(e);
|
|
167
|
+
if (!r.ok)
|
|
168
|
+
throw new Error(`Fetch failed: ${r.status} ${r.statusText}`);
|
|
169
|
+
const a = await r.arrayBuffer(), o = window.AudioContext ?? window.webkitAudioContext;
|
|
170
|
+
if (!o)
|
|
171
|
+
throw new Error("Web Audio API is not available");
|
|
172
|
+
const n = new o();
|
|
173
|
+
try {
|
|
174
|
+
const l = await n.decodeAudioData(a.slice(0));
|
|
175
|
+
return b(l, t);
|
|
176
|
+
} finally {
|
|
177
|
+
await n.close();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function I(e, t = {}) {
|
|
181
|
+
const [r, a] = x({
|
|
182
|
+
data: null,
|
|
183
|
+
isLoading: !1,
|
|
184
|
+
error: null
|
|
185
|
+
});
|
|
186
|
+
return y(() => {
|
|
187
|
+
if (!e) {
|
|
188
|
+
a({ data: null, isLoading: !1, error: null });
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (typeof window > "u") {
|
|
192
|
+
a({ data: null, isLoading: !1, error: null });
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
let o = !1;
|
|
196
|
+
return a((n) => ({ ...n, isLoading: !0, error: null })), (async () => {
|
|
197
|
+
try {
|
|
198
|
+
const n = await z(e, t);
|
|
199
|
+
o || a({ data: n, isLoading: !1, error: null });
|
|
200
|
+
} catch (n) {
|
|
201
|
+
o || a({
|
|
202
|
+
data: null,
|
|
203
|
+
isLoading: !1,
|
|
204
|
+
error: n instanceof Error ? n.message : "Failed to analyze audio file"
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
})(), () => {
|
|
208
|
+
o = !0;
|
|
209
|
+
};
|
|
210
|
+
}, [
|
|
211
|
+
e,
|
|
212
|
+
t.timeSlices,
|
|
213
|
+
t.samplesPerSlice,
|
|
214
|
+
t.spectrogram,
|
|
215
|
+
t.fftSize,
|
|
216
|
+
t.frequencyBins,
|
|
217
|
+
t.channel
|
|
218
|
+
]), r;
|
|
38
219
|
}
|
|
39
220
|
export {
|
|
40
|
-
|
|
221
|
+
b as analyzeAudioBuffer,
|
|
222
|
+
z as analyzeAudioFile,
|
|
223
|
+
I as useAudioFileAnalysis,
|
|
224
|
+
D as useAudioPeaks
|
|
41
225
|
};
|
|
42
226
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/waveform/useAudioPeaks.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\nexport type UseAudioPeaksState = {\n peaks: number[];\n isLoading: boolean;\n error: string | null;\n};\n\nexport function useAudioPeaks(fileUrl: string | null | undefined, buckets = 64): UseAudioPeaksState {\n const [state, setState] = useState<UseAudioPeaksState>({\n peaks: [],\n isLoading: false,\n error: null,\n });\n\n useEffect(() => {\n if (!fileUrl) {\n setState({ peaks: [], isLoading: false, error: null });\n return;\n }\n let cancelled = false;\n setState((prev) => ({ ...prev, isLoading: true, error: null }));\n void (async () => {\n try {\n const response = await fetch(fileUrl);\n if (!response.ok) throw new Error(`Fetch failed: ${response.status} ${response.statusText}`);\n const buffer = await response.arrayBuffer();\n const audioContext = new AudioContext();\n const audioBuffer = await audioContext.decodeAudioData(buffer);\n const channel = audioBuffer.getChannelData(0);\n const step = Math.max(1, Math.floor(channel.length / buckets));\n const peaks: number[] = [];\n for (let i = 0; i < buckets; i += 1) {\n let max = 0;\n const start = i * step;\n const end = Math.min(channel.length, start + step);\n for (let j = start; j < end; j += 1) {\n max = Math.max(max, Math.abs(channel[j] ?? 0));\n }\n peaks.push(max);\n }\n await audioContext.close();\n if (!cancelled) {\n setState({ peaks, isLoading: false, error: null });\n }\n } catch (error) {\n if (!cancelled) {\n setState({\n peaks: [],\n isLoading: false,\n error: error instanceof Error ? error.message : \"Failed to decode peaks\",\n });\n }\n }\n })();\n\n return () => {\n cancelled = true;\n };\n }, [buckets, fileUrl]);\n\n return state;\n}\n"],"names":["useAudioPeaks","fileUrl","buckets","state","setState","useState","useEffect","cancelled","prev","response","buffer","audioContext","channel","step","peaks","i","max","start","end","j","error"],"mappings":";AAQO,SAASA,EAAcC,GAAoCC,IAAU,IAAwB;AAClG,QAAM,CAACC,GAAOC,CAAQ,IAAIC,EAA6B;AAAA,IACrD,OAAO,CAAA;AAAA,IACP,WAAW;AAAA,IACX,OAAO;AAAA,EAAA,CACR;AAED,SAAAC,EAAU,MAAM;AACd,QAAI,CAACL,GAAS;AACZ,MAAAG,EAAS,EAAE,OAAO,CAAA,GAAI,WAAW,IAAO,OAAO,MAAM;AACrD;AAAA,IACF;AACA,QAAIG,IAAY;AAChB,WAAAH,EAAS,CAACI,OAAU,EAAE,GAAGA,GAAM,WAAW,IAAM,OAAO,KAAA,EAAO,IACxD,YAAY;AAChB,UAAI;AACF,cAAMC,IAAW,MAAM,MAAMR,CAAO;AACpC,YAAI,CAACQ,EAAS,GAAI,OAAM,IAAI,MAAM,iBAAiBA,EAAS,MAAM,IAAIA,EAAS,UAAU,EAAE;AAC3F,cAAMC,IAAS,MAAMD,EAAS,YAAA,GACxBE,IAAe,IAAI,aAAA,GAEnBC,KADc,MAAMD,EAAa,gBAAgBD,CAAM,GACjC,eAAe,CAAC,GACtCG,IAAO,KAAK,IAAI,GAAG,KAAK,MAAMD,EAAQ,SAASV,CAAO,CAAC,GACvDY,IAAkB,CAAA;AACxB,iBAASC,IAAI,GAAGA,IAAIb,GAASa,KAAK,GAAG;AACnC,cAAIC,IAAM;AACV,gBAAMC,IAAQF,IAAIF,GACZK,IAAM,KAAK,IAAIN,EAAQ,QAAQK,IAAQJ,CAAI;AACjD,mBAASM,IAAIF,GAAOE,IAAID,GAAKC,KAAK;AAChC,YAAAH,IAAM,KAAK,IAAIA,GAAK,KAAK,IAAIJ,EAAQO,CAAC,KAAK,CAAC,CAAC;AAE/C,UAAAL,EAAM,KAAKE,CAAG;AAAA,QAChB;AACA,cAAML,EAAa,MAAA,GACdJ,KACHH,EAAS,EAAE,OAAAU,GAAO,WAAW,IAAO,OAAO,MAAM;AAAA,MAErD,SAASM,GAAO;AACd,QAAKb,KACHH,EAAS;AAAA,UACP,OAAO,CAAA;AAAA,UACP,WAAW;AAAA,UACX,OAAOgB,aAAiB,QAAQA,EAAM,UAAU;AAAA,QAAA,CACjD;AAAA,MAEL;AAAA,IACF,GAAA,GAEO,MAAM;AACX,MAAAb,IAAY;AAAA,IACd;AAAA,EACF,GAAG,CAACL,GAASD,CAAO,CAAC,GAEdE;AACT;"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/waveform/useAudioPeaks.ts","../../src/internal/fft.ts","../../src/waveform/analyzeAudioFile.ts","../../src/waveform/useAudioFileAnalysis.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\nexport type UseAudioPeaksState = {\n peaks: number[];\n isLoading: boolean;\n error: string | null;\n};\n\nexport function useAudioPeaks(fileUrl: string | null | undefined, buckets = 64): UseAudioPeaksState {\n const [state, setState] = useState<UseAudioPeaksState>({\n peaks: [],\n isLoading: false,\n error: null,\n });\n\n useEffect(() => {\n if (!fileUrl) {\n setState({ peaks: [], isLoading: false, error: null });\n return;\n }\n let cancelled = false;\n setState((prev) => ({ ...prev, isLoading: true, error: null }));\n void (async () => {\n try {\n const response = await fetch(fileUrl);\n if (!response.ok) throw new Error(`Fetch failed: ${response.status} ${response.statusText}`);\n const buffer = await response.arrayBuffer();\n const audioContext = new AudioContext();\n const audioBuffer = await audioContext.decodeAudioData(buffer);\n const channel = audioBuffer.getChannelData(0);\n const step = Math.max(1, Math.floor(channel.length / buckets));\n const peaks: number[] = [];\n for (let i = 0; i < buckets; i += 1) {\n let max = 0;\n const start = i * step;\n const end = Math.min(channel.length, start + step);\n for (let j = start; j < end; j += 1) {\n max = Math.max(max, Math.abs(channel[j] ?? 0));\n }\n peaks.push(max);\n }\n await audioContext.close();\n if (!cancelled) {\n setState({ peaks, isLoading: false, error: null });\n }\n } catch (error) {\n if (!cancelled) {\n setState({\n peaks: [],\n isLoading: false,\n error: error instanceof Error ? error.message : \"Failed to decode peaks\",\n });\n }\n }\n })();\n\n return () => {\n cancelled = true;\n };\n }, [buckets, fileUrl]);\n\n return state;\n}\n","/** In-place radix-2 Cooley–Tukey FFT; `length` must be a power of 2 and >= 2. */\nexport function fftInPlace(re: Float64Array, im: Float64Array): void {\n const n = re.length;\n if (n !== im.length || n < 2 || (n & (n - 1)) !== 0) {\n throw new Error(\"fftInPlace: length must be equal powers of 2 >= 2\");\n }\n\n let j = 0;\n for (let i = 0; i < n - 1; i += 1) {\n if (i < j) {\n const tr = re[i]!;\n const ti = im[i]!;\n re[i] = re[j]!;\n im[i] = im[j]!;\n re[j] = tr;\n im[j] = ti;\n }\n let k = n >> 1;\n while (k <= j) {\n j -= k;\n k >>= 1;\n }\n j += k;\n }\n\n for (let len = 2; len <= n; len <<= 1) {\n const ang = (-2 * Math.PI) / len;\n const wlenR = Math.cos(ang);\n const wlenI = Math.sin(ang);\n for (let i = 0; i < n; i += len) {\n let wr = 1;\n let wi = 0;\n const half = len >> 1;\n for (let k = 0; k < half; k += 1) {\n const u = i + k;\n const v = u + half;\n const tr = wr * re[v]! - wi * im[v]!;\n const ti = wr * im[v]! + wi * re[v]!;\n re[v] = re[u]! - tr;\n im[v] = im[u]! - ti;\n re[u] = re[u]! + tr;\n im[u] = im[u]! + ti;\n const nwr = wr * wlenR - wi * wlenI;\n const nwi = wr * wlenI + wi * wlenR;\n wr = nwr;\n wi = nwi;\n }\n }\n }\n}\n\n/** Magnitude spectrum for real input: length `n` (power of 2). Returns `n/2` magnitudes for bins 0..n/2-1. */\nexport function realFftMagnitudes(samples: Float64Array): Float64Array {\n const n = samples.length;\n if (n < 2 || (n & (n - 1)) !== 0) {\n throw new Error(\"realFftMagnitudes: length must be a power of 2 >= 2\");\n }\n const re = new Float64Array(n);\n const im = new Float64Array(n);\n for (let i = 0; i < n; i += 1) re[i] = samples[i]!;\n fftInPlace(re, im);\n const out = new Float64Array(n >> 1);\n for (let k = 0; k < n >> 1; k += 1) {\n out[k] = Math.hypot(re[k]!, im[k]!);\n }\n return out;\n}\n\nexport function hanningWindow(length: number): Float64Array {\n const w = new Float64Array(length);\n if (length === 1) {\n w[0] = 1;\n return w;\n }\n const denom = length - 1;\n for (let i = 0; i < length; i += 1) {\n w[i] = 0.5 * (1 - Math.cos((2 * Math.PI * i) / denom));\n }\n return w;\n}\n\nexport function clampFftSize(n: number): number {\n const p = 2 ** Math.round(Math.log2(n));\n return Math.min(8192, Math.max(32, p));\n}\n","import { clampFftSize, hanningWindow, realFftMagnitudes } from \"../internal/fft\";\n\nexport type AnalyzeAudioFileOptions = {\n /** Number of time rows in the amplitude grid (and spectrogram if enabled). Default 128. */\n timeSlices?: number;\n /** Sub-buckets per time slice for the amplitude grid. Default 8. */\n samplesPerSlice?: number;\n /** When true, include `spectrogram` using windowed FFT per time slice. Default false. */\n spectrogram?: boolean;\n /** FFT length for spectrogram (power of 2). Default 1024. */\n fftSize?: number;\n /** Number of frequency bins to keep per row (first bins; capped by fftSize/2). Default 256. */\n frequencyBins?: number;\n /** Channel index, or `\"mix\"` for equal mix of all channels. Default 0. */\n channel?: number | \"mix\";\n};\n\nexport type AudioFileAnalysis = {\n duration: number;\n sampleRate: number;\n /** Peak amplitudes in [0, 1]: `timeSlices` rows × `samplesPerSlice` columns. */\n amplitudeGrid: number[][];\n /** Optional magnitude spectrogram rows, each length `frequencyBins`, normalized to [0, 1] globally. */\n spectrogram?: number[][];\n};\n\nfunction mixChannels(buffer: AudioBuffer): Float32Array {\n const { numberOfChannels, length } = buffer;\n if (numberOfChannels === 1) {\n return buffer.getChannelData(0);\n }\n const out = new Float32Array(length);\n const scale = 1 / numberOfChannels;\n for (let c = 0; c < numberOfChannels; c += 1) {\n const ch = buffer.getChannelData(c);\n for (let i = 0; i < length; i += 1) {\n out[i] += ch[i]! * scale;\n }\n }\n return out;\n}\n\nfunction getChannel(buffer: AudioBuffer, channel: number | \"mix\"): Float32Array {\n if (channel === \"mix\") return mixChannels(buffer);\n const idx = Math.max(0, Math.min(buffer.numberOfChannels - 1, channel));\n return buffer.getChannelData(idx);\n}\n\nfunction buildAmplitudeGrid(\n channel: Float32Array,\n timeSlices: number,\n samplesPerSlice: number,\n): number[][] {\n const grid: number[][] = [];\n const len = channel.length;\n if (len === 0) {\n for (let t = 0; t < timeSlices; t += 1) {\n grid.push(Array.from({ length: samplesPerSlice }, () => 0));\n }\n return grid;\n }\n\n const segmentLen = len / timeSlices;\n\n for (let t = 0; t < timeSlices; t += 1) {\n const row: number[] = [];\n const segStart = Math.floor(t * segmentLen);\n const segEnd = Math.floor((t + 1) * segmentLen);\n const segLen = Math.max(1, segEnd - segStart);\n const subLen = segLen / samplesPerSlice;\n\n for (let s = 0; s < samplesPerSlice; s += 1) {\n const a = Math.floor(segStart + s * subLen);\n const b = Math.min(segEnd, Math.floor(segStart + (s + 1) * subLen));\n let peak = 0;\n for (let i = a; i < b; i += 1) {\n peak = Math.max(peak, Math.abs(channel[i] ?? 0));\n }\n row.push(peak);\n }\n grid.push(row);\n }\n return grid;\n}\n\nfunction buildSpectrogram(\n channel: Float32Array,\n timeSlices: number,\n fftSize: number,\n frequencyBins: number,\n): { rows: number[][]; maxMag: number } {\n const rows: number[][] = [];\n let maxMag = 1e-12;\n const len = channel.length;\n const n = clampFftSize(fftSize);\n const half = n >> 1;\n const bins = Math.min(frequencyBins, half);\n const window = hanningWindow(n);\n\n if (len < n) {\n for (let t = 0; t < timeSlices; t += 1) {\n rows.push(Array.from({ length: bins }, () => 0));\n }\n return { rows, maxMag: 1 };\n }\n\n for (let t = 0; t < timeSlices; t += 1) {\n const start =\n timeSlices <= 1\n ? 0\n : Math.min(Math.floor((t * (len - n)) / (timeSlices - 1)), len - n);\n const frame = new Float64Array(n);\n for (let i = 0; i < n; i += 1) {\n frame[i] = (channel[start + i] ?? 0) * (window[i] ?? 0);\n }\n const mags = realFftMagnitudes(frame);\n const row: number[] = [];\n for (let k = 0; k < bins; k += 1) {\n const v = mags[k] ?? 0;\n row.push(v);\n maxMag = Math.max(maxMag, v);\n }\n rows.push(row);\n }\n\n return { rows, maxMag };\n}\n\n/**\n * Decodes an `AudioBuffer` into visualization-friendly grids (no network).\n */\nexport function analyzeAudioBuffer(buffer: AudioBuffer, options: AnalyzeAudioFileOptions = {}): AudioFileAnalysis {\n const timeSlices = Math.max(1, options.timeSlices ?? 128);\n const samplesPerSlice = Math.max(1, options.samplesPerSlice ?? 8);\n const wantSpec = Boolean(options.spectrogram);\n const fftSize = options.fftSize ?? 1024;\n const frequencyBins = Math.max(1, options.frequencyBins ?? 256);\n const channel = options.channel ?? 0;\n\n const data = getChannel(buffer, channel);\n const amplitudeGrid = buildAmplitudeGrid(data, timeSlices, samplesPerSlice);\n\n const result: AudioFileAnalysis = {\n duration: buffer.duration,\n sampleRate: buffer.sampleRate,\n amplitudeGrid,\n };\n\n if (wantSpec) {\n const { rows, maxMag } = buildSpectrogram(data, timeSlices, fftSize, frequencyBins);\n const norm = maxMag > 0 ? 1 / maxMag : 1;\n result.spectrogram = rows.map((row) => row.map((v) => v * norm));\n }\n\n return result;\n}\n\n/**\n * Fetches a URL, decodes audio to an `AudioBuffer`, runs {@link analyzeAudioBuffer}, then closes the temporary `AudioContext`.\n */\nexport async function analyzeAudioFile(\n fileUrl: string,\n options: AnalyzeAudioFileOptions = {},\n): Promise<AudioFileAnalysis> {\n const response = await fetch(fileUrl);\n if (!response.ok) {\n throw new Error(`Fetch failed: ${response.status} ${response.statusText}`);\n }\n const raw = await response.arrayBuffer();\n const Context = window.AudioContext ?? (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n if (!Context) {\n throw new Error(\"Web Audio API is not available\");\n }\n const audioContext = new Context();\n try {\n const buffer = await audioContext.decodeAudioData(raw.slice(0));\n return analyzeAudioBuffer(buffer, options);\n } finally {\n await audioContext.close();\n }\n}\n","import { useEffect, useState } from \"react\";\nimport { analyzeAudioFile, type AnalyzeAudioFileOptions, type AudioFileAnalysis } from \"./analyzeAudioFile\";\n\nexport type UseAudioFileAnalysisState = {\n data: AudioFileAnalysis | null;\n isLoading: boolean;\n error: string | null;\n};\n\nexport function useAudioFileAnalysis(\n fileUrl: string | null | undefined,\n options: AnalyzeAudioFileOptions = {},\n): UseAudioFileAnalysisState {\n const [state, setState] = useState<UseAudioFileAnalysisState>({\n data: null,\n isLoading: false,\n error: null,\n });\n\n useEffect(() => {\n if (!fileUrl) {\n setState({ data: null, isLoading: false, error: null });\n return;\n }\n if (typeof window === \"undefined\") {\n setState({ data: null, isLoading: false, error: null });\n return;\n }\n\n let cancelled = false;\n setState((prev) => ({ ...prev, isLoading: true, error: null }));\n\n void (async () => {\n try {\n const data = await analyzeAudioFile(fileUrl, options);\n if (!cancelled) {\n setState({ data, isLoading: false, error: null });\n }\n } catch (error) {\n if (!cancelled) {\n setState({\n data: null,\n isLoading: false,\n error: error instanceof Error ? error.message : \"Failed to analyze audio file\",\n });\n }\n }\n })();\n\n return () => {\n cancelled = true;\n };\n }, [\n fileUrl,\n options.timeSlices,\n options.samplesPerSlice,\n options.spectrogram,\n options.fftSize,\n options.frequencyBins,\n options.channel,\n ]);\n\n return state;\n}\n"],"names":["useAudioPeaks","fileUrl","buckets","state","setState","useState","useEffect","cancelled","prev","response","buffer","audioContext","channel","step","peaks","i","max","start","end","j","error","fftInPlace","re","im","n","tr","ti","k","len","ang","wlenR","wlenI","wr","wi","half","u","v","nwr","nwi","realFftMagnitudes","samples","out","hanningWindow","length","w","denom","clampFftSize","p","mixChannels","numberOfChannels","scale","c","ch","getChannel","idx","buildAmplitudeGrid","timeSlices","samplesPerSlice","grid","t","segmentLen","row","segStart","segEnd","subLen","s","a","b","peak","buildSpectrogram","fftSize","frequencyBins","rows","maxMag","bins","window","frame","mags","analyzeAudioBuffer","options","wantSpec","data","amplitudeGrid","result","norm","analyzeAudioFile","raw","Context","useAudioFileAnalysis"],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { AnalyzeAudioFileOptions, AudioFileAnalysis } from './analyzeAudioFile';
|
|
2
|
+
export type UseAudioFileAnalysisState = {
|
|
3
|
+
data: AudioFileAnalysis | null;
|
|
4
|
+
isLoading: boolean;
|
|
5
|
+
error: string | null;
|
|
6
|
+
};
|
|
7
|
+
export declare function useAudioFileAnalysis(fileUrl: string | null | undefined, options?: AnalyzeAudioFileOptions): UseAudioFileAnalysisState;
|
|
8
|
+
//# sourceMappingURL=useAudioFileAnalysis.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAudioFileAnalysis.d.ts","sourceRoot":"","sources":["../../src/waveform/useAudioFileAnalysis.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,KAAK,uBAAuB,EAAE,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5G,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,CAAC;AAEF,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAClC,OAAO,GAAE,uBAA4B,GACpC,yBAAyB,CAmD3B"}
|
package/package.json
CHANGED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";const a=require("react"),f=require("./GingerSplitContexts-Bze1Bqe2.cjs"),m=require("./ginger-DYoHDn8T.cjs");function h(){const e=f.useGingerPlayback(),t=f.useGingerMedia();return a.useMemo(()=>{const r=f.gingerStateFromContextValues(e,t);return{state:r,currentTrack:m.getCurrentTrack(r),playbackUi:m.derivePlaybackUiState(r),duration:m.effectiveDuration(r),remaining:m.effectiveRemaining(r),progress:m.progressFraction(r),artworkUrl:m.resolvedArtwork(r),albumLine:m.resolvedAlbumLine(r),play:e.play,pause:e.pause,togglePlayPause:e.togglePlayPause,seek:t.seek,setVolume:t.setVolume,setMuted:t.setMuted,toggleMute:t.toggleMute,setPlaybackRate:t.setPlaybackRate,next:e.next,prev:e.prev,setRepeatMode:e.setRepeatMode,cycleRepeat:e.cycleRepeat,toggleShuffle:e.toggleShuffle,setQueue:e.setQueue,insertTrackAt:e.insertTrackAt,removeTrackAt:e.removeTrackAt,moveTrack:e.moveTrack,enqueueNext:e.enqueueNext,playTrackAt:e.playTrackAt,selectTrackAt:e.selectTrackAt,setPlaylistMeta:e.setPlaylistMeta,init:e.init,audioRef:t.audioRef,dispatch:e.dispatch}},[e,t])}function S(e=!0,t={}){const{togglePlayPause:r,next:s,prev:o}=f.useGingerPlayback(),{toggleMute:c}=f.useGingerMedia(),n=t.mute;a.useEffect(()=>{if(!e||typeof window>"u")return;const i=(t.playPause??" ").toLowerCase(),l=(t.next??"ArrowRight").toLowerCase(),g=(t.previous??"ArrowLeft").toLowerCase(),y=n==null?void 0:n.toLowerCase(),p=u=>{const k=u.target;if(k&&(["INPUT","TEXTAREA","SELECT"].includes(k.tagName)||k.isContentEditable))return;const d=u.key.toLowerCase();d===i?(u.preventDefault(),r()):d===l?(u.preventDefault(),s()):d===g?(u.preventDefault(),o()):y&&d===y&&(u.preventDefault(),c())};return window.addEventListener("keydown",p),()=>window.removeEventListener("keydown",p)},[t.next,t.playPause,t.previous,e,n,s,o,c,r])}function x(){const{tracks:e,currentIndex:t}=f.useGingerPlayback(),{currentTime:r,seek:s}=f.useGingerMedia(),o=a.useMemo(()=>{var i;return[...((i=e[t])==null?void 0:i.chapters)??[]].filter(l=>l&&Number.isFinite(l.startSeconds)&&l.startSeconds>=0).sort((l,g)=>l.startSeconds-g.startSeconds)},[t,e]),c=a.useMemo(()=>{if(o.length===0)return-1;for(let n=o.length-1;n>=0;n-=1)if(r>=o[n].startSeconds)return n;return-1},[r,o]);return{list:o,activeIndex:c,active:c>=0?o[c]??null:null,seekTo:n=>{const i=o[n];i&&s(i.startSeconds)}}}const G=/\[(\d{1,2}):(\d{2})(?:\.(\d{1,3}))?\]/g;function L(e){const t=[];for(const r of e.split(/\r?\n/)){const s=[...r.matchAll(G)];if(s.length===0)continue;const o=r.replace(G,"").trim();for(const c of s){const n=Number(c[1]??0),i=Number(c[2]??0),l=Number((c[3]??"0").padEnd(3,"0")),g=n*60+i+l/1e3;Number.isFinite(g)&&g>=0&&t.push({time:g,text:o})}}return t.sort((r,s)=>r.time-s.time)}function w(){const{tracks:e,currentIndex:t}=f.useGingerPlayback(),{currentTime:r}=f.useGingerMedia(),s=e[t],o=a.useMemo(()=>s?Array.isArray(s.lyricsTimed)&&s.lyricsTimed.length>0?[...s.lyricsTimed].filter(n=>Number.isFinite(n.time)&&n.time>=0).sort((n,i)=>n.time-i.time):typeof s.lyrics=="string"?L(s.lyrics):[]:[],[s]),c=a.useMemo(()=>{for(let n=o.length-1;n>=0;n-=1)if(r>=o[n].time)return n;return-1},[r,o]);return{lines:o,activeIndex:c,activeLine:c>=0?o[c]??null:null}}function b(e){const{durationMs:t,stopAfterTracks:r,respectPause:s=!0,enabled:o=!0,onFire:c}=e,{currentIndex:n,pause:i,isPaused:l}=f.useGingerPlayback(),g=a.useRef(r??0),y=a.useRef(n);a.useEffect(()=>{g.current=r??0},[r]),a.useEffect(()=>{if(!o||!t||t<=0||s&&l)return;const p=setTimeout(()=>{i(),c==null||c()},t);return()=>clearTimeout(p)},[t,o,l,c,i,s]),a.useEffect(()=>{if(!o||!r||r<=0)return;const p=y.current;y.current=n,n!==p&&(g.current-=1,g.current<=0&&(i(),c==null||c()))},[n,o,c,i,r])}function E(e=!1){const t=f.useGingerState(),r=a.useRef(t);a.useEffect(()=>{if(!e||typeof console>"u")return;const s=r.current;s!==t&&console.debug("[ginger]",{from:{currentIndex:s.currentIndex,isPaused:s.isPaused,currentTime:s.currentTime,repeatMode:s.repeatMode},to:{currentIndex:t.currentIndex,isPaused:t.isPaused,currentTime:t.currentTime,repeatMode:t.repeatMode}}),r.current=t},[e,t])}function R(e){return Math.max(0,Math.min(1,e))}function A(e){const t=f.useGingerMedia(),r=f.useGingerPlayback(),{seek:s}=t,[o,c]=a.useState(0),[n,i]=a.useState(!1),l=m.progressFraction(f.gingerStateFromContextValues(r,t)),g=n?o:l,y=a.useCallback(p=>{if(!(e>0))return;const u=p.currentTarget,k=u.getBoundingClientRect(),d=v=>{const M=R((v-k.left)/k.width);c(M),s(M*e)};i(!0),u.setPointerCapture(p.pointerId),d(p.clientX);const P=v=>d(v.clientX),T=v=>{d(v.clientX),i(!1),u.releasePointerCapture(p.pointerId),u.removeEventListener("pointermove",P),u.removeEventListener("pointerup",T),u.removeEventListener("pointercancel",T)};u.addEventListener("pointermove",P),u.addEventListener("pointerup",T),u.addEventListener("pointercancel",T)},[e,s]);return{fraction:o,displayFraction:g,isDragging:n,onPointerDown:y}}exports.parseLrc=L;exports.useGinger=h;exports.useGingerChapters=x;exports.useGingerDebugLog=E;exports.useGingerKeyboardShortcuts=S;exports.useGingerLyricsSync=w;exports.useGingerSleepTimer=b;exports.useSeekDrag=A;
|
|
2
|
-
//# sourceMappingURL=useSeekDrag-DBzoym0-.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useSeekDrag-DBzoym0-.cjs","sources":["../src/hooks/useGinger.ts","../src/hooks/useGingerKeyboardShortcuts.ts","../src/hooks/useGingerChapters.ts","../src/internal/lyrics.ts","../src/hooks/useGingerLyricsSync.ts","../src/hooks/useGingerSleepTimer.ts","../src/hooks/useGingerDebugLog.ts","../src/hooks/useSeekDrag.ts"],"sourcesContent":["import { useMemo } from \"react\";\nimport { gingerStateFromContextValues, useGingerMedia, useGingerPlayback } from \"../context/GingerSplitContexts\";\nimport {\n derivePlaybackUiState,\n effectiveDuration,\n effectiveRemaining,\n getCurrentTrack,\n progressFraction,\n resolvedAlbumLine,\n resolvedArtwork,\n} from \"../internal/selectors\";\n\nexport function useGinger() {\n const pb = useGingerPlayback();\n const md = useGingerMedia();\n\n return useMemo(\n () => {\n const state = gingerStateFromContextValues(pb, md);\n return {\n state,\n currentTrack: getCurrentTrack(state),\n playbackUi: derivePlaybackUiState(state),\n duration: effectiveDuration(state),\n remaining: effectiveRemaining(state),\n progress: progressFraction(state),\n artworkUrl: resolvedArtwork(state),\n albumLine: resolvedAlbumLine(state),\n play: pb.play,\n pause: pb.pause,\n togglePlayPause: pb.togglePlayPause,\n seek: md.seek,\n setVolume: md.setVolume,\n setMuted: md.setMuted,\n toggleMute: md.toggleMute,\n setPlaybackRate: md.setPlaybackRate,\n next: pb.next,\n prev: pb.prev,\n setRepeatMode: pb.setRepeatMode,\n cycleRepeat: pb.cycleRepeat,\n toggleShuffle: pb.toggleShuffle,\n setQueue: pb.setQueue,\n insertTrackAt: pb.insertTrackAt,\n removeTrackAt: pb.removeTrackAt,\n moveTrack: pb.moveTrack,\n enqueueNext: pb.enqueueNext,\n playTrackAt: pb.playTrackAt,\n selectTrackAt: pb.selectTrackAt,\n setPlaylistMeta: pb.setPlaylistMeta,\n init: pb.init,\n audioRef: md.audioRef,\n dispatch: pb.dispatch,\n };\n },\n [pb, md],\n );\n}\n","import { useEffect } from \"react\";\nimport { useGingerMedia, useGingerPlayback } from \"../context/GingerSplitContexts\";\n\nexport type GingerKeyboardShortcutBindings = {\n playPause?: string;\n next?: string;\n previous?: string;\n mute?: string;\n};\n\nexport function useGingerKeyboardShortcuts(\n enabled = true,\n bindings: GingerKeyboardShortcutBindings = {},\n): void {\n const { togglePlayPause, next, prev } = useGingerPlayback();\n const { toggleMute } = useGingerMedia();\n\n const muteBinding = bindings.mute;\n\n useEffect(() => {\n if (!enabled || typeof window === \"undefined\") return;\n const playPause = (bindings.playPause ?? \" \").toLowerCase();\n const nextKey = (bindings.next ?? \"ArrowRight\").toLowerCase();\n const prevKey = (bindings.previous ?? \"ArrowLeft\").toLowerCase();\n const muteKey = muteBinding?.toLowerCase();\n\n const onKeyDown = (event: KeyboardEvent) => {\n const target = event.target as HTMLElement | null;\n if (target && ([\"INPUT\", \"TEXTAREA\", \"SELECT\"].includes(target.tagName) || target.isContentEditable)) return;\n const key = event.key.toLowerCase();\n if (key === playPause) {\n event.preventDefault();\n togglePlayPause();\n } else if (key === nextKey) {\n event.preventDefault();\n next();\n } else if (key === prevKey) {\n event.preventDefault();\n prev();\n } else if (muteKey && key === muteKey) {\n event.preventDefault();\n toggleMute();\n }\n };\n window.addEventListener(\"keydown\", onKeyDown);\n return () => window.removeEventListener(\"keydown\", onKeyDown);\n }, [bindings.next, bindings.playPause, bindings.previous, enabled, muteBinding, next, prev, toggleMute, togglePlayPause]);\n}\n","import { useMemo } from \"react\";\nimport { useGingerMedia, useGingerPlayback } from \"../context/GingerSplitContexts\";\n\nexport type GingerChapter = {\n title: string;\n startSeconds: number;\n};\n\nexport type GingerChapterState = {\n list: GingerChapter[];\n activeIndex: number;\n active: GingerChapter | null;\n seekTo: (index: number) => void;\n};\n\nexport function useGingerChapters(): GingerChapterState {\n const { tracks, currentIndex } = useGingerPlayback();\n const { currentTime, seek } = useGingerMedia();\n\n const list = useMemo(() => {\n const chapters = tracks[currentIndex]?.chapters ?? [];\n return [...chapters]\n .filter((item) => item && Number.isFinite(item.startSeconds) && item.startSeconds >= 0)\n .sort((a, b) => a.startSeconds - b.startSeconds);\n }, [currentIndex, tracks]);\n\n const activeIndex = useMemo(() => {\n if (list.length === 0) return -1;\n for (let i = list.length - 1; i >= 0; i -= 1) {\n if (currentTime >= list[i]!.startSeconds) return i;\n }\n return -1;\n }, [currentTime, list]);\n\n return {\n list,\n activeIndex,\n active: activeIndex >= 0 ? list[activeIndex] ?? null : null,\n seekTo: (index: number) => {\n const chapter = list[index];\n if (chapter) seek(chapter.startSeconds);\n },\n };\n}\n","export type TimedLyricLine = {\n time: number;\n text: string;\n};\n\nconst lrcTag = /\\[(\\d{1,2}):(\\d{2})(?:\\.(\\d{1,3}))?\\]/g;\n\nexport function parseLrc(lrc: string): TimedLyricLine[] {\n const lines: TimedLyricLine[] = [];\n for (const rawLine of lrc.split(/\\r?\\n/)) {\n const matches = [...rawLine.matchAll(lrcTag)];\n if (matches.length === 0) continue;\n const text = rawLine.replace(lrcTag, \"\").trim();\n for (const m of matches) {\n const minutes = Number(m[1] ?? 0);\n const seconds = Number(m[2] ?? 0);\n const millis = Number((m[3] ?? \"0\").padEnd(3, \"0\"));\n const time = minutes * 60 + seconds + millis / 1000;\n if (Number.isFinite(time) && time >= 0) {\n lines.push({ time, text });\n }\n }\n }\n return lines.sort((a, b) => a.time - b.time);\n}\n","import { useMemo } from \"react\";\nimport { useGingerMedia, useGingerPlayback } from \"../context/GingerSplitContexts\";\nimport { parseLrc, type TimedLyricLine } from \"../internal/lyrics\";\n\nexport type GingerLyricsSyncState = {\n lines: TimedLyricLine[];\n activeIndex: number;\n activeLine: TimedLyricLine | null;\n};\n\nexport function useGingerLyricsSync(): GingerLyricsSyncState {\n const { tracks, currentIndex } = useGingerPlayback();\n const { currentTime } = useGingerMedia();\n const currentTrack = tracks[currentIndex];\n\n const lines = useMemo(() => {\n if (!currentTrack) return [];\n if (Array.isArray(currentTrack.lyricsTimed) && currentTrack.lyricsTimed.length > 0) {\n return [...currentTrack.lyricsTimed]\n .filter((line) => Number.isFinite(line.time) && line.time >= 0)\n .sort((a, b) => a.time - b.time);\n }\n if (typeof currentTrack.lyrics === \"string\") {\n return parseLrc(currentTrack.lyrics);\n }\n return [];\n }, [currentTrack]);\n\n const activeIndex = useMemo(() => {\n for (let i = lines.length - 1; i >= 0; i -= 1) {\n if (currentTime >= lines[i]!.time) return i;\n }\n return -1;\n }, [currentTime, lines]);\n\n return {\n lines,\n activeIndex,\n activeLine: activeIndex >= 0 ? lines[activeIndex] ?? null : null,\n };\n}\n","import { useEffect, useRef } from \"react\";\nimport { useGingerPlayback } from \"../context/GingerSplitContexts\";\n\nexport type GingerSleepTimerOptions = {\n durationMs?: number;\n stopAfterTracks?: number;\n respectPause?: boolean;\n enabled?: boolean;\n onFire?: () => void;\n};\n\nexport function useGingerSleepTimer(options: GingerSleepTimerOptions): void {\n const { durationMs, stopAfterTracks, respectPause = true, enabled = true, onFire } = options;\n const { currentIndex, pause, isPaused } = useGingerPlayback();\n const remainingTracksRef = useRef(stopAfterTracks ?? 0);\n const prevIndexRef = useRef(currentIndex);\n\n useEffect(() => {\n remainingTracksRef.current = stopAfterTracks ?? 0;\n }, [stopAfterTracks]);\n\n useEffect(() => {\n if (!enabled || !durationMs || durationMs <= 0) return;\n if (respectPause && isPaused) return;\n const id = setTimeout(() => {\n pause();\n onFire?.();\n }, durationMs);\n return () => clearTimeout(id);\n }, [durationMs, enabled, isPaused, onFire, pause, respectPause]);\n\n useEffect(() => {\n if (!enabled || !stopAfterTracks || stopAfterTracks <= 0) return;\n const prev = prevIndexRef.current;\n prevIndexRef.current = currentIndex;\n if (currentIndex === prev) return;\n remainingTracksRef.current -= 1;\n if (remainingTracksRef.current <= 0) {\n pause();\n onFire?.();\n }\n }, [currentIndex, enabled, onFire, pause, stopAfterTracks]);\n}\n","import { useEffect, useRef } from \"react\";\nimport { useGingerState } from \"../context/GingerSplitContexts\";\n\nexport function useGingerDebugLog(enabled = false): void {\n const state = useGingerState();\n const prevRef = useRef(state);\n\n useEffect(() => {\n if (!enabled || typeof console === \"undefined\") return;\n const prev = prevRef.current;\n if (prev !== state) {\n console.debug(\"[ginger]\", {\n from: {\n currentIndex: prev.currentIndex,\n isPaused: prev.isPaused,\n currentTime: prev.currentTime,\n repeatMode: prev.repeatMode,\n },\n to: {\n currentIndex: state.currentIndex,\n isPaused: state.isPaused,\n currentTime: state.currentTime,\n repeatMode: state.repeatMode,\n },\n });\n }\n prevRef.current = state;\n }, [enabled, state]);\n}\n","import { useCallback, useState } from \"react\";\nimport type { PointerEvent as ReactPointerEvent } from \"react\";\nimport { useGingerMedia, useGingerPlayback, gingerStateFromContextValues } from \"../context/GingerSplitContexts\";\nimport { progressFraction } from \"../internal/selectors\";\n\nexport type SeekDragState = {\n /** Raw drag fraction — only updated during an active drag gesture. */\n fraction: number;\n /** Blended fraction: follows live playback when idle, drag position when dragging. */\n displayFraction: number;\n isDragging: boolean;\n onPointerDown: (event: ReactPointerEvent<HTMLElement>) => void;\n};\n\nfunction clamp01(value: number): number {\n return Math.max(0, Math.min(1, value));\n}\n\nexport function useSeekDrag(duration: number): SeekDragState {\n const media = useGingerMedia();\n const playback = useGingerPlayback();\n const { seek } = media;\n const [fraction, setFraction] = useState(0);\n const [isDragging, setIsDragging] = useState(false);\n\n const liveFraction = progressFraction(gingerStateFromContextValues(playback, media));\n const displayFraction = isDragging ? fraction : liveFraction;\n\n const onPointerDown = useCallback(\n (event: ReactPointerEvent<HTMLElement>) => {\n if (!(duration > 0)) return;\n const target = event.currentTarget;\n const rect = target.getBoundingClientRect();\n const update = (clientX: number) => {\n const ratio = clamp01((clientX - rect.left) / rect.width);\n setFraction(ratio);\n seek(ratio * duration);\n };\n setIsDragging(true);\n target.setPointerCapture(event.pointerId);\n update(event.clientX);\n const onMove = (moveEvent: PointerEvent) => update(moveEvent.clientX);\n const onUp = (upEvent: PointerEvent) => {\n update(upEvent.clientX);\n setIsDragging(false);\n target.releasePointerCapture(event.pointerId);\n target.removeEventListener(\"pointermove\", onMove);\n target.removeEventListener(\"pointerup\", onUp);\n target.removeEventListener(\"pointercancel\", onUp);\n };\n target.addEventListener(\"pointermove\", onMove);\n target.addEventListener(\"pointerup\", onUp);\n target.addEventListener(\"pointercancel\", onUp);\n },\n [duration, seek],\n );\n\n return { fraction, displayFraction, isDragging, onPointerDown };\n}\n"],"names":["useGinger","pb","useGingerPlayback","md","useGingerMedia","useMemo","state","gingerStateFromContextValues","getCurrentTrack","derivePlaybackUiState","effectiveDuration","effectiveRemaining","progressFraction","resolvedArtwork","resolvedAlbumLine","useGingerKeyboardShortcuts","enabled","bindings","togglePlayPause","next","prev","toggleMute","muteBinding","useEffect","playPause","nextKey","prevKey","muteKey","onKeyDown","event","target","key","useGingerChapters","tracks","currentIndex","currentTime","seek","list","_a","item","a","b","activeIndex","i","index","chapter","lrcTag","parseLrc","lrc","lines","rawLine","matches","text","m","minutes","seconds","millis","time","useGingerLyricsSync","currentTrack","line","useGingerSleepTimer","options","durationMs","stopAfterTracks","respectPause","onFire","pause","isPaused","remainingTracksRef","useRef","prevIndexRef","id","useGingerDebugLog","useGingerState","prevRef","clamp01","value","useSeekDrag","duration","media","playback","fraction","setFraction","useState","isDragging","setIsDragging","liveFraction","displayFraction","onPointerDown","useCallback","rect","update","clientX","ratio","onMove","moveEvent","onUp","upEvent"],"mappings":"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"}
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import { useMemo as v, useEffect as k, useRef as h, useState as w, useCallback as b } from "react";
|
|
2
|
-
import { b as g, u as T, g as A, c as R } from "./GingerSplitContexts-4YZ-OJ9V.js";
|
|
3
|
-
import { r as E, h as C, p as S, i as I, j as D, b as N, k as G } from "./ginger-Dntdd6zH.js";
|
|
4
|
-
function q() {
|
|
5
|
-
const e = g(), t = T();
|
|
6
|
-
return v(
|
|
7
|
-
() => {
|
|
8
|
-
const r = A(e, t);
|
|
9
|
-
return {
|
|
10
|
-
state: r,
|
|
11
|
-
currentTrack: G(r),
|
|
12
|
-
playbackUi: N(r),
|
|
13
|
-
duration: D(r),
|
|
14
|
-
remaining: I(r),
|
|
15
|
-
progress: S(r),
|
|
16
|
-
artworkUrl: C(r),
|
|
17
|
-
albumLine: E(r),
|
|
18
|
-
play: e.play,
|
|
19
|
-
pause: e.pause,
|
|
20
|
-
togglePlayPause: e.togglePlayPause,
|
|
21
|
-
seek: t.seek,
|
|
22
|
-
setVolume: t.setVolume,
|
|
23
|
-
setMuted: t.setMuted,
|
|
24
|
-
toggleMute: t.toggleMute,
|
|
25
|
-
setPlaybackRate: t.setPlaybackRate,
|
|
26
|
-
next: e.next,
|
|
27
|
-
prev: e.prev,
|
|
28
|
-
setRepeatMode: e.setRepeatMode,
|
|
29
|
-
cycleRepeat: e.cycleRepeat,
|
|
30
|
-
toggleShuffle: e.toggleShuffle,
|
|
31
|
-
setQueue: e.setQueue,
|
|
32
|
-
insertTrackAt: e.insertTrackAt,
|
|
33
|
-
removeTrackAt: e.removeTrackAt,
|
|
34
|
-
moveTrack: e.moveTrack,
|
|
35
|
-
enqueueNext: e.enqueueNext,
|
|
36
|
-
playTrackAt: e.playTrackAt,
|
|
37
|
-
selectTrackAt: e.selectTrackAt,
|
|
38
|
-
setPlaylistMeta: e.setPlaylistMeta,
|
|
39
|
-
init: e.init,
|
|
40
|
-
audioRef: t.audioRef,
|
|
41
|
-
dispatch: e.dispatch
|
|
42
|
-
};
|
|
43
|
-
},
|
|
44
|
-
[e, t]
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
function Q(e = !0, t = {}) {
|
|
48
|
-
const { togglePlayPause: r, next: s, prev: o } = g(), { toggleMute: a } = T(), n = t.mute;
|
|
49
|
-
k(() => {
|
|
50
|
-
if (!e || typeof window > "u") return;
|
|
51
|
-
const c = (t.playPause ?? " ").toLowerCase(), u = (t.next ?? "ArrowRight").toLowerCase(), l = (t.previous ?? "ArrowLeft").toLowerCase(), d = n == null ? void 0 : n.toLowerCase(), f = (i) => {
|
|
52
|
-
const m = i.target;
|
|
53
|
-
if (m && (["INPUT", "TEXTAREA", "SELECT"].includes(m.tagName) || m.isContentEditable)) return;
|
|
54
|
-
const p = i.key.toLowerCase();
|
|
55
|
-
p === c ? (i.preventDefault(), r()) : p === u ? (i.preventDefault(), s()) : p === l ? (i.preventDefault(), o()) : d && p === d && (i.preventDefault(), a());
|
|
56
|
-
};
|
|
57
|
-
return window.addEventListener("keydown", f), () => window.removeEventListener("keydown", f);
|
|
58
|
-
}, [t.next, t.playPause, t.previous, e, n, s, o, a, r]);
|
|
59
|
-
}
|
|
60
|
-
function j() {
|
|
61
|
-
const { tracks: e, currentIndex: t } = g(), { currentTime: r, seek: s } = T(), o = v(() => {
|
|
62
|
-
var c;
|
|
63
|
-
return [...((c = e[t]) == null ? void 0 : c.chapters) ?? []].filter((u) => u && Number.isFinite(u.startSeconds) && u.startSeconds >= 0).sort((u, l) => u.startSeconds - l.startSeconds);
|
|
64
|
-
}, [t, e]), a = v(() => {
|
|
65
|
-
if (o.length === 0) return -1;
|
|
66
|
-
for (let n = o.length - 1; n >= 0; n -= 1)
|
|
67
|
-
if (r >= o[n].startSeconds) return n;
|
|
68
|
-
return -1;
|
|
69
|
-
}, [r, o]);
|
|
70
|
-
return {
|
|
71
|
-
list: o,
|
|
72
|
-
activeIndex: a,
|
|
73
|
-
active: a >= 0 ? o[a] ?? null : null,
|
|
74
|
-
seekTo: (n) => {
|
|
75
|
-
const c = o[n];
|
|
76
|
-
c && s(c.startSeconds);
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
const M = /\[(\d{1,2}):(\d{2})(?:\.(\d{1,3}))?\]/g;
|
|
81
|
-
function F(e) {
|
|
82
|
-
const t = [];
|
|
83
|
-
for (const r of e.split(/\r?\n/)) {
|
|
84
|
-
const s = [...r.matchAll(M)];
|
|
85
|
-
if (s.length === 0) continue;
|
|
86
|
-
const o = r.replace(M, "").trim();
|
|
87
|
-
for (const a of s) {
|
|
88
|
-
const n = Number(a[1] ?? 0), c = Number(a[2] ?? 0), u = Number((a[3] ?? "0").padEnd(3, "0")), l = n * 60 + c + u / 1e3;
|
|
89
|
-
Number.isFinite(l) && l >= 0 && t.push({ time: l, text: o });
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return t.sort((r, s) => r.time - s.time);
|
|
93
|
-
}
|
|
94
|
-
function z() {
|
|
95
|
-
const { tracks: e, currentIndex: t } = g(), { currentTime: r } = T(), s = e[t], o = v(() => s ? Array.isArray(s.lyricsTimed) && s.lyricsTimed.length > 0 ? [...s.lyricsTimed].filter((n) => Number.isFinite(n.time) && n.time >= 0).sort((n, c) => n.time - c.time) : typeof s.lyrics == "string" ? F(s.lyrics) : [] : [], [s]), a = v(() => {
|
|
96
|
-
for (let n = o.length - 1; n >= 0; n -= 1)
|
|
97
|
-
if (r >= o[n].time) return n;
|
|
98
|
-
return -1;
|
|
99
|
-
}, [r, o]);
|
|
100
|
-
return {
|
|
101
|
-
lines: o,
|
|
102
|
-
activeIndex: a,
|
|
103
|
-
activeLine: a >= 0 ? o[a] ?? null : null
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
function B(e) {
|
|
107
|
-
const { durationMs: t, stopAfterTracks: r, respectPause: s = !0, enabled: o = !0, onFire: a } = e, { currentIndex: n, pause: c, isPaused: u } = g(), l = h(r ?? 0), d = h(n);
|
|
108
|
-
k(() => {
|
|
109
|
-
l.current = r ?? 0;
|
|
110
|
-
}, [r]), k(() => {
|
|
111
|
-
if (!o || !t || t <= 0 || s && u) return;
|
|
112
|
-
const f = setTimeout(() => {
|
|
113
|
-
c(), a == null || a();
|
|
114
|
-
}, t);
|
|
115
|
-
return () => clearTimeout(f);
|
|
116
|
-
}, [t, o, u, a, c, s]), k(() => {
|
|
117
|
-
if (!o || !r || r <= 0) return;
|
|
118
|
-
const f = d.current;
|
|
119
|
-
d.current = n, n !== f && (l.current -= 1, l.current <= 0 && (c(), a == null || a()));
|
|
120
|
-
}, [n, o, a, c, r]);
|
|
121
|
-
}
|
|
122
|
-
function H(e = !1) {
|
|
123
|
-
const t = R(), r = h(t);
|
|
124
|
-
k(() => {
|
|
125
|
-
if (!e || typeof console > "u") return;
|
|
126
|
-
const s = r.current;
|
|
127
|
-
s !== t && console.debug("[ginger]", {
|
|
128
|
-
from: {
|
|
129
|
-
currentIndex: s.currentIndex,
|
|
130
|
-
isPaused: s.isPaused,
|
|
131
|
-
currentTime: s.currentTime,
|
|
132
|
-
repeatMode: s.repeatMode
|
|
133
|
-
},
|
|
134
|
-
to: {
|
|
135
|
-
currentIndex: t.currentIndex,
|
|
136
|
-
isPaused: t.isPaused,
|
|
137
|
-
currentTime: t.currentTime,
|
|
138
|
-
repeatMode: t.repeatMode
|
|
139
|
-
}
|
|
140
|
-
}), r.current = t;
|
|
141
|
-
}, [e, t]);
|
|
142
|
-
}
|
|
143
|
-
function K(e) {
|
|
144
|
-
return Math.max(0, Math.min(1, e));
|
|
145
|
-
}
|
|
146
|
-
function J(e) {
|
|
147
|
-
const t = T(), r = g(), { seek: s } = t, [o, a] = w(0), [n, c] = w(!1), u = S(A(r, t)), l = n ? o : u, d = b(
|
|
148
|
-
(f) => {
|
|
149
|
-
if (!(e > 0)) return;
|
|
150
|
-
const i = f.currentTarget, m = i.getBoundingClientRect(), p = (y) => {
|
|
151
|
-
const L = K((y - m.left) / m.width);
|
|
152
|
-
a(L), s(L * e);
|
|
153
|
-
};
|
|
154
|
-
c(!0), i.setPointerCapture(f.pointerId), p(f.clientX);
|
|
155
|
-
const x = (y) => p(y.clientX), P = (y) => {
|
|
156
|
-
p(y.clientX), c(!1), i.releasePointerCapture(f.pointerId), i.removeEventListener("pointermove", x), i.removeEventListener("pointerup", P), i.removeEventListener("pointercancel", P);
|
|
157
|
-
};
|
|
158
|
-
i.addEventListener("pointermove", x), i.addEventListener("pointerup", P), i.addEventListener("pointercancel", P);
|
|
159
|
-
},
|
|
160
|
-
[e, s]
|
|
161
|
-
);
|
|
162
|
-
return { fraction: o, displayFraction: l, isDragging: n, onPointerDown: d };
|
|
163
|
-
}
|
|
164
|
-
export {
|
|
165
|
-
j as a,
|
|
166
|
-
H as b,
|
|
167
|
-
Q as c,
|
|
168
|
-
z as d,
|
|
169
|
-
B as e,
|
|
170
|
-
J as f,
|
|
171
|
-
F as p,
|
|
172
|
-
q as u
|
|
173
|
-
};
|
|
174
|
-
//# sourceMappingURL=useSeekDrag-jLsYA-uG.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useSeekDrag-jLsYA-uG.js","sources":["../src/hooks/useGinger.ts","../src/hooks/useGingerKeyboardShortcuts.ts","../src/hooks/useGingerChapters.ts","../src/internal/lyrics.ts","../src/hooks/useGingerLyricsSync.ts","../src/hooks/useGingerSleepTimer.ts","../src/hooks/useGingerDebugLog.ts","../src/hooks/useSeekDrag.ts"],"sourcesContent":["import { useMemo } from \"react\";\nimport { gingerStateFromContextValues, useGingerMedia, useGingerPlayback } from \"../context/GingerSplitContexts\";\nimport {\n derivePlaybackUiState,\n effectiveDuration,\n effectiveRemaining,\n getCurrentTrack,\n progressFraction,\n resolvedAlbumLine,\n resolvedArtwork,\n} from \"../internal/selectors\";\n\nexport function useGinger() {\n const pb = useGingerPlayback();\n const md = useGingerMedia();\n\n return useMemo(\n () => {\n const state = gingerStateFromContextValues(pb, md);\n return {\n state,\n currentTrack: getCurrentTrack(state),\n playbackUi: derivePlaybackUiState(state),\n duration: effectiveDuration(state),\n remaining: effectiveRemaining(state),\n progress: progressFraction(state),\n artworkUrl: resolvedArtwork(state),\n albumLine: resolvedAlbumLine(state),\n play: pb.play,\n pause: pb.pause,\n togglePlayPause: pb.togglePlayPause,\n seek: md.seek,\n setVolume: md.setVolume,\n setMuted: md.setMuted,\n toggleMute: md.toggleMute,\n setPlaybackRate: md.setPlaybackRate,\n next: pb.next,\n prev: pb.prev,\n setRepeatMode: pb.setRepeatMode,\n cycleRepeat: pb.cycleRepeat,\n toggleShuffle: pb.toggleShuffle,\n setQueue: pb.setQueue,\n insertTrackAt: pb.insertTrackAt,\n removeTrackAt: pb.removeTrackAt,\n moveTrack: pb.moveTrack,\n enqueueNext: pb.enqueueNext,\n playTrackAt: pb.playTrackAt,\n selectTrackAt: pb.selectTrackAt,\n setPlaylistMeta: pb.setPlaylistMeta,\n init: pb.init,\n audioRef: md.audioRef,\n dispatch: pb.dispatch,\n };\n },\n [pb, md],\n );\n}\n","import { useEffect } from \"react\";\nimport { useGingerMedia, useGingerPlayback } from \"../context/GingerSplitContexts\";\n\nexport type GingerKeyboardShortcutBindings = {\n playPause?: string;\n next?: string;\n previous?: string;\n mute?: string;\n};\n\nexport function useGingerKeyboardShortcuts(\n enabled = true,\n bindings: GingerKeyboardShortcutBindings = {},\n): void {\n const { togglePlayPause, next, prev } = useGingerPlayback();\n const { toggleMute } = useGingerMedia();\n\n const muteBinding = bindings.mute;\n\n useEffect(() => {\n if (!enabled || typeof window === \"undefined\") return;\n const playPause = (bindings.playPause ?? \" \").toLowerCase();\n const nextKey = (bindings.next ?? \"ArrowRight\").toLowerCase();\n const prevKey = (bindings.previous ?? \"ArrowLeft\").toLowerCase();\n const muteKey = muteBinding?.toLowerCase();\n\n const onKeyDown = (event: KeyboardEvent) => {\n const target = event.target as HTMLElement | null;\n if (target && ([\"INPUT\", \"TEXTAREA\", \"SELECT\"].includes(target.tagName) || target.isContentEditable)) return;\n const key = event.key.toLowerCase();\n if (key === playPause) {\n event.preventDefault();\n togglePlayPause();\n } else if (key === nextKey) {\n event.preventDefault();\n next();\n } else if (key === prevKey) {\n event.preventDefault();\n prev();\n } else if (muteKey && key === muteKey) {\n event.preventDefault();\n toggleMute();\n }\n };\n window.addEventListener(\"keydown\", onKeyDown);\n return () => window.removeEventListener(\"keydown\", onKeyDown);\n }, [bindings.next, bindings.playPause, bindings.previous, enabled, muteBinding, next, prev, toggleMute, togglePlayPause]);\n}\n","import { useMemo } from \"react\";\nimport { useGingerMedia, useGingerPlayback } from \"../context/GingerSplitContexts\";\n\nexport type GingerChapter = {\n title: string;\n startSeconds: number;\n};\n\nexport type GingerChapterState = {\n list: GingerChapter[];\n activeIndex: number;\n active: GingerChapter | null;\n seekTo: (index: number) => void;\n};\n\nexport function useGingerChapters(): GingerChapterState {\n const { tracks, currentIndex } = useGingerPlayback();\n const { currentTime, seek } = useGingerMedia();\n\n const list = useMemo(() => {\n const chapters = tracks[currentIndex]?.chapters ?? [];\n return [...chapters]\n .filter((item) => item && Number.isFinite(item.startSeconds) && item.startSeconds >= 0)\n .sort((a, b) => a.startSeconds - b.startSeconds);\n }, [currentIndex, tracks]);\n\n const activeIndex = useMemo(() => {\n if (list.length === 0) return -1;\n for (let i = list.length - 1; i >= 0; i -= 1) {\n if (currentTime >= list[i]!.startSeconds) return i;\n }\n return -1;\n }, [currentTime, list]);\n\n return {\n list,\n activeIndex,\n active: activeIndex >= 0 ? list[activeIndex] ?? null : null,\n seekTo: (index: number) => {\n const chapter = list[index];\n if (chapter) seek(chapter.startSeconds);\n },\n };\n}\n","export type TimedLyricLine = {\n time: number;\n text: string;\n};\n\nconst lrcTag = /\\[(\\d{1,2}):(\\d{2})(?:\\.(\\d{1,3}))?\\]/g;\n\nexport function parseLrc(lrc: string): TimedLyricLine[] {\n const lines: TimedLyricLine[] = [];\n for (const rawLine of lrc.split(/\\r?\\n/)) {\n const matches = [...rawLine.matchAll(lrcTag)];\n if (matches.length === 0) continue;\n const text = rawLine.replace(lrcTag, \"\").trim();\n for (const m of matches) {\n const minutes = Number(m[1] ?? 0);\n const seconds = Number(m[2] ?? 0);\n const millis = Number((m[3] ?? \"0\").padEnd(3, \"0\"));\n const time = minutes * 60 + seconds + millis / 1000;\n if (Number.isFinite(time) && time >= 0) {\n lines.push({ time, text });\n }\n }\n }\n return lines.sort((a, b) => a.time - b.time);\n}\n","import { useMemo } from \"react\";\nimport { useGingerMedia, useGingerPlayback } from \"../context/GingerSplitContexts\";\nimport { parseLrc, type TimedLyricLine } from \"../internal/lyrics\";\n\nexport type GingerLyricsSyncState = {\n lines: TimedLyricLine[];\n activeIndex: number;\n activeLine: TimedLyricLine | null;\n};\n\nexport function useGingerLyricsSync(): GingerLyricsSyncState {\n const { tracks, currentIndex } = useGingerPlayback();\n const { currentTime } = useGingerMedia();\n const currentTrack = tracks[currentIndex];\n\n const lines = useMemo(() => {\n if (!currentTrack) return [];\n if (Array.isArray(currentTrack.lyricsTimed) && currentTrack.lyricsTimed.length > 0) {\n return [...currentTrack.lyricsTimed]\n .filter((line) => Number.isFinite(line.time) && line.time >= 0)\n .sort((a, b) => a.time - b.time);\n }\n if (typeof currentTrack.lyrics === \"string\") {\n return parseLrc(currentTrack.lyrics);\n }\n return [];\n }, [currentTrack]);\n\n const activeIndex = useMemo(() => {\n for (let i = lines.length - 1; i >= 0; i -= 1) {\n if (currentTime >= lines[i]!.time) return i;\n }\n return -1;\n }, [currentTime, lines]);\n\n return {\n lines,\n activeIndex,\n activeLine: activeIndex >= 0 ? lines[activeIndex] ?? null : null,\n };\n}\n","import { useEffect, useRef } from \"react\";\nimport { useGingerPlayback } from \"../context/GingerSplitContexts\";\n\nexport type GingerSleepTimerOptions = {\n durationMs?: number;\n stopAfterTracks?: number;\n respectPause?: boolean;\n enabled?: boolean;\n onFire?: () => void;\n};\n\nexport function useGingerSleepTimer(options: GingerSleepTimerOptions): void {\n const { durationMs, stopAfterTracks, respectPause = true, enabled = true, onFire } = options;\n const { currentIndex, pause, isPaused } = useGingerPlayback();\n const remainingTracksRef = useRef(stopAfterTracks ?? 0);\n const prevIndexRef = useRef(currentIndex);\n\n useEffect(() => {\n remainingTracksRef.current = stopAfterTracks ?? 0;\n }, [stopAfterTracks]);\n\n useEffect(() => {\n if (!enabled || !durationMs || durationMs <= 0) return;\n if (respectPause && isPaused) return;\n const id = setTimeout(() => {\n pause();\n onFire?.();\n }, durationMs);\n return () => clearTimeout(id);\n }, [durationMs, enabled, isPaused, onFire, pause, respectPause]);\n\n useEffect(() => {\n if (!enabled || !stopAfterTracks || stopAfterTracks <= 0) return;\n const prev = prevIndexRef.current;\n prevIndexRef.current = currentIndex;\n if (currentIndex === prev) return;\n remainingTracksRef.current -= 1;\n if (remainingTracksRef.current <= 0) {\n pause();\n onFire?.();\n }\n }, [currentIndex, enabled, onFire, pause, stopAfterTracks]);\n}\n","import { useEffect, useRef } from \"react\";\nimport { useGingerState } from \"../context/GingerSplitContexts\";\n\nexport function useGingerDebugLog(enabled = false): void {\n const state = useGingerState();\n const prevRef = useRef(state);\n\n useEffect(() => {\n if (!enabled || typeof console === \"undefined\") return;\n const prev = prevRef.current;\n if (prev !== state) {\n console.debug(\"[ginger]\", {\n from: {\n currentIndex: prev.currentIndex,\n isPaused: prev.isPaused,\n currentTime: prev.currentTime,\n repeatMode: prev.repeatMode,\n },\n to: {\n currentIndex: state.currentIndex,\n isPaused: state.isPaused,\n currentTime: state.currentTime,\n repeatMode: state.repeatMode,\n },\n });\n }\n prevRef.current = state;\n }, [enabled, state]);\n}\n","import { useCallback, useState } from \"react\";\nimport type { PointerEvent as ReactPointerEvent } from \"react\";\nimport { useGingerMedia, useGingerPlayback, gingerStateFromContextValues } from \"../context/GingerSplitContexts\";\nimport { progressFraction } from \"../internal/selectors\";\n\nexport type SeekDragState = {\n /** Raw drag fraction — only updated during an active drag gesture. */\n fraction: number;\n /** Blended fraction: follows live playback when idle, drag position when dragging. */\n displayFraction: number;\n isDragging: boolean;\n onPointerDown: (event: ReactPointerEvent<HTMLElement>) => void;\n};\n\nfunction clamp01(value: number): number {\n return Math.max(0, Math.min(1, value));\n}\n\nexport function useSeekDrag(duration: number): SeekDragState {\n const media = useGingerMedia();\n const playback = useGingerPlayback();\n const { seek } = media;\n const [fraction, setFraction] = useState(0);\n const [isDragging, setIsDragging] = useState(false);\n\n const liveFraction = progressFraction(gingerStateFromContextValues(playback, media));\n const displayFraction = isDragging ? fraction : liveFraction;\n\n const onPointerDown = useCallback(\n (event: ReactPointerEvent<HTMLElement>) => {\n if (!(duration > 0)) return;\n const target = event.currentTarget;\n const rect = target.getBoundingClientRect();\n const update = (clientX: number) => {\n const ratio = clamp01((clientX - rect.left) / rect.width);\n setFraction(ratio);\n seek(ratio * duration);\n };\n setIsDragging(true);\n target.setPointerCapture(event.pointerId);\n update(event.clientX);\n const onMove = (moveEvent: PointerEvent) => update(moveEvent.clientX);\n const onUp = (upEvent: PointerEvent) => {\n update(upEvent.clientX);\n setIsDragging(false);\n target.releasePointerCapture(event.pointerId);\n target.removeEventListener(\"pointermove\", onMove);\n target.removeEventListener(\"pointerup\", onUp);\n target.removeEventListener(\"pointercancel\", onUp);\n };\n target.addEventListener(\"pointermove\", onMove);\n target.addEventListener(\"pointerup\", onUp);\n target.addEventListener(\"pointercancel\", onUp);\n },\n [duration, seek],\n );\n\n return { fraction, displayFraction, isDragging, onPointerDown };\n}\n"],"names":["useGinger","pb","useGingerPlayback","md","useGingerMedia","useMemo","state","gingerStateFromContextValues","getCurrentTrack","derivePlaybackUiState","effectiveDuration","effectiveRemaining","progressFraction","resolvedArtwork","resolvedAlbumLine","useGingerKeyboardShortcuts","enabled","bindings","togglePlayPause","next","prev","toggleMute","muteBinding","useEffect","playPause","nextKey","prevKey","muteKey","onKeyDown","event","target","key","useGingerChapters","tracks","currentIndex","currentTime","seek","list","_a","item","a","b","activeIndex","i","index","chapter","lrcTag","parseLrc","lrc","lines","rawLine","matches","text","m","minutes","seconds","millis","time","useGingerLyricsSync","currentTrack","line","useGingerSleepTimer","options","durationMs","stopAfterTracks","respectPause","onFire","pause","isPaused","remainingTracksRef","useRef","prevIndexRef","id","useGingerDebugLog","useGingerState","prevRef","clamp01","value","useSeekDrag","duration","media","playback","fraction","setFraction","useState","isDragging","setIsDragging","liveFraction","displayFraction","onPointerDown","useCallback","rect","update","clientX","ratio","onMove","moveEvent","onUp","upEvent"],"mappings":"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"}
|