@gentomiyano/optimized-web-audio-player 0.2.0 → 0.2.2
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 +25 -0
- package/dist/config/index.d.ts +1 -1
- package/dist/consumer/index.d.ts +1 -1
- package/dist/contracts/index.d.ts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/media-delivery/index.d.ts +1 -1
- package/dist/{media-element-DyMO-PhK.d.ts → media-element-vFtmw4mQ.d.ts} +1 -1
- package/dist/{player-BxNFzRq4.d.ts → player-CE714iU_.d.ts} +2 -1
- package/dist/ports/index.d.ts +2 -2
- package/dist/react/index.d.ts +14 -3
- package/dist/react/index.js +708 -25
- package/dist/react/index.js.map +1 -1
- package/dist/testing/index.d.ts +1 -1
- package/dist/ui/index.d.ts +15 -6
- package/dist/ui/index.js +33 -109
- package/dist/ui/index.js.map +1 -1
- package/package.json +3 -2
package/dist/react/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createContext, useRef, useState, useEffect,
|
|
1
|
+
import { createContext, useRef, useState, useEffect, useMemo, useSyncExternalStore, useContext, useId } from 'react';
|
|
2
2
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
3
3
|
|
|
4
4
|
// src/react/player-provider.tsx
|
|
@@ -2077,6 +2077,147 @@ function createPlayerStore(options) {
|
|
|
2077
2077
|
return new CorePlayerStore(options);
|
|
2078
2078
|
}
|
|
2079
2079
|
|
|
2080
|
+
// src/react/player-runtime-bridge.ts
|
|
2081
|
+
var RUNTIME_PENDING_ERROR = Object.freeze({
|
|
2082
|
+
code: "unknown",
|
|
2083
|
+
recoverable: true,
|
|
2084
|
+
messageKey: "player.error.runtime-pending"
|
|
2085
|
+
});
|
|
2086
|
+
var RUNTIME_PENDING_RESULT = Object.freeze({
|
|
2087
|
+
ok: false,
|
|
2088
|
+
error: RUNTIME_PENDING_ERROR
|
|
2089
|
+
});
|
|
2090
|
+
function notify(listeners) {
|
|
2091
|
+
for (const listener of listeners) {
|
|
2092
|
+
listener();
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
function createPlayerRuntimeBridge(experienceConfig) {
|
|
2096
|
+
let pendingState = createInitialPlayerState(experienceConfig);
|
|
2097
|
+
let activeStore = null;
|
|
2098
|
+
let activeCommands = null;
|
|
2099
|
+
let unsubscribeStore = null;
|
|
2100
|
+
let unsubscribeExperience = null;
|
|
2101
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
2102
|
+
const experienceListeners = /* @__PURE__ */ new Set();
|
|
2103
|
+
const pendingResult = () => Promise.resolve(RUNTIME_PENDING_RESULT);
|
|
2104
|
+
const commandsImplementation = {
|
|
2105
|
+
load: (item) => activeCommands?.load(item) ?? pendingResult(),
|
|
2106
|
+
setQueue: (queue, options) => activeCommands?.setQueue(queue, options) ?? pendingResult(),
|
|
2107
|
+
activateContext: (options) => activeCommands?.activateContext(options) ?? pendingResult(),
|
|
2108
|
+
appendToManualHistory: (contextId, item) => {
|
|
2109
|
+
activeCommands?.appendToManualHistory(contextId, item);
|
|
2110
|
+
},
|
|
2111
|
+
play: () => activeCommands?.play() ?? pendingResult(),
|
|
2112
|
+
pause: (options) => activeCommands?.pause(options) ?? Promise.resolve(),
|
|
2113
|
+
stop: (options) => activeCommands?.stop(options) ?? Promise.resolve(),
|
|
2114
|
+
togglePlayback: () => activeCommands?.togglePlayback() ?? pendingResult(),
|
|
2115
|
+
seekTo: (seconds) => {
|
|
2116
|
+
activeCommands?.seekTo(seconds);
|
|
2117
|
+
},
|
|
2118
|
+
seekBy: (deltaSeconds) => {
|
|
2119
|
+
activeCommands?.seekBy(deltaSeconds);
|
|
2120
|
+
},
|
|
2121
|
+
setVolume: (volume) => {
|
|
2122
|
+
activeCommands?.setVolume(volume);
|
|
2123
|
+
},
|
|
2124
|
+
toggleMute: () => {
|
|
2125
|
+
activeCommands?.toggleMute();
|
|
2126
|
+
},
|
|
2127
|
+
setShuffle: (enabled) => {
|
|
2128
|
+
activeCommands?.setShuffle(enabled);
|
|
2129
|
+
},
|
|
2130
|
+
setRepeatMode: (mode) => {
|
|
2131
|
+
activeCommands?.setRepeatMode(mode);
|
|
2132
|
+
},
|
|
2133
|
+
next: () => activeCommands?.next() ?? pendingResult(),
|
|
2134
|
+
previous: () => activeCommands?.previous() ?? pendingResult(),
|
|
2135
|
+
retry: () => activeCommands?.retry() ?? pendingResult(),
|
|
2136
|
+
clear: () => activeCommands?.clear() ?? Promise.resolve()
|
|
2137
|
+
};
|
|
2138
|
+
const commands = Object.freeze(commandsImplementation);
|
|
2139
|
+
const experienceImplementation = {
|
|
2140
|
+
getConfig: () => activeStore?.experience.getConfig() ?? pendingState.experienceConfig,
|
|
2141
|
+
updateConfig: (patch) => {
|
|
2142
|
+
if (activeStore !== null) {
|
|
2143
|
+
activeStore.experience.updateConfig(patch);
|
|
2144
|
+
return;
|
|
2145
|
+
}
|
|
2146
|
+
pendingState = createInitialPlayerState({
|
|
2147
|
+
...pendingState.experienceConfig,
|
|
2148
|
+
...patch
|
|
2149
|
+
});
|
|
2150
|
+
notify(listeners);
|
|
2151
|
+
notify(experienceListeners);
|
|
2152
|
+
},
|
|
2153
|
+
resetConfig: () => {
|
|
2154
|
+
if (activeStore !== null) {
|
|
2155
|
+
activeStore.experience.resetConfig();
|
|
2156
|
+
return;
|
|
2157
|
+
}
|
|
2158
|
+
pendingState = createInitialPlayerState(
|
|
2159
|
+
PREVIEW_PLAYER_EXPERIENCE_CONFIG
|
|
2160
|
+
);
|
|
2161
|
+
notify(listeners);
|
|
2162
|
+
notify(experienceListeners);
|
|
2163
|
+
},
|
|
2164
|
+
subscribe: (listener) => {
|
|
2165
|
+
experienceListeners.add(listener);
|
|
2166
|
+
return () => {
|
|
2167
|
+
experienceListeners.delete(listener);
|
|
2168
|
+
};
|
|
2169
|
+
}
|
|
2170
|
+
};
|
|
2171
|
+
const experience = Object.freeze(experienceImplementation);
|
|
2172
|
+
const storeImplementation = {
|
|
2173
|
+
getState: () => activeStore?.getState() ?? pendingState,
|
|
2174
|
+
subscribe: (listener) => {
|
|
2175
|
+
listeners.add(listener);
|
|
2176
|
+
return () => {
|
|
2177
|
+
listeners.delete(listener);
|
|
2178
|
+
};
|
|
2179
|
+
},
|
|
2180
|
+
commands,
|
|
2181
|
+
experience,
|
|
2182
|
+
synchronizeFromMedia: (reason) => activeStore?.synchronizeFromMedia(reason) ?? Promise.resolve(),
|
|
2183
|
+
destroy: () => activeStore?.destroy() ?? Promise.resolve()
|
|
2184
|
+
};
|
|
2185
|
+
const store = Object.freeze(storeImplementation);
|
|
2186
|
+
const detachActiveStore = () => {
|
|
2187
|
+
unsubscribeStore?.();
|
|
2188
|
+
unsubscribeExperience?.();
|
|
2189
|
+
unsubscribeStore = null;
|
|
2190
|
+
unsubscribeExperience = null;
|
|
2191
|
+
activeStore = null;
|
|
2192
|
+
activeCommands = null;
|
|
2193
|
+
};
|
|
2194
|
+
const bridge = {
|
|
2195
|
+
store,
|
|
2196
|
+
attach(nextStore, nextCommands) {
|
|
2197
|
+
detachActiveStore();
|
|
2198
|
+
activeStore = nextStore;
|
|
2199
|
+
activeCommands = nextCommands;
|
|
2200
|
+
unsubscribeStore = nextStore.subscribe(() => {
|
|
2201
|
+
notify(listeners);
|
|
2202
|
+
});
|
|
2203
|
+
unsubscribeExperience = nextStore.experience.subscribe(() => {
|
|
2204
|
+
notify(experienceListeners);
|
|
2205
|
+
});
|
|
2206
|
+
notify(listeners);
|
|
2207
|
+
notify(experienceListeners);
|
|
2208
|
+
return () => {
|
|
2209
|
+
if (activeStore !== nextStore) {
|
|
2210
|
+
return;
|
|
2211
|
+
}
|
|
2212
|
+
detachActiveStore();
|
|
2213
|
+
notify(listeners);
|
|
2214
|
+
notify(experienceListeners);
|
|
2215
|
+
};
|
|
2216
|
+
}
|
|
2217
|
+
};
|
|
2218
|
+
return Object.freeze(bridge);
|
|
2219
|
+
}
|
|
2220
|
+
|
|
2080
2221
|
// src/react/runtime-commands.ts
|
|
2081
2222
|
function activate(gain, options) {
|
|
2082
2223
|
options.onUserActivation?.();
|
|
@@ -2216,7 +2357,10 @@ function PlayerProvider({
|
|
|
2216
2357
|
const initialQueueItemIdRef = useRef(initialQueueItemId);
|
|
2217
2358
|
const initialExperienceConfigRef = useRef(experienceConfig);
|
|
2218
2359
|
const diagnosticListenerRef = useRef(onDiagnosticEvent);
|
|
2219
|
-
const [
|
|
2360
|
+
const [runtimeBridge] = useState(
|
|
2361
|
+
() => createPlayerRuntimeBridge(experienceConfig)
|
|
2362
|
+
);
|
|
2363
|
+
const [runtimeStatus, setRuntimeStatus] = useState("pending");
|
|
2220
2364
|
const [audioContextState, setAudioContextState] = useState("uninitialized");
|
|
2221
2365
|
const [resolvedAudioMode, setResolvedAudioMode] = useState("web-audio");
|
|
2222
2366
|
const [mediaSessionSupported, setMediaSessionSupported] = useState(false);
|
|
@@ -2295,6 +2439,7 @@ function PlayerProvider({
|
|
|
2295
2439
|
});
|
|
2296
2440
|
}
|
|
2297
2441
|
});
|
|
2442
|
+
const detachRuntime = runtimeBridge.attach(store, commands);
|
|
2298
2443
|
mediaSessionController = createBrowserMediaSessionController({
|
|
2299
2444
|
commands,
|
|
2300
2445
|
getState: () => store.getState(),
|
|
@@ -2391,16 +2536,7 @@ function PlayerProvider({
|
|
|
2391
2536
|
);
|
|
2392
2537
|
window.addEventListener("pageshow", handlePageShow);
|
|
2393
2538
|
window.addEventListener("focus", handleFocus);
|
|
2394
|
-
|
|
2395
|
-
store,
|
|
2396
|
-
commands,
|
|
2397
|
-
experience: store.experience,
|
|
2398
|
-
audioContextState: "uninitialized",
|
|
2399
|
-
audioMode: nextAudioMode,
|
|
2400
|
-
mediaSessionSupported: activeMediaSessionController.supported,
|
|
2401
|
-
mediaSessionActionsEnabled: false
|
|
2402
|
-
};
|
|
2403
|
-
setRuntime(contextValue2);
|
|
2539
|
+
setRuntimeStatus("ready");
|
|
2404
2540
|
void store.commands.setQueue(initialQueueRef.current, {
|
|
2405
2541
|
...initialQueueItemIdRef.current === void 0 ? {} : { startQueueItemId: initialQueueItemIdRef.current },
|
|
2406
2542
|
autoplay: false
|
|
@@ -2419,21 +2555,35 @@ function PlayerProvider({
|
|
|
2419
2555
|
}
|
|
2420
2556
|
unsubscribeMediaSession();
|
|
2421
2557
|
activeMediaSessionController.destroy();
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
);
|
|
2558
|
+
setRuntimeStatus("pending");
|
|
2559
|
+
detachRuntime();
|
|
2425
2560
|
void store.destroy();
|
|
2426
2561
|
};
|
|
2427
2562
|
}, [
|
|
2428
|
-
audioMode
|
|
2563
|
+
audioMode,
|
|
2564
|
+
runtimeBridge
|
|
2429
2565
|
]);
|
|
2430
|
-
const contextValue =
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2566
|
+
const contextValue = useMemo(
|
|
2567
|
+
() => ({
|
|
2568
|
+
runtimeStatus,
|
|
2569
|
+
store: runtimeBridge.store,
|
|
2570
|
+
commands: runtimeBridge.store.commands,
|
|
2571
|
+
experience: runtimeBridge.store.experience,
|
|
2572
|
+
audioContextState,
|
|
2573
|
+
audioMode: resolvedAudioMode,
|
|
2574
|
+
mediaSessionSupported,
|
|
2575
|
+
mediaSessionActionsEnabled
|
|
2576
|
+
}),
|
|
2577
|
+
[
|
|
2578
|
+
audioContextState,
|
|
2579
|
+
mediaSessionActionsEnabled,
|
|
2580
|
+
mediaSessionSupported,
|
|
2581
|
+
resolvedAudioMode,
|
|
2582
|
+
runtimeBridge,
|
|
2583
|
+
runtimeStatus
|
|
2584
|
+
]
|
|
2585
|
+
);
|
|
2586
|
+
const consumerTree = children ?? (runtimeStatus === "pending" ? loadingFallback : null);
|
|
2437
2587
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2438
2588
|
/* @__PURE__ */ jsx(
|
|
2439
2589
|
"audio",
|
|
@@ -2444,11 +2594,12 @@ function PlayerProvider({
|
|
|
2444
2594
|
"data-media-session-actions": mediaSessionActionsEnabled ? "enabled" : "inactive",
|
|
2445
2595
|
"data-media-session-supported": mediaSessionSupported ? "true" : "false",
|
|
2446
2596
|
"data-player-audio": "true",
|
|
2597
|
+
"data-player-runtime-status": runtimeStatus,
|
|
2447
2598
|
preload: "metadata",
|
|
2448
2599
|
ref: audioRef
|
|
2449
2600
|
}
|
|
2450
2601
|
),
|
|
2451
|
-
|
|
2602
|
+
/* @__PURE__ */ jsx(PlayerRuntimeContext.Provider, { value: contextValue, children: consumerTree })
|
|
2452
2603
|
] });
|
|
2453
2604
|
}
|
|
2454
2605
|
function MiniPlayerHost({
|
|
@@ -2511,6 +2662,7 @@ function usePlayer() {
|
|
|
2511
2662
|
() => context.store.getState()
|
|
2512
2663
|
);
|
|
2513
2664
|
return {
|
|
2665
|
+
runtimeStatus: context.runtimeStatus,
|
|
2514
2666
|
state,
|
|
2515
2667
|
commands: context.commands,
|
|
2516
2668
|
experience: context.experience,
|
|
@@ -2568,7 +2720,538 @@ function useBoundPlayer({
|
|
|
2568
2720
|
}, [context, player.commands, player.state, queueItem]);
|
|
2569
2721
|
return { state, commands };
|
|
2570
2722
|
}
|
|
2723
|
+
function IconFrame({
|
|
2724
|
+
children,
|
|
2725
|
+
size = 20,
|
|
2726
|
+
...props
|
|
2727
|
+
}) {
|
|
2728
|
+
return /* @__PURE__ */ jsx(
|
|
2729
|
+
"svg",
|
|
2730
|
+
{
|
|
2731
|
+
"aria-hidden": "true",
|
|
2732
|
+
focusable: "false",
|
|
2733
|
+
viewBox: "0 0 24 24",
|
|
2734
|
+
width: size,
|
|
2735
|
+
height: size,
|
|
2736
|
+
fill: "currentColor",
|
|
2737
|
+
...props,
|
|
2738
|
+
children
|
|
2739
|
+
}
|
|
2740
|
+
);
|
|
2741
|
+
}
|
|
2742
|
+
function PlayIcon(props) {
|
|
2743
|
+
return /* @__PURE__ */ jsx(IconFrame, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M7 4.8c0-1.05 1.16-1.68 2.04-1.1l11 7.2a1.3 1.3 0 0 1 0 2.2l-11 7.2A1.31 1.31 0 0 1 7 19.2V4.8Z" }) });
|
|
2744
|
+
}
|
|
2745
|
+
function PauseIcon(props) {
|
|
2746
|
+
return /* @__PURE__ */ jsxs(IconFrame, { ...props, children: [
|
|
2747
|
+
/* @__PURE__ */ jsx("rect", { x: "5", y: "4", width: "5", height: "16", rx: "0.8" }),
|
|
2748
|
+
/* @__PURE__ */ jsx("rect", { x: "14", y: "4", width: "5", height: "16", rx: "0.8" })
|
|
2749
|
+
] });
|
|
2750
|
+
}
|
|
2751
|
+
|
|
2752
|
+
// src/ui/player-utils.ts
|
|
2753
|
+
function clamp(value, min, max) {
|
|
2754
|
+
return Math.min(Math.max(value, min), max);
|
|
2755
|
+
}
|
|
2756
|
+
function formatTime(seconds) {
|
|
2757
|
+
if (seconds === null || !Number.isFinite(seconds) || seconds < 0) {
|
|
2758
|
+
return "--:--";
|
|
2759
|
+
}
|
|
2760
|
+
const wholeSeconds = Math.floor(seconds);
|
|
2761
|
+
const hours = Math.floor(wholeSeconds / 3600);
|
|
2762
|
+
const minutes = Math.floor(wholeSeconds % 3600 / 60);
|
|
2763
|
+
const remainder = wholeSeconds % 60;
|
|
2764
|
+
if (hours > 0) {
|
|
2765
|
+
return `${String(hours)}:${String(minutes).padStart(2, "0")}:${String(remainder).padStart(2, "0")}`;
|
|
2766
|
+
}
|
|
2767
|
+
return `${String(minutes).padStart(2, "0")}:${String(remainder).padStart(2, "0")}`;
|
|
2768
|
+
}
|
|
2769
|
+
function formatTimeAria(seconds) {
|
|
2770
|
+
const safeSeconds = Math.max(0, Math.floor(seconds));
|
|
2771
|
+
const hours = Math.floor(safeSeconds / 3600);
|
|
2772
|
+
const minutes = Math.floor(safeSeconds % 3600 / 60);
|
|
2773
|
+
const remainder = safeSeconds % 60;
|
|
2774
|
+
const parts = [
|
|
2775
|
+
hours > 0 ? `${String(hours)} hour${hours === 1 ? "" : "s"}` : "",
|
|
2776
|
+
minutes > 0 ? `${String(minutes)} minute${minutes === 1 ? "" : "s"}` : "",
|
|
2777
|
+
`${String(remainder)} second${remainder === 1 ? "" : "s"}`
|
|
2778
|
+
].filter(Boolean);
|
|
2779
|
+
return parts.join(" ");
|
|
2780
|
+
}
|
|
2781
|
+
function PlayerSurface({
|
|
2782
|
+
appearance,
|
|
2783
|
+
children,
|
|
2784
|
+
className,
|
|
2785
|
+
label
|
|
2786
|
+
}) {
|
|
2787
|
+
return /* @__PURE__ */ jsx(
|
|
2788
|
+
"section",
|
|
2789
|
+
{
|
|
2790
|
+
"aria-label": label,
|
|
2791
|
+
className: ["player-surface", className].filter(Boolean).join(" "),
|
|
2792
|
+
"data-appearance": appearance,
|
|
2793
|
+
children
|
|
2794
|
+
}
|
|
2795
|
+
);
|
|
2796
|
+
}
|
|
2797
|
+
function Artwork({ className, item }) {
|
|
2798
|
+
const classes = ["player-artwork", className].filter(Boolean).join(" ");
|
|
2799
|
+
if (item?.artwork !== void 0) {
|
|
2800
|
+
return /* @__PURE__ */ jsx("span", { className: `${classes} player-artwork--image`, children: /* @__PURE__ */ jsx(
|
|
2801
|
+
"img",
|
|
2802
|
+
{
|
|
2803
|
+
alt: item.artwork.alt,
|
|
2804
|
+
className: "player-artwork__image",
|
|
2805
|
+
decoding: "async",
|
|
2806
|
+
src: item.artwork.url
|
|
2807
|
+
}
|
|
2808
|
+
) });
|
|
2809
|
+
}
|
|
2810
|
+
return /* @__PURE__ */ jsx(
|
|
2811
|
+
"div",
|
|
2812
|
+
{
|
|
2813
|
+
"aria-label": "Artwork unavailable",
|
|
2814
|
+
className: `${classes} player-artwork--fallback`,
|
|
2815
|
+
role: "img",
|
|
2816
|
+
children: /* @__PURE__ */ jsx("span", { "aria-hidden": "true" })
|
|
2817
|
+
}
|
|
2818
|
+
);
|
|
2819
|
+
}
|
|
2820
|
+
function ScrollingTitle({
|
|
2821
|
+
enabled,
|
|
2822
|
+
title
|
|
2823
|
+
}) {
|
|
2824
|
+
const containsJapanese = /[\u3040-\u30ff\u3400-\u9fff]/u.test(title);
|
|
2825
|
+
const viewportRef = useRef(null);
|
|
2826
|
+
const contentRef = useRef(null);
|
|
2827
|
+
const [marquee, setMarquee] = useState({
|
|
2828
|
+
distance: 0,
|
|
2829
|
+
duration: 10,
|
|
2830
|
+
overflow: false
|
|
2831
|
+
});
|
|
2832
|
+
useEffect(() => {
|
|
2833
|
+
const viewport = viewportRef.current;
|
|
2834
|
+
const content = contentRef.current;
|
|
2835
|
+
if (viewport === null || content === null) {
|
|
2836
|
+
return;
|
|
2837
|
+
}
|
|
2838
|
+
const viewportElement = viewport;
|
|
2839
|
+
const contentElement = content;
|
|
2840
|
+
function measure() {
|
|
2841
|
+
const viewportWidth = Math.max(
|
|
2842
|
+
viewportElement.getBoundingClientRect().width,
|
|
2843
|
+
viewportElement.clientWidth
|
|
2844
|
+
);
|
|
2845
|
+
const contentWidth = Math.max(
|
|
2846
|
+
contentElement.getBoundingClientRect().width,
|
|
2847
|
+
contentElement.scrollWidth
|
|
2848
|
+
);
|
|
2849
|
+
const overflow = contentWidth - viewportWidth > 1;
|
|
2850
|
+
const distance = enabled ? contentWidth + 12 : 0;
|
|
2851
|
+
setMarquee({
|
|
2852
|
+
distance,
|
|
2853
|
+
duration: Math.max(10, distance / 22 + 4),
|
|
2854
|
+
overflow
|
|
2855
|
+
});
|
|
2856
|
+
}
|
|
2857
|
+
measure();
|
|
2858
|
+
if (typeof ResizeObserver === "undefined") {
|
|
2859
|
+
window.addEventListener("resize", measure);
|
|
2860
|
+
return () => {
|
|
2861
|
+
window.removeEventListener("resize", measure);
|
|
2862
|
+
};
|
|
2863
|
+
}
|
|
2864
|
+
const observer = new ResizeObserver(measure);
|
|
2865
|
+
observer.observe(viewportElement);
|
|
2866
|
+
observer.observe(contentElement);
|
|
2867
|
+
return () => {
|
|
2868
|
+
observer.disconnect();
|
|
2869
|
+
};
|
|
2870
|
+
}, [enabled, title]);
|
|
2871
|
+
const style = {
|
|
2872
|
+
"--player-title-scroll-offset": `${String(-marquee.distance)}px`,
|
|
2873
|
+
"--player-title-scroll-duration": `${String(marquee.duration)}s`
|
|
2874
|
+
};
|
|
2875
|
+
return /* @__PURE__ */ jsx(
|
|
2876
|
+
"strong",
|
|
2877
|
+
{
|
|
2878
|
+
className: "player-title",
|
|
2879
|
+
"data-overflow": marquee.overflow ? "true" : "false",
|
|
2880
|
+
"data-scroll": enabled ? "true" : "false",
|
|
2881
|
+
"data-script": containsJapanese ? "japanese" : "latin",
|
|
2882
|
+
ref: viewportRef,
|
|
2883
|
+
style,
|
|
2884
|
+
children: /* @__PURE__ */ jsx("span", { className: "player-title__text", ref: contentRef, children: title })
|
|
2885
|
+
}
|
|
2886
|
+
);
|
|
2887
|
+
}
|
|
2888
|
+
function TrackMetadata({
|
|
2889
|
+
className,
|
|
2890
|
+
item,
|
|
2891
|
+
scrollTitle = false,
|
|
2892
|
+
status
|
|
2893
|
+
}) {
|
|
2894
|
+
return /* @__PURE__ */ jsxs(
|
|
2895
|
+
"div",
|
|
2896
|
+
{
|
|
2897
|
+
className: ["player-metadata", className].filter(Boolean).join(" "),
|
|
2898
|
+
children: [
|
|
2899
|
+
status === void 0 ? null : /* @__PURE__ */ jsx("span", { className: "player-kicker", children: status }),
|
|
2900
|
+
/* @__PURE__ */ jsx(
|
|
2901
|
+
ScrollingTitle,
|
|
2902
|
+
{
|
|
2903
|
+
enabled: item !== null && scrollTitle,
|
|
2904
|
+
title: item?.title ?? "No media selected"
|
|
2905
|
+
},
|
|
2906
|
+
item?.title ?? "No media selected"
|
|
2907
|
+
),
|
|
2908
|
+
/* @__PURE__ */ jsx("span", { className: "player-creator", children: item === null ? "Select media to begin" : item.creatorName ?? "Artist unavailable" }),
|
|
2909
|
+
item?.collectionTitle === void 0 ? null : /* @__PURE__ */ jsx("span", { className: "player-collection", children: item.collectionTitle })
|
|
2910
|
+
]
|
|
2911
|
+
}
|
|
2912
|
+
);
|
|
2913
|
+
}
|
|
2914
|
+
function TimeReadout({
|
|
2915
|
+
className,
|
|
2916
|
+
currentTimeSec,
|
|
2917
|
+
durationSec
|
|
2918
|
+
}) {
|
|
2919
|
+
return /* @__PURE__ */ jsxs(
|
|
2920
|
+
"div",
|
|
2921
|
+
{
|
|
2922
|
+
"aria-label": `${formatTime(currentTimeSec)} of ${formatTime(durationSec)}`,
|
|
2923
|
+
className: ["player-time", className].filter(Boolean).join(" "),
|
|
2924
|
+
children: [
|
|
2925
|
+
/* @__PURE__ */ jsx("time", { children: formatTime(currentTimeSec) }),
|
|
2926
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "/" }),
|
|
2927
|
+
/* @__PURE__ */ jsx("time", { children: formatTime(durationSec) })
|
|
2928
|
+
]
|
|
2929
|
+
}
|
|
2930
|
+
);
|
|
2931
|
+
}
|
|
2932
|
+
function IconButton({
|
|
2933
|
+
children,
|
|
2934
|
+
className,
|
|
2935
|
+
disabled = false,
|
|
2936
|
+
label,
|
|
2937
|
+
onClick,
|
|
2938
|
+
pressed
|
|
2939
|
+
}) {
|
|
2940
|
+
return /* @__PURE__ */ jsx(
|
|
2941
|
+
"button",
|
|
2942
|
+
{
|
|
2943
|
+
"aria-label": label,
|
|
2944
|
+
...pressed === void 0 ? {} : { "aria-pressed": pressed },
|
|
2945
|
+
className: ["player-icon-button", className].filter(Boolean).join(" "),
|
|
2946
|
+
disabled,
|
|
2947
|
+
onClick,
|
|
2948
|
+
title: label,
|
|
2949
|
+
type: "button",
|
|
2950
|
+
children
|
|
2951
|
+
}
|
|
2952
|
+
);
|
|
2953
|
+
}
|
|
2954
|
+
function SeekBar({
|
|
2955
|
+
canSeek,
|
|
2956
|
+
className,
|
|
2957
|
+
commitMode,
|
|
2958
|
+
currentTimeSec,
|
|
2959
|
+
durationSec,
|
|
2960
|
+
label = "Seek",
|
|
2961
|
+
onSeek,
|
|
2962
|
+
status
|
|
2963
|
+
}) {
|
|
2964
|
+
const duration = durationSec !== null && Number.isFinite(durationSec) && durationSec > 0 ? durationSec : 0;
|
|
2965
|
+
const disabled = !canSeek || duration === 0 || status === "loading";
|
|
2966
|
+
const safeCurrentTime = clamp(currentTimeSec, 0, duration);
|
|
2967
|
+
const [previewTime, setPreviewTime] = useState(safeCurrentTime);
|
|
2968
|
+
const previewProgress = duration === 0 ? 0 : clamp(previewTime / duration, 0, 1) * 100;
|
|
2969
|
+
const isAdjusting = useRef(false);
|
|
2970
|
+
const lastCommitted = useRef(null);
|
|
2971
|
+
useEffect(() => {
|
|
2972
|
+
if (!isAdjusting.current) {
|
|
2973
|
+
setPreviewTime(safeCurrentTime);
|
|
2974
|
+
}
|
|
2975
|
+
}, [safeCurrentTime]);
|
|
2976
|
+
function updatePreview(value) {
|
|
2977
|
+
const nextValue = clamp(value, 0, duration);
|
|
2978
|
+
isAdjusting.current = true;
|
|
2979
|
+
setPreviewTime(nextValue);
|
|
2980
|
+
if (commitMode === "live") {
|
|
2981
|
+
onSeek(nextValue);
|
|
2982
|
+
lastCommitted.current = nextValue;
|
|
2983
|
+
}
|
|
2984
|
+
}
|
|
2985
|
+
function commitPreview(value = previewTime) {
|
|
2986
|
+
const nextValue = clamp(value, 0, duration);
|
|
2987
|
+
if (disabled || !isAdjusting.current || commitMode === "live" && lastCommitted.current === nextValue) {
|
|
2988
|
+
isAdjusting.current = false;
|
|
2989
|
+
return;
|
|
2990
|
+
}
|
|
2991
|
+
onSeek(nextValue);
|
|
2992
|
+
lastCommitted.current = nextValue;
|
|
2993
|
+
isAdjusting.current = false;
|
|
2994
|
+
}
|
|
2995
|
+
function cancelPreview() {
|
|
2996
|
+
isAdjusting.current = false;
|
|
2997
|
+
lastCommitted.current = null;
|
|
2998
|
+
setPreviewTime(safeCurrentTime);
|
|
2999
|
+
}
|
|
3000
|
+
return /* @__PURE__ */ jsxs(
|
|
3001
|
+
"label",
|
|
3002
|
+
{
|
|
3003
|
+
className: ["player-seek", className].filter(Boolean).join(" "),
|
|
3004
|
+
"data-disabled": disabled ? "true" : "false",
|
|
3005
|
+
children: [
|
|
3006
|
+
/* @__PURE__ */ jsx("span", { className: "player-visually-hidden", children: label }),
|
|
3007
|
+
/* @__PURE__ */ jsx(
|
|
3008
|
+
"span",
|
|
3009
|
+
{
|
|
3010
|
+
"aria-hidden": "true",
|
|
3011
|
+
className: "player-seek__progress",
|
|
3012
|
+
style: { width: `${String(disabled ? 0 : previewProgress)}%` }
|
|
3013
|
+
}
|
|
3014
|
+
),
|
|
3015
|
+
/* @__PURE__ */ jsx(
|
|
3016
|
+
"input",
|
|
3017
|
+
{
|
|
3018
|
+
"aria-label": label,
|
|
3019
|
+
"aria-valuetext": formatTimeAria(previewTime),
|
|
3020
|
+
disabled,
|
|
3021
|
+
max: duration || 1,
|
|
3022
|
+
min: 0,
|
|
3023
|
+
onBlur: (event) => {
|
|
3024
|
+
commitPreview(Number(event.currentTarget.value));
|
|
3025
|
+
},
|
|
3026
|
+
onChange: (event) => {
|
|
3027
|
+
updatePreview(Number(event.currentTarget.value));
|
|
3028
|
+
},
|
|
3029
|
+
onKeyUp: (event) => {
|
|
3030
|
+
commitPreview(Number(event.currentTarget.value));
|
|
3031
|
+
},
|
|
3032
|
+
onPointerDown: () => {
|
|
3033
|
+
isAdjusting.current = true;
|
|
3034
|
+
},
|
|
3035
|
+
onPointerCancel: cancelPreview,
|
|
3036
|
+
onPointerUp: (event) => {
|
|
3037
|
+
commitPreview(Number(event.currentTarget.value));
|
|
3038
|
+
},
|
|
3039
|
+
step: 0.1,
|
|
3040
|
+
type: "range",
|
|
3041
|
+
value: disabled ? 0 : previewTime
|
|
3042
|
+
}
|
|
3043
|
+
)
|
|
3044
|
+
]
|
|
3045
|
+
}
|
|
3046
|
+
);
|
|
3047
|
+
}
|
|
3048
|
+
var FALLBACK_PEAKS = [
|
|
3049
|
+
0.16,
|
|
3050
|
+
0.24,
|
|
3051
|
+
0.42,
|
|
3052
|
+
0.3,
|
|
3053
|
+
0.58,
|
|
3054
|
+
0.76,
|
|
3055
|
+
0.48,
|
|
3056
|
+
0.34,
|
|
3057
|
+
0.66,
|
|
3058
|
+
0.88,
|
|
3059
|
+
0.62,
|
|
3060
|
+
0.4,
|
|
3061
|
+
0.52,
|
|
3062
|
+
0.72,
|
|
3063
|
+
0.44,
|
|
3064
|
+
0.28,
|
|
3065
|
+
0.56,
|
|
3066
|
+
0.82,
|
|
3067
|
+
0.68,
|
|
3068
|
+
0.38,
|
|
3069
|
+
0.24,
|
|
3070
|
+
0.46,
|
|
3071
|
+
0.32,
|
|
3072
|
+
0.18
|
|
3073
|
+
];
|
|
3074
|
+
var BAR_COUNT = 512;
|
|
3075
|
+
var VIEWBOX_WIDTH = BAR_COUNT;
|
|
3076
|
+
function createWaveformBars(peaks) {
|
|
3077
|
+
const source = peaks.length > 1 ? peaks : FALLBACK_PEAKS;
|
|
3078
|
+
return Array.from({ length: BAR_COUNT }, (_, index) => {
|
|
3079
|
+
const sourcePosition = index / Math.max(BAR_COUNT - 1, 1) * (source.length - 1);
|
|
3080
|
+
const leftIndex = Math.floor(sourcePosition);
|
|
3081
|
+
const rightIndex = Math.min(leftIndex + 1, source.length - 1);
|
|
3082
|
+
const mix = sourcePosition - leftIndex;
|
|
3083
|
+
const leftPeak = Math.abs(source[leftIndex] ?? 0);
|
|
3084
|
+
const rightPeak = Math.abs(source[rightIndex] ?? leftPeak);
|
|
3085
|
+
const interpolated = leftPeak + (rightPeak - leftPeak) * mix;
|
|
3086
|
+
if (source.length >= BAR_COUNT) {
|
|
3087
|
+
return clamp(Math.pow(interpolated, 1.18), 0.04, 1);
|
|
3088
|
+
}
|
|
3089
|
+
const noiseSeed = Math.sin((index + 1) * 12.9898 + source.length * 78.233) * 43758.5453;
|
|
3090
|
+
const noise = noiseSeed - Math.floor(noiseSeed);
|
|
3091
|
+
const fineDetail = 0.76 + noise * 0.34;
|
|
3092
|
+
const transient = noise > 0.975 ? 3.2 : noise > 0.91 ? 1.75 : 1;
|
|
3093
|
+
return clamp(
|
|
3094
|
+
Math.pow(interpolated, 1.22) * fineDetail * transient,
|
|
3095
|
+
0.04,
|
|
3096
|
+
1
|
|
3097
|
+
);
|
|
3098
|
+
});
|
|
3099
|
+
}
|
|
3100
|
+
function Waveform({
|
|
3101
|
+
className,
|
|
3102
|
+
opacity = 1,
|
|
3103
|
+
playedStateEnabled = true,
|
|
3104
|
+
progress = 0,
|
|
3105
|
+
waveform
|
|
3106
|
+
}) {
|
|
3107
|
+
const clipId = useId().replaceAll(":", "");
|
|
3108
|
+
const bars = createWaveformBars(waveform?.peaks ?? FALLBACK_PEAKS);
|
|
3109
|
+
const progressWidth = clamp(progress, 0, 1) * VIEWBOX_WIDTH;
|
|
3110
|
+
const classes = ["player-waveform", className].filter(Boolean).join(" ");
|
|
3111
|
+
const barPath = bars.map((peak, index) => {
|
|
3112
|
+
const halfHeight = peak * 47;
|
|
3113
|
+
const x = index + 0.5;
|
|
3114
|
+
return `M ${String(x)} ${String(50 - halfHeight)} V ${String(
|
|
3115
|
+
50 + halfHeight
|
|
3116
|
+
)}`;
|
|
3117
|
+
}).join(" ");
|
|
3118
|
+
return /* @__PURE__ */ jsxs(
|
|
3119
|
+
"svg",
|
|
3120
|
+
{
|
|
3121
|
+
"aria-hidden": "true",
|
|
3122
|
+
className: classes,
|
|
3123
|
+
focusable: "false",
|
|
3124
|
+
preserveAspectRatio: "none",
|
|
3125
|
+
style: { opacity: clamp(opacity, 0, 1), pointerEvents: "none" },
|
|
3126
|
+
viewBox: `0 0 ${String(VIEWBOX_WIDTH)} 100`,
|
|
3127
|
+
children: [
|
|
3128
|
+
/* @__PURE__ */ jsx("path", { className: "player-waveform__base", d: barPath }),
|
|
3129
|
+
playedStateEnabled ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3130
|
+
/* @__PURE__ */ jsx("clipPath", { id: clipId, children: /* @__PURE__ */ jsx("rect", { width: progressWidth, height: "100" }) }),
|
|
3131
|
+
/* @__PURE__ */ jsx(
|
|
3132
|
+
"path",
|
|
3133
|
+
{
|
|
3134
|
+
className: "player-waveform__played",
|
|
3135
|
+
clipPath: `url(#${clipId})`,
|
|
3136
|
+
d: barPath
|
|
3137
|
+
}
|
|
3138
|
+
)
|
|
3139
|
+
] }) : null
|
|
3140
|
+
]
|
|
3141
|
+
}
|
|
3142
|
+
);
|
|
3143
|
+
}
|
|
3144
|
+
function BoundCompactPlayerView({
|
|
3145
|
+
appearance,
|
|
3146
|
+
className,
|
|
3147
|
+
commands,
|
|
3148
|
+
contextId,
|
|
3149
|
+
contextMode,
|
|
3150
|
+
options,
|
|
3151
|
+
runtimeStatus,
|
|
3152
|
+
state
|
|
3153
|
+
}) {
|
|
3154
|
+
const progress = state.durationSec === null || state.durationSec <= 0 ? 0 : state.currentTimeSec / state.durationSec;
|
|
3155
|
+
return /* @__PURE__ */ jsx(
|
|
3156
|
+
PlayerSurface,
|
|
3157
|
+
{
|
|
3158
|
+
appearance,
|
|
3159
|
+
className: ["player-bound-compact", className].filter(Boolean).join(" "),
|
|
3160
|
+
label: `Inline player: ${state.item.title}`,
|
|
3161
|
+
children: /* @__PURE__ */ jsxs(
|
|
3162
|
+
"div",
|
|
3163
|
+
{
|
|
3164
|
+
className: "player-bound-compact__main",
|
|
3165
|
+
"data-active": state.isActive ? "true" : "false",
|
|
3166
|
+
"data-playback-context-id": contextId,
|
|
3167
|
+
"data-playback-context-mode": contextMode,
|
|
3168
|
+
"data-player-runtime-status": runtimeStatus,
|
|
3169
|
+
"data-queue-item-id": state.queueItemId,
|
|
3170
|
+
children: [
|
|
3171
|
+
/* @__PURE__ */ jsx(
|
|
3172
|
+
IconButton,
|
|
3173
|
+
{
|
|
3174
|
+
className: "player-play-button",
|
|
3175
|
+
label: state.isPlaying ? "Pause" : "Play",
|
|
3176
|
+
onClick: () => {
|
|
3177
|
+
void commands.togglePlayback();
|
|
3178
|
+
},
|
|
3179
|
+
children: state.isPlaying ? /* @__PURE__ */ jsx(PauseIcon, { size: 22 }) : /* @__PURE__ */ jsx(PlayIcon, { size: 22 })
|
|
3180
|
+
}
|
|
3181
|
+
),
|
|
3182
|
+
/* @__PURE__ */ jsx(
|
|
3183
|
+
Artwork,
|
|
3184
|
+
{
|
|
3185
|
+
className: "player-bound-compact__artwork",
|
|
3186
|
+
item: state.item
|
|
3187
|
+
}
|
|
3188
|
+
),
|
|
3189
|
+
/* @__PURE__ */ jsx(TrackMetadata, { item: state.item, scrollTitle: state.isPlaying }),
|
|
3190
|
+
/* @__PURE__ */ jsxs("div", { className: "player-bound-compact__signal", children: [
|
|
3191
|
+
/* @__PURE__ */ jsx(
|
|
3192
|
+
Waveform,
|
|
3193
|
+
{
|
|
3194
|
+
opacity: options.waveformOpacity,
|
|
3195
|
+
playedStateEnabled: options.waveformPlayedStateEnabled,
|
|
3196
|
+
progress,
|
|
3197
|
+
waveform: state.item.waveform
|
|
3198
|
+
}
|
|
3199
|
+
),
|
|
3200
|
+
/* @__PURE__ */ jsx(
|
|
3201
|
+
TimeReadout,
|
|
3202
|
+
{
|
|
3203
|
+
currentTimeSec: state.currentTimeSec,
|
|
3204
|
+
durationSec: state.durationSec
|
|
3205
|
+
}
|
|
3206
|
+
)
|
|
3207
|
+
] }),
|
|
3208
|
+
/* @__PURE__ */ jsx(
|
|
3209
|
+
SeekBar,
|
|
3210
|
+
{
|
|
3211
|
+
canSeek: state.canSeek,
|
|
3212
|
+
commitMode: options.seekCommitMode,
|
|
3213
|
+
currentTimeSec: state.currentTimeSec,
|
|
3214
|
+
durationSec: state.durationSec,
|
|
3215
|
+
label: `Seek in ${state.item.title}`,
|
|
3216
|
+
onSeek: (seconds) => {
|
|
3217
|
+
commands.seekTo(seconds);
|
|
3218
|
+
},
|
|
3219
|
+
status: state.status
|
|
3220
|
+
}
|
|
3221
|
+
)
|
|
3222
|
+
]
|
|
3223
|
+
}
|
|
3224
|
+
)
|
|
3225
|
+
}
|
|
3226
|
+
);
|
|
3227
|
+
}
|
|
3228
|
+
function BoundCompactPlayer({
|
|
3229
|
+
appearance,
|
|
3230
|
+
className,
|
|
3231
|
+
context,
|
|
3232
|
+
queueItem
|
|
3233
|
+
}) {
|
|
3234
|
+
const bound = useBoundPlayer({ context, queueItem });
|
|
3235
|
+
const player = usePlayer();
|
|
3236
|
+
return /* @__PURE__ */ jsx(
|
|
3237
|
+
BoundCompactPlayerView,
|
|
3238
|
+
{
|
|
3239
|
+
appearance,
|
|
3240
|
+
...className === void 0 ? {} : { className },
|
|
3241
|
+
commands: bound.commands,
|
|
3242
|
+
contextId: context.id,
|
|
3243
|
+
contextMode: context.mode,
|
|
3244
|
+
runtimeStatus: player.runtimeStatus,
|
|
3245
|
+
options: {
|
|
3246
|
+
seekCommitMode: player.state.experienceConfig.seekCommitMode,
|
|
3247
|
+
waveformOpacity: player.state.experienceConfig.waveformOpacity,
|
|
3248
|
+
waveformPlayedStateEnabled: player.state.experienceConfig.waveformPlayedStateEnabled
|
|
3249
|
+
},
|
|
3250
|
+
state: bound.state
|
|
3251
|
+
}
|
|
3252
|
+
);
|
|
3253
|
+
}
|
|
2571
3254
|
|
|
2572
|
-
export { MiniPlayerHost, PlayerProvider, deriveBoundPlayerViewState, useBoundPlayer, usePlayer };
|
|
3255
|
+
export { BoundCompactPlayer, MiniPlayerHost, PlayerProvider, deriveBoundPlayerViewState, useBoundPlayer, usePlayer };
|
|
2573
3256
|
//# sourceMappingURL=index.js.map
|
|
2574
3257
|
//# sourceMappingURL=index.js.map
|