@livepeer-frameworks/player-svelte 0.1.2 → 0.2.1
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/LICENSE.md +24 -0
- package/README.md +6 -2
- package/dist/DevModePanel.svelte +53 -16
- package/dist/IdleScreen.svelte +36 -28
- package/dist/LoadingScreen.svelte +107 -67
- package/dist/LoadingScreen.svelte.bak +702 -0
- package/dist/Player.svelte +200 -53
- package/dist/Player.svelte.d.ts +6 -1
- package/dist/PlayerControls.svelte +114 -32
- package/dist/PlayerControls.svelte.d.ts +3 -0
- package/dist/StreamStateOverlay.svelte +33 -21
- package/dist/SubtitleRenderer.svelte +2 -2
- package/dist/controls/FullscreenButton.svelte +26 -0
- package/dist/controls/FullscreenButton.svelte.d.ts +3 -0
- package/dist/controls/LiveBadge.svelte +23 -0
- package/dist/controls/LiveBadge.svelte.d.ts +3 -0
- package/dist/controls/PlayButton.svelte +26 -0
- package/dist/controls/PlayButton.svelte.d.ts +3 -0
- package/dist/controls/SettingsMenu.svelte +208 -0
- package/dist/controls/SettingsMenu.svelte.d.ts +28 -0
- package/dist/controls/SkipButton.svelte +33 -0
- package/dist/controls/SkipButton.svelte.d.ts +7 -0
- package/dist/controls/TimeDisplay.svelte +18 -0
- package/dist/controls/TimeDisplay.svelte.d.ts +3 -0
- package/dist/controls/VolumeControl.svelte +26 -0
- package/dist/controls/VolumeControl.svelte.d.ts +3 -0
- package/dist/controls/index.d.ts +7 -0
- package/dist/controls/index.js +7 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -1
- package/dist/stores/i18n.d.ts +3 -0
- package/dist/stores/i18n.js +4 -0
- package/dist/stores/index.d.ts +1 -0
- package/dist/stores/index.js +2 -0
- package/dist/stores/playerController.d.ts +2 -0
- package/dist/stores/playerController.js +4 -0
- package/package.json +19 -19
- package/src/DevModePanel.svelte +53 -16
- package/src/IdleScreen.svelte +12 -4
- package/src/LoadingScreen.svelte +90 -50
- package/src/LoadingScreen.svelte.bak +702 -0
- package/src/Player.svelte +200 -53
- package/src/PlayerControls.svelte +114 -32
- package/src/StreamStateOverlay.svelte +17 -5
- package/src/controls/FullscreenButton.svelte +26 -0
- package/src/controls/LiveBadge.svelte +23 -0
- package/src/controls/PlayButton.svelte +26 -0
- package/src/controls/SettingsMenu.svelte +208 -0
- package/src/controls/SkipButton.svelte +33 -0
- package/src/controls/TimeDisplay.svelte +18 -0
- package/src/controls/VolumeControl.svelte +26 -0
- package/src/controls/index.ts +7 -0
- package/src/index.ts +10 -0
- package/src/stores/i18n.ts +7 -0
- package/src/stores/index.ts +3 -0
- package/src/stores/playerController.ts +7 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getContext } from "svelte";
|
|
3
|
+
import type { Readable } from "svelte/store";
|
|
4
|
+
import { SettingsIcon } from "../icons";
|
|
5
|
+
import {
|
|
6
|
+
SPEED_PRESETS,
|
|
7
|
+
getAvailableLocales,
|
|
8
|
+
getLocaleDisplayName,
|
|
9
|
+
createTranslator,
|
|
10
|
+
type TranslateFn,
|
|
11
|
+
} from "@livepeer-frameworks/player-core";
|
|
12
|
+
import type { FwLocale } from "@livepeer-frameworks/player-core";
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
qualities?: Array<{ id: string; label: string; active?: boolean }>;
|
|
16
|
+
activeQuality?: string;
|
|
17
|
+
onSelectQuality?: (id: string) => void;
|
|
18
|
+
textTracks?: Array<{ id: string; label: string; active?: boolean }>;
|
|
19
|
+
activeCaption?: string;
|
|
20
|
+
onSelectCaption?: (id: string) => void;
|
|
21
|
+
playbackRate?: number;
|
|
22
|
+
onSpeedChange?: (rate: number) => void;
|
|
23
|
+
supportsSpeed?: boolean;
|
|
24
|
+
playbackMode?: "auto" | "low-latency" | "quality";
|
|
25
|
+
onModeChange?: (mode: "auto" | "low-latency" | "quality") => void;
|
|
26
|
+
showModeSelector?: boolean;
|
|
27
|
+
activeLocale?: FwLocale;
|
|
28
|
+
onLocaleChange?: (locale: FwLocale) => void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let {
|
|
32
|
+
qualities: propQualities,
|
|
33
|
+
activeQuality,
|
|
34
|
+
onSelectQuality,
|
|
35
|
+
textTracks: propTextTracks,
|
|
36
|
+
activeCaption,
|
|
37
|
+
onSelectCaption,
|
|
38
|
+
playbackRate = 1,
|
|
39
|
+
onSpeedChange,
|
|
40
|
+
supportsSpeed = true,
|
|
41
|
+
playbackMode,
|
|
42
|
+
onModeChange,
|
|
43
|
+
showModeSelector = false,
|
|
44
|
+
activeLocale = undefined,
|
|
45
|
+
onLocaleChange = undefined,
|
|
46
|
+
}: Props = $props();
|
|
47
|
+
|
|
48
|
+
let availableLocales = getAvailableLocales();
|
|
49
|
+
|
|
50
|
+
let controller: any = getContext("fw-player-controller");
|
|
51
|
+
const translatorCtx = getContext<Readable<TranslateFn> | undefined>("fw-translator");
|
|
52
|
+
const fallbackT = createTranslator({ locale: "en" });
|
|
53
|
+
let t: TranslateFn = $derived(translatorCtx ? $translatorCtx : fallbackT);
|
|
54
|
+
let isOpen = $state(false);
|
|
55
|
+
|
|
56
|
+
let qualities = $derived(propQualities ?? controller?.getQualities?.() ?? []);
|
|
57
|
+
let qualityValue = $derived(activeQuality ?? qualities.find((q: any) => q.active)?.id ?? "auto");
|
|
58
|
+
let textTracks = $derived(propTextTracks ?? []);
|
|
59
|
+
let captionValue = $derived(activeCaption ?? textTracks.find((t: any) => t.active)?.id ?? "none");
|
|
60
|
+
|
|
61
|
+
function selectQuality(id: string) {
|
|
62
|
+
if (onSelectQuality) onSelectQuality(id);
|
|
63
|
+
else controller?.selectQuality?.(id);
|
|
64
|
+
isOpen = false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function handleKeyDown(e: KeyboardEvent) {
|
|
68
|
+
if (e.key === "Escape") {
|
|
69
|
+
isOpen = false;
|
|
70
|
+
e.preventDefault();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (e.key === "ArrowDown" || e.key === "ArrowUp") {
|
|
74
|
+
e.preventDefault();
|
|
75
|
+
const menu = e.currentTarget as HTMLElement;
|
|
76
|
+
const items = menu.querySelectorAll<HTMLButtonElement>("button");
|
|
77
|
+
if (!items.length) return;
|
|
78
|
+
const current = Array.from(items).indexOf(document.activeElement as HTMLButtonElement);
|
|
79
|
+
const next =
|
|
80
|
+
e.key === "ArrowDown"
|
|
81
|
+
? (current + 1) % items.length
|
|
82
|
+
: (current - 1 + items.length) % items.length;
|
|
83
|
+
items[next]?.focus();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
</script>
|
|
87
|
+
|
|
88
|
+
<div class="fw-control-group" style="position: relative">
|
|
89
|
+
<button
|
|
90
|
+
type="button"
|
|
91
|
+
class="fw-btn-flush group"
|
|
92
|
+
class:fw-btn-flush--active={isOpen}
|
|
93
|
+
aria-label={t("settings")}
|
|
94
|
+
title={t("settings")}
|
|
95
|
+
onclick={() => (isOpen = !isOpen)}
|
|
96
|
+
>
|
|
97
|
+
<SettingsIcon size={16} />
|
|
98
|
+
</button>
|
|
99
|
+
|
|
100
|
+
{#if isOpen}
|
|
101
|
+
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
|
102
|
+
<div class="fw-settings-menu" role="menu" aria-label={t("settings")} onkeydown={handleKeyDown}>
|
|
103
|
+
{#if showModeSelector && onModeChange}
|
|
104
|
+
<div class="fw-settings-section">
|
|
105
|
+
<div class="fw-settings-label">{t("mode")}</div>
|
|
106
|
+
<div class="fw-settings-options">
|
|
107
|
+
{#each ["auto", "low-latency", "quality"] as mode}
|
|
108
|
+
<button
|
|
109
|
+
class="fw-settings-btn"
|
|
110
|
+
class:fw-settings-btn--active={playbackMode === mode}
|
|
111
|
+
onclick={() => {
|
|
112
|
+
onModeChange?.(mode as any);
|
|
113
|
+
isOpen = false;
|
|
114
|
+
}}
|
|
115
|
+
>
|
|
116
|
+
{mode === "low-latency" ? t("fast") : mode === "quality" ? t("stable") : t("auto")}
|
|
117
|
+
</button>
|
|
118
|
+
{/each}
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
{/if}
|
|
122
|
+
|
|
123
|
+
{#if supportsSpeed}
|
|
124
|
+
<div class="fw-settings-section">
|
|
125
|
+
<div class="fw-settings-label">{t("speed")}</div>
|
|
126
|
+
<div class="fw-settings-options fw-settings-options--wrap">
|
|
127
|
+
{#each SPEED_PRESETS as rate}
|
|
128
|
+
<button
|
|
129
|
+
class="fw-settings-btn"
|
|
130
|
+
class:fw-settings-btn--active={playbackRate === rate}
|
|
131
|
+
onclick={() => {
|
|
132
|
+
onSpeedChange?.(rate);
|
|
133
|
+
isOpen = false;
|
|
134
|
+
}}
|
|
135
|
+
>
|
|
136
|
+
{rate}x
|
|
137
|
+
</button>
|
|
138
|
+
{/each}
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
{/if}
|
|
142
|
+
|
|
143
|
+
{#if qualities.length > 0}
|
|
144
|
+
<div class="fw-settings-section">
|
|
145
|
+
<div class="fw-settings-label">{t("quality")}</div>
|
|
146
|
+
<div class="fw-settings-list">
|
|
147
|
+
<button
|
|
148
|
+
class="fw-settings-list-item"
|
|
149
|
+
class:fw-settings-list-item--active={qualityValue === "auto"}
|
|
150
|
+
onclick={() => selectQuality("auto")}>{t("auto")}</button
|
|
151
|
+
>
|
|
152
|
+
{#each qualities as q}
|
|
153
|
+
<button
|
|
154
|
+
class="fw-settings-list-item"
|
|
155
|
+
class:fw-settings-list-item--active={qualityValue === q.id}
|
|
156
|
+
onclick={() => selectQuality(q.id)}>{q.label}</button
|
|
157
|
+
>
|
|
158
|
+
{/each}
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
{/if}
|
|
162
|
+
|
|
163
|
+
{#if textTracks.length > 0}
|
|
164
|
+
<div class="fw-settings-section">
|
|
165
|
+
<div class="fw-settings-label">{t("captions")}</div>
|
|
166
|
+
<div class="fw-settings-list">
|
|
167
|
+
<button
|
|
168
|
+
class="fw-settings-list-item"
|
|
169
|
+
class:fw-settings-list-item--active={captionValue === "none"}
|
|
170
|
+
onclick={() => {
|
|
171
|
+
onSelectCaption?.("none");
|
|
172
|
+
isOpen = false;
|
|
173
|
+
}}>{t("captionsOff")}</button
|
|
174
|
+
>
|
|
175
|
+
{#each textTracks as tt}
|
|
176
|
+
<button
|
|
177
|
+
class="fw-settings-list-item"
|
|
178
|
+
class:fw-settings-list-item--active={captionValue === tt.id}
|
|
179
|
+
onclick={() => {
|
|
180
|
+
onSelectCaption?.(tt.id);
|
|
181
|
+
isOpen = false;
|
|
182
|
+
}}>{tt.label || tt.id}</button
|
|
183
|
+
>
|
|
184
|
+
{/each}
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
187
|
+
{/if}
|
|
188
|
+
|
|
189
|
+
{#if onLocaleChange}
|
|
190
|
+
<div class="fw-settings-section">
|
|
191
|
+
<div class="fw-settings-label">{t("language")}</div>
|
|
192
|
+
<div class="fw-settings-list">
|
|
193
|
+
{#each availableLocales as l}
|
|
194
|
+
<button
|
|
195
|
+
class="fw-settings-list-item"
|
|
196
|
+
class:fw-settings-list-item--active={activeLocale === l}
|
|
197
|
+
onclick={() => {
|
|
198
|
+
onLocaleChange?.(l);
|
|
199
|
+
isOpen = false;
|
|
200
|
+
}}>{getLocaleDisplayName(l)}</button
|
|
201
|
+
>
|
|
202
|
+
{/each}
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
{/if}
|
|
206
|
+
</div>
|
|
207
|
+
{/if}
|
|
208
|
+
</div>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { FwLocale } from "@livepeer-frameworks/player-core";
|
|
2
|
+
interface Props {
|
|
3
|
+
qualities?: Array<{
|
|
4
|
+
id: string;
|
|
5
|
+
label: string;
|
|
6
|
+
active?: boolean;
|
|
7
|
+
}>;
|
|
8
|
+
activeQuality?: string;
|
|
9
|
+
onSelectQuality?: (id: string) => void;
|
|
10
|
+
textTracks?: Array<{
|
|
11
|
+
id: string;
|
|
12
|
+
label: string;
|
|
13
|
+
active?: boolean;
|
|
14
|
+
}>;
|
|
15
|
+
activeCaption?: string;
|
|
16
|
+
onSelectCaption?: (id: string) => void;
|
|
17
|
+
playbackRate?: number;
|
|
18
|
+
onSpeedChange?: (rate: number) => void;
|
|
19
|
+
supportsSpeed?: boolean;
|
|
20
|
+
playbackMode?: "auto" | "low-latency" | "quality";
|
|
21
|
+
onModeChange?: (mode: "auto" | "low-latency" | "quality") => void;
|
|
22
|
+
showModeSelector?: boolean;
|
|
23
|
+
activeLocale?: FwLocale;
|
|
24
|
+
onLocaleChange?: (locale: FwLocale) => void;
|
|
25
|
+
}
|
|
26
|
+
declare const SettingsMenu: import("svelte").Component<Props, {}, "">;
|
|
27
|
+
type SettingsMenu = ReturnType<typeof SettingsMenu>;
|
|
28
|
+
export default SettingsMenu;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getContext } from "svelte";
|
|
3
|
+
import type { Readable } from "svelte/store";
|
|
4
|
+
import { createTranslator, type TranslateFn } from "@livepeer-frameworks/player-core";
|
|
5
|
+
import { SkipBackIcon, SkipForwardIcon } from "../icons";
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
direction: "back" | "forward";
|
|
9
|
+
seconds?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let { direction, seconds = 10 }: Props = $props();
|
|
13
|
+
let pc: any = getContext("fw-player-controller");
|
|
14
|
+
const translatorCtx = getContext<Readable<TranslateFn> | undefined>("fw-translator");
|
|
15
|
+
const fallbackT = createTranslator({ locale: "en" });
|
|
16
|
+
let t: TranslateFn = $derived(translatorCtx ? $translatorCtx : fallbackT);
|
|
17
|
+
|
|
18
|
+
let label = $derived(direction === "back" ? t("skipBackward") : t("skipForward"));
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<button
|
|
22
|
+
type="button"
|
|
23
|
+
class="fw-btn-flush"
|
|
24
|
+
aria-label={label}
|
|
25
|
+
title={label}
|
|
26
|
+
onclick={() => pc?.seek((pc?.currentTime ?? 0) + (direction === "back" ? -seconds : seconds))}
|
|
27
|
+
>
|
|
28
|
+
{#if direction === "back"}
|
|
29
|
+
<SkipBackIcon size={16} />
|
|
30
|
+
{:else}
|
|
31
|
+
<SkipForwardIcon size={16} />
|
|
32
|
+
{/if}
|
|
33
|
+
</button>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getContext } from "svelte";
|
|
3
|
+
import { formatTimeDisplay } from "@livepeer-frameworks/player-core";
|
|
4
|
+
|
|
5
|
+
let pc: any = getContext("fw-player-controller");
|
|
6
|
+
|
|
7
|
+
let timeText = $derived(
|
|
8
|
+
formatTimeDisplay({
|
|
9
|
+
isLive: pc?.isEffectivelyLive ?? false,
|
|
10
|
+
currentTime: pc?.currentTime ?? 0,
|
|
11
|
+
duration: pc?.duration ?? NaN,
|
|
12
|
+
liveEdge: pc?.duration ?? 0,
|
|
13
|
+
seekableStart: 0,
|
|
14
|
+
})
|
|
15
|
+
);
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<span class="fw-time-display">{timeText}</span>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getContext } from "svelte";
|
|
3
|
+
import type { Readable } from "svelte/store";
|
|
4
|
+
import { createTranslator, type TranslateFn } from "@livepeer-frameworks/player-core";
|
|
5
|
+
import { VolumeUpIcon, VolumeOffIcon } from "../icons";
|
|
6
|
+
|
|
7
|
+
let pc: any = getContext("fw-player-controller");
|
|
8
|
+
const translatorCtx = getContext<Readable<TranslateFn> | undefined>("fw-translator");
|
|
9
|
+
const fallbackT = createTranslator({ locale: "en" });
|
|
10
|
+
let t: TranslateFn = $derived(translatorCtx ? $translatorCtx : fallbackT);
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<button
|
|
14
|
+
type="button"
|
|
15
|
+
class="fw-btn-flush"
|
|
16
|
+
aria-label={pc?.isMuted ? t("unmute") : t("mute")}
|
|
17
|
+
aria-pressed={pc?.isMuted ?? false}
|
|
18
|
+
title={pc?.isMuted ? t("unmute") : t("mute")}
|
|
19
|
+
onclick={() => pc?.toggleMute()}
|
|
20
|
+
>
|
|
21
|
+
{#if pc?.isMuted}
|
|
22
|
+
<VolumeOffIcon size={16} />
|
|
23
|
+
{:else}
|
|
24
|
+
<VolumeUpIcon size={16} />
|
|
25
|
+
{/if}
|
|
26
|
+
</button>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { default as PlayButton } from "./PlayButton.svelte";
|
|
2
|
+
export { default as SkipButton } from "./SkipButton.svelte";
|
|
3
|
+
export { default as VolumeControl } from "./VolumeControl.svelte";
|
|
4
|
+
export { default as TimeDisplay } from "./TimeDisplay.svelte";
|
|
5
|
+
export { default as LiveBadge } from "./LiveBadge.svelte";
|
|
6
|
+
export { default as FullscreenButton } from "./FullscreenButton.svelte";
|
|
7
|
+
export { default as SettingsMenu } from "./SettingsMenu.svelte";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { default as PlayButton } from "./PlayButton.svelte";
|
|
2
|
+
export { default as SkipButton } from "./SkipButton.svelte";
|
|
3
|
+
export { default as VolumeControl } from "./VolumeControl.svelte";
|
|
4
|
+
export { default as TimeDisplay } from "./TimeDisplay.svelte";
|
|
5
|
+
export { default as LiveBadge } from "./LiveBadge.svelte";
|
|
6
|
+
export { default as FullscreenButton } from "./FullscreenButton.svelte";
|
|
7
|
+
export { default as SettingsMenu } from "./SettingsMenu.svelte";
|
package/dist/index.d.ts
CHANGED
|
@@ -41,10 +41,11 @@ export { default as TitleOverlay } from "./TitleOverlay.svelte";
|
|
|
41
41
|
export { default as ThumbnailOverlay } from "./ThumbnailOverlay.svelte";
|
|
42
42
|
export { default as StatsPanel } from "./StatsPanel.svelte";
|
|
43
43
|
export { default as DevModePanel } from "./DevModePanel.svelte";
|
|
44
|
+
export * from "./controls";
|
|
44
45
|
export * from "./icons";
|
|
45
46
|
export * from "./stores";
|
|
46
47
|
export * from "./ui/context-menu";
|
|
47
48
|
export type { SkipDirection } from "./types";
|
|
48
|
-
export { PlayerController, PlayerManager, globalPlayerManager, } from "@livepeer-frameworks/player-core";
|
|
49
|
+
export { PlayerController, PlayerManager, globalPlayerManager, createTranslator, getAvailableLocales, getLocaleDisplayName, } from "@livepeer-frameworks/player-core";
|
|
49
50
|
export type { PlayerControllerConfig, PlayerControllerEvents, } from "@livepeer-frameworks/player-core";
|
|
50
|
-
export type { PlayerState, PlayerStateContext, StreamState, StreamStatus, ContentMetadata, ContentEndpoints, EndpointInfo, PlaybackMode, PlaybackQuality, MistStreamInfo, PlayerOptions, PlayerMetadata, PlayerSelection, PlayerCombination, PlayerManagerOptions, PlayerManagerEvents, } from "@livepeer-frameworks/player-core";
|
|
51
|
+
export type { PlayerState, PlayerStateContext, StreamState, StreamStatus, ContentMetadata, ContentEndpoints, EndpointInfo, PlaybackMode, PlaybackQuality, MistStreamInfo, PlayerOptions, PlayerMetadata, PlayerSelection, PlayerCombination, PlayerManagerOptions, PlayerManagerEvents, FwLocale, TranslateFn, TranslationStrings, I18nConfig, } from "@livepeer-frameworks/player-core";
|
package/dist/index.js
CHANGED
|
@@ -44,6 +44,8 @@ export { default as TitleOverlay } from "./TitleOverlay.svelte";
|
|
|
44
44
|
export { default as ThumbnailOverlay } from "./ThumbnailOverlay.svelte";
|
|
45
45
|
export { default as StatsPanel } from "./StatsPanel.svelte";
|
|
46
46
|
export { default as DevModePanel } from "./DevModePanel.svelte";
|
|
47
|
+
// Composable control components
|
|
48
|
+
export * from "./controls";
|
|
47
49
|
// Icon components
|
|
48
50
|
export * from "./icons";
|
|
49
51
|
// Stores
|
|
@@ -51,4 +53,4 @@ export * from "./stores";
|
|
|
51
53
|
// Context menu components
|
|
52
54
|
export * from "./ui/context-menu";
|
|
53
55
|
// Re-export core types and classes for Svelte users
|
|
54
|
-
export { PlayerController, PlayerManager, globalPlayerManager, } from "@livepeer-frameworks/player-core";
|
|
56
|
+
export { PlayerController, PlayerManager, globalPlayerManager, createTranslator, getAvailableLocales, getLocaleDisplayName, } from "@livepeer-frameworks/player-core";
|
package/dist/stores/index.d.ts
CHANGED
|
@@ -12,4 +12,5 @@ export { createEndpointResolver, createDerivedEndpoints, createDerivedPrimaryEnd
|
|
|
12
12
|
export { createPlayerContext, setPlayerContextInComponent, getPlayerContextFromComponent, getPlayerContextOrFallback, createDerivedVideoElement, createDerivedIsReady, createDerivedPlayerInfo, type PlayerContextState, type PlayerContextStore, } from "./playerContext";
|
|
13
13
|
export { createPlaybackQualityMonitor, createDerivedQualityScore, createDerivedStallCount, createDerivedFrameDropRate, createDerivedBitrate, createDerivedLatency, type PlaybackQualityOptions, type PlaybackQualityStore, } from "./playbackQuality";
|
|
14
14
|
export { createPlayerSelectionStore, createDerivedSelection, createDerivedCombinations, createDerivedReady, createDerivedSelectedPlayer, createDerivedSelectedSourceType, createDerivedCompatibleCombinations, createDerivedIncompatibleCombinations, type PlayerSelectionOptions, type PlayerSelectionState, type PlayerSelectionStore, } from "./playerSelection";
|
|
15
|
+
export { localeStore, translatorStore } from "./i18n";
|
|
15
16
|
export { createPlayerControllerStore, createDerivedState, createDerivedIsPlaying, createDerivedCurrentTime, createDerivedDuration, createDerivedError, createDerivedVideoElement as createDerivedControllerVideoElement, createDerivedShouldShowControls, createDerivedShouldShowIdleScreen, type PlayerControllerStoreConfig, type PlayerControllerState, type PlayerControllerStore, } from "./playerController";
|
package/dist/stores/index.js
CHANGED
|
@@ -17,5 +17,7 @@ export { createPlayerContext, setPlayerContextInComponent, getPlayerContextFromC
|
|
|
17
17
|
export { createPlaybackQualityMonitor, createDerivedQualityScore, createDerivedStallCount, createDerivedFrameDropRate, createDerivedBitrate, createDerivedLatency, } from "./playbackQuality";
|
|
18
18
|
// Player selection (event-driven, cached)
|
|
19
19
|
export { createPlayerSelectionStore, createDerivedSelection, createDerivedCombinations, createDerivedReady, createDerivedSelectedPlayer, createDerivedSelectedSourceType, createDerivedCompatibleCombinations, createDerivedIncompatibleCombinations, } from "./playerSelection";
|
|
20
|
+
// i18n (locale + translator)
|
|
21
|
+
export { localeStore, translatorStore } from "./i18n";
|
|
20
22
|
// PlayerController store (central orchestrator)
|
|
21
23
|
export { createPlayerControllerStore, createDerivedState, createDerivedIsPlaying, createDerivedCurrentTime, createDerivedDuration, createDerivedError, createDerivedVideoElement as createDerivedControllerVideoElement, createDerivedShouldShowControls, createDerivedShouldShowIdleScreen, } from "./playerController";
|
|
@@ -98,6 +98,8 @@ export interface PlayerControllerStore extends Readable<PlayerControllerState> {
|
|
|
98
98
|
seek: (time: number) => void;
|
|
99
99
|
/** Seek by delta */
|
|
100
100
|
seekBy: (delta: number) => void;
|
|
101
|
+
/** Jump to live edge (for live streams) */
|
|
102
|
+
jumpToLive: () => void;
|
|
101
103
|
/** Set volume */
|
|
102
104
|
setVolume: (volume: number) => void;
|
|
103
105
|
/** Toggle mute */
|
|
@@ -267,6 +267,9 @@ export function createPlayerControllerStore(config) {
|
|
|
267
267
|
function seekBy(delta) {
|
|
268
268
|
controller?.seekBy(delta);
|
|
269
269
|
}
|
|
270
|
+
function jumpToLive() {
|
|
271
|
+
controller?.jumpToLive();
|
|
272
|
+
}
|
|
270
273
|
function setVolume(volume) {
|
|
271
274
|
controller?.setVolume(volume);
|
|
272
275
|
}
|
|
@@ -338,6 +341,7 @@ export function createPlayerControllerStore(config) {
|
|
|
338
341
|
togglePlay,
|
|
339
342
|
seek,
|
|
340
343
|
seekBy,
|
|
344
|
+
jumpToLive,
|
|
341
345
|
setVolume,
|
|
342
346
|
toggleMute,
|
|
343
347
|
toggleLoop,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livepeer-frameworks/player-svelte",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Svelte 5 components for FrameWorks streaming player",
|
|
6
6
|
"svelte": "./dist/index.js",
|
|
@@ -20,30 +20,22 @@
|
|
|
20
20
|
},
|
|
21
21
|
"./player.css": "./src/player.css"
|
|
22
22
|
},
|
|
23
|
-
"scripts": {
|
|
24
|
-
"build": "svelte-package -i src -o dist",
|
|
25
|
-
"build:watch": "svelte-package -i src -o dist --watch",
|
|
26
|
-
"type-check": "svelte-check",
|
|
27
|
-
"test": "vitest run",
|
|
28
|
-
"test:watch": "vitest",
|
|
29
|
-
"test:coverage": "vitest run --coverage"
|
|
30
|
-
},
|
|
31
23
|
"dependencies": {
|
|
32
|
-
"
|
|
33
|
-
"
|
|
24
|
+
"bits-ui": "^2.15.5",
|
|
25
|
+
"@livepeer-frameworks/player-core": "0.2.1"
|
|
34
26
|
},
|
|
35
27
|
"peerDependencies": {
|
|
36
28
|
"svelte": "^5.0.0"
|
|
37
29
|
},
|
|
38
30
|
"devDependencies": {
|
|
39
|
-
"@sveltejs/package": "^2.
|
|
31
|
+
"@sveltejs/package": "^2.5.7",
|
|
40
32
|
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
|
41
|
-
"@testing-library/svelte": "^5.
|
|
33
|
+
"@testing-library/svelte": "^5.3.1",
|
|
42
34
|
"@vitest/coverage-v8": "^4.0.18",
|
|
43
|
-
"jsdom": "^28.
|
|
44
|
-
"svelte": "^5.
|
|
45
|
-
"svelte-check": "^4.
|
|
46
|
-
"typescript": "^5.9.
|
|
35
|
+
"jsdom": "^28.1.0",
|
|
36
|
+
"svelte": "^5.51.2",
|
|
37
|
+
"svelte-check": "^4.4.0",
|
|
38
|
+
"typescript": "^5.9.3",
|
|
47
39
|
"vite": "^7.3.1",
|
|
48
40
|
"vitest": "^4.0.18"
|
|
49
41
|
},
|
|
@@ -55,5 +47,13 @@
|
|
|
55
47
|
"video"
|
|
56
48
|
],
|
|
57
49
|
"author": "Livepeer FrameWorks",
|
|
58
|
-
"license": "Unlicense"
|
|
59
|
-
|
|
50
|
+
"license": "Unlicense",
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "svelte-package -i src -o dist",
|
|
53
|
+
"build:watch": "svelte-package -i src -o dist --watch",
|
|
54
|
+
"type-check": "svelte-check",
|
|
55
|
+
"test": "vitest run",
|
|
56
|
+
"test:watch": "vitest",
|
|
57
|
+
"test:coverage": "vitest run --coverage"
|
|
58
|
+
}
|
|
59
|
+
}
|
package/src/DevModePanel.svelte
CHANGED
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
let internalIsOpen = $state(false);
|
|
68
68
|
let activeTab = $state<"config" | "stats">("config");
|
|
69
69
|
let hoveredComboIndex = $state<number | null>(null);
|
|
70
|
-
let
|
|
70
|
+
let tooltipPos = $state<{ top: number; left: number } | null>(null);
|
|
71
71
|
let showDisabledPlayers = $state(false);
|
|
72
72
|
let comboListRef: HTMLDivElement | undefined = $state();
|
|
73
73
|
|
|
@@ -163,14 +163,12 @@
|
|
|
163
163
|
|
|
164
164
|
function handleComboHover(index: number, e: MouseEvent) {
|
|
165
165
|
hoveredComboIndex = index;
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
tooltipAbove = relativePosition > 0.6;
|
|
173
|
-
}
|
|
166
|
+
const row = e.currentTarget as HTMLElement;
|
|
167
|
+
const rowRect = row.getBoundingClientRect();
|
|
168
|
+
tooltipPos = {
|
|
169
|
+
top: Math.max(8, Math.min(rowRect.top, window.innerHeight - 200)),
|
|
170
|
+
left: Math.max(8, rowRect.left - 228),
|
|
171
|
+
};
|
|
174
172
|
}
|
|
175
173
|
|
|
176
174
|
// Quality monitoring
|
|
@@ -387,7 +385,10 @@
|
|
|
387
385
|
class="fw-dev-combo"
|
|
388
386
|
role="listitem"
|
|
389
387
|
onmouseenter={(e) => handleComboHover(index, e)}
|
|
390
|
-
onmouseleave={() =>
|
|
388
|
+
onmouseleave={() => {
|
|
389
|
+
hoveredComboIndex = null;
|
|
390
|
+
tooltipPos = null;
|
|
391
|
+
}}
|
|
391
392
|
>
|
|
392
393
|
<button
|
|
393
394
|
type="button"
|
|
@@ -446,12 +447,10 @@
|
|
|
446
447
|
</button>
|
|
447
448
|
|
|
448
449
|
<!-- Tooltip -->
|
|
449
|
-
{#if hoveredComboIndex === index}
|
|
450
|
+
{#if hoveredComboIndex === index && tooltipPos}
|
|
450
451
|
<div
|
|
451
|
-
class=
|
|
452
|
-
|
|
453
|
-
tooltipAbove ? "fw-dev-tooltip--above" : "fw-dev-tooltip--below"
|
|
454
|
-
)}
|
|
452
|
+
class="fw-dev-tooltip"
|
|
453
|
+
style="top: {tooltipPos.top}px; left: {tooltipPos.left}px;"
|
|
455
454
|
>
|
|
456
455
|
<div class="fw-dev-tooltip-header">
|
|
457
456
|
<div class="fw-dev-tooltip-title">{combo.playerName}</div>
|
|
@@ -467,7 +466,8 @@
|
|
|
467
466
|
{#if combo.compatible && combo.scoreBreakdown}
|
|
468
467
|
<div class="fw-dev-tooltip-score">Score: {combo.score.toFixed(2)}</div>
|
|
469
468
|
<div class="fw-dev-tooltip-row">
|
|
470
|
-
Tracks
|
|
469
|
+
Tracks [{combo.scoreBreakdown.trackTypes.join(", ")}]:
|
|
470
|
+
<span class="fw-dev-tooltip-value"
|
|
471
471
|
>{combo.scoreBreakdown.trackScore.toFixed(2)}</span
|
|
472
472
|
>
|
|
473
473
|
<span class="fw-dev-tooltip-weight"
|
|
@@ -490,6 +490,43 @@
|
|
|
490
490
|
>x{combo.scoreBreakdown.weights.source}</span
|
|
491
491
|
>
|
|
492
492
|
</div>
|
|
493
|
+
{#if combo.scoreBreakdown.reliabilityScore !== undefined}
|
|
494
|
+
<div class="fw-dev-tooltip-row">
|
|
495
|
+
Reliability: <span class="fw-dev-tooltip-value"
|
|
496
|
+
>{combo.scoreBreakdown.reliabilityScore.toFixed(2)}</span
|
|
497
|
+
>
|
|
498
|
+
<span class="fw-dev-tooltip-weight"
|
|
499
|
+
>x{combo.scoreBreakdown.weights.reliability ?? 0}</span
|
|
500
|
+
>
|
|
501
|
+
</div>
|
|
502
|
+
{/if}
|
|
503
|
+
{#if combo.scoreBreakdown.modeBonus !== undefined && combo.scoreBreakdown.modeBonus !== 0}
|
|
504
|
+
<div class="fw-dev-tooltip-row">
|
|
505
|
+
Mode ({playbackMode}):
|
|
506
|
+
<span class="fw-dev-tooltip-bonus"
|
|
507
|
+
>+{combo.scoreBreakdown.modeBonus.toFixed(2)}</span
|
|
508
|
+
>
|
|
509
|
+
<span class="fw-dev-tooltip-weight"
|
|
510
|
+
>x{combo.scoreBreakdown.weights.mode ?? 0}</span
|
|
511
|
+
>
|
|
512
|
+
</div>
|
|
513
|
+
{/if}
|
|
514
|
+
{#if combo.scoreBreakdown.routingBonus !== undefined && combo.scoreBreakdown.routingBonus !== 0}
|
|
515
|
+
<div class="fw-dev-tooltip-row">
|
|
516
|
+
Routing: <span
|
|
517
|
+
class={combo.scoreBreakdown.routingBonus > 0
|
|
518
|
+
? "fw-dev-tooltip-bonus"
|
|
519
|
+
: "fw-dev-tooltip-penalty"}
|
|
520
|
+
>
|
|
521
|
+
{combo.scoreBreakdown.routingBonus > 0
|
|
522
|
+
? "+"
|
|
523
|
+
: ""}{combo.scoreBreakdown.routingBonus.toFixed(2)}
|
|
524
|
+
</span>
|
|
525
|
+
<span class="fw-dev-tooltip-weight"
|
|
526
|
+
>x{combo.scoreBreakdown.weights.routing ?? 0}</span
|
|
527
|
+
>
|
|
528
|
+
</div>
|
|
529
|
+
{/if}
|
|
493
530
|
{:else}
|
|
494
531
|
<div class="fw-dev-tooltip-error">
|
|
495
532
|
{combo.incompatibleReason || "Incompatible"}
|