@lightbird/ui 0.5.0 → 0.7.0
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/dist/index.cjs +842 -726
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +764 -648
- package/dist/styles.css +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import * as
|
|
3
|
-
import
|
|
2
|
+
import * as React11 from 'react';
|
|
3
|
+
import React11__default, { useRef, useState, useMemo, useEffect, useCallback, Component } from 'react';
|
|
4
4
|
import { clsx } from 'clsx';
|
|
5
5
|
import { twMerge } from 'tailwind-merge';
|
|
6
6
|
import * as SliderPrimitive from '@radix-ui/react-slider';
|
|
@@ -10,7 +10,8 @@ import { cva } from 'class-variance-authority';
|
|
|
10
10
|
import * as PopoverPrimitive from '@radix-ui/react-popover';
|
|
11
11
|
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
12
12
|
import * as LabelPrimitive from '@radix-ui/react-label';
|
|
13
|
-
import {
|
|
13
|
+
import { useSmoothProgress, usePlaylist, useVideoPlayback, useVideoFilters, useSubtitles, useFullscreen, usePictureInPicture, useSeekPreview, useABLoop, useTouchGestures, useVideoInfo, useProgressPersistence, useChapters, useMagnet, useKeyboardShortcuts, useMediaSession } from '@lightbird/core/react';
|
|
14
|
+
import { Circle, SkipBack, Pause, Play, SkipForward, VolumeX, Volume2, AudioLines, Loader2, Subtitles, Plus, X, Settings2, PictureInPicture2, Camera, Info, Keyboard, List, Minimize, Maximize, ChevronDown, ChevronUp, Check, ChevronLeft, ListVideo, Download, Upload, Pin, PinOff, Minimize2, Maximize2, ChevronRight, FilePlus, FolderOpen, Link, Link2, Globe, AlertCircle, Film, GripVertical, Tv, FastForward, Rewind, Sun } from 'lucide-react';
|
|
14
15
|
import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
|
|
15
16
|
import { DndContext, closestCenter } from '@dnd-kit/core';
|
|
16
17
|
import { SortableContext, verticalListSortingStrategy, arrayMove, useSortable } from '@dnd-kit/sortable';
|
|
@@ -19,7 +20,6 @@ import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
|
|
|
19
20
|
import * as SelectPrimitive from '@radix-ui/react-select';
|
|
20
21
|
import { exportPlaylist, formatShortcutKey, FLAG_MAGNET_LINK, loadShortcuts, ProgressEstimator, createVideoPlayer, CancellationError, hasAcceptedDisclaimer, acceptDisclaimer, initFeatureFlags, parseMediaError, captureVideoThumbnail, validateFile, parseM3U8, matchesShortcut, saveShortcuts, DEFAULT_SHORTCUTS } from '@lightbird/core';
|
|
21
22
|
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
22
|
-
import { usePlaylist, useVideoPlayback, useVideoFilters, useSubtitles, useFullscreen, usePictureInPicture, useSeekPreview, useABLoop, useTouchGestures, useVideoInfo, useProgressPersistence, useChapters, useMagnet, useKeyboardShortcuts, useMediaSession } from '@lightbird/core/react';
|
|
23
23
|
import { useBooleanFlagValue, OpenFeatureProvider } from '@openfeature/react-sdk';
|
|
24
24
|
import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
|
|
25
25
|
import * as ToastPrimitives from '@radix-ui/react-toast';
|
|
@@ -27,7 +27,7 @@ import * as ToastPrimitives from '@radix-ui/react-toast';
|
|
|
27
27
|
function cn(...inputs) {
|
|
28
28
|
return twMerge(clsx(inputs));
|
|
29
29
|
}
|
|
30
|
-
var Slider =
|
|
30
|
+
var Slider = React11.forwardRef(({ className, trackClassName, rangeClassName, thumbClassName, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
31
31
|
SliderPrimitive.Root,
|
|
32
32
|
{
|
|
33
33
|
ref,
|
|
@@ -37,8 +37,33 @@ var Slider = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ *
|
|
|
37
37
|
),
|
|
38
38
|
...props,
|
|
39
39
|
children: [
|
|
40
|
-
/* @__PURE__ */ jsx(
|
|
41
|
-
|
|
40
|
+
/* @__PURE__ */ jsx(
|
|
41
|
+
SliderPrimitive.Track,
|
|
42
|
+
{
|
|
43
|
+
className: cn(
|
|
44
|
+
"relative h-2 w-full grow overflow-hidden rounded-full bg-secondary transition-all duration-150 ease-out",
|
|
45
|
+
trackClassName
|
|
46
|
+
),
|
|
47
|
+
children: /* @__PURE__ */ jsx(
|
|
48
|
+
SliderPrimitive.Range,
|
|
49
|
+
{
|
|
50
|
+
className: cn(
|
|
51
|
+
"absolute h-full bg-primary transition-[box-shadow] duration-150",
|
|
52
|
+
rangeClassName
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
),
|
|
58
|
+
/* @__PURE__ */ jsx(
|
|
59
|
+
SliderPrimitive.Thumb,
|
|
60
|
+
{
|
|
61
|
+
className: cn(
|
|
62
|
+
"block h-5 w-5 rounded-full border-2 border-primary bg-background ring-offset-background transition-[transform,box-shadow] duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
|
63
|
+
thumbClassName
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
)
|
|
42
67
|
]
|
|
43
68
|
}
|
|
44
69
|
));
|
|
@@ -68,7 +93,7 @@ var buttonVariants = cva(
|
|
|
68
93
|
}
|
|
69
94
|
}
|
|
70
95
|
);
|
|
71
|
-
var Button =
|
|
96
|
+
var Button = React11.forwardRef(
|
|
72
97
|
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
73
98
|
const Comp = asChild ? Slot : "button";
|
|
74
99
|
return /* @__PURE__ */ jsx(
|
|
@@ -84,7 +109,7 @@ var Button = React10.forwardRef(
|
|
|
84
109
|
Button.displayName = "Button";
|
|
85
110
|
var Popover = PopoverPrimitive.Root;
|
|
86
111
|
var PopoverTrigger = PopoverPrimitive.Trigger;
|
|
87
|
-
var PopoverContent =
|
|
112
|
+
var PopoverContent = React11.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx(
|
|
88
113
|
PopoverPrimitive.Content,
|
|
89
114
|
{
|
|
90
115
|
ref,
|
|
@@ -101,7 +126,7 @@ PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
|
|
101
126
|
var TooltipProvider = TooltipPrimitive.Provider;
|
|
102
127
|
var Tooltip = TooltipPrimitive.Root;
|
|
103
128
|
var TooltipTrigger = TooltipPrimitive.Trigger;
|
|
104
|
-
var TooltipContent =
|
|
129
|
+
var TooltipContent = React11.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
105
130
|
TooltipPrimitive.Content,
|
|
106
131
|
{
|
|
107
132
|
ref,
|
|
@@ -117,7 +142,7 @@ TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
|
117
142
|
var labelVariants = cva(
|
|
118
143
|
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
119
144
|
);
|
|
120
|
-
var Label =
|
|
145
|
+
var Label = React11.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
121
146
|
LabelPrimitive.Root,
|
|
122
147
|
{
|
|
123
148
|
ref,
|
|
@@ -126,7 +151,164 @@ var Label = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */
|
|
|
126
151
|
}
|
|
127
152
|
));
|
|
128
153
|
Label.displayName = LabelPrimitive.Root.displayName;
|
|
129
|
-
var
|
|
154
|
+
var formatTime = (time) => {
|
|
155
|
+
if (isNaN(time)) return "00:00";
|
|
156
|
+
const date = /* @__PURE__ */ new Date(0);
|
|
157
|
+
date.setSeconds(time);
|
|
158
|
+
const timeString = date.toISOString().substr(11, 8);
|
|
159
|
+
return timeString.startsWith("00:") ? timeString.substr(3) : timeString;
|
|
160
|
+
};
|
|
161
|
+
var SeekBar = React11__default.memo(function SeekBar2({
|
|
162
|
+
progress,
|
|
163
|
+
duration,
|
|
164
|
+
isPlaying,
|
|
165
|
+
onSeek,
|
|
166
|
+
videoRef,
|
|
167
|
+
chapters = [],
|
|
168
|
+
abLoop = { pointA: null, pointB: null, isLooping: false },
|
|
169
|
+
onSeekHover,
|
|
170
|
+
seekPreviewThumbnail = null
|
|
171
|
+
}) {
|
|
172
|
+
const noopRef = useRef(null);
|
|
173
|
+
const smooth = useSmoothProgress(videoRef ?? noopRef, {
|
|
174
|
+
isPlaying: !!videoRef && isPlaying,
|
|
175
|
+
fallback: progress
|
|
176
|
+
});
|
|
177
|
+
const displayProgress = videoRef ? smooth : progress;
|
|
178
|
+
const [seekHover, setSeekHover] = useState(null);
|
|
179
|
+
const [isHovering, setIsHovering] = useState(false);
|
|
180
|
+
const [isScrubbing, setIsScrubbing] = useState(false);
|
|
181
|
+
const active = isHovering || isScrubbing;
|
|
182
|
+
const handleSeekHover = (e) => {
|
|
183
|
+
if (duration <= 0) return;
|
|
184
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
185
|
+
if (rect.width <= 0) return;
|
|
186
|
+
const ratio = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
|
|
187
|
+
const time = ratio * duration;
|
|
188
|
+
setSeekHover({ ratio, time });
|
|
189
|
+
onSeekHover?.(time);
|
|
190
|
+
};
|
|
191
|
+
const handleSeekLeave = () => {
|
|
192
|
+
setSeekHover(null);
|
|
193
|
+
setIsHovering(false);
|
|
194
|
+
onSeekHover?.(null);
|
|
195
|
+
};
|
|
196
|
+
return /* @__PURE__ */ jsxs(
|
|
197
|
+
"div",
|
|
198
|
+
{
|
|
199
|
+
className: "relative w-full py-2",
|
|
200
|
+
"data-testid": "seek-bar",
|
|
201
|
+
"data-scrubbing": isScrubbing || void 0,
|
|
202
|
+
"data-hover": isHovering || void 0,
|
|
203
|
+
onMouseEnter: () => setIsHovering(true),
|
|
204
|
+
onMouseMove: handleSeekHover,
|
|
205
|
+
onMouseLeave: handleSeekLeave,
|
|
206
|
+
children: [
|
|
207
|
+
seekHover && /* @__PURE__ */ jsxs(
|
|
208
|
+
"div",
|
|
209
|
+
{
|
|
210
|
+
"data-testid": "seek-preview",
|
|
211
|
+
className: "absolute bottom-full mb-3 -translate-x-1/2 pointer-events-none flex flex-col items-center z-20",
|
|
212
|
+
style: { left: `${seekHover.ratio * 100}%` },
|
|
213
|
+
children: [
|
|
214
|
+
seekPreviewThumbnail ? /* @__PURE__ */ jsx(
|
|
215
|
+
"img",
|
|
216
|
+
{
|
|
217
|
+
src: seekPreviewThumbnail,
|
|
218
|
+
alt: "",
|
|
219
|
+
className: "w-40 h-[90px] rounded border border-white/20 bg-black object-cover shadow-lg"
|
|
220
|
+
}
|
|
221
|
+
) : /* @__PURE__ */ jsx("div", { className: "w-40 h-[90px] rounded border border-white/20 bg-black/80 shadow-lg" }),
|
|
222
|
+
/* @__PURE__ */ jsx("span", { className: "mt-1 rounded bg-black/80 px-1.5 py-0.5 font-mono text-xs text-white", children: formatTime(seekHover.time) })
|
|
223
|
+
]
|
|
224
|
+
}
|
|
225
|
+
),
|
|
226
|
+
/* @__PURE__ */ jsx(
|
|
227
|
+
Slider,
|
|
228
|
+
{
|
|
229
|
+
value: [displayProgress],
|
|
230
|
+
max: duration || 0,
|
|
231
|
+
step: 0.05,
|
|
232
|
+
onValueChange: ([val]) => onSeek(val),
|
|
233
|
+
onPointerDown: () => setIsScrubbing(true),
|
|
234
|
+
onPointerUp: () => setIsScrubbing(false),
|
|
235
|
+
onLostPointerCapture: () => setIsScrubbing(false),
|
|
236
|
+
className: "w-full",
|
|
237
|
+
trackClassName: cn(
|
|
238
|
+
"rounded-none transition-all duration-150 ease-out",
|
|
239
|
+
active ? "h-1.5" : "h-[3px]"
|
|
240
|
+
),
|
|
241
|
+
rangeClassName: cn(
|
|
242
|
+
"rounded-none",
|
|
243
|
+
active && "shadow-[0_0_6px_hsl(var(--primary)/0.6)]"
|
|
244
|
+
),
|
|
245
|
+
thumbClassName: cn(
|
|
246
|
+
"h-3 w-3 border transition-[transform,opacity] duration-150",
|
|
247
|
+
active ? "scale-100 opacity-100" : "scale-0 opacity-0",
|
|
248
|
+
isScrubbing && "scale-125"
|
|
249
|
+
)
|
|
250
|
+
}
|
|
251
|
+
),
|
|
252
|
+
chapters.length > 0 && duration > 0 && chapters.slice(1).map((chapter) => /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
253
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
254
|
+
"div",
|
|
255
|
+
{
|
|
256
|
+
"data-testid": "chapter-tick",
|
|
257
|
+
style: {
|
|
258
|
+
position: "absolute",
|
|
259
|
+
left: `${chapter.startTime / duration * 100}%`,
|
|
260
|
+
top: "50%",
|
|
261
|
+
width: "2px",
|
|
262
|
+
height: active ? "10px" : "6px",
|
|
263
|
+
background: "white",
|
|
264
|
+
opacity: 0.55,
|
|
265
|
+
pointerEvents: "none",
|
|
266
|
+
transform: "translate(-1px, -50%)",
|
|
267
|
+
transition: "height 150ms ease-out"
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
) }),
|
|
271
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsxs("p", { children: [
|
|
272
|
+
chapter.title,
|
|
273
|
+
" \u2014 ",
|
|
274
|
+
formatTime(chapter.startTime)
|
|
275
|
+
] }) })
|
|
276
|
+
] }, chapter.index)),
|
|
277
|
+
duration > 0 && abLoop.pointA !== null && abLoop.pointB !== null && /* @__PURE__ */ jsx(
|
|
278
|
+
"div",
|
|
279
|
+
{
|
|
280
|
+
"data-testid": "ab-loop-region",
|
|
281
|
+
className: cn(
|
|
282
|
+
"pointer-events-none absolute top-1/2 -translate-y-1/2 bg-primary/40 transition-[height] duration-150",
|
|
283
|
+
active ? "h-1.5" : "h-[3px]"
|
|
284
|
+
),
|
|
285
|
+
style: {
|
|
286
|
+
left: `${abLoop.pointA / duration * 100}%`,
|
|
287
|
+
width: `${(abLoop.pointB - abLoop.pointA) / duration * 100}%`
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
),
|
|
291
|
+
duration > 0 && abLoop.pointA !== null && /* @__PURE__ */ jsx(
|
|
292
|
+
"div",
|
|
293
|
+
{
|
|
294
|
+
"data-testid": "ab-marker-a",
|
|
295
|
+
className: "pointer-events-none absolute top-1/2 -translate-y-1/2 h-4 w-0.5 -translate-x-1/2 bg-primary",
|
|
296
|
+
style: { left: `${abLoop.pointA / duration * 100}%` }
|
|
297
|
+
}
|
|
298
|
+
),
|
|
299
|
+
duration > 0 && abLoop.pointB !== null && /* @__PURE__ */ jsx(
|
|
300
|
+
"div",
|
|
301
|
+
{
|
|
302
|
+
"data-testid": "ab-marker-b",
|
|
303
|
+
className: "pointer-events-none absolute top-1/2 -translate-y-1/2 h-4 w-0.5 -translate-x-1/2 bg-primary",
|
|
304
|
+
style: { left: `${abLoop.pointB / duration * 100}%` }
|
|
305
|
+
}
|
|
306
|
+
)
|
|
307
|
+
]
|
|
308
|
+
}
|
|
309
|
+
);
|
|
310
|
+
});
|
|
311
|
+
var RadioGroup = React11.forwardRef(({ className, ...props }, ref) => {
|
|
130
312
|
return /* @__PURE__ */ jsx(
|
|
131
313
|
RadioGroupPrimitive.Root,
|
|
132
314
|
{
|
|
@@ -137,7 +319,7 @@ var RadioGroup = React10.forwardRef(({ className, ...props }, ref) => {
|
|
|
137
319
|
);
|
|
138
320
|
});
|
|
139
321
|
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;
|
|
140
|
-
var RadioGroupItem =
|
|
322
|
+
var RadioGroupItem = React11.forwardRef(({ className, ...props }, ref) => {
|
|
141
323
|
return /* @__PURE__ */ jsx(
|
|
142
324
|
RadioGroupPrimitive.Item,
|
|
143
325
|
{
|
|
@@ -152,14 +334,14 @@ var RadioGroupItem = React10.forwardRef(({ className, ...props }, ref) => {
|
|
|
152
334
|
);
|
|
153
335
|
});
|
|
154
336
|
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
|
|
155
|
-
var
|
|
337
|
+
var formatTime2 = (time) => {
|
|
156
338
|
if (isNaN(time)) return "00:00";
|
|
157
339
|
const date = /* @__PURE__ */ new Date(0);
|
|
158
340
|
date.setSeconds(time);
|
|
159
341
|
const timeString = date.toISOString().substr(11, 8);
|
|
160
342
|
return timeString.startsWith("00:") ? timeString.substr(3) : timeString;
|
|
161
343
|
};
|
|
162
|
-
var PlayerControls =
|
|
344
|
+
var PlayerControls = React11__default.memo(function PlayerControls2({
|
|
163
345
|
isPlaying,
|
|
164
346
|
progress,
|
|
165
347
|
duration,
|
|
@@ -203,355 +385,260 @@ var PlayerControls = React10__default.memo(function PlayerControls2({
|
|
|
203
385
|
onSeekHover,
|
|
204
386
|
seekPreviewThumbnail = null,
|
|
205
387
|
abLoop = { pointA: null, pointB: null, isLooping: false },
|
|
206
|
-
onABLoopCycle
|
|
388
|
+
onABLoopCycle,
|
|
389
|
+
videoRef,
|
|
390
|
+
isDisabled = false
|
|
207
391
|
}) {
|
|
208
|
-
const formattedProgress = useMemo(() =>
|
|
209
|
-
const formattedDuration = useMemo(() =>
|
|
392
|
+
const formattedProgress = useMemo(() => formatTime2(progress), [progress]);
|
|
393
|
+
const formattedDuration = useMemo(() => formatTime2(duration), [duration]);
|
|
210
394
|
const [chaptersMenuOpen, setChaptersMenuOpen] = useState(false);
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
children:
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
className: "w-full h-2"
|
|
261
|
-
}
|
|
262
|
-
),
|
|
263
|
-
chapters.length > 0 && duration > 0 && chapters.slice(1).map((chapter) => /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
264
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
265
|
-
"div",
|
|
266
|
-
{
|
|
267
|
-
"data-testid": "chapter-tick",
|
|
268
|
-
style: {
|
|
269
|
-
position: "absolute",
|
|
270
|
-
left: `${chapter.startTime / duration * 100}%`,
|
|
271
|
-
top: 0,
|
|
272
|
-
width: "2px",
|
|
273
|
-
height: "100%",
|
|
274
|
-
background: "white",
|
|
275
|
-
opacity: 0.5,
|
|
276
|
-
pointerEvents: "none",
|
|
277
|
-
transform: "translateX(-1px)"
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
) }),
|
|
281
|
-
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsxs("p", { children: [
|
|
282
|
-
chapter.title,
|
|
283
|
-
" \u2014 ",
|
|
284
|
-
formatTime(chapter.startTime)
|
|
285
|
-
] }) })
|
|
286
|
-
] }, chapter.index)),
|
|
287
|
-
duration > 0 && abLoop.pointA !== null && abLoop.pointB !== null && /* @__PURE__ */ jsx(
|
|
288
|
-
"div",
|
|
289
|
-
{
|
|
290
|
-
"data-testid": "ab-loop-region",
|
|
291
|
-
className: "pointer-events-none absolute top-0 h-full bg-primary/30",
|
|
292
|
-
style: {
|
|
293
|
-
left: `${abLoop.pointA / duration * 100}%`,
|
|
294
|
-
width: `${(abLoop.pointB - abLoop.pointA) / duration * 100}%`
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
),
|
|
298
|
-
duration > 0 && abLoop.pointA !== null && /* @__PURE__ */ jsx(
|
|
299
|
-
"div",
|
|
300
|
-
{
|
|
301
|
-
"data-testid": "ab-marker-a",
|
|
302
|
-
className: "pointer-events-none absolute top-0 h-full w-0.5 -translate-x-1/2 bg-primary",
|
|
303
|
-
style: { left: `${abLoop.pointA / duration * 100}%` }
|
|
304
|
-
}
|
|
305
|
-
),
|
|
306
|
-
duration > 0 && abLoop.pointB !== null && /* @__PURE__ */ jsx(
|
|
307
|
-
"div",
|
|
308
|
-
{
|
|
309
|
-
"data-testid": "ab-marker-b",
|
|
310
|
-
className: "pointer-events-none absolute top-0 h-full w-0.5 -translate-x-1/2 bg-primary",
|
|
311
|
-
style: { left: `${abLoop.pointB / duration * 100}%` }
|
|
312
|
-
}
|
|
313
|
-
)
|
|
314
|
-
]
|
|
315
|
-
}
|
|
316
|
-
),
|
|
317
|
-
currentChapter && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: currentChapter.title }),
|
|
318
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-white", children: [
|
|
319
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
|
|
320
|
-
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
321
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", onClick: onPrevious, children: /* @__PURE__ */ jsx(SkipBack, {}) }) }),
|
|
322
|
-
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: "Previous (N)" }) })
|
|
323
|
-
] }),
|
|
324
|
-
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
325
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", onClick: onPlayPause, children: isPlaying ? /* @__PURE__ */ jsx(Pause, {}) : /* @__PURE__ */ jsx(Play, {}) }) }),
|
|
326
|
-
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsxs("p", { children: [
|
|
327
|
-
isPlaying ? "Pause" : "Play",
|
|
328
|
-
" (Space)"
|
|
329
|
-
] }) })
|
|
330
|
-
] }),
|
|
331
|
-
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
332
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", onClick: onNext, children: /* @__PURE__ */ jsx(SkipForward, {}) }) }),
|
|
333
|
-
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: "Next (P)" }) })
|
|
334
|
-
] }),
|
|
335
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
336
|
-
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
337
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", onClick: onMuteToggle, children: isMuted || volume === 0 ? /* @__PURE__ */ jsx(VolumeX, {}) : /* @__PURE__ */ jsx(Volume2, {}) }) }),
|
|
338
|
-
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: "Mute (M)" }) })
|
|
339
|
-
] }),
|
|
340
|
-
/* @__PURE__ */ jsx(Slider, { value: [isMuted ? 0 : volume], max: 1, step: 0.05, onValueChange: ([val]) => onVolumeChange(val), className: "w-24" })
|
|
341
|
-
] }),
|
|
342
|
-
/* @__PURE__ */ jsxs("span", { className: "font-mono text-sm", children: [
|
|
343
|
-
formattedProgress,
|
|
344
|
-
" / ",
|
|
345
|
-
formattedDuration
|
|
346
|
-
] })
|
|
347
|
-
] }),
|
|
348
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
349
|
-
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
350
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", onClick: () => onFrameStep("backward"), children: /* @__PURE__ */ jsx(Rewind, { size: 18 }) }) }),
|
|
351
|
-
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: "Frame Backward" }) })
|
|
352
|
-
] }),
|
|
353
|
-
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
354
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", onClick: () => onFrameStep("forward"), children: /* @__PURE__ */ jsx(FastForward, { size: 18 }) }) }),
|
|
355
|
-
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: "Frame Forward" }) })
|
|
356
|
-
] }),
|
|
357
|
-
/* @__PURE__ */ jsxs(Popover, { children: [
|
|
358
|
-
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "ghost", className: "font-mono w-16", children: [
|
|
359
|
-
playbackRate,
|
|
360
|
-
"x"
|
|
361
|
-
] }) }),
|
|
362
|
-
/* @__PURE__ */ jsx(PopoverContent, { className: "w-40", children: /* @__PURE__ */ jsx(RadioGroup, { value: String(playbackRate), onValueChange: (val) => onPlaybackRateChange(Number(val)), children: [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 4].map((rate) => /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
363
|
-
/* @__PURE__ */ jsx(RadioGroupItem, { value: String(rate), id: `rate-${rate}` }),
|
|
364
|
-
/* @__PURE__ */ jsxs(Label, { htmlFor: `rate-${rate}`, children: [
|
|
365
|
-
rate,
|
|
366
|
-
"x"
|
|
367
|
-
] })
|
|
368
|
-
] }, rate)) }) })
|
|
369
|
-
] }),
|
|
370
|
-
audioTracks.length > 0 && /* @__PURE__ */ jsxs(Popover, { children: [
|
|
371
|
-
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "icon", className: "relative", children: [
|
|
372
|
-
/* @__PURE__ */ jsx(AudioLines, {}),
|
|
373
|
-
tracksLoading && /* @__PURE__ */ jsx(Loader2, { className: "absolute top-0 right-0 h-2.5 w-2.5 animate-spin text-primary" })
|
|
374
|
-
] }) }),
|
|
375
|
-
/* @__PURE__ */ jsx(PopoverContent, { children: /* @__PURE__ */ jsx("div", { className: "max-h-48 overflow-y-auto overscroll-contain pr-1", children: /* @__PURE__ */ jsx(RadioGroup, { value: activeAudioTrack, onValueChange: onAudioTrackChange, children: audioTracks.map((track) => /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
376
|
-
/* @__PURE__ */ jsx(RadioGroupItem, { value: track.id, id: `audio-${track.id}` }),
|
|
377
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: `audio-${track.id}`, children: track.name })
|
|
378
|
-
] }, track.id)) }) }) })
|
|
379
|
-
] }),
|
|
380
|
-
/* @__PURE__ */ jsxs(Popover, { children: [
|
|
381
|
-
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "icon", className: "relative", children: [
|
|
382
|
-
/* @__PURE__ */ jsx(Subtitles, {}),
|
|
383
|
-
tracksLoading ? /* @__PURE__ */ jsx(Loader2, { className: "absolute top-0 right-0 h-2.5 w-2.5 animate-spin text-primary" }) : activeSubtitle !== "-1" && /* @__PURE__ */ jsx("span", { className: "absolute top-0 right-0 block h-2 w-2 rounded-full bg-primary ring-2 ring-background" })
|
|
384
|
-
] }) }),
|
|
385
|
-
/* @__PURE__ */ jsx(PopoverContent, { className: "w-64", children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
386
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
387
|
-
/* @__PURE__ */ jsx(Label, { className: "text-sm font-medium", children: "Subtitles" }),
|
|
388
|
-
/* @__PURE__ */ jsxs(
|
|
389
|
-
Button,
|
|
395
|
+
return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(
|
|
396
|
+
"div",
|
|
397
|
+
{
|
|
398
|
+
"aria-disabled": isDisabled || void 0,
|
|
399
|
+
className: cn(
|
|
400
|
+
"absolute bottom-0 left-0 right-0 px-4 pb-3 pt-10 flex flex-col gap-1.5",
|
|
401
|
+
"bg-gradient-to-t from-black/80 via-black/40 to-transparent transition-opacity duration-300 ease-in-out",
|
|
402
|
+
isDisabled ? "opacity-60 pointer-events-none" : "opacity-0 group-hover:opacity-100"
|
|
403
|
+
),
|
|
404
|
+
children: [
|
|
405
|
+
/* @__PURE__ */ jsx(
|
|
406
|
+
SeekBar,
|
|
407
|
+
{
|
|
408
|
+
progress,
|
|
409
|
+
duration,
|
|
410
|
+
isPlaying,
|
|
411
|
+
onSeek,
|
|
412
|
+
videoRef,
|
|
413
|
+
chapters,
|
|
414
|
+
abLoop,
|
|
415
|
+
onSeekHover,
|
|
416
|
+
seekPreviewThumbnail
|
|
417
|
+
}
|
|
418
|
+
),
|
|
419
|
+
currentChapter && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground -mt-0.5", children: currentChapter.title }),
|
|
420
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-white", children: [
|
|
421
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
422
|
+
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
423
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", onClick: onPrevious, children: /* @__PURE__ */ jsx(SkipBack, {}) }) }),
|
|
424
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: "Previous (N)" }) })
|
|
425
|
+
] }),
|
|
426
|
+
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
427
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", onClick: onPlayPause, children: isPlaying ? /* @__PURE__ */ jsx(Pause, {}) : /* @__PURE__ */ jsx(Play, {}) }) }),
|
|
428
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsxs("p", { children: [
|
|
429
|
+
isPlaying ? "Pause" : "Play",
|
|
430
|
+
" (Space)"
|
|
431
|
+
] }) })
|
|
432
|
+
] }),
|
|
433
|
+
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
434
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", onClick: onNext, children: /* @__PURE__ */ jsx(SkipForward, {}) }) }),
|
|
435
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: "Next (P)" }) })
|
|
436
|
+
] }),
|
|
437
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 ml-1", children: [
|
|
438
|
+
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
439
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", onClick: onMuteToggle, children: isMuted || volume === 0 ? /* @__PURE__ */ jsx(VolumeX, {}) : /* @__PURE__ */ jsx(Volume2, {}) }) }),
|
|
440
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: "Mute (M)" }) })
|
|
441
|
+
] }),
|
|
442
|
+
/* @__PURE__ */ jsx(
|
|
443
|
+
Slider,
|
|
390
444
|
{
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
445
|
+
value: [isMuted ? 0 : volume],
|
|
446
|
+
max: 1,
|
|
447
|
+
step: 0.05,
|
|
448
|
+
onValueChange: ([val]) => onVolumeChange(val),
|
|
449
|
+
"aria-label": "Volume",
|
|
450
|
+
className: "w-24",
|
|
451
|
+
trackClassName: "h-[3px]",
|
|
452
|
+
thumbClassName: "h-3 w-3 border"
|
|
399
453
|
}
|
|
400
454
|
)
|
|
401
455
|
] }),
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: `sub-${sub.id}`, className: "truncate", children: sub.name })
|
|
411
|
-
] }),
|
|
412
|
-
sub.type === "external" && onSubtitleRemove && /* @__PURE__ */ jsx(
|
|
413
|
-
Button,
|
|
414
|
-
{
|
|
415
|
-
variant: "ghost",
|
|
416
|
-
size: "sm",
|
|
417
|
-
onClick: () => onSubtitleRemove(sub.id),
|
|
418
|
-
className: "h-6 w-6 p-0",
|
|
419
|
-
children: /* @__PURE__ */ jsx(X, { className: "h-3 w-3" })
|
|
420
|
-
}
|
|
421
|
-
)
|
|
422
|
-
] }, sub.id))
|
|
423
|
-
] }) }) : /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground text-center py-2", children: "No subtitles available" })
|
|
424
|
-
] }) })
|
|
425
|
-
] }),
|
|
426
|
-
chapters.length > 0 && /* @__PURE__ */ jsxs(Popover, { open: chaptersMenuOpen, onOpenChange: setChaptersMenuOpen, children: [
|
|
427
|
-
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
428
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", "aria-label": "Chapters", children: /* @__PURE__ */ jsx(List, { className: "h-4 w-4" }) }) }) }),
|
|
429
|
-
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: "Chapters" }) })
|
|
456
|
+
/* @__PURE__ */ jsxs("span", { className: "font-mono text-xs tabular-nums ml-2 text-white/90", children: [
|
|
457
|
+
formattedProgress,
|
|
458
|
+
" ",
|
|
459
|
+
/* @__PURE__ */ jsxs("span", { className: "text-white/50", children: [
|
|
460
|
+
"/ ",
|
|
461
|
+
formattedDuration
|
|
462
|
+
] })
|
|
463
|
+
] })
|
|
430
464
|
] }),
|
|
431
|
-
/* @__PURE__ */
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
onGoToChapter?.(chapter.index);
|
|
440
|
-
setChaptersMenuOpen(false);
|
|
441
|
-
},
|
|
442
|
-
children: [
|
|
443
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: chapter.title }),
|
|
444
|
-
/* @__PURE__ */ jsx("span", { className: "ml-4 font-mono text-xs text-muted-foreground shrink-0", children: formatTime(chapter.startTime) })
|
|
445
|
-
]
|
|
446
|
-
},
|
|
447
|
-
chapter.index
|
|
448
|
-
)) }) })
|
|
449
|
-
] }),
|
|
450
|
-
/* @__PURE__ */ jsxs(Popover, { children: [
|
|
451
|
-
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", children: /* @__PURE__ */ jsx(Settings2, {}) }) }),
|
|
452
|
-
/* @__PURE__ */ jsxs(PopoverContent, { className: "w-64 space-y-4", children: [
|
|
453
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
454
|
-
/* @__PURE__ */ jsxs(Label, { children: [
|
|
455
|
-
"Brightness: ",
|
|
456
|
-
filters.brightness,
|
|
457
|
-
"%"
|
|
465
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
466
|
+
audioTracks.length > 0 && /* @__PURE__ */ jsxs(Popover, { children: [
|
|
467
|
+
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
468
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "icon", className: "relative", "aria-label": "Audio track", children: [
|
|
469
|
+
/* @__PURE__ */ jsx(AudioLines, {}),
|
|
470
|
+
tracksLoading && /* @__PURE__ */ jsx(Loader2, { className: "absolute top-0 right-0 h-2.5 w-2.5 animate-spin text-primary" })
|
|
471
|
+
] }) }) }),
|
|
472
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: "Audio track" }) })
|
|
458
473
|
] }),
|
|
459
|
-
/* @__PURE__ */ jsx(
|
|
474
|
+
/* @__PURE__ */ jsx(PopoverContent, { className: "w-56", children: /* @__PURE__ */ jsx("div", { className: "max-h-48 overflow-y-auto overscroll-contain pr-1", children: /* @__PURE__ */ jsx(RadioGroup, { value: activeAudioTrack, onValueChange: onAudioTrackChange, children: audioTracks.map((track) => /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
475
|
+
/* @__PURE__ */ jsx(RadioGroupItem, { value: track.id, id: `audio-${track.id}` }),
|
|
476
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: `audio-${track.id}`, children: track.name })
|
|
477
|
+
] }, track.id)) }) }) })
|
|
460
478
|
] }),
|
|
461
|
-
/* @__PURE__ */ jsxs(
|
|
462
|
-
/* @__PURE__ */ jsxs(
|
|
463
|
-
"
|
|
464
|
-
|
|
465
|
-
|
|
479
|
+
/* @__PURE__ */ jsxs(Popover, { children: [
|
|
480
|
+
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
481
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "icon", className: "relative", "aria-label": "Subtitles", children: [
|
|
482
|
+
/* @__PURE__ */ jsx(Subtitles, {}),
|
|
483
|
+
tracksLoading ? /* @__PURE__ */ jsx(Loader2, { className: "absolute top-0 right-0 h-2.5 w-2.5 animate-spin text-primary" }) : activeSubtitle !== "-1" && /* @__PURE__ */ jsx("span", { className: "absolute top-0 right-0 block h-2 w-2 rounded-full bg-primary ring-2 ring-background" })
|
|
484
|
+
] }) }) }),
|
|
485
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: "Subtitles" }) })
|
|
466
486
|
] }),
|
|
467
|
-
/* @__PURE__ */ jsx(
|
|
487
|
+
/* @__PURE__ */ jsx(PopoverContent, { className: "w-64", children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
488
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
489
|
+
/* @__PURE__ */ jsx(Label, { className: "text-sm font-medium", children: "Subtitles" }),
|
|
490
|
+
onSubtitleUpload && /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: onSubtitleUpload, className: "h-7 px-2", children: [
|
|
491
|
+
/* @__PURE__ */ jsx(Plus, { className: "h-3 w-3 mr-1" }),
|
|
492
|
+
"Add"
|
|
493
|
+
] })
|
|
494
|
+
] }),
|
|
495
|
+
subtitles.length > 0 ? /* @__PURE__ */ jsx("div", { className: "max-h-48 overflow-y-auto overscroll-contain pr-1", children: /* @__PURE__ */ jsxs(RadioGroup, { value: activeSubtitle, onValueChange: onSubtitleChange, children: [
|
|
496
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
497
|
+
/* @__PURE__ */ jsx(RadioGroupItem, { value: "-1", id: "sub-off" }),
|
|
498
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "sub-off", children: "Off" })
|
|
499
|
+
] }),
|
|
500
|
+
subtitles.map((sub) => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between space-x-2", children: [
|
|
501
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2 flex-1", children: [
|
|
502
|
+
/* @__PURE__ */ jsx(RadioGroupItem, { value: sub.id, id: `sub-${sub.id}` }),
|
|
503
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: `sub-${sub.id}`, className: "truncate", children: sub.name })
|
|
504
|
+
] }),
|
|
505
|
+
sub.type === "external" && onSubtitleRemove && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: () => onSubtitleRemove(sub.id), className: "h-6 w-6 p-0", children: /* @__PURE__ */ jsx(X, { className: "h-3 w-3" }) })
|
|
506
|
+
] }, sub.id))
|
|
507
|
+
] }) }) : /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground text-center py-2", children: "No subtitles available" })
|
|
508
|
+
] }) })
|
|
468
509
|
] }),
|
|
469
|
-
/* @__PURE__ */ jsxs(
|
|
470
|
-
/* @__PURE__ */ jsxs(
|
|
471
|
-
"
|
|
472
|
-
|
|
473
|
-
|
|
510
|
+
/* @__PURE__ */ jsxs(Popover, { children: [
|
|
511
|
+
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
512
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "ghost", className: "font-mono w-12 h-9 px-2 text-xs", children: [
|
|
513
|
+
playbackRate,
|
|
514
|
+
"x"
|
|
515
|
+
] }) }) }),
|
|
516
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: "Playback speed" }) })
|
|
474
517
|
] }),
|
|
475
|
-
/* @__PURE__ */ jsx(
|
|
518
|
+
/* @__PURE__ */ jsx(PopoverContent, { className: "w-40", children: /* @__PURE__ */ jsx(RadioGroup, { value: String(playbackRate), onValueChange: (val) => onPlaybackRateChange(Number(val)), children: [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 4].map((rate) => /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
519
|
+
/* @__PURE__ */ jsx(RadioGroupItem, { value: String(rate), id: `rate-${rate}` }),
|
|
520
|
+
/* @__PURE__ */ jsxs(Label, { htmlFor: `rate-${rate}`, children: [
|
|
521
|
+
rate,
|
|
522
|
+
"x"
|
|
523
|
+
] })
|
|
524
|
+
] }, rate)) }) })
|
|
476
525
|
] }),
|
|
477
|
-
/* @__PURE__ */ jsxs(
|
|
478
|
-
/* @__PURE__ */ jsxs(
|
|
479
|
-
"
|
|
480
|
-
|
|
481
|
-
"\xB0"
|
|
526
|
+
/* @__PURE__ */ jsxs(Popover, { children: [
|
|
527
|
+
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
528
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", "aria-label": "Settings", children: /* @__PURE__ */ jsx(Settings2, {}) }) }) }),
|
|
529
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: "Settings" }) })
|
|
482
530
|
] }),
|
|
483
|
-
/* @__PURE__ */
|
|
531
|
+
/* @__PURE__ */ jsxs(PopoverContent, { className: "w-72 p-2 space-y-1", align: "end", children: [
|
|
532
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-0.5", children: [
|
|
533
|
+
pipSupported && /* @__PURE__ */ jsxs(
|
|
534
|
+
"button",
|
|
535
|
+
{
|
|
536
|
+
onClick: onTogglePiP,
|
|
537
|
+
"aria-label": isPiP ? "Exit picture-in-picture" : "Enter picture-in-picture",
|
|
538
|
+
className: cn(
|
|
539
|
+
"flex items-center gap-2 w-full px-2 py-1.5 rounded text-sm hover:bg-accent text-left",
|
|
540
|
+
isPiP && "text-primary"
|
|
541
|
+
),
|
|
542
|
+
children: [
|
|
543
|
+
/* @__PURE__ */ jsx(PictureInPicture2, { className: "h-4 w-4" }),
|
|
544
|
+
"Picture-in-Picture"
|
|
545
|
+
]
|
|
546
|
+
}
|
|
547
|
+
),
|
|
548
|
+
/* @__PURE__ */ jsxs("button", { onClick: onScreenshot, className: "flex items-center gap-2 w-full px-2 py-1.5 rounded text-sm hover:bg-accent text-left", children: [
|
|
549
|
+
/* @__PURE__ */ jsx(Camera, { className: "h-4 w-4" }),
|
|
550
|
+
"Screenshot"
|
|
551
|
+
] }),
|
|
552
|
+
onShowInfo && /* @__PURE__ */ jsxs("button", { onClick: onShowInfo, className: "flex items-center gap-2 w-full px-2 py-1.5 rounded text-sm hover:bg-accent text-left", children: [
|
|
553
|
+
/* @__PURE__ */ jsx(Info, { className: "h-4 w-4" }),
|
|
554
|
+
"Video information"
|
|
555
|
+
] }),
|
|
556
|
+
onOpenShortcuts && /* @__PURE__ */ jsxs("button", { onClick: onOpenShortcuts, className: "flex items-center gap-2 w-full px-2 py-1.5 rounded text-sm hover:bg-accent text-left", children: [
|
|
557
|
+
/* @__PURE__ */ jsx(Keyboard, { className: "h-4 w-4" }),
|
|
558
|
+
"Keyboard shortcuts"
|
|
559
|
+
] })
|
|
560
|
+
] }),
|
|
561
|
+
/* @__PURE__ */ jsxs("div", { className: "border-t border-border/40 pt-2 space-y-2 px-1", children: [
|
|
562
|
+
/* @__PURE__ */ jsx(Label, { className: "text-[10px] uppercase tracking-wider text-muted-foreground", children: "Display" }),
|
|
563
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
564
|
+
/* @__PURE__ */ jsxs(Label, { className: "text-xs text-muted-foreground", children: [
|
|
565
|
+
"Brightness: ",
|
|
566
|
+
filters.brightness,
|
|
567
|
+
"%"
|
|
568
|
+
] }),
|
|
569
|
+
/* @__PURE__ */ jsx(Slider, { value: [filters.brightness], max: 200, onValueChange: ([val]) => onFiltersChange({ ...filters, brightness: val }), trackClassName: "h-[3px]", thumbClassName: "h-3 w-3 border" })
|
|
570
|
+
] }),
|
|
571
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
572
|
+
/* @__PURE__ */ jsxs(Label, { className: "text-xs text-muted-foreground", children: [
|
|
573
|
+
"Contrast: ",
|
|
574
|
+
filters.contrast,
|
|
575
|
+
"%"
|
|
576
|
+
] }),
|
|
577
|
+
/* @__PURE__ */ jsx(Slider, { value: [filters.contrast], max: 200, onValueChange: ([val]) => onFiltersChange({ ...filters, contrast: val }), trackClassName: "h-[3px]", thumbClassName: "h-3 w-3 border" })
|
|
578
|
+
] }),
|
|
579
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
580
|
+
/* @__PURE__ */ jsxs(Label, { className: "text-xs text-muted-foreground", children: [
|
|
581
|
+
"Saturation: ",
|
|
582
|
+
filters.saturate,
|
|
583
|
+
"%"
|
|
584
|
+
] }),
|
|
585
|
+
/* @__PURE__ */ jsx(Slider, { value: [filters.saturate], max: 200, onValueChange: ([val]) => onFiltersChange({ ...filters, saturate: val }), trackClassName: "h-[3px]", thumbClassName: "h-3 w-3 border" })
|
|
586
|
+
] }),
|
|
587
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
588
|
+
/* @__PURE__ */ jsxs(Label, { className: "text-xs text-muted-foreground", children: [
|
|
589
|
+
"Hue: ",
|
|
590
|
+
filters.hue,
|
|
591
|
+
"\xB0"
|
|
592
|
+
] }),
|
|
593
|
+
/* @__PURE__ */ jsx(Slider, { value: [filters.hue], max: 360, onValueChange: ([val]) => onFiltersChange({ ...filters, hue: val }), trackClassName: "h-[3px]", thumbClassName: "h-3 w-3 border" })
|
|
594
|
+
] }),
|
|
595
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
596
|
+
/* @__PURE__ */ jsxs(Label, { className: "text-xs text-muted-foreground", children: [
|
|
597
|
+
"Zoom: ",
|
|
598
|
+
Math.round(zoom * 100),
|
|
599
|
+
"%"
|
|
600
|
+
] }),
|
|
601
|
+
/* @__PURE__ */ jsx(Slider, { value: [zoom], min: 1, max: 3, step: 0.1, onValueChange: ([val]) => onZoomChange(val), trackClassName: "h-[3px]", thumbClassName: "h-3 w-3 border" })
|
|
602
|
+
] })
|
|
603
|
+
] })
|
|
604
|
+
] })
|
|
484
605
|
] }),
|
|
485
|
-
/* @__PURE__ */ jsxs(
|
|
486
|
-
/* @__PURE__ */ jsxs(
|
|
487
|
-
"
|
|
488
|
-
|
|
489
|
-
"%"
|
|
606
|
+
chapters.length > 0 && /* @__PURE__ */ jsxs(Popover, { open: chaptersMenuOpen, onOpenChange: setChaptersMenuOpen, children: [
|
|
607
|
+
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
608
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", "aria-label": "Chapters", children: /* @__PURE__ */ jsx(List, { className: "h-4 w-4" }) }) }) }),
|
|
609
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: "Chapters" }) })
|
|
490
610
|
] }),
|
|
491
|
-
/* @__PURE__ */ jsx(
|
|
611
|
+
/* @__PURE__ */ jsx(PopoverContent, { className: "w-72 p-0", align: "end", children: /* @__PURE__ */ jsx("div", { className: "flex flex-col max-h-64 overflow-y-auto", children: chapters.map((chapter) => /* @__PURE__ */ jsxs(
|
|
612
|
+
"button",
|
|
613
|
+
{
|
|
614
|
+
className: cn(
|
|
615
|
+
"flex items-center justify-between px-4 py-2 text-sm hover:bg-accent transition-colors text-left",
|
|
616
|
+
currentChapter?.index === chapter.index && "bg-accent font-medium"
|
|
617
|
+
),
|
|
618
|
+
onClick: () => {
|
|
619
|
+
onGoToChapter?.(chapter.index);
|
|
620
|
+
setChaptersMenuOpen(false);
|
|
621
|
+
},
|
|
622
|
+
children: [
|
|
623
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: chapter.title }),
|
|
624
|
+
/* @__PURE__ */ jsx("span", { className: "ml-4 font-mono text-xs text-muted-foreground shrink-0", children: formatTime2(chapter.startTime) })
|
|
625
|
+
]
|
|
626
|
+
},
|
|
627
|
+
chapter.index
|
|
628
|
+
)) }) })
|
|
629
|
+
] }),
|
|
630
|
+
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
631
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", onClick: onFullScreenToggle, children: isFullScreen ? /* @__PURE__ */ jsx(Minimize, {}) : /* @__PURE__ */ jsx(Maximize, {}) }) }),
|
|
632
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: "Fullscreen (F)" }) })
|
|
492
633
|
] })
|
|
493
634
|
] })
|
|
494
|
-
] }),
|
|
495
|
-
onShowInfo && /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
496
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", onClick: onShowInfo, children: /* @__PURE__ */ jsx(Info, { className: "h-4 w-4" }) }) }),
|
|
497
|
-
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: "Video Information" }) })
|
|
498
|
-
] }),
|
|
499
|
-
onOpenShortcuts && /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
500
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", onClick: onOpenShortcuts, children: /* @__PURE__ */ jsx(Keyboard, { className: "h-4 w-4" }) }) }),
|
|
501
|
-
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: "Keyboard Shortcuts" }) })
|
|
502
|
-
] }),
|
|
503
|
-
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
504
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", onClick: onScreenshot, children: /* @__PURE__ */ jsx(Camera, {}) }) }),
|
|
505
|
-
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: "Screenshot" }) })
|
|
506
|
-
] }),
|
|
507
|
-
onABLoopCycle && /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
508
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
509
|
-
Button,
|
|
510
|
-
{
|
|
511
|
-
variant: "ghost",
|
|
512
|
-
size: "icon",
|
|
513
|
-
onClick: onABLoopCycle,
|
|
514
|
-
"aria-label": "A-B loop",
|
|
515
|
-
"data-testid": "ab-loop-button",
|
|
516
|
-
"data-active": abLoop.isLooping,
|
|
517
|
-
className: cn("font-mono text-xs font-bold", abLoop.isLooping && "text-primary"),
|
|
518
|
-
children: [
|
|
519
|
-
/* @__PURE__ */ jsx("span", { className: cn(abLoop.pointA !== null && "text-primary"), children: "A" }),
|
|
520
|
-
/* @__PURE__ */ jsx("span", { className: "opacity-50", children: "-" }),
|
|
521
|
-
/* @__PURE__ */ jsx("span", { className: cn(abLoop.pointB !== null && "text-primary"), children: "B" })
|
|
522
|
-
]
|
|
523
|
-
}
|
|
524
|
-
) }),
|
|
525
|
-
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: abLoop.pointA === null ? "Set loop start (A)" : abLoop.pointB === null ? "Set loop end (B)" : "Clear A-B loop" }) })
|
|
526
|
-
] }),
|
|
527
|
-
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
528
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", onClick: onLoopToggle, "data-active": loop, className: "data-[active=true]:text-primary", children: /* @__PURE__ */ jsx(RotateCcw, {}) }) }),
|
|
529
|
-
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: "Loop" }) })
|
|
530
|
-
] }),
|
|
531
|
-
pipSupported && /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
532
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
533
|
-
Button,
|
|
534
|
-
{
|
|
535
|
-
variant: "ghost",
|
|
536
|
-
size: "icon",
|
|
537
|
-
onClick: onTogglePiP,
|
|
538
|
-
"aria-label": isPiP ? "Exit picture-in-picture" : "Enter picture-in-picture",
|
|
539
|
-
className: isPiP ? "text-primary" : "",
|
|
540
|
-
children: /* @__PURE__ */ jsx(PictureInPicture2, {})
|
|
541
|
-
}
|
|
542
|
-
) }),
|
|
543
|
-
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: isPiP ? "Exit picture-in-picture" : "Enter picture-in-picture" }) })
|
|
544
|
-
] }),
|
|
545
|
-
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
546
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", onClick: onFullScreenToggle, children: isFullScreen ? /* @__PURE__ */ jsx(Minimize, {}) : /* @__PURE__ */ jsx(Maximize, {}) }) }),
|
|
547
|
-
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: "Fullscreen (F)" }) })
|
|
548
635
|
] })
|
|
549
|
-
]
|
|
550
|
-
|
|
551
|
-
|
|
636
|
+
]
|
|
637
|
+
}
|
|
638
|
+
) });
|
|
552
639
|
});
|
|
553
640
|
var player_controls_default = PlayerControls;
|
|
554
|
-
var ScrollArea =
|
|
641
|
+
var ScrollArea = React11.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
555
642
|
ScrollAreaPrimitive.Root,
|
|
556
643
|
{
|
|
557
644
|
ref,
|
|
@@ -565,23 +652,23 @@ var ScrollArea = React10.forwardRef(({ className, children, ...props }, ref) =>
|
|
|
565
652
|
}
|
|
566
653
|
));
|
|
567
654
|
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
|
|
568
|
-
var ScrollBar =
|
|
655
|
+
var ScrollBar = React11.forwardRef(({ className, orientation = "vertical", ...props }, ref) => /* @__PURE__ */ jsx(
|
|
569
656
|
ScrollAreaPrimitive.ScrollAreaScrollbar,
|
|
570
657
|
{
|
|
571
658
|
ref,
|
|
572
659
|
orientation,
|
|
573
660
|
className: cn(
|
|
574
661
|
"flex touch-none select-none transition-colors",
|
|
575
|
-
orientation === "vertical" && "h-full w-2
|
|
576
|
-
orientation === "horizontal" && "h-2
|
|
662
|
+
orientation === "vertical" && "h-full w-2 border-l border-l-transparent p-[1px]",
|
|
663
|
+
orientation === "horizontal" && "h-2 flex-col border-t border-t-transparent p-[1px]",
|
|
577
664
|
className
|
|
578
665
|
),
|
|
579
666
|
...props,
|
|
580
|
-
children: /* @__PURE__ */ jsx(ScrollAreaPrimitive.ScrollAreaThumb, { className: "relative flex-1 rounded-full bg-
|
|
667
|
+
children: /* @__PURE__ */ jsx(ScrollAreaPrimitive.ScrollAreaThumb, { className: "relative flex-1 rounded-full bg-muted-foreground/30 hover:bg-muted-foreground/55 active:bg-muted-foreground/75 transition-colors" })
|
|
581
668
|
}
|
|
582
669
|
));
|
|
583
670
|
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
|
|
584
|
-
var Input =
|
|
671
|
+
var Input = React11.forwardRef(
|
|
585
672
|
({ className, type, ...props }, ref) => {
|
|
586
673
|
return /* @__PURE__ */ jsx(
|
|
587
674
|
"input",
|
|
@@ -600,7 +687,7 @@ var Input = React10.forwardRef(
|
|
|
600
687
|
Input.displayName = "Input";
|
|
601
688
|
var Select = SelectPrimitive.Root;
|
|
602
689
|
var SelectValue = SelectPrimitive.Value;
|
|
603
|
-
var SelectTrigger =
|
|
690
|
+
var SelectTrigger = React11.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
604
691
|
SelectPrimitive.Trigger,
|
|
605
692
|
{
|
|
606
693
|
ref,
|
|
@@ -616,7 +703,7 @@ var SelectTrigger = React10.forwardRef(({ className, children, ...props }, ref)
|
|
|
616
703
|
}
|
|
617
704
|
));
|
|
618
705
|
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
619
|
-
var SelectScrollUpButton =
|
|
706
|
+
var SelectScrollUpButton = React11.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
620
707
|
SelectPrimitive.ScrollUpButton,
|
|
621
708
|
{
|
|
622
709
|
ref,
|
|
@@ -629,7 +716,7 @@ var SelectScrollUpButton = React10.forwardRef(({ className, ...props }, ref) =>
|
|
|
629
716
|
}
|
|
630
717
|
));
|
|
631
718
|
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
|
632
|
-
var SelectScrollDownButton =
|
|
719
|
+
var SelectScrollDownButton = React11.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
633
720
|
SelectPrimitive.ScrollDownButton,
|
|
634
721
|
{
|
|
635
722
|
ref,
|
|
@@ -642,7 +729,7 @@ var SelectScrollDownButton = React10.forwardRef(({ className, ...props }, ref) =
|
|
|
642
729
|
}
|
|
643
730
|
));
|
|
644
731
|
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
|
|
645
|
-
var SelectContent =
|
|
732
|
+
var SelectContent = React11.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
|
|
646
733
|
SelectPrimitive.Content,
|
|
647
734
|
{
|
|
648
735
|
ref,
|
|
@@ -670,7 +757,7 @@ var SelectContent = React10.forwardRef(({ className, children, position = "poppe
|
|
|
670
757
|
}
|
|
671
758
|
) }));
|
|
672
759
|
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
673
|
-
var SelectLabel =
|
|
760
|
+
var SelectLabel = React11.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
674
761
|
SelectPrimitive.Label,
|
|
675
762
|
{
|
|
676
763
|
ref,
|
|
@@ -679,7 +766,7 @@ var SelectLabel = React10.forwardRef(({ className, ...props }, ref) => /* @__PUR
|
|
|
679
766
|
}
|
|
680
767
|
));
|
|
681
768
|
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
682
|
-
var SelectItem =
|
|
769
|
+
var SelectItem = React11.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
683
770
|
SelectPrimitive.Item,
|
|
684
771
|
{
|
|
685
772
|
ref,
|
|
@@ -695,7 +782,7 @@ var SelectItem = React10.forwardRef(({ className, children, ...props }, ref) =>
|
|
|
695
782
|
}
|
|
696
783
|
));
|
|
697
784
|
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
698
|
-
var SelectSeparator =
|
|
785
|
+
var SelectSeparator = React11.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
699
786
|
SelectPrimitive.Separator,
|
|
700
787
|
{
|
|
701
788
|
ref,
|
|
@@ -714,7 +801,7 @@ var NEXT_SIZE = {
|
|
|
714
801
|
md: "lg",
|
|
715
802
|
lg: "sm"
|
|
716
803
|
};
|
|
717
|
-
function
|
|
804
|
+
function formatTime3(seconds) {
|
|
718
805
|
if (!seconds || !isFinite(seconds)) return "";
|
|
719
806
|
const h = Math.floor(seconds / 3600);
|
|
720
807
|
const m = Math.floor(seconds % 3600 / 60);
|
|
@@ -743,7 +830,7 @@ function SortablePlaylistItem({ item, index, isActive, downloadReady, onSelect,
|
|
|
743
830
|
transition,
|
|
744
831
|
opacity: isDragging ? 0.5 : 1
|
|
745
832
|
};
|
|
746
|
-
const duration =
|
|
833
|
+
const duration = formatTime3(item.duration ?? 0);
|
|
747
834
|
return /* @__PURE__ */ jsxs(
|
|
748
835
|
"div",
|
|
749
836
|
{
|
|
@@ -897,296 +984,306 @@ var PlaylistPanel = ({
|
|
|
897
984
|
});
|
|
898
985
|
onReorder(sorted);
|
|
899
986
|
};
|
|
900
|
-
return /* @__PURE__ */ jsx(TooltipProvider, { children:
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
"aria-label": "Expand Playlist",
|
|
912
|
-
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
|
|
913
|
-
}
|
|
914
|
-
) }),
|
|
915
|
-
/* @__PURE__ */ jsx(TooltipContent, { side: "left", children: /* @__PURE__ */ jsx("p", { children: "Expand Playlist" }) })
|
|
916
|
-
] }),
|
|
917
|
-
/* @__PURE__ */ jsx("div", { className: "flex-1 flex items-center justify-center overflow-hidden", children: /* @__PURE__ */ jsx(
|
|
918
|
-
"span",
|
|
919
|
-
{
|
|
920
|
-
className: "text-[10px] font-semibold text-muted-foreground tracking-widest uppercase select-none",
|
|
921
|
-
style: { writingMode: "vertical-rl", transform: "rotate(180deg)" },
|
|
922
|
-
children: "Playlist"
|
|
923
|
-
}
|
|
924
|
-
) }),
|
|
925
|
-
playlist.length > 0 && /* @__PURE__ */ jsx("span", { className: "mb-3 text-[10px] font-bold text-primary bg-primary/10 rounded-full w-6 h-6 flex items-center justify-center shrink-0", children: playlist.length > 99 ? "99+" : playlist.length })
|
|
926
|
-
] })
|
|
927
|
-
) : (
|
|
928
|
-
/* ── Full panel ── */
|
|
929
|
-
/* @__PURE__ */ jsxs("div", { className: cn("h-full flex flex-col bg-card border-l border-border shrink-0 transition-[width] duration-200", SIZE_WIDTHS[size]), children: [
|
|
930
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-3 py-2 border-b border-border shrink-0", children: [
|
|
931
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
|
|
932
|
-
/* @__PURE__ */ jsx(ListVideo, { className: "h-4 w-4 text-primary shrink-0" }),
|
|
933
|
-
/* @__PURE__ */ jsx("span", { className: "font-semibold text-sm truncate", children: "Playlist" }),
|
|
934
|
-
playlist.length > 0 && /* @__PURE__ */ jsx("span", { className: "text-[10px] font-bold text-muted-foreground bg-muted rounded-full px-1.5 py-0.5 shrink-0 leading-none", children: playlist.length })
|
|
935
|
-
] }),
|
|
936
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 shrink-0", children: [
|
|
937
|
-
playlist.length > 0 && /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
938
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
939
|
-
Button,
|
|
940
|
-
{
|
|
941
|
-
variant: "ghost",
|
|
942
|
-
size: "icon",
|
|
943
|
-
className: "h-7 w-7",
|
|
944
|
-
onClick: () => exportPlaylist(playlist),
|
|
945
|
-
"aria-label": "Export Playlist",
|
|
946
|
-
children: /* @__PURE__ */ jsx(Download, { className: "h-3.5 w-3.5" })
|
|
947
|
-
}
|
|
948
|
-
) }),
|
|
949
|
-
/* @__PURE__ */ jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsx("p", { children: "Export as M3U8" }) })
|
|
950
|
-
] }),
|
|
951
|
-
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
952
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
953
|
-
Button,
|
|
954
|
-
{
|
|
955
|
-
variant: "ghost",
|
|
956
|
-
size: "icon",
|
|
957
|
-
className: "h-7 w-7",
|
|
958
|
-
onClick: () => m3uInputRef.current?.click(),
|
|
959
|
-
"aria-label": "Import Playlist",
|
|
960
|
-
children: /* @__PURE__ */ jsx(Upload, { className: "h-3.5 w-3.5" })
|
|
961
|
-
}
|
|
962
|
-
) }),
|
|
963
|
-
/* @__PURE__ */ jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsx("p", { children: "Import M3U/M3U8" }) })
|
|
964
|
-
] }),
|
|
965
|
-
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
966
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
967
|
-
Button,
|
|
968
|
-
{
|
|
969
|
-
variant: "ghost",
|
|
970
|
-
size: "icon",
|
|
971
|
-
className: cn("h-7 w-7", isPinned && "text-primary bg-primary/10"),
|
|
972
|
-
onClick: onTogglePin,
|
|
973
|
-
"aria-label": isPinned ? "Unpin Playlist" : "Pin Playlist",
|
|
974
|
-
children: isPinned ? /* @__PURE__ */ jsx(Pin, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(PinOff, { className: "h-3.5 w-3.5" })
|
|
975
|
-
}
|
|
976
|
-
) }),
|
|
977
|
-
/* @__PURE__ */ jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsx("p", { children: isPinned ? "Unpin (allow auto-hide on play)" : "Pin (keep open while playing)" }) })
|
|
978
|
-
] }),
|
|
979
|
-
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
980
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
981
|
-
Button,
|
|
982
|
-
{
|
|
983
|
-
variant: "ghost",
|
|
984
|
-
size: "icon",
|
|
985
|
-
className: "h-7 w-7",
|
|
986
|
-
onClick: () => onSizeChange(NEXT_SIZE[size]),
|
|
987
|
-
"aria-label": "Resize Playlist",
|
|
988
|
-
children: size === "lg" ? /* @__PURE__ */ jsx(Minimize2, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(Maximize2, { className: "h-3.5 w-3.5" })
|
|
989
|
-
}
|
|
990
|
-
) }),
|
|
991
|
-
/* @__PURE__ */ jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsx("p", { children: size === "lg" ? "Make smaller" : "Make larger" }) })
|
|
992
|
-
] }),
|
|
987
|
+
return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsx(
|
|
988
|
+
"div",
|
|
989
|
+
{
|
|
990
|
+
className: cn(
|
|
991
|
+
"h-full flex flex-col bg-card border-l border-border shrink-0 overflow-hidden",
|
|
992
|
+
"transition-[width] duration-300 ease-in-out motion-reduce:transition-none",
|
|
993
|
+
isOpen ? SIZE_WIDTHS[size] : "w-11"
|
|
994
|
+
),
|
|
995
|
+
children: !isOpen ? (
|
|
996
|
+
/* ── Collapsed drawer strip ── */
|
|
997
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center w-11 h-full shrink-0", children: [
|
|
993
998
|
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
994
999
|
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
995
1000
|
Button,
|
|
996
1001
|
{
|
|
997
1002
|
variant: "ghost",
|
|
998
1003
|
size: "icon",
|
|
999
|
-
className: "h-
|
|
1004
|
+
className: "h-9 w-9 mt-2 shrink-0",
|
|
1000
1005
|
onClick: onToggle,
|
|
1001
|
-
"aria-label": "
|
|
1002
|
-
children: /* @__PURE__ */ jsx(
|
|
1006
|
+
"aria-label": "Expand Playlist",
|
|
1007
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
|
|
1003
1008
|
}
|
|
1004
1009
|
) }),
|
|
1005
|
-
/* @__PURE__ */ jsx(TooltipContent, { side: "
|
|
1006
|
-
] })
|
|
1007
|
-
] })
|
|
1008
|
-
] }),
|
|
1009
|
-
/* @__PURE__ */ jsxs("div", { className: "p-3 space-y-2 border-b border-border shrink-0", children: [
|
|
1010
|
-
/* @__PURE__ */ jsxs("div", { className: "flex gap-1.5", children: [
|
|
1011
|
-
/* @__PURE__ */ jsxs(Button, { onClick: () => fileInputRef.current?.click(), className: "flex-1 h-8 text-xs", children: [
|
|
1012
|
-
/* @__PURE__ */ jsx(FilePlus, { className: "mr-1.5 h-3.5 w-3.5" }),
|
|
1013
|
-
" Add Files"
|
|
1010
|
+
/* @__PURE__ */ jsx(TooltipContent, { side: "left", children: /* @__PURE__ */ jsx("p", { children: "Expand Playlist" }) })
|
|
1014
1011
|
] }),
|
|
1015
|
-
/* @__PURE__ */
|
|
1016
|
-
|
|
1017
|
-
Button,
|
|
1018
|
-
{
|
|
1019
|
-
variant: "outline",
|
|
1020
|
-
size: "icon",
|
|
1021
|
-
className: "h-8 w-8 shrink-0",
|
|
1022
|
-
onClick: () => folderInputRef.current?.click(),
|
|
1023
|
-
"aria-label": "Open Folder",
|
|
1024
|
-
children: /* @__PURE__ */ jsx(FolderOpen, { className: "h-3.5 w-3.5" })
|
|
1025
|
-
}
|
|
1026
|
-
) }),
|
|
1027
|
-
/* @__PURE__ */ jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsx("p", { children: "Open Folder" }) })
|
|
1028
|
-
] })
|
|
1029
|
-
] }),
|
|
1030
|
-
/* @__PURE__ */ jsx(
|
|
1031
|
-
"input",
|
|
1032
|
-
{
|
|
1033
|
-
type: "file",
|
|
1034
|
-
ref: fileInputRef,
|
|
1035
|
-
className: "hidden",
|
|
1036
|
-
multiple: true,
|
|
1037
|
-
accept: "video/*,.mkv,.avi,.mov,.wmv,.flv,.webm,.vtt,.srt",
|
|
1038
|
-
onChange: (e) => e.target.files && onFilesAdded(e.target.files)
|
|
1039
|
-
}
|
|
1040
|
-
),
|
|
1041
|
-
/* @__PURE__ */ jsx(
|
|
1042
|
-
"input",
|
|
1043
|
-
{
|
|
1044
|
-
type: "file",
|
|
1045
|
-
ref: folderInputRef,
|
|
1046
|
-
className: "hidden",
|
|
1047
|
-
multiple: true,
|
|
1048
|
-
accept: "video/*",
|
|
1049
|
-
webkitdirectory: "",
|
|
1050
|
-
onChange: handleFolderSelect
|
|
1051
|
-
}
|
|
1052
|
-
),
|
|
1053
|
-
/* @__PURE__ */ jsx(
|
|
1054
|
-
"input",
|
|
1055
|
-
{
|
|
1056
|
-
type: "file",
|
|
1057
|
-
ref: m3uInputRef,
|
|
1058
|
-
className: "hidden",
|
|
1059
|
-
accept: ".m3u,.m3u8",
|
|
1060
|
-
onChange: handleM3USelect
|
|
1061
|
-
}
|
|
1062
|
-
),
|
|
1063
|
-
/* @__PURE__ */ jsxs("form", { onSubmit: handleStreamUrlSubmit, className: "flex gap-1.5", children: [
|
|
1064
|
-
/* @__PURE__ */ jsx(
|
|
1065
|
-
Input,
|
|
1012
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 flex items-center justify-center overflow-hidden", children: /* @__PURE__ */ jsx(
|
|
1013
|
+
"span",
|
|
1066
1014
|
{
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
onChange: (e) => setStreamUrl(e.target.value),
|
|
1071
|
-
className: "h-8 text-xs"
|
|
1015
|
+
className: "text-[10px] font-semibold text-muted-foreground tracking-widest uppercase select-none",
|
|
1016
|
+
style: { writingMode: "vertical-rl", transform: "rotate(180deg)" },
|
|
1017
|
+
children: "Playlist"
|
|
1072
1018
|
}
|
|
1073
|
-
),
|
|
1074
|
-
/* @__PURE__ */ jsx(
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1019
|
+
) }),
|
|
1020
|
+
playlist.length > 0 && /* @__PURE__ */ jsx("span", { className: "mb-3 text-[10px] font-bold text-primary bg-primary/10 rounded-full w-6 h-6 flex items-center justify-center shrink-0", children: playlist.length > 99 ? "99+" : playlist.length })
|
|
1021
|
+
] })
|
|
1022
|
+
) : (
|
|
1023
|
+
/* ── Full panel ── */
|
|
1024
|
+
/* @__PURE__ */ jsxs("div", { className: cn("h-full flex flex-col shrink-0 transition-[width] duration-200", SIZE_WIDTHS[size]), children: [
|
|
1025
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-3 py-2 border-b border-border shrink-0", children: [
|
|
1026
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
|
|
1027
|
+
/* @__PURE__ */ jsx(ListVideo, { className: "h-4 w-4 text-primary shrink-0" }),
|
|
1028
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold text-sm truncate", children: "Playlist" }),
|
|
1029
|
+
playlist.length > 0 && /* @__PURE__ */ jsx("span", { className: "text-[10px] font-bold text-muted-foreground bg-muted rounded-full px-1.5 py-0.5 shrink-0 leading-none", children: playlist.length })
|
|
1030
|
+
] }),
|
|
1031
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 shrink-0", children: [
|
|
1032
|
+
playlist.length > 0 && /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
1033
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
1034
|
+
Button,
|
|
1035
|
+
{
|
|
1036
|
+
variant: "ghost",
|
|
1037
|
+
size: "icon",
|
|
1038
|
+
className: "h-7 w-7",
|
|
1039
|
+
onClick: () => exportPlaylist(playlist),
|
|
1040
|
+
"aria-label": "Export Playlist",
|
|
1041
|
+
children: /* @__PURE__ */ jsx(Download, { className: "h-3.5 w-3.5" })
|
|
1042
|
+
}
|
|
1043
|
+
) }),
|
|
1044
|
+
/* @__PURE__ */ jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsx("p", { children: "Export as M3U8" }) })
|
|
1045
|
+
] }),
|
|
1046
|
+
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
1047
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
1048
|
+
Button,
|
|
1049
|
+
{
|
|
1050
|
+
variant: "ghost",
|
|
1051
|
+
size: "icon",
|
|
1052
|
+
className: "h-7 w-7",
|
|
1053
|
+
onClick: () => m3uInputRef.current?.click(),
|
|
1054
|
+
"aria-label": "Import Playlist",
|
|
1055
|
+
children: /* @__PURE__ */ jsx(Upload, { className: "h-3.5 w-3.5" })
|
|
1056
|
+
}
|
|
1057
|
+
) }),
|
|
1058
|
+
/* @__PURE__ */ jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsx("p", { children: "Import M3U/M3U8" }) })
|
|
1059
|
+
] }),
|
|
1060
|
+
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
1061
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
1062
|
+
Button,
|
|
1063
|
+
{
|
|
1064
|
+
variant: "ghost",
|
|
1065
|
+
size: "icon",
|
|
1066
|
+
className: cn("h-7 w-7", isPinned && "text-primary bg-primary/10"),
|
|
1067
|
+
onClick: onTogglePin,
|
|
1068
|
+
"aria-label": isPinned ? "Unpin Playlist" : "Pin Playlist",
|
|
1069
|
+
children: isPinned ? /* @__PURE__ */ jsx(Pin, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(PinOff, { className: "h-3.5 w-3.5" })
|
|
1070
|
+
}
|
|
1071
|
+
) }),
|
|
1072
|
+
/* @__PURE__ */ jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsx("p", { children: isPinned ? "Unpin (allow auto-hide on play)" : "Pin (keep open while playing)" }) })
|
|
1073
|
+
] }),
|
|
1074
|
+
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
1075
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
1076
|
+
Button,
|
|
1077
|
+
{
|
|
1078
|
+
variant: "ghost",
|
|
1079
|
+
size: "icon",
|
|
1080
|
+
className: "h-7 w-7",
|
|
1081
|
+
onClick: () => onSizeChange(NEXT_SIZE[size]),
|
|
1082
|
+
"aria-label": "Resize Playlist",
|
|
1083
|
+
children: size === "lg" ? /* @__PURE__ */ jsx(Minimize2, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(Maximize2, { className: "h-3.5 w-3.5" })
|
|
1084
|
+
}
|
|
1085
|
+
) }),
|
|
1086
|
+
/* @__PURE__ */ jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsx("p", { children: size === "lg" ? "Make smaller" : "Make larger" }) })
|
|
1087
|
+
] }),
|
|
1088
|
+
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
1089
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
1090
|
+
Button,
|
|
1091
|
+
{
|
|
1092
|
+
variant: "ghost",
|
|
1093
|
+
size: "icon",
|
|
1094
|
+
className: "h-7 w-7",
|
|
1095
|
+
onClick: onToggle,
|
|
1096
|
+
"aria-label": "Collapse Playlist",
|
|
1097
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
|
|
1098
|
+
}
|
|
1099
|
+
) }),
|
|
1100
|
+
/* @__PURE__ */ jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsx("p", { children: "Collapse" }) })
|
|
1101
|
+
] })
|
|
1102
|
+
] })
|
|
1103
|
+
] }),
|
|
1104
|
+
/* @__PURE__ */ jsxs("div", { className: "p-3 space-y-2 border-b border-border shrink-0", children: [
|
|
1105
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-1.5", children: [
|
|
1106
|
+
/* @__PURE__ */ jsxs(Button, { onClick: () => fileInputRef.current?.click(), className: "flex-1 h-8 text-xs", children: [
|
|
1107
|
+
/* @__PURE__ */ jsx(FilePlus, { className: "mr-1.5 h-3.5 w-3.5" }),
|
|
1108
|
+
" Add Files"
|
|
1109
|
+
] }),
|
|
1110
|
+
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
1111
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
1112
|
+
Button,
|
|
1113
|
+
{
|
|
1114
|
+
variant: "outline",
|
|
1115
|
+
size: "icon",
|
|
1116
|
+
className: "h-8 w-8 shrink-0",
|
|
1117
|
+
onClick: () => folderInputRef.current?.click(),
|
|
1118
|
+
"aria-label": "Open Folder",
|
|
1119
|
+
children: /* @__PURE__ */ jsx(FolderOpen, { className: "h-3.5 w-3.5" })
|
|
1120
|
+
}
|
|
1121
|
+
) }),
|
|
1122
|
+
/* @__PURE__ */ jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsx("p", { children: "Open Folder" }) })
|
|
1123
|
+
] })
|
|
1124
|
+
] }),
|
|
1125
|
+
/* @__PURE__ */ jsx(
|
|
1126
|
+
"input",
|
|
1078
1127
|
{
|
|
1079
|
-
type: "
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
setMagnetError(null);
|
|
1086
|
-
},
|
|
1087
|
-
"aria-label": "Add magnet link",
|
|
1088
|
-
children: /* @__PURE__ */ jsx(Link2, { className: "h-3.5 w-3.5" })
|
|
1128
|
+
type: "file",
|
|
1129
|
+
ref: fileInputRef,
|
|
1130
|
+
className: "hidden",
|
|
1131
|
+
multiple: true,
|
|
1132
|
+
accept: "video/*,.mkv,.avi,.mov,.wmv,.flv,.webm,.vtt,.srt",
|
|
1133
|
+
onChange: (e) => e.target.files && onFilesAdded(e.target.files)
|
|
1089
1134
|
}
|
|
1090
|
-
)
|
|
1091
|
-
/* @__PURE__ */ jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsx("p", { children: "Add Magnet Link" }) })
|
|
1092
|
-
] })
|
|
1093
|
-
] }),
|
|
1094
|
-
showMagnet && showMagnetInput && /* @__PURE__ */ jsxs("form", { onSubmit: handleMagnetSubmit, className: "space-y-1.5", children: [
|
|
1095
|
-
/* @__PURE__ */ jsxs("div", { className: "flex gap-1.5", children: [
|
|
1135
|
+
),
|
|
1096
1136
|
/* @__PURE__ */ jsx(
|
|
1097
|
-
|
|
1137
|
+
"input",
|
|
1098
1138
|
{
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
disabled: torrentStatus.status === "loading-metadata"
|
|
1139
|
+
type: "file",
|
|
1140
|
+
ref: folderInputRef,
|
|
1141
|
+
className: "hidden",
|
|
1142
|
+
multiple: true,
|
|
1143
|
+
accept: "video/*",
|
|
1144
|
+
webkitdirectory: "",
|
|
1145
|
+
onChange: handleFolderSelect
|
|
1107
1146
|
}
|
|
1108
1147
|
),
|
|
1109
1148
|
/* @__PURE__ */ jsx(
|
|
1110
|
-
|
|
1149
|
+
"input",
|
|
1111
1150
|
{
|
|
1112
|
-
type: "
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
"aria-label": "Load magnet link",
|
|
1118
|
-
children: torrentStatus.status === "loading-metadata" ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx(Globe, { className: "h-3.5 w-3.5" })
|
|
1151
|
+
type: "file",
|
|
1152
|
+
ref: m3uInputRef,
|
|
1153
|
+
className: "hidden",
|
|
1154
|
+
accept: ".m3u,.m3u8",
|
|
1155
|
+
onChange: handleM3USelect
|
|
1119
1156
|
}
|
|
1120
|
-
)
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1157
|
+
),
|
|
1158
|
+
/* @__PURE__ */ jsxs("form", { onSubmit: handleStreamUrlSubmit, className: "flex gap-1.5", children: [
|
|
1159
|
+
/* @__PURE__ */ jsx(
|
|
1160
|
+
Input,
|
|
1161
|
+
{
|
|
1162
|
+
type: "url",
|
|
1163
|
+
placeholder: "Enter stream URL",
|
|
1164
|
+
value: streamUrl,
|
|
1165
|
+
onChange: (e) => setStreamUrl(e.target.value),
|
|
1166
|
+
className: "h-8 text-xs"
|
|
1167
|
+
}
|
|
1168
|
+
),
|
|
1169
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", size: "icon", variant: "secondary", className: "h-8 w-8 shrink-0", children: /* @__PURE__ */ jsx(Link, { className: "h-3.5 w-3.5" }) }),
|
|
1170
|
+
showMagnet && /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
1171
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
1172
|
+
Button,
|
|
1173
|
+
{
|
|
1174
|
+
type: "button",
|
|
1175
|
+
size: "icon",
|
|
1176
|
+
variant: showMagnetInput ? "default" : "outline",
|
|
1177
|
+
className: "h-8 w-8 shrink-0",
|
|
1178
|
+
onClick: () => {
|
|
1179
|
+
setShowMagnetInput((v) => !v);
|
|
1180
|
+
setMagnetError(null);
|
|
1181
|
+
},
|
|
1182
|
+
"aria-label": "Add magnet link",
|
|
1183
|
+
children: /* @__PURE__ */ jsx(Link2, { className: "h-3.5 w-3.5" })
|
|
1184
|
+
}
|
|
1185
|
+
) }),
|
|
1186
|
+
/* @__PURE__ */ jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsx("p", { children: "Add Magnet Link" }) })
|
|
1187
|
+
] })
|
|
1188
|
+
] }),
|
|
1189
|
+
showMagnet && showMagnetInput && /* @__PURE__ */ jsxs("form", { onSubmit: handleMagnetSubmit, className: "space-y-1.5", children: [
|
|
1190
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-1.5", children: [
|
|
1191
|
+
/* @__PURE__ */ jsx(
|
|
1192
|
+
Input,
|
|
1193
|
+
{
|
|
1194
|
+
placeholder: "magnet:?xt=urn:btih:\u2026",
|
|
1195
|
+
value: magnetUri,
|
|
1196
|
+
onChange: (e) => {
|
|
1197
|
+
setMagnetUri(e.target.value);
|
|
1198
|
+
setMagnetError(null);
|
|
1199
|
+
},
|
|
1200
|
+
className: "h-8 text-xs font-mono",
|
|
1201
|
+
disabled: torrentStatus.status === "loading-metadata"
|
|
1202
|
+
}
|
|
1203
|
+
),
|
|
1204
|
+
/* @__PURE__ */ jsx(
|
|
1205
|
+
Button,
|
|
1206
|
+
{
|
|
1207
|
+
type: "submit",
|
|
1208
|
+
size: "icon",
|
|
1209
|
+
variant: "default",
|
|
1210
|
+
className: "h-8 w-8 shrink-0",
|
|
1211
|
+
disabled: !magnetUri.trim() || torrentStatus.status === "loading-metadata",
|
|
1212
|
+
"aria-label": "Load magnet link",
|
|
1213
|
+
children: torrentStatus.status === "loading-metadata" ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx(Globe, { className: "h-3.5 w-3.5" })
|
|
1214
|
+
}
|
|
1215
|
+
)
|
|
1216
|
+
] }),
|
|
1217
|
+
torrentStatus.status === "loading-metadata" && /* @__PURE__ */ jsxs("p", { className: "text-[11px] text-muted-foreground flex items-center gap-1", children: [
|
|
1218
|
+
/* @__PURE__ */ jsx(Loader2, { className: "h-3 w-3 animate-spin" }),
|
|
1219
|
+
"Fetching torrent info\u2026"
|
|
1220
|
+
] }),
|
|
1221
|
+
magnetError && /* @__PURE__ */ jsxs("p", { className: "text-[11px] text-destructive flex items-center gap-1", children: [
|
|
1222
|
+
/* @__PURE__ */ jsx(AlertCircle, { className: "h-3 w-3 shrink-0" }),
|
|
1223
|
+
magnetError
|
|
1224
|
+
] })
|
|
1225
|
+
] }),
|
|
1226
|
+
showMagnet && torrentStatus.status === "ready" && (torrentStatus.progress >= 1 ? /* @__PURE__ */ jsxs("p", { className: "text-[10px] text-emerald-500 flex items-center gap-1", children: [
|
|
1227
|
+
/* @__PURE__ */ jsx(Download, { className: "h-3 w-3 shrink-0" }),
|
|
1228
|
+
"Download ready \u2014 hover a file to save it"
|
|
1229
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
1230
|
+
/* @__PURE__ */ jsx("div", { className: "w-full bg-muted rounded-full h-1 overflow-hidden", children: /* @__PURE__ */ jsx(
|
|
1231
|
+
"div",
|
|
1232
|
+
{
|
|
1233
|
+
className: "bg-emerald-500 h-1 rounded-full transition-all duration-500",
|
|
1234
|
+
style: { width: `${Math.round(torrentStatus.progress * 100)}%` }
|
|
1235
|
+
}
|
|
1236
|
+
) }),
|
|
1237
|
+
/* @__PURE__ */ jsxs("p", { className: "text-[10px] text-muted-foreground flex items-center justify-between", children: [
|
|
1238
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
1239
|
+
"\u2193 ",
|
|
1240
|
+
formatBytes(torrentStatus.downloadSpeed),
|
|
1241
|
+
"/s \xB7 ",
|
|
1242
|
+
torrentStatus.numPeers,
|
|
1243
|
+
" peer",
|
|
1244
|
+
torrentStatus.numPeers !== 1 ? "s" : ""
|
|
1245
|
+
] }),
|
|
1246
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
1247
|
+
Math.round(torrentStatus.progress * 100),
|
|
1248
|
+
"%"
|
|
1249
|
+
] })
|
|
1250
|
+
] })
|
|
1251
|
+
] })),
|
|
1252
|
+
playlist.length > 1 && /* @__PURE__ */ jsxs(Select, { value: sortKey, onValueChange: handleSort, children: [
|
|
1253
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "h-7 text-xs", "aria-label": "Sort playlist", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Sort by\u2026" }) }),
|
|
1254
|
+
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
1255
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "name-asc", children: "Name A\u2013Z" }),
|
|
1256
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "name-desc", children: "Name Z\u2013A" }),
|
|
1257
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "duration-asc", children: "Shortest first" }),
|
|
1258
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "duration-desc", children: "Longest first" })
|
|
1259
|
+
] })
|
|
1260
|
+
] })
|
|
1125
1261
|
] }),
|
|
1126
|
-
|
|
1127
|
-
/* @__PURE__ */ jsx(
|
|
1128
|
-
|
|
1129
|
-
] })
|
|
1130
|
-
|
|
1131
|
-
showMagnet && torrentStatus.status === "ready" && (torrentStatus.progress >= 1 ? /* @__PURE__ */ jsxs("p", { className: "text-[10px] text-emerald-500 flex items-center gap-1", children: [
|
|
1132
|
-
/* @__PURE__ */ jsx(Download, { className: "h-3 w-3 shrink-0" }),
|
|
1133
|
-
"Download ready \u2014 hover a file to save it"
|
|
1134
|
-
] }) : /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
1135
|
-
/* @__PURE__ */ jsx("div", { className: "w-full bg-muted rounded-full h-1 overflow-hidden", children: /* @__PURE__ */ jsx(
|
|
1136
|
-
"div",
|
|
1262
|
+
/* @__PURE__ */ jsx(ScrollArea, { className: "flex-1", children: /* @__PURE__ */ jsx("div", { className: "p-2 space-y-0.5", children: playlist.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "text-center text-xs text-muted-foreground py-10 px-2", children: [
|
|
1263
|
+
/* @__PURE__ */ jsx("p", { children: "Your playlist is empty." }),
|
|
1264
|
+
/* @__PURE__ */ jsx("p", { children: "Add files or a stream URL to get started." })
|
|
1265
|
+
] }) : /* @__PURE__ */ jsx(DndContext, { collisionDetection: closestCenter, onDragEnd: handleDragEnd, children: /* @__PURE__ */ jsx(
|
|
1266
|
+
SortableContext,
|
|
1137
1267
|
{
|
|
1138
|
-
|
|
1139
|
-
|
|
1268
|
+
items: playlist.map((i) => i.id),
|
|
1269
|
+
strategy: verticalListSortingStrategy,
|
|
1270
|
+
children: playlist.map((item, index) => /* @__PURE__ */ jsx(
|
|
1271
|
+
SortablePlaylistItem,
|
|
1272
|
+
{
|
|
1273
|
+
item,
|
|
1274
|
+
index,
|
|
1275
|
+
isActive: index === currentVideoIndex,
|
|
1276
|
+
downloadReady: item.source === "torrent" && torrentStatus.progress >= 1,
|
|
1277
|
+
onSelect: onSelectVideo,
|
|
1278
|
+
onRemove: onRemoveItem
|
|
1279
|
+
},
|
|
1280
|
+
item.id
|
|
1281
|
+
))
|
|
1140
1282
|
}
|
|
1141
|
-
) })
|
|
1142
|
-
/* @__PURE__ */ jsxs("p", { className: "text-[10px] text-muted-foreground flex items-center justify-between", children: [
|
|
1143
|
-
/* @__PURE__ */ jsxs("span", { children: [
|
|
1144
|
-
"\u2193 ",
|
|
1145
|
-
formatBytes(torrentStatus.downloadSpeed),
|
|
1146
|
-
"/s \xB7 ",
|
|
1147
|
-
torrentStatus.numPeers,
|
|
1148
|
-
" peer",
|
|
1149
|
-
torrentStatus.numPeers !== 1 ? "s" : ""
|
|
1150
|
-
] }),
|
|
1151
|
-
/* @__PURE__ */ jsxs("span", { children: [
|
|
1152
|
-
Math.round(torrentStatus.progress * 100),
|
|
1153
|
-
"%"
|
|
1154
|
-
] })
|
|
1155
|
-
] })
|
|
1156
|
-
] })),
|
|
1157
|
-
playlist.length > 1 && /* @__PURE__ */ jsxs(Select, { value: sortKey, onValueChange: handleSort, children: [
|
|
1158
|
-
/* @__PURE__ */ jsx(SelectTrigger, { className: "h-7 text-xs", "aria-label": "Sort playlist", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Sort by\u2026" }) }),
|
|
1159
|
-
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
1160
|
-
/* @__PURE__ */ jsx(SelectItem, { value: "name-asc", children: "Name A\u2013Z" }),
|
|
1161
|
-
/* @__PURE__ */ jsx(SelectItem, { value: "name-desc", children: "Name Z\u2013A" }),
|
|
1162
|
-
/* @__PURE__ */ jsx(SelectItem, { value: "duration-asc", children: "Shortest first" }),
|
|
1163
|
-
/* @__PURE__ */ jsx(SelectItem, { value: "duration-desc", children: "Longest first" })
|
|
1164
|
-
] })
|
|
1283
|
+
) }) }) })
|
|
1165
1284
|
] })
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
/* @__PURE__ */ jsx("p", { children: "Your playlist is empty." }),
|
|
1169
|
-
/* @__PURE__ */ jsx("p", { children: "Add files or a stream URL to get started." })
|
|
1170
|
-
] }) : /* @__PURE__ */ jsx(DndContext, { collisionDetection: closestCenter, onDragEnd: handleDragEnd, children: /* @__PURE__ */ jsx(
|
|
1171
|
-
SortableContext,
|
|
1172
|
-
{
|
|
1173
|
-
items: playlist.map((i) => i.id),
|
|
1174
|
-
strategy: verticalListSortingStrategy,
|
|
1175
|
-
children: playlist.map((item, index) => /* @__PURE__ */ jsx(
|
|
1176
|
-
SortablePlaylistItem,
|
|
1177
|
-
{
|
|
1178
|
-
item,
|
|
1179
|
-
index,
|
|
1180
|
-
isActive: index === currentVideoIndex,
|
|
1181
|
-
downloadReady: item.source === "torrent" && torrentStatus.progress >= 1,
|
|
1182
|
-
onSelect: onSelectVideo,
|
|
1183
|
-
onRemove: onRemoveItem
|
|
1184
|
-
},
|
|
1185
|
-
item.id
|
|
1186
|
-
))
|
|
1187
|
-
}
|
|
1188
|
-
) }) }) })
|
|
1189
|
-
] })
|
|
1285
|
+
)
|
|
1286
|
+
}
|
|
1190
1287
|
) });
|
|
1191
1288
|
};
|
|
1192
1289
|
var playlist_panel_default = PlaylistPanel;
|
|
@@ -1279,7 +1376,7 @@ function PlayerErrorDisplay({ error, onRetry, onSkip, onDismiss }) {
|
|
|
1279
1376
|
] })
|
|
1280
1377
|
] });
|
|
1281
1378
|
}
|
|
1282
|
-
function
|
|
1379
|
+
function formatTime4(seconds) {
|
|
1283
1380
|
if (isNaN(seconds) || seconds === 0) return "\u2014";
|
|
1284
1381
|
const h = Math.floor(seconds / 3600);
|
|
1285
1382
|
const m = Math.floor(seconds % 3600 / 60);
|
|
@@ -1302,7 +1399,7 @@ function VideoInfoPanel({ metadata, onClose }) {
|
|
|
1302
1399
|
const rows = [
|
|
1303
1400
|
["File", metadata.filename ? metadata.filename.split("/").pop() ?? metadata.filename : "\u2014"],
|
|
1304
1401
|
["Size", formatSize(metadata.fileSize)],
|
|
1305
|
-
["Duration",
|
|
1402
|
+
["Duration", formatTime4(metadata.duration)],
|
|
1306
1403
|
["Container", metadata.container || "\u2014"],
|
|
1307
1404
|
["Resolution", metadata.width && metadata.height ? `${metadata.width} \xD7 ${metadata.height}` : "\u2014"],
|
|
1308
1405
|
["Frame Rate", metadata.frameRate ? `${metadata.frameRate} fps` : "\u2014"],
|
|
@@ -1340,7 +1437,7 @@ function VideoInfoPanel({ metadata, onClose }) {
|
|
|
1340
1437
|
}
|
|
1341
1438
|
var Dialog = DialogPrimitive.Root;
|
|
1342
1439
|
var DialogPortal = DialogPrimitive.Portal;
|
|
1343
|
-
var DialogOverlay =
|
|
1440
|
+
var DialogOverlay = React11.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1344
1441
|
DialogPrimitive.Overlay,
|
|
1345
1442
|
{
|
|
1346
1443
|
ref,
|
|
@@ -1352,7 +1449,7 @@ var DialogOverlay = React10.forwardRef(({ className, ...props }, ref) => /* @__P
|
|
|
1352
1449
|
}
|
|
1353
1450
|
));
|
|
1354
1451
|
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
|
1355
|
-
var DialogContent =
|
|
1452
|
+
var DialogContent = React11.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(DialogPortal, { children: [
|
|
1356
1453
|
/* @__PURE__ */ jsx(DialogOverlay, {}),
|
|
1357
1454
|
/* @__PURE__ */ jsxs(
|
|
1358
1455
|
DialogPrimitive.Content,
|
|
@@ -1388,7 +1485,7 @@ var DialogHeader = ({
|
|
|
1388
1485
|
}
|
|
1389
1486
|
);
|
|
1390
1487
|
DialogHeader.displayName = "DialogHeader";
|
|
1391
|
-
var DialogTitle =
|
|
1488
|
+
var DialogTitle = React11.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1392
1489
|
DialogPrimitive.Title,
|
|
1393
1490
|
{
|
|
1394
1491
|
ref,
|
|
@@ -1400,7 +1497,7 @@ var DialogTitle = React10.forwardRef(({ className, ...props }, ref) => /* @__PUR
|
|
|
1400
1497
|
}
|
|
1401
1498
|
));
|
|
1402
1499
|
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
|
1403
|
-
var DialogDescription =
|
|
1500
|
+
var DialogDescription = React11.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1404
1501
|
DialogPrimitive.Description,
|
|
1405
1502
|
{
|
|
1406
1503
|
ref,
|
|
@@ -1509,8 +1606,8 @@ function toast({ ...props }) {
|
|
|
1509
1606
|
};
|
|
1510
1607
|
}
|
|
1511
1608
|
function useToast() {
|
|
1512
|
-
const [state, setState] =
|
|
1513
|
-
|
|
1609
|
+
const [state, setState] = React11.useState(memoryState);
|
|
1610
|
+
React11.useEffect(() => {
|
|
1514
1611
|
listeners.push(setState);
|
|
1515
1612
|
return () => {
|
|
1516
1613
|
const index = listeners.indexOf(setState);
|
|
@@ -1706,7 +1803,7 @@ function SubtitleOverlay({ videoRef, activeSubtitle }) {
|
|
|
1706
1803
|
}
|
|
1707
1804
|
var AlertDialog = AlertDialogPrimitive.Root;
|
|
1708
1805
|
var AlertDialogPortal = AlertDialogPrimitive.Portal;
|
|
1709
|
-
var AlertDialogOverlay =
|
|
1806
|
+
var AlertDialogOverlay = React11.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1710
1807
|
AlertDialogPrimitive.Overlay,
|
|
1711
1808
|
{
|
|
1712
1809
|
className: cn(
|
|
@@ -1718,7 +1815,7 @@ var AlertDialogOverlay = React10.forwardRef(({ className, ...props }, ref) => /*
|
|
|
1718
1815
|
}
|
|
1719
1816
|
));
|
|
1720
1817
|
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
|
|
1721
|
-
var AlertDialogContent =
|
|
1818
|
+
var AlertDialogContent = React11.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxs(AlertDialogPortal, { children: [
|
|
1722
1819
|
/* @__PURE__ */ jsx(AlertDialogOverlay, {}),
|
|
1723
1820
|
/* @__PURE__ */ jsx(
|
|
1724
1821
|
AlertDialogPrimitive.Content,
|
|
@@ -1761,7 +1858,7 @@ var AlertDialogFooter = ({
|
|
|
1761
1858
|
}
|
|
1762
1859
|
);
|
|
1763
1860
|
AlertDialogFooter.displayName = "AlertDialogFooter";
|
|
1764
|
-
var AlertDialogTitle =
|
|
1861
|
+
var AlertDialogTitle = React11.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1765
1862
|
AlertDialogPrimitive.Title,
|
|
1766
1863
|
{
|
|
1767
1864
|
ref,
|
|
@@ -1770,7 +1867,7 @@ var AlertDialogTitle = React10.forwardRef(({ className, ...props }, ref) => /* @
|
|
|
1770
1867
|
}
|
|
1771
1868
|
));
|
|
1772
1869
|
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
|
|
1773
|
-
var AlertDialogDescription =
|
|
1870
|
+
var AlertDialogDescription = React11.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1774
1871
|
AlertDialogPrimitive.Description,
|
|
1775
1872
|
{
|
|
1776
1873
|
ref,
|
|
@@ -1779,7 +1876,7 @@ var AlertDialogDescription = React10.forwardRef(({ className, ...props }, ref) =
|
|
|
1779
1876
|
}
|
|
1780
1877
|
));
|
|
1781
1878
|
AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName;
|
|
1782
|
-
var AlertDialogAction =
|
|
1879
|
+
var AlertDialogAction = React11.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1783
1880
|
AlertDialogPrimitive.Action,
|
|
1784
1881
|
{
|
|
1785
1882
|
ref,
|
|
@@ -1788,7 +1885,7 @@ var AlertDialogAction = React10.forwardRef(({ className, ...props }, ref) => /*
|
|
|
1788
1885
|
}
|
|
1789
1886
|
));
|
|
1790
1887
|
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
|
|
1791
|
-
var AlertDialogCancel =
|
|
1888
|
+
var AlertDialogCancel = React11.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1792
1889
|
AlertDialogPrimitive.Cancel,
|
|
1793
1890
|
{
|
|
1794
1891
|
ref,
|
|
@@ -1857,6 +1954,8 @@ var LightBirdPlayer = () => {
|
|
|
1857
1954
|
const [cancellableProcessing, setCancellableProcessing] = useState(false);
|
|
1858
1955
|
const [mediaThumbnail, setMediaThumbnail] = useState(null);
|
|
1859
1956
|
const [tracksLoading, setTracksLoading] = useState(false);
|
|
1957
|
+
const abLoopCycleRef = useRef(() => {
|
|
1958
|
+
});
|
|
1860
1959
|
const shortcutHandlers = useMemo(() => ({
|
|
1861
1960
|
"play-pause": () => playback.togglePlay(),
|
|
1862
1961
|
"seek-forward-5": () => {
|
|
@@ -1906,9 +2005,13 @@ var LightBirdPlayer = () => {
|
|
|
1906
2005
|
const prev = chapters[cur.index - 1];
|
|
1907
2006
|
if (prev) el.currentTime = prev.startTime;
|
|
1908
2007
|
}
|
|
1909
|
-
}
|
|
2008
|
+
},
|
|
2009
|
+
"frame-step-forward": () => playback.frameStep("forward"),
|
|
2010
|
+
"frame-step-backward": () => playback.frameStep("backward"),
|
|
2011
|
+
"loop-toggle": () => playback.toggleLoop(),
|
|
2012
|
+
"ab-loop-cycle": () => abLoopCycleRef.current()
|
|
1910
2013
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1911
|
-
}), [playback.togglePlay, playback.seek, playback.setVolume, playback.toggleMute, fullscreen.toggle, chapters, currentChapter]);
|
|
2014
|
+
}), [playback.togglePlay, playback.seek, playback.setVolume, playback.toggleMute, playback.frameStep, playback.toggleLoop, fullscreen.toggle, chapters, currentChapter]);
|
|
1912
2015
|
useKeyboardShortcuts(shortcuts, shortcutHandlers);
|
|
1913
2016
|
const stopStallDetection = () => {
|
|
1914
2017
|
if (streamStallDetectorRef.current) {
|
|
@@ -2311,6 +2414,9 @@ var LightBirdPlayer = () => {
|
|
|
2311
2414
|
else if (abLoop.pointB === null) abLoop.setPointB();
|
|
2312
2415
|
else abLoop.clear();
|
|
2313
2416
|
}, [abLoop.pointA, abLoop.pointB, abLoop.setPointA, abLoop.setPointB, abLoop.clear]);
|
|
2417
|
+
useEffect(() => {
|
|
2418
|
+
abLoopCycleRef.current = handleABLoopCycle;
|
|
2419
|
+
}, [handleABLoopCycle]);
|
|
2314
2420
|
const abLoopState = useMemo(
|
|
2315
2421
|
() => ({ pointA: abLoop.pointA, pointB: abLoop.pointB, isLooping: abLoop.isLooping }),
|
|
2316
2422
|
[abLoop.pointA, abLoop.pointB, abLoop.isLooping]
|
|
@@ -2439,9 +2545,11 @@ var LightBirdPlayer = () => {
|
|
|
2439
2545
|
)
|
|
2440
2546
|
}
|
|
2441
2547
|
),
|
|
2442
|
-
|
|
2548
|
+
/* @__PURE__ */ jsx(
|
|
2443
2549
|
player_controls_default,
|
|
2444
2550
|
{
|
|
2551
|
+
isDisabled: !playlist.currentItem,
|
|
2552
|
+
videoRef,
|
|
2445
2553
|
isPlaying: playback.isPlaying,
|
|
2446
2554
|
progress: playback.progress,
|
|
2447
2555
|
duration: playback.duration,
|
|
@@ -2510,9 +2618,17 @@ var LightBirdPlayer = () => {
|
|
|
2510
2618
|
}
|
|
2511
2619
|
}
|
|
2512
2620
|
),
|
|
2513
|
-
!playlist.currentItem && !isLoading && !loadingMessage && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "
|
|
2514
|
-
/* @__PURE__ */ jsx(
|
|
2515
|
-
|
|
2621
|
+
!playlist.currentItem && !isLoading && !loadingMessage && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center px-6", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center text-center max-w-sm", children: [
|
|
2622
|
+
/* @__PURE__ */ jsx(
|
|
2623
|
+
"div",
|
|
2624
|
+
{
|
|
2625
|
+
"aria-hidden": true,
|
|
2626
|
+
className: "flex h-16 w-16 items-center justify-center rounded-full bg-accent/10 ring-1 ring-accent/30",
|
|
2627
|
+
children: /* @__PURE__ */ jsx(Film, { className: "h-7 w-7", style: { color: "hsl(var(--accent))" } })
|
|
2628
|
+
}
|
|
2629
|
+
),
|
|
2630
|
+
/* @__PURE__ */ jsx("p", { className: "mt-5 text-lg font-medium text-foreground", children: "No video loaded" }),
|
|
2631
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: "Drop files anywhere, or pick a source from the playlist panel." })
|
|
2516
2632
|
] }) })
|
|
2517
2633
|
]
|
|
2518
2634
|
}
|
|
@@ -2595,7 +2711,7 @@ var PlayerErrorBoundary = class extends Component {
|
|
|
2595
2711
|
}
|
|
2596
2712
|
};
|
|
2597
2713
|
var ToastProvider = ToastPrimitives.Provider;
|
|
2598
|
-
var ToastViewport =
|
|
2714
|
+
var ToastViewport = React11.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
2599
2715
|
ToastPrimitives.Viewport,
|
|
2600
2716
|
{
|
|
2601
2717
|
ref,
|
|
@@ -2621,7 +2737,7 @@ var toastVariants = cva(
|
|
|
2621
2737
|
}
|
|
2622
2738
|
}
|
|
2623
2739
|
);
|
|
2624
|
-
var Toast =
|
|
2740
|
+
var Toast = React11.forwardRef(({ className, variant, ...props }, ref) => {
|
|
2625
2741
|
return /* @__PURE__ */ jsx(
|
|
2626
2742
|
ToastPrimitives.Root,
|
|
2627
2743
|
{
|
|
@@ -2632,7 +2748,7 @@ var Toast = React10.forwardRef(({ className, variant, ...props }, ref) => {
|
|
|
2632
2748
|
);
|
|
2633
2749
|
});
|
|
2634
2750
|
Toast.displayName = ToastPrimitives.Root.displayName;
|
|
2635
|
-
var ToastAction =
|
|
2751
|
+
var ToastAction = React11.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
2636
2752
|
ToastPrimitives.Action,
|
|
2637
2753
|
{
|
|
2638
2754
|
ref,
|
|
@@ -2644,7 +2760,7 @@ var ToastAction = React10.forwardRef(({ className, ...props }, ref) => /* @__PUR
|
|
|
2644
2760
|
}
|
|
2645
2761
|
));
|
|
2646
2762
|
ToastAction.displayName = ToastPrimitives.Action.displayName;
|
|
2647
|
-
var ToastClose =
|
|
2763
|
+
var ToastClose = React11.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
2648
2764
|
ToastPrimitives.Close,
|
|
2649
2765
|
{
|
|
2650
2766
|
ref,
|
|
@@ -2658,7 +2774,7 @@ var ToastClose = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE
|
|
|
2658
2774
|
}
|
|
2659
2775
|
));
|
|
2660
2776
|
ToastClose.displayName = ToastPrimitives.Close.displayName;
|
|
2661
|
-
var ToastTitle =
|
|
2777
|
+
var ToastTitle = React11.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
2662
2778
|
ToastPrimitives.Title,
|
|
2663
2779
|
{
|
|
2664
2780
|
ref,
|
|
@@ -2667,7 +2783,7 @@ var ToastTitle = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE
|
|
|
2667
2783
|
}
|
|
2668
2784
|
));
|
|
2669
2785
|
ToastTitle.displayName = ToastPrimitives.Title.displayName;
|
|
2670
|
-
var ToastDescription =
|
|
2786
|
+
var ToastDescription = React11.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
2671
2787
|
ToastPrimitives.Description,
|
|
2672
2788
|
{
|
|
2673
2789
|
ref,
|