@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.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var
|
|
4
|
+
var React11 = require('react');
|
|
5
5
|
var clsx = require('clsx');
|
|
6
6
|
var tailwindMerge = require('tailwind-merge');
|
|
7
7
|
var SliderPrimitive = require('@radix-ui/react-slider');
|
|
@@ -11,6 +11,7 @@ var classVarianceAuthority = require('class-variance-authority');
|
|
|
11
11
|
var PopoverPrimitive = require('@radix-ui/react-popover');
|
|
12
12
|
var TooltipPrimitive = require('@radix-ui/react-tooltip');
|
|
13
13
|
var LabelPrimitive = require('@radix-ui/react-label');
|
|
14
|
+
var react = require('@lightbird/core/react');
|
|
14
15
|
var lucideReact = require('lucide-react');
|
|
15
16
|
var RadioGroupPrimitive = require('@radix-ui/react-radio-group');
|
|
16
17
|
var core$1 = require('@dnd-kit/core');
|
|
@@ -20,7 +21,6 @@ var ScrollAreaPrimitive = require('@radix-ui/react-scroll-area');
|
|
|
20
21
|
var SelectPrimitive = require('@radix-ui/react-select');
|
|
21
22
|
var core = require('@lightbird/core');
|
|
22
23
|
var DialogPrimitive = require('@radix-ui/react-dialog');
|
|
23
|
-
var react = require('@lightbird/core/react');
|
|
24
24
|
var reactSdk = require('@openfeature/react-sdk');
|
|
25
25
|
var AlertDialogPrimitive = require('@radix-ui/react-alert-dialog');
|
|
26
26
|
var ToastPrimitives = require('@radix-ui/react-toast');
|
|
@@ -43,7 +43,7 @@ function _interopNamespace(e) {
|
|
|
43
43
|
return Object.freeze(n);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
var
|
|
46
|
+
var React11__namespace = /*#__PURE__*/_interopNamespace(React11);
|
|
47
47
|
var SliderPrimitive__namespace = /*#__PURE__*/_interopNamespace(SliderPrimitive);
|
|
48
48
|
var PopoverPrimitive__namespace = /*#__PURE__*/_interopNamespace(PopoverPrimitive);
|
|
49
49
|
var TooltipPrimitive__namespace = /*#__PURE__*/_interopNamespace(TooltipPrimitive);
|
|
@@ -58,7 +58,7 @@ var ToastPrimitives__namespace = /*#__PURE__*/_interopNamespace(ToastPrimitives)
|
|
|
58
58
|
function cn(...inputs) {
|
|
59
59
|
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
60
60
|
}
|
|
61
|
-
var Slider =
|
|
61
|
+
var Slider = React11__namespace.forwardRef(({ className, trackClassName, rangeClassName, thumbClassName, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
62
62
|
SliderPrimitive__namespace.Root,
|
|
63
63
|
{
|
|
64
64
|
ref,
|
|
@@ -68,8 +68,33 @@ var Slider = React10__namespace.forwardRef(({ className, ...props }, ref) => /*
|
|
|
68
68
|
),
|
|
69
69
|
...props,
|
|
70
70
|
children: [
|
|
71
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
72
|
-
|
|
71
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
72
|
+
SliderPrimitive__namespace.Track,
|
|
73
|
+
{
|
|
74
|
+
className: cn(
|
|
75
|
+
"relative h-2 w-full grow overflow-hidden rounded-full bg-secondary transition-all duration-150 ease-out",
|
|
76
|
+
trackClassName
|
|
77
|
+
),
|
|
78
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
79
|
+
SliderPrimitive__namespace.Range,
|
|
80
|
+
{
|
|
81
|
+
className: cn(
|
|
82
|
+
"absolute h-full bg-primary transition-[box-shadow] duration-150",
|
|
83
|
+
rangeClassName
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
),
|
|
89
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
90
|
+
SliderPrimitive__namespace.Thumb,
|
|
91
|
+
{
|
|
92
|
+
className: cn(
|
|
93
|
+
"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",
|
|
94
|
+
thumbClassName
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
)
|
|
73
98
|
]
|
|
74
99
|
}
|
|
75
100
|
));
|
|
@@ -99,7 +124,7 @@ var buttonVariants = classVarianceAuthority.cva(
|
|
|
99
124
|
}
|
|
100
125
|
}
|
|
101
126
|
);
|
|
102
|
-
var Button =
|
|
127
|
+
var Button = React11__namespace.forwardRef(
|
|
103
128
|
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
104
129
|
const Comp = asChild ? reactSlot.Slot : "button";
|
|
105
130
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -115,7 +140,7 @@ var Button = React10__namespace.forwardRef(
|
|
|
115
140
|
Button.displayName = "Button";
|
|
116
141
|
var Popover = PopoverPrimitive__namespace.Root;
|
|
117
142
|
var PopoverTrigger = PopoverPrimitive__namespace.Trigger;
|
|
118
|
-
var PopoverContent =
|
|
143
|
+
var PopoverContent = React11__namespace.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(PopoverPrimitive__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
119
144
|
PopoverPrimitive__namespace.Content,
|
|
120
145
|
{
|
|
121
146
|
ref,
|
|
@@ -132,7 +157,7 @@ PopoverContent.displayName = PopoverPrimitive__namespace.Content.displayName;
|
|
|
132
157
|
var TooltipProvider = TooltipPrimitive__namespace.Provider;
|
|
133
158
|
var Tooltip = TooltipPrimitive__namespace.Root;
|
|
134
159
|
var TooltipTrigger = TooltipPrimitive__namespace.Trigger;
|
|
135
|
-
var TooltipContent =
|
|
160
|
+
var TooltipContent = React11__namespace.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
136
161
|
TooltipPrimitive__namespace.Content,
|
|
137
162
|
{
|
|
138
163
|
ref,
|
|
@@ -148,7 +173,7 @@ TooltipContent.displayName = TooltipPrimitive__namespace.Content.displayName;
|
|
|
148
173
|
var labelVariants = classVarianceAuthority.cva(
|
|
149
174
|
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
150
175
|
);
|
|
151
|
-
var Label =
|
|
176
|
+
var Label = React11__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
152
177
|
LabelPrimitive__namespace.Root,
|
|
153
178
|
{
|
|
154
179
|
ref,
|
|
@@ -157,7 +182,164 @@ var Label = React10__namespace.forwardRef(({ className, ...props }, ref) => /* @
|
|
|
157
182
|
}
|
|
158
183
|
));
|
|
159
184
|
Label.displayName = LabelPrimitive__namespace.Root.displayName;
|
|
160
|
-
var
|
|
185
|
+
var formatTime = (time) => {
|
|
186
|
+
if (isNaN(time)) return "00:00";
|
|
187
|
+
const date = /* @__PURE__ */ new Date(0);
|
|
188
|
+
date.setSeconds(time);
|
|
189
|
+
const timeString = date.toISOString().substr(11, 8);
|
|
190
|
+
return timeString.startsWith("00:") ? timeString.substr(3) : timeString;
|
|
191
|
+
};
|
|
192
|
+
var SeekBar = React11__namespace.default.memo(function SeekBar2({
|
|
193
|
+
progress,
|
|
194
|
+
duration,
|
|
195
|
+
isPlaying,
|
|
196
|
+
onSeek,
|
|
197
|
+
videoRef,
|
|
198
|
+
chapters = [],
|
|
199
|
+
abLoop = { pointA: null, pointB: null, isLooping: false },
|
|
200
|
+
onSeekHover,
|
|
201
|
+
seekPreviewThumbnail = null
|
|
202
|
+
}) {
|
|
203
|
+
const noopRef = React11.useRef(null);
|
|
204
|
+
const smooth = react.useSmoothProgress(videoRef ?? noopRef, {
|
|
205
|
+
isPlaying: !!videoRef && isPlaying,
|
|
206
|
+
fallback: progress
|
|
207
|
+
});
|
|
208
|
+
const displayProgress = videoRef ? smooth : progress;
|
|
209
|
+
const [seekHover, setSeekHover] = React11.useState(null);
|
|
210
|
+
const [isHovering, setIsHovering] = React11.useState(false);
|
|
211
|
+
const [isScrubbing, setIsScrubbing] = React11.useState(false);
|
|
212
|
+
const active = isHovering || isScrubbing;
|
|
213
|
+
const handleSeekHover = (e) => {
|
|
214
|
+
if (duration <= 0) return;
|
|
215
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
216
|
+
if (rect.width <= 0) return;
|
|
217
|
+
const ratio = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
|
|
218
|
+
const time = ratio * duration;
|
|
219
|
+
setSeekHover({ ratio, time });
|
|
220
|
+
onSeekHover?.(time);
|
|
221
|
+
};
|
|
222
|
+
const handleSeekLeave = () => {
|
|
223
|
+
setSeekHover(null);
|
|
224
|
+
setIsHovering(false);
|
|
225
|
+
onSeekHover?.(null);
|
|
226
|
+
};
|
|
227
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
228
|
+
"div",
|
|
229
|
+
{
|
|
230
|
+
className: "relative w-full py-2",
|
|
231
|
+
"data-testid": "seek-bar",
|
|
232
|
+
"data-scrubbing": isScrubbing || void 0,
|
|
233
|
+
"data-hover": isHovering || void 0,
|
|
234
|
+
onMouseEnter: () => setIsHovering(true),
|
|
235
|
+
onMouseMove: handleSeekHover,
|
|
236
|
+
onMouseLeave: handleSeekLeave,
|
|
237
|
+
children: [
|
|
238
|
+
seekHover && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
239
|
+
"div",
|
|
240
|
+
{
|
|
241
|
+
"data-testid": "seek-preview",
|
|
242
|
+
className: "absolute bottom-full mb-3 -translate-x-1/2 pointer-events-none flex flex-col items-center z-20",
|
|
243
|
+
style: { left: `${seekHover.ratio * 100}%` },
|
|
244
|
+
children: [
|
|
245
|
+
seekPreviewThumbnail ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
246
|
+
"img",
|
|
247
|
+
{
|
|
248
|
+
src: seekPreviewThumbnail,
|
|
249
|
+
alt: "",
|
|
250
|
+
className: "w-40 h-[90px] rounded border border-white/20 bg-black object-cover shadow-lg"
|
|
251
|
+
}
|
|
252
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-40 h-[90px] rounded border border-white/20 bg-black/80 shadow-lg" }),
|
|
253
|
+
/* @__PURE__ */ jsxRuntime.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) })
|
|
254
|
+
]
|
|
255
|
+
}
|
|
256
|
+
),
|
|
257
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
258
|
+
Slider,
|
|
259
|
+
{
|
|
260
|
+
value: [displayProgress],
|
|
261
|
+
max: duration || 0,
|
|
262
|
+
step: 0.05,
|
|
263
|
+
onValueChange: ([val]) => onSeek(val),
|
|
264
|
+
onPointerDown: () => setIsScrubbing(true),
|
|
265
|
+
onPointerUp: () => setIsScrubbing(false),
|
|
266
|
+
onLostPointerCapture: () => setIsScrubbing(false),
|
|
267
|
+
className: "w-full",
|
|
268
|
+
trackClassName: cn(
|
|
269
|
+
"rounded-none transition-all duration-150 ease-out",
|
|
270
|
+
active ? "h-1.5" : "h-[3px]"
|
|
271
|
+
),
|
|
272
|
+
rangeClassName: cn(
|
|
273
|
+
"rounded-none",
|
|
274
|
+
active && "shadow-[0_0_6px_hsl(var(--primary)/0.6)]"
|
|
275
|
+
),
|
|
276
|
+
thumbClassName: cn(
|
|
277
|
+
"h-3 w-3 border transition-[transform,opacity] duration-150",
|
|
278
|
+
active ? "scale-100 opacity-100" : "scale-0 opacity-0",
|
|
279
|
+
isScrubbing && "scale-125"
|
|
280
|
+
)
|
|
281
|
+
}
|
|
282
|
+
),
|
|
283
|
+
chapters.length > 0 && duration > 0 && chapters.slice(1).map((chapter) => /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
284
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
285
|
+
"div",
|
|
286
|
+
{
|
|
287
|
+
"data-testid": "chapter-tick",
|
|
288
|
+
style: {
|
|
289
|
+
position: "absolute",
|
|
290
|
+
left: `${chapter.startTime / duration * 100}%`,
|
|
291
|
+
top: "50%",
|
|
292
|
+
width: "2px",
|
|
293
|
+
height: active ? "10px" : "6px",
|
|
294
|
+
background: "white",
|
|
295
|
+
opacity: 0.55,
|
|
296
|
+
pointerEvents: "none",
|
|
297
|
+
transform: "translate(-1px, -50%)",
|
|
298
|
+
transition: "height 150ms ease-out"
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
) }),
|
|
302
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
|
|
303
|
+
chapter.title,
|
|
304
|
+
" \u2014 ",
|
|
305
|
+
formatTime(chapter.startTime)
|
|
306
|
+
] }) })
|
|
307
|
+
] }, chapter.index)),
|
|
308
|
+
duration > 0 && abLoop.pointA !== null && abLoop.pointB !== null && /* @__PURE__ */ jsxRuntime.jsx(
|
|
309
|
+
"div",
|
|
310
|
+
{
|
|
311
|
+
"data-testid": "ab-loop-region",
|
|
312
|
+
className: cn(
|
|
313
|
+
"pointer-events-none absolute top-1/2 -translate-y-1/2 bg-primary/40 transition-[height] duration-150",
|
|
314
|
+
active ? "h-1.5" : "h-[3px]"
|
|
315
|
+
),
|
|
316
|
+
style: {
|
|
317
|
+
left: `${abLoop.pointA / duration * 100}%`,
|
|
318
|
+
width: `${(abLoop.pointB - abLoop.pointA) / duration * 100}%`
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
),
|
|
322
|
+
duration > 0 && abLoop.pointA !== null && /* @__PURE__ */ jsxRuntime.jsx(
|
|
323
|
+
"div",
|
|
324
|
+
{
|
|
325
|
+
"data-testid": "ab-marker-a",
|
|
326
|
+
className: "pointer-events-none absolute top-1/2 -translate-y-1/2 h-4 w-0.5 -translate-x-1/2 bg-primary",
|
|
327
|
+
style: { left: `${abLoop.pointA / duration * 100}%` }
|
|
328
|
+
}
|
|
329
|
+
),
|
|
330
|
+
duration > 0 && abLoop.pointB !== null && /* @__PURE__ */ jsxRuntime.jsx(
|
|
331
|
+
"div",
|
|
332
|
+
{
|
|
333
|
+
"data-testid": "ab-marker-b",
|
|
334
|
+
className: "pointer-events-none absolute top-1/2 -translate-y-1/2 h-4 w-0.5 -translate-x-1/2 bg-primary",
|
|
335
|
+
style: { left: `${abLoop.pointB / duration * 100}%` }
|
|
336
|
+
}
|
|
337
|
+
)
|
|
338
|
+
]
|
|
339
|
+
}
|
|
340
|
+
);
|
|
341
|
+
});
|
|
342
|
+
var RadioGroup = React11__namespace.forwardRef(({ className, ...props }, ref) => {
|
|
161
343
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
162
344
|
RadioGroupPrimitive__namespace.Root,
|
|
163
345
|
{
|
|
@@ -168,7 +350,7 @@ var RadioGroup = React10__namespace.forwardRef(({ className, ...props }, ref) =>
|
|
|
168
350
|
);
|
|
169
351
|
});
|
|
170
352
|
RadioGroup.displayName = RadioGroupPrimitive__namespace.Root.displayName;
|
|
171
|
-
var RadioGroupItem =
|
|
353
|
+
var RadioGroupItem = React11__namespace.forwardRef(({ className, ...props }, ref) => {
|
|
172
354
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
173
355
|
RadioGroupPrimitive__namespace.Item,
|
|
174
356
|
{
|
|
@@ -183,14 +365,14 @@ var RadioGroupItem = React10__namespace.forwardRef(({ className, ...props }, ref
|
|
|
183
365
|
);
|
|
184
366
|
});
|
|
185
367
|
RadioGroupItem.displayName = RadioGroupPrimitive__namespace.Item.displayName;
|
|
186
|
-
var
|
|
368
|
+
var formatTime2 = (time) => {
|
|
187
369
|
if (isNaN(time)) return "00:00";
|
|
188
370
|
const date = /* @__PURE__ */ new Date(0);
|
|
189
371
|
date.setSeconds(time);
|
|
190
372
|
const timeString = date.toISOString().substr(11, 8);
|
|
191
373
|
return timeString.startsWith("00:") ? timeString.substr(3) : timeString;
|
|
192
374
|
};
|
|
193
|
-
var PlayerControls =
|
|
375
|
+
var PlayerControls = React11__namespace.default.memo(function PlayerControls2({
|
|
194
376
|
isPlaying,
|
|
195
377
|
progress,
|
|
196
378
|
duration,
|
|
@@ -234,355 +416,260 @@ var PlayerControls = React10__namespace.default.memo(function PlayerControls2({
|
|
|
234
416
|
onSeekHover,
|
|
235
417
|
seekPreviewThumbnail = null,
|
|
236
418
|
abLoop = { pointA: null, pointB: null, isLooping: false },
|
|
237
|
-
onABLoopCycle
|
|
419
|
+
onABLoopCycle,
|
|
420
|
+
videoRef,
|
|
421
|
+
isDisabled = false
|
|
238
422
|
}) {
|
|
239
|
-
const formattedProgress =
|
|
240
|
-
const formattedDuration =
|
|
241
|
-
const [chaptersMenuOpen, setChaptersMenuOpen] =
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
children:
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
className: "w-full h-2"
|
|
292
|
-
}
|
|
293
|
-
),
|
|
294
|
-
chapters.length > 0 && duration > 0 && chapters.slice(1).map((chapter) => /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
295
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
296
|
-
"div",
|
|
297
|
-
{
|
|
298
|
-
"data-testid": "chapter-tick",
|
|
299
|
-
style: {
|
|
300
|
-
position: "absolute",
|
|
301
|
-
left: `${chapter.startTime / duration * 100}%`,
|
|
302
|
-
top: 0,
|
|
303
|
-
width: "2px",
|
|
304
|
-
height: "100%",
|
|
305
|
-
background: "white",
|
|
306
|
-
opacity: 0.5,
|
|
307
|
-
pointerEvents: "none",
|
|
308
|
-
transform: "translateX(-1px)"
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
) }),
|
|
312
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
|
|
313
|
-
chapter.title,
|
|
314
|
-
" \u2014 ",
|
|
315
|
-
formatTime(chapter.startTime)
|
|
316
|
-
] }) })
|
|
317
|
-
] }, chapter.index)),
|
|
318
|
-
duration > 0 && abLoop.pointA !== null && abLoop.pointB !== null && /* @__PURE__ */ jsxRuntime.jsx(
|
|
319
|
-
"div",
|
|
320
|
-
{
|
|
321
|
-
"data-testid": "ab-loop-region",
|
|
322
|
-
className: "pointer-events-none absolute top-0 h-full bg-primary/30",
|
|
323
|
-
style: {
|
|
324
|
-
left: `${abLoop.pointA / duration * 100}%`,
|
|
325
|
-
width: `${(abLoop.pointB - abLoop.pointA) / duration * 100}%`
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
),
|
|
329
|
-
duration > 0 && abLoop.pointA !== null && /* @__PURE__ */ jsxRuntime.jsx(
|
|
330
|
-
"div",
|
|
331
|
-
{
|
|
332
|
-
"data-testid": "ab-marker-a",
|
|
333
|
-
className: "pointer-events-none absolute top-0 h-full w-0.5 -translate-x-1/2 bg-primary",
|
|
334
|
-
style: { left: `${abLoop.pointA / duration * 100}%` }
|
|
335
|
-
}
|
|
336
|
-
),
|
|
337
|
-
duration > 0 && abLoop.pointB !== null && /* @__PURE__ */ jsxRuntime.jsx(
|
|
338
|
-
"div",
|
|
339
|
-
{
|
|
340
|
-
"data-testid": "ab-marker-b",
|
|
341
|
-
className: "pointer-events-none absolute top-0 h-full w-0.5 -translate-x-1/2 bg-primary",
|
|
342
|
-
style: { left: `${abLoop.pointB / duration * 100}%` }
|
|
343
|
-
}
|
|
344
|
-
)
|
|
345
|
-
]
|
|
346
|
-
}
|
|
347
|
-
),
|
|
348
|
-
currentChapter && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: currentChapter.title }),
|
|
349
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-white", children: [
|
|
350
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4", children: [
|
|
351
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
352
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "icon", onClick: onPrevious, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SkipBack, {}) }) }),
|
|
353
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Previous (N)" }) })
|
|
354
|
-
] }),
|
|
355
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
356
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "icon", onClick: onPlayPause, children: isPlaying ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pause, {}) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Play, {}) }) }),
|
|
357
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
|
|
358
|
-
isPlaying ? "Pause" : "Play",
|
|
359
|
-
" (Space)"
|
|
360
|
-
] }) })
|
|
361
|
-
] }),
|
|
362
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
363
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "icon", onClick: onNext, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SkipForward, {}) }) }),
|
|
364
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Next (P)" }) })
|
|
365
|
-
] }),
|
|
366
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
367
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
368
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "icon", onClick: onMuteToggle, children: isMuted || volume === 0 ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.VolumeX, {}) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Volume2, {}) }) }),
|
|
369
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Mute (M)" }) })
|
|
370
|
-
] }),
|
|
371
|
-
/* @__PURE__ */ jsxRuntime.jsx(Slider, { value: [isMuted ? 0 : volume], max: 1, step: 0.05, onValueChange: ([val]) => onVolumeChange(val), className: "w-24" })
|
|
372
|
-
] }),
|
|
373
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-mono text-sm", children: [
|
|
374
|
-
formattedProgress,
|
|
375
|
-
" / ",
|
|
376
|
-
formattedDuration
|
|
377
|
-
] })
|
|
378
|
-
] }),
|
|
379
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
380
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
381
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "icon", onClick: () => onFrameStep("backward"), children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Rewind, { size: 18 }) }) }),
|
|
382
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Frame Backward" }) })
|
|
383
|
-
] }),
|
|
384
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
385
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "icon", onClick: () => onFrameStep("forward"), children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FastForward, { size: 18 }) }) }),
|
|
386
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Frame Forward" }) })
|
|
387
|
-
] }),
|
|
388
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Popover, { children: [
|
|
389
|
-
/* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "ghost", className: "font-mono w-16", children: [
|
|
390
|
-
playbackRate,
|
|
391
|
-
"x"
|
|
392
|
-
] }) }),
|
|
393
|
-
/* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { className: "w-40", children: /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
394
|
-
/* @__PURE__ */ jsxRuntime.jsx(RadioGroupItem, { value: String(rate), id: `rate-${rate}` }),
|
|
395
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Label, { htmlFor: `rate-${rate}`, children: [
|
|
396
|
-
rate,
|
|
397
|
-
"x"
|
|
398
|
-
] })
|
|
399
|
-
] }, rate)) }) })
|
|
400
|
-
] }),
|
|
401
|
-
audioTracks.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(Popover, { children: [
|
|
402
|
-
/* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "ghost", size: "icon", className: "relative", children: [
|
|
403
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.AudioLines, {}),
|
|
404
|
-
tracksLoading && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "absolute top-0 right-0 h-2.5 w-2.5 animate-spin text-primary" })
|
|
405
|
-
] }) }),
|
|
406
|
-
/* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-48 overflow-y-auto overscroll-contain pr-1", children: /* @__PURE__ */ jsxRuntime.jsx(RadioGroup, { value: activeAudioTrack, onValueChange: onAudioTrackChange, children: audioTracks.map((track) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
407
|
-
/* @__PURE__ */ jsxRuntime.jsx(RadioGroupItem, { value: track.id, id: `audio-${track.id}` }),
|
|
408
|
-
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: `audio-${track.id}`, children: track.name })
|
|
409
|
-
] }, track.id)) }) }) })
|
|
410
|
-
] }),
|
|
411
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Popover, { children: [
|
|
412
|
-
/* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "ghost", size: "icon", className: "relative", children: [
|
|
413
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Subtitles, {}),
|
|
414
|
-
tracksLoading ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "absolute top-0 right-0 h-2.5 w-2.5 animate-spin text-primary" }) : activeSubtitle !== "-1" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute top-0 right-0 block h-2 w-2 rounded-full bg-primary ring-2 ring-background" })
|
|
415
|
-
] }) }),
|
|
416
|
-
/* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { className: "w-64", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
417
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
418
|
-
/* @__PURE__ */ jsxRuntime.jsx(Label, { className: "text-sm font-medium", children: "Subtitles" }),
|
|
419
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
420
|
-
Button,
|
|
423
|
+
const formattedProgress = React11.useMemo(() => formatTime2(progress), [progress]);
|
|
424
|
+
const formattedDuration = React11.useMemo(() => formatTime2(duration), [duration]);
|
|
425
|
+
const [chaptersMenuOpen, setChaptersMenuOpen] = React11.useState(false);
|
|
426
|
+
return /* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
427
|
+
"div",
|
|
428
|
+
{
|
|
429
|
+
"aria-disabled": isDisabled || void 0,
|
|
430
|
+
className: cn(
|
|
431
|
+
"absolute bottom-0 left-0 right-0 px-4 pb-3 pt-10 flex flex-col gap-1.5",
|
|
432
|
+
"bg-gradient-to-t from-black/80 via-black/40 to-transparent transition-opacity duration-300 ease-in-out",
|
|
433
|
+
isDisabled ? "opacity-60 pointer-events-none" : "opacity-0 group-hover:opacity-100"
|
|
434
|
+
),
|
|
435
|
+
children: [
|
|
436
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
437
|
+
SeekBar,
|
|
438
|
+
{
|
|
439
|
+
progress,
|
|
440
|
+
duration,
|
|
441
|
+
isPlaying,
|
|
442
|
+
onSeek,
|
|
443
|
+
videoRef,
|
|
444
|
+
chapters,
|
|
445
|
+
abLoop,
|
|
446
|
+
onSeekHover,
|
|
447
|
+
seekPreviewThumbnail
|
|
448
|
+
}
|
|
449
|
+
),
|
|
450
|
+
currentChapter && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground -mt-0.5", children: currentChapter.title }),
|
|
451
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-white", children: [
|
|
452
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
453
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
454
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "icon", onClick: onPrevious, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SkipBack, {}) }) }),
|
|
455
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Previous (N)" }) })
|
|
456
|
+
] }),
|
|
457
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
458
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "icon", onClick: onPlayPause, children: isPlaying ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pause, {}) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Play, {}) }) }),
|
|
459
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
|
|
460
|
+
isPlaying ? "Pause" : "Play",
|
|
461
|
+
" (Space)"
|
|
462
|
+
] }) })
|
|
463
|
+
] }),
|
|
464
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
465
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "icon", onClick: onNext, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SkipForward, {}) }) }),
|
|
466
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Next (P)" }) })
|
|
467
|
+
] }),
|
|
468
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 ml-1", children: [
|
|
469
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
470
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "icon", onClick: onMuteToggle, children: isMuted || volume === 0 ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.VolumeX, {}) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Volume2, {}) }) }),
|
|
471
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Mute (M)" }) })
|
|
472
|
+
] }),
|
|
473
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
474
|
+
Slider,
|
|
421
475
|
{
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
476
|
+
value: [isMuted ? 0 : volume],
|
|
477
|
+
max: 1,
|
|
478
|
+
step: 0.05,
|
|
479
|
+
onValueChange: ([val]) => onVolumeChange(val),
|
|
480
|
+
"aria-label": "Volume",
|
|
481
|
+
className: "w-24",
|
|
482
|
+
trackClassName: "h-[3px]",
|
|
483
|
+
thumbClassName: "h-3 w-3 border"
|
|
430
484
|
}
|
|
431
485
|
)
|
|
432
486
|
] }),
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: `sub-${sub.id}`, className: "truncate", children: sub.name })
|
|
442
|
-
] }),
|
|
443
|
-
sub.type === "external" && onSubtitleRemove && /* @__PURE__ */ jsxRuntime.jsx(
|
|
444
|
-
Button,
|
|
445
|
-
{
|
|
446
|
-
variant: "ghost",
|
|
447
|
-
size: "sm",
|
|
448
|
-
onClick: () => onSubtitleRemove(sub.id),
|
|
449
|
-
className: "h-6 w-6 p-0",
|
|
450
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3 w-3" })
|
|
451
|
-
}
|
|
452
|
-
)
|
|
453
|
-
] }, sub.id))
|
|
454
|
-
] }) }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground text-center py-2", children: "No subtitles available" })
|
|
455
|
-
] }) })
|
|
456
|
-
] }),
|
|
457
|
-
chapters.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: chaptersMenuOpen, onOpenChange: setChaptersMenuOpen, children: [
|
|
458
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
459
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "icon", "aria-label": "Chapters", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.List, { className: "h-4 w-4" }) }) }) }),
|
|
460
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Chapters" }) })
|
|
487
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-mono text-xs tabular-nums ml-2 text-white/90", children: [
|
|
488
|
+
formattedProgress,
|
|
489
|
+
" ",
|
|
490
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-white/50", children: [
|
|
491
|
+
"/ ",
|
|
492
|
+
formattedDuration
|
|
493
|
+
] })
|
|
494
|
+
] })
|
|
461
495
|
] }),
|
|
462
|
-
/* @__PURE__ */ jsxRuntime.
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
onGoToChapter?.(chapter.index);
|
|
471
|
-
setChaptersMenuOpen(false);
|
|
472
|
-
},
|
|
473
|
-
children: [
|
|
474
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 truncate", children: chapter.title }),
|
|
475
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-4 font-mono text-xs text-muted-foreground shrink-0", children: formatTime(chapter.startTime) })
|
|
476
|
-
]
|
|
477
|
-
},
|
|
478
|
-
chapter.index
|
|
479
|
-
)) }) })
|
|
480
|
-
] }),
|
|
481
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Popover, { children: [
|
|
482
|
-
/* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "icon", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Settings2, {}) }) }),
|
|
483
|
-
/* @__PURE__ */ jsxRuntime.jsxs(PopoverContent, { className: "w-64 space-y-4", children: [
|
|
484
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
485
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Label, { children: [
|
|
486
|
-
"Brightness: ",
|
|
487
|
-
filters.brightness,
|
|
488
|
-
"%"
|
|
496
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
497
|
+
audioTracks.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(Popover, { children: [
|
|
498
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
499
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "ghost", size: "icon", className: "relative", "aria-label": "Audio track", children: [
|
|
500
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.AudioLines, {}),
|
|
501
|
+
tracksLoading && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "absolute top-0 right-0 h-2.5 w-2.5 animate-spin text-primary" })
|
|
502
|
+
] }) }) }),
|
|
503
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Audio track" }) })
|
|
489
504
|
] }),
|
|
490
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
505
|
+
/* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { className: "w-56", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-48 overflow-y-auto overscroll-contain pr-1", children: /* @__PURE__ */ jsxRuntime.jsx(RadioGroup, { value: activeAudioTrack, onValueChange: onAudioTrackChange, children: audioTracks.map((track) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
506
|
+
/* @__PURE__ */ jsxRuntime.jsx(RadioGroupItem, { value: track.id, id: `audio-${track.id}` }),
|
|
507
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: `audio-${track.id}`, children: track.name })
|
|
508
|
+
] }, track.id)) }) }) })
|
|
491
509
|
] }),
|
|
492
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
493
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
494
|
-
"
|
|
495
|
-
|
|
496
|
-
|
|
510
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Popover, { children: [
|
|
511
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
512
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "ghost", size: "icon", className: "relative", "aria-label": "Subtitles", children: [
|
|
513
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Subtitles, {}),
|
|
514
|
+
tracksLoading ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "absolute top-0 right-0 h-2.5 w-2.5 animate-spin text-primary" }) : activeSubtitle !== "-1" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute top-0 right-0 block h-2 w-2 rounded-full bg-primary ring-2 ring-background" })
|
|
515
|
+
] }) }) }),
|
|
516
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Subtitles" }) })
|
|
497
517
|
] }),
|
|
498
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
518
|
+
/* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { className: "w-64", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
519
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
520
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { className: "text-sm font-medium", children: "Subtitles" }),
|
|
521
|
+
onSubtitleUpload && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "outline", size: "sm", onClick: onSubtitleUpload, className: "h-7 px-2", children: [
|
|
522
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-3 w-3 mr-1" }),
|
|
523
|
+
"Add"
|
|
524
|
+
] })
|
|
525
|
+
] }),
|
|
526
|
+
subtitles.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-48 overflow-y-auto overscroll-contain pr-1", children: /* @__PURE__ */ jsxRuntime.jsxs(RadioGroup, { value: activeSubtitle, onValueChange: onSubtitleChange, children: [
|
|
527
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
528
|
+
/* @__PURE__ */ jsxRuntime.jsx(RadioGroupItem, { value: "-1", id: "sub-off" }),
|
|
529
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "sub-off", children: "Off" })
|
|
530
|
+
] }),
|
|
531
|
+
subtitles.map((sub) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between space-x-2", children: [
|
|
532
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-2 flex-1", children: [
|
|
533
|
+
/* @__PURE__ */ jsxRuntime.jsx(RadioGroupItem, { value: sub.id, id: `sub-${sub.id}` }),
|
|
534
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: `sub-${sub.id}`, className: "truncate", children: sub.name })
|
|
535
|
+
] }),
|
|
536
|
+
sub.type === "external" && onSubtitleRemove && /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", onClick: () => onSubtitleRemove(sub.id), className: "h-6 w-6 p-0", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3 w-3" }) })
|
|
537
|
+
] }, sub.id))
|
|
538
|
+
] }) }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground text-center py-2", children: "No subtitles available" })
|
|
539
|
+
] }) })
|
|
499
540
|
] }),
|
|
500
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
501
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
502
|
-
"
|
|
503
|
-
|
|
504
|
-
|
|
541
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Popover, { children: [
|
|
542
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
543
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "ghost", className: "font-mono w-12 h-9 px-2 text-xs", children: [
|
|
544
|
+
playbackRate,
|
|
545
|
+
"x"
|
|
546
|
+
] }) }) }),
|
|
547
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Playback speed" }) })
|
|
505
548
|
] }),
|
|
506
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
549
|
+
/* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { className: "w-40", children: /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
550
|
+
/* @__PURE__ */ jsxRuntime.jsx(RadioGroupItem, { value: String(rate), id: `rate-${rate}` }),
|
|
551
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Label, { htmlFor: `rate-${rate}`, children: [
|
|
552
|
+
rate,
|
|
553
|
+
"x"
|
|
554
|
+
] })
|
|
555
|
+
] }, rate)) }) })
|
|
507
556
|
] }),
|
|
508
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
509
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
510
|
-
"
|
|
511
|
-
|
|
512
|
-
"\xB0"
|
|
557
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Popover, { children: [
|
|
558
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
559
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "icon", "aria-label": "Settings", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Settings2, {}) }) }) }),
|
|
560
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Settings" }) })
|
|
513
561
|
] }),
|
|
514
|
-
/* @__PURE__ */ jsxRuntime.
|
|
562
|
+
/* @__PURE__ */ jsxRuntime.jsxs(PopoverContent, { className: "w-72 p-2 space-y-1", align: "end", children: [
|
|
563
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
|
|
564
|
+
pipSupported && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
565
|
+
"button",
|
|
566
|
+
{
|
|
567
|
+
onClick: onTogglePiP,
|
|
568
|
+
"aria-label": isPiP ? "Exit picture-in-picture" : "Enter picture-in-picture",
|
|
569
|
+
className: cn(
|
|
570
|
+
"flex items-center gap-2 w-full px-2 py-1.5 rounded text-sm hover:bg-accent text-left",
|
|
571
|
+
isPiP && "text-primary"
|
|
572
|
+
),
|
|
573
|
+
children: [
|
|
574
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.PictureInPicture2, { className: "h-4 w-4" }),
|
|
575
|
+
"Picture-in-Picture"
|
|
576
|
+
]
|
|
577
|
+
}
|
|
578
|
+
),
|
|
579
|
+
/* @__PURE__ */ jsxRuntime.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: [
|
|
580
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Camera, { className: "h-4 w-4" }),
|
|
581
|
+
"Screenshot"
|
|
582
|
+
] }),
|
|
583
|
+
onShowInfo && /* @__PURE__ */ jsxRuntime.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: [
|
|
584
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Info, { className: "h-4 w-4" }),
|
|
585
|
+
"Video information"
|
|
586
|
+
] }),
|
|
587
|
+
onOpenShortcuts && /* @__PURE__ */ jsxRuntime.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: [
|
|
588
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Keyboard, { className: "h-4 w-4" }),
|
|
589
|
+
"Keyboard shortcuts"
|
|
590
|
+
] })
|
|
591
|
+
] }),
|
|
592
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-border/40 pt-2 space-y-2 px-1", children: [
|
|
593
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { className: "text-[10px] uppercase tracking-wider text-muted-foreground", children: "Display" }),
|
|
594
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
595
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Label, { className: "text-xs text-muted-foreground", children: [
|
|
596
|
+
"Brightness: ",
|
|
597
|
+
filters.brightness,
|
|
598
|
+
"%"
|
|
599
|
+
] }),
|
|
600
|
+
/* @__PURE__ */ jsxRuntime.jsx(Slider, { value: [filters.brightness], max: 200, onValueChange: ([val]) => onFiltersChange({ ...filters, brightness: val }), trackClassName: "h-[3px]", thumbClassName: "h-3 w-3 border" })
|
|
601
|
+
] }),
|
|
602
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
603
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Label, { className: "text-xs text-muted-foreground", children: [
|
|
604
|
+
"Contrast: ",
|
|
605
|
+
filters.contrast,
|
|
606
|
+
"%"
|
|
607
|
+
] }),
|
|
608
|
+
/* @__PURE__ */ jsxRuntime.jsx(Slider, { value: [filters.contrast], max: 200, onValueChange: ([val]) => onFiltersChange({ ...filters, contrast: val }), trackClassName: "h-[3px]", thumbClassName: "h-3 w-3 border" })
|
|
609
|
+
] }),
|
|
610
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
611
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Label, { className: "text-xs text-muted-foreground", children: [
|
|
612
|
+
"Saturation: ",
|
|
613
|
+
filters.saturate,
|
|
614
|
+
"%"
|
|
615
|
+
] }),
|
|
616
|
+
/* @__PURE__ */ jsxRuntime.jsx(Slider, { value: [filters.saturate], max: 200, onValueChange: ([val]) => onFiltersChange({ ...filters, saturate: val }), trackClassName: "h-[3px]", thumbClassName: "h-3 w-3 border" })
|
|
617
|
+
] }),
|
|
618
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
619
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Label, { className: "text-xs text-muted-foreground", children: [
|
|
620
|
+
"Hue: ",
|
|
621
|
+
filters.hue,
|
|
622
|
+
"\xB0"
|
|
623
|
+
] }),
|
|
624
|
+
/* @__PURE__ */ jsxRuntime.jsx(Slider, { value: [filters.hue], max: 360, onValueChange: ([val]) => onFiltersChange({ ...filters, hue: val }), trackClassName: "h-[3px]", thumbClassName: "h-3 w-3 border" })
|
|
625
|
+
] }),
|
|
626
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
627
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Label, { className: "text-xs text-muted-foreground", children: [
|
|
628
|
+
"Zoom: ",
|
|
629
|
+
Math.round(zoom * 100),
|
|
630
|
+
"%"
|
|
631
|
+
] }),
|
|
632
|
+
/* @__PURE__ */ jsxRuntime.jsx(Slider, { value: [zoom], min: 1, max: 3, step: 0.1, onValueChange: ([val]) => onZoomChange(val), trackClassName: "h-[3px]", thumbClassName: "h-3 w-3 border" })
|
|
633
|
+
] })
|
|
634
|
+
] })
|
|
635
|
+
] })
|
|
515
636
|
] }),
|
|
516
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
517
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
518
|
-
"
|
|
519
|
-
|
|
520
|
-
"%"
|
|
637
|
+
chapters.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: chaptersMenuOpen, onOpenChange: setChaptersMenuOpen, children: [
|
|
638
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
639
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "icon", "aria-label": "Chapters", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.List, { className: "h-4 w-4" }) }) }) }),
|
|
640
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Chapters" }) })
|
|
521
641
|
] }),
|
|
522
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
642
|
+
/* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { className: "w-72 p-0", align: "end", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col max-h-64 overflow-y-auto", children: chapters.map((chapter) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
643
|
+
"button",
|
|
644
|
+
{
|
|
645
|
+
className: cn(
|
|
646
|
+
"flex items-center justify-between px-4 py-2 text-sm hover:bg-accent transition-colors text-left",
|
|
647
|
+
currentChapter?.index === chapter.index && "bg-accent font-medium"
|
|
648
|
+
),
|
|
649
|
+
onClick: () => {
|
|
650
|
+
onGoToChapter?.(chapter.index);
|
|
651
|
+
setChaptersMenuOpen(false);
|
|
652
|
+
},
|
|
653
|
+
children: [
|
|
654
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 truncate", children: chapter.title }),
|
|
655
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-4 font-mono text-xs text-muted-foreground shrink-0", children: formatTime2(chapter.startTime) })
|
|
656
|
+
]
|
|
657
|
+
},
|
|
658
|
+
chapter.index
|
|
659
|
+
)) }) })
|
|
660
|
+
] }),
|
|
661
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
662
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "icon", onClick: onFullScreenToggle, children: isFullScreen ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Minimize, {}) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Maximize, {}) }) }),
|
|
663
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Fullscreen (F)" }) })
|
|
523
664
|
] })
|
|
524
665
|
] })
|
|
525
|
-
] }),
|
|
526
|
-
onShowInfo && /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
527
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "icon", onClick: onShowInfo, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Info, { className: "h-4 w-4" }) }) }),
|
|
528
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Video Information" }) })
|
|
529
|
-
] }),
|
|
530
|
-
onOpenShortcuts && /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
531
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "icon", onClick: onOpenShortcuts, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Keyboard, { className: "h-4 w-4" }) }) }),
|
|
532
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Keyboard Shortcuts" }) })
|
|
533
|
-
] }),
|
|
534
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
535
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "icon", onClick: onScreenshot, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Camera, {}) }) }),
|
|
536
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Screenshot" }) })
|
|
537
|
-
] }),
|
|
538
|
-
onABLoopCycle && /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
539
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
540
|
-
Button,
|
|
541
|
-
{
|
|
542
|
-
variant: "ghost",
|
|
543
|
-
size: "icon",
|
|
544
|
-
onClick: onABLoopCycle,
|
|
545
|
-
"aria-label": "A-B loop",
|
|
546
|
-
"data-testid": "ab-loop-button",
|
|
547
|
-
"data-active": abLoop.isLooping,
|
|
548
|
-
className: cn("font-mono text-xs font-bold", abLoop.isLooping && "text-primary"),
|
|
549
|
-
children: [
|
|
550
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(abLoop.pointA !== null && "text-primary"), children: "A" }),
|
|
551
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-50", children: "-" }),
|
|
552
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(abLoop.pointB !== null && "text-primary"), children: "B" })
|
|
553
|
-
]
|
|
554
|
-
}
|
|
555
|
-
) }),
|
|
556
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: abLoop.pointA === null ? "Set loop start (A)" : abLoop.pointB === null ? "Set loop end (B)" : "Clear A-B loop" }) })
|
|
557
|
-
] }),
|
|
558
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
559
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "icon", onClick: onLoopToggle, "data-active": loop, className: "data-[active=true]:text-primary", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCcw, {}) }) }),
|
|
560
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Loop" }) })
|
|
561
|
-
] }),
|
|
562
|
-
pipSupported && /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
563
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
564
|
-
Button,
|
|
565
|
-
{
|
|
566
|
-
variant: "ghost",
|
|
567
|
-
size: "icon",
|
|
568
|
-
onClick: onTogglePiP,
|
|
569
|
-
"aria-label": isPiP ? "Exit picture-in-picture" : "Enter picture-in-picture",
|
|
570
|
-
className: isPiP ? "text-primary" : "",
|
|
571
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PictureInPicture2, {})
|
|
572
|
-
}
|
|
573
|
-
) }),
|
|
574
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: isPiP ? "Exit picture-in-picture" : "Enter picture-in-picture" }) })
|
|
575
|
-
] }),
|
|
576
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
577
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "icon", onClick: onFullScreenToggle, children: isFullScreen ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Minimize, {}) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Maximize, {}) }) }),
|
|
578
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Fullscreen (F)" }) })
|
|
579
666
|
] })
|
|
580
|
-
]
|
|
581
|
-
|
|
582
|
-
|
|
667
|
+
]
|
|
668
|
+
}
|
|
669
|
+
) });
|
|
583
670
|
});
|
|
584
671
|
var player_controls_default = PlayerControls;
|
|
585
|
-
var ScrollArea =
|
|
672
|
+
var ScrollArea = React11__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
586
673
|
ScrollAreaPrimitive__namespace.Root,
|
|
587
674
|
{
|
|
588
675
|
ref,
|
|
@@ -596,23 +683,23 @@ var ScrollArea = React10__namespace.forwardRef(({ className, children, ...props
|
|
|
596
683
|
}
|
|
597
684
|
));
|
|
598
685
|
ScrollArea.displayName = ScrollAreaPrimitive__namespace.Root.displayName;
|
|
599
|
-
var ScrollBar =
|
|
686
|
+
var ScrollBar = React11__namespace.forwardRef(({ className, orientation = "vertical", ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
600
687
|
ScrollAreaPrimitive__namespace.ScrollAreaScrollbar,
|
|
601
688
|
{
|
|
602
689
|
ref,
|
|
603
690
|
orientation,
|
|
604
691
|
className: cn(
|
|
605
692
|
"flex touch-none select-none transition-colors",
|
|
606
|
-
orientation === "vertical" && "h-full w-2
|
|
607
|
-
orientation === "horizontal" && "h-2
|
|
693
|
+
orientation === "vertical" && "h-full w-2 border-l border-l-transparent p-[1px]",
|
|
694
|
+
orientation === "horizontal" && "h-2 flex-col border-t border-t-transparent p-[1px]",
|
|
608
695
|
className
|
|
609
696
|
),
|
|
610
697
|
...props,
|
|
611
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(ScrollAreaPrimitive__namespace.ScrollAreaThumb, { className: "relative flex-1 rounded-full bg-
|
|
698
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(ScrollAreaPrimitive__namespace.ScrollAreaThumb, { className: "relative flex-1 rounded-full bg-muted-foreground/30 hover:bg-muted-foreground/55 active:bg-muted-foreground/75 transition-colors" })
|
|
612
699
|
}
|
|
613
700
|
));
|
|
614
701
|
ScrollBar.displayName = ScrollAreaPrimitive__namespace.ScrollAreaScrollbar.displayName;
|
|
615
|
-
var Input =
|
|
702
|
+
var Input = React11__namespace.forwardRef(
|
|
616
703
|
({ className, type, ...props }, ref) => {
|
|
617
704
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
618
705
|
"input",
|
|
@@ -631,7 +718,7 @@ var Input = React10__namespace.forwardRef(
|
|
|
631
718
|
Input.displayName = "Input";
|
|
632
719
|
var Select = SelectPrimitive__namespace.Root;
|
|
633
720
|
var SelectValue = SelectPrimitive__namespace.Value;
|
|
634
|
-
var SelectTrigger =
|
|
721
|
+
var SelectTrigger = React11__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
635
722
|
SelectPrimitive__namespace.Trigger,
|
|
636
723
|
{
|
|
637
724
|
ref,
|
|
@@ -647,7 +734,7 @@ var SelectTrigger = React10__namespace.forwardRef(({ className, children, ...pro
|
|
|
647
734
|
}
|
|
648
735
|
));
|
|
649
736
|
SelectTrigger.displayName = SelectPrimitive__namespace.Trigger.displayName;
|
|
650
|
-
var SelectScrollUpButton =
|
|
737
|
+
var SelectScrollUpButton = React11__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
651
738
|
SelectPrimitive__namespace.ScrollUpButton,
|
|
652
739
|
{
|
|
653
740
|
ref,
|
|
@@ -660,7 +747,7 @@ var SelectScrollUpButton = React10__namespace.forwardRef(({ className, ...props
|
|
|
660
747
|
}
|
|
661
748
|
));
|
|
662
749
|
SelectScrollUpButton.displayName = SelectPrimitive__namespace.ScrollUpButton.displayName;
|
|
663
|
-
var SelectScrollDownButton =
|
|
750
|
+
var SelectScrollDownButton = React11__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
664
751
|
SelectPrimitive__namespace.ScrollDownButton,
|
|
665
752
|
{
|
|
666
753
|
ref,
|
|
@@ -673,7 +760,7 @@ var SelectScrollDownButton = React10__namespace.forwardRef(({ className, ...prop
|
|
|
673
760
|
}
|
|
674
761
|
));
|
|
675
762
|
SelectScrollDownButton.displayName = SelectPrimitive__namespace.ScrollDownButton.displayName;
|
|
676
|
-
var SelectContent =
|
|
763
|
+
var SelectContent = React11__namespace.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
677
764
|
SelectPrimitive__namespace.Content,
|
|
678
765
|
{
|
|
679
766
|
ref,
|
|
@@ -701,7 +788,7 @@ var SelectContent = React10__namespace.forwardRef(({ className, children, positi
|
|
|
701
788
|
}
|
|
702
789
|
) }));
|
|
703
790
|
SelectContent.displayName = SelectPrimitive__namespace.Content.displayName;
|
|
704
|
-
var SelectLabel =
|
|
791
|
+
var SelectLabel = React11__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
705
792
|
SelectPrimitive__namespace.Label,
|
|
706
793
|
{
|
|
707
794
|
ref,
|
|
@@ -710,7 +797,7 @@ var SelectLabel = React10__namespace.forwardRef(({ className, ...props }, ref) =
|
|
|
710
797
|
}
|
|
711
798
|
));
|
|
712
799
|
SelectLabel.displayName = SelectPrimitive__namespace.Label.displayName;
|
|
713
|
-
var SelectItem =
|
|
800
|
+
var SelectItem = React11__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
714
801
|
SelectPrimitive__namespace.Item,
|
|
715
802
|
{
|
|
716
803
|
ref,
|
|
@@ -726,7 +813,7 @@ var SelectItem = React10__namespace.forwardRef(({ className, children, ...props
|
|
|
726
813
|
}
|
|
727
814
|
));
|
|
728
815
|
SelectItem.displayName = SelectPrimitive__namespace.Item.displayName;
|
|
729
|
-
var SelectSeparator =
|
|
816
|
+
var SelectSeparator = React11__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
730
817
|
SelectPrimitive__namespace.Separator,
|
|
731
818
|
{
|
|
732
819
|
ref,
|
|
@@ -745,7 +832,7 @@ var NEXT_SIZE = {
|
|
|
745
832
|
md: "lg",
|
|
746
833
|
lg: "sm"
|
|
747
834
|
};
|
|
748
|
-
function
|
|
835
|
+
function formatTime3(seconds) {
|
|
749
836
|
if (!seconds || !isFinite(seconds)) return "";
|
|
750
837
|
const h = Math.floor(seconds / 3600);
|
|
751
838
|
const m = Math.floor(seconds % 3600 / 60);
|
|
@@ -774,7 +861,7 @@ function SortablePlaylistItem({ item, index, isActive, downloadReady, onSelect,
|
|
|
774
861
|
transition,
|
|
775
862
|
opacity: isDragging ? 0.5 : 1
|
|
776
863
|
};
|
|
777
|
-
const duration =
|
|
864
|
+
const duration = formatTime3(item.duration ?? 0);
|
|
778
865
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
779
866
|
"div",
|
|
780
867
|
{
|
|
@@ -858,14 +945,14 @@ var PlaylistPanel = ({
|
|
|
858
945
|
onTogglePin,
|
|
859
946
|
onSizeChange
|
|
860
947
|
}) => {
|
|
861
|
-
const fileInputRef =
|
|
862
|
-
const folderInputRef =
|
|
863
|
-
const m3uInputRef =
|
|
864
|
-
const [streamUrl, setStreamUrl] =
|
|
865
|
-
const [magnetUri, setMagnetUri] =
|
|
866
|
-
const [showMagnetInput, setShowMagnetInput] =
|
|
867
|
-
const [magnetError, setMagnetError] =
|
|
868
|
-
const [sortKey, setSortKey] =
|
|
948
|
+
const fileInputRef = React11.useRef(null);
|
|
949
|
+
const folderInputRef = React11.useRef(null);
|
|
950
|
+
const m3uInputRef = React11.useRef(null);
|
|
951
|
+
const [streamUrl, setStreamUrl] = React11.useState("");
|
|
952
|
+
const [magnetUri, setMagnetUri] = React11.useState("");
|
|
953
|
+
const [showMagnetInput, setShowMagnetInput] = React11.useState(false);
|
|
954
|
+
const [magnetError, setMagnetError] = React11.useState(null);
|
|
955
|
+
const [sortKey, setSortKey] = React11.useState("");
|
|
869
956
|
const handleStreamUrlSubmit = (e) => {
|
|
870
957
|
e.preventDefault();
|
|
871
958
|
if (streamUrl) {
|
|
@@ -928,296 +1015,306 @@ var PlaylistPanel = ({
|
|
|
928
1015
|
});
|
|
929
1016
|
onReorder(sorted);
|
|
930
1017
|
};
|
|
931
|
-
return /* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { children:
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
"aria-label": "Expand Playlist",
|
|
943
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "h-4 w-4" })
|
|
944
|
-
}
|
|
945
|
-
) }),
|
|
946
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "left", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Expand Playlist" }) })
|
|
947
|
-
] }),
|
|
948
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex items-center justify-center overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
949
|
-
"span",
|
|
950
|
-
{
|
|
951
|
-
className: "text-[10px] font-semibold text-muted-foreground tracking-widest uppercase select-none",
|
|
952
|
-
style: { writingMode: "vertical-rl", transform: "rotate(180deg)" },
|
|
953
|
-
children: "Playlist"
|
|
954
|
-
}
|
|
955
|
-
) }),
|
|
956
|
-
playlist.length > 0 && /* @__PURE__ */ jsxRuntime.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 })
|
|
957
|
-
] })
|
|
958
|
-
) : (
|
|
959
|
-
/* ── Full panel ── */
|
|
960
|
-
/* @__PURE__ */ jsxRuntime.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: [
|
|
961
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-3 py-2 border-b border-border shrink-0", children: [
|
|
962
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
|
|
963
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ListVideo, { className: "h-4 w-4 text-primary shrink-0" }),
|
|
964
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-sm truncate", children: "Playlist" }),
|
|
965
|
-
playlist.length > 0 && /* @__PURE__ */ jsxRuntime.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 })
|
|
966
|
-
] }),
|
|
967
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5 shrink-0", children: [
|
|
968
|
-
playlist.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
969
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
970
|
-
Button,
|
|
971
|
-
{
|
|
972
|
-
variant: "ghost",
|
|
973
|
-
size: "icon",
|
|
974
|
-
className: "h-7 w-7",
|
|
975
|
-
onClick: () => core.exportPlaylist(playlist),
|
|
976
|
-
"aria-label": "Export Playlist",
|
|
977
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { className: "h-3.5 w-3.5" })
|
|
978
|
-
}
|
|
979
|
-
) }),
|
|
980
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Export as M3U8" }) })
|
|
981
|
-
] }),
|
|
982
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
983
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
984
|
-
Button,
|
|
985
|
-
{
|
|
986
|
-
variant: "ghost",
|
|
987
|
-
size: "icon",
|
|
988
|
-
className: "h-7 w-7",
|
|
989
|
-
onClick: () => m3uInputRef.current?.click(),
|
|
990
|
-
"aria-label": "Import Playlist",
|
|
991
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Upload, { className: "h-3.5 w-3.5" })
|
|
992
|
-
}
|
|
993
|
-
) }),
|
|
994
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Import M3U/M3U8" }) })
|
|
995
|
-
] }),
|
|
996
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
997
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
998
|
-
Button,
|
|
999
|
-
{
|
|
1000
|
-
variant: "ghost",
|
|
1001
|
-
size: "icon",
|
|
1002
|
-
className: cn("h-7 w-7", isPinned && "text-primary bg-primary/10"),
|
|
1003
|
-
onClick: onTogglePin,
|
|
1004
|
-
"aria-label": isPinned ? "Unpin Playlist" : "Pin Playlist",
|
|
1005
|
-
children: isPinned ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pin, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PinOff, { className: "h-3.5 w-3.5" })
|
|
1006
|
-
}
|
|
1007
|
-
) }),
|
|
1008
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: isPinned ? "Unpin (allow auto-hide on play)" : "Pin (keep open while playing)" }) })
|
|
1009
|
-
] }),
|
|
1010
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
1011
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1012
|
-
Button,
|
|
1013
|
-
{
|
|
1014
|
-
variant: "ghost",
|
|
1015
|
-
size: "icon",
|
|
1016
|
-
className: "h-7 w-7",
|
|
1017
|
-
onClick: () => onSizeChange(NEXT_SIZE[size]),
|
|
1018
|
-
"aria-label": "Resize Playlist",
|
|
1019
|
-
children: size === "lg" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Minimize2, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Maximize2, { className: "h-3.5 w-3.5" })
|
|
1020
|
-
}
|
|
1021
|
-
) }),
|
|
1022
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: size === "lg" ? "Make smaller" : "Make larger" }) })
|
|
1023
|
-
] }),
|
|
1018
|
+
return /* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1019
|
+
"div",
|
|
1020
|
+
{
|
|
1021
|
+
className: cn(
|
|
1022
|
+
"h-full flex flex-col bg-card border-l border-border shrink-0 overflow-hidden",
|
|
1023
|
+
"transition-[width] duration-300 ease-in-out motion-reduce:transition-none",
|
|
1024
|
+
isOpen ? SIZE_WIDTHS[size] : "w-11"
|
|
1025
|
+
),
|
|
1026
|
+
children: !isOpen ? (
|
|
1027
|
+
/* ── Collapsed drawer strip ── */
|
|
1028
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center w-11 h-full shrink-0", children: [
|
|
1024
1029
|
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
1025
1030
|
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1026
1031
|
Button,
|
|
1027
1032
|
{
|
|
1028
1033
|
variant: "ghost",
|
|
1029
1034
|
size: "icon",
|
|
1030
|
-
className: "h-
|
|
1035
|
+
className: "h-9 w-9 mt-2 shrink-0",
|
|
1031
1036
|
onClick: onToggle,
|
|
1032
|
-
"aria-label": "
|
|
1033
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.
|
|
1037
|
+
"aria-label": "Expand Playlist",
|
|
1038
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "h-4 w-4" })
|
|
1034
1039
|
}
|
|
1035
1040
|
) }),
|
|
1036
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "
|
|
1037
|
-
] })
|
|
1038
|
-
] })
|
|
1039
|
-
] }),
|
|
1040
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-3 space-y-2 border-b border-border shrink-0", children: [
|
|
1041
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1.5", children: [
|
|
1042
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Button, { onClick: () => fileInputRef.current?.click(), className: "flex-1 h-8 text-xs", children: [
|
|
1043
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.FilePlus, { className: "mr-1.5 h-3.5 w-3.5" }),
|
|
1044
|
-
" Add Files"
|
|
1041
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "left", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Expand Playlist" }) })
|
|
1045
1042
|
] }),
|
|
1046
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1047
|
-
|
|
1048
|
-
Button,
|
|
1049
|
-
{
|
|
1050
|
-
variant: "outline",
|
|
1051
|
-
size: "icon",
|
|
1052
|
-
className: "h-8 w-8 shrink-0",
|
|
1053
|
-
onClick: () => folderInputRef.current?.click(),
|
|
1054
|
-
"aria-label": "Open Folder",
|
|
1055
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FolderOpen, { className: "h-3.5 w-3.5" })
|
|
1056
|
-
}
|
|
1057
|
-
) }),
|
|
1058
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Open Folder" }) })
|
|
1059
|
-
] })
|
|
1060
|
-
] }),
|
|
1061
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1062
|
-
"input",
|
|
1063
|
-
{
|
|
1064
|
-
type: "file",
|
|
1065
|
-
ref: fileInputRef,
|
|
1066
|
-
className: "hidden",
|
|
1067
|
-
multiple: true,
|
|
1068
|
-
accept: "video/*,.mkv,.avi,.mov,.wmv,.flv,.webm,.vtt,.srt",
|
|
1069
|
-
onChange: (e) => e.target.files && onFilesAdded(e.target.files)
|
|
1070
|
-
}
|
|
1071
|
-
),
|
|
1072
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1073
|
-
"input",
|
|
1074
|
-
{
|
|
1075
|
-
type: "file",
|
|
1076
|
-
ref: folderInputRef,
|
|
1077
|
-
className: "hidden",
|
|
1078
|
-
multiple: true,
|
|
1079
|
-
accept: "video/*",
|
|
1080
|
-
webkitdirectory: "",
|
|
1081
|
-
onChange: handleFolderSelect
|
|
1082
|
-
}
|
|
1083
|
-
),
|
|
1084
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1085
|
-
"input",
|
|
1086
|
-
{
|
|
1087
|
-
type: "file",
|
|
1088
|
-
ref: m3uInputRef,
|
|
1089
|
-
className: "hidden",
|
|
1090
|
-
accept: ".m3u,.m3u8",
|
|
1091
|
-
onChange: handleM3USelect
|
|
1092
|
-
}
|
|
1093
|
-
),
|
|
1094
|
-
/* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleStreamUrlSubmit, className: "flex gap-1.5", children: [
|
|
1095
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1096
|
-
Input,
|
|
1043
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex items-center justify-center overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1044
|
+
"span",
|
|
1097
1045
|
{
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
onChange: (e) => setStreamUrl(e.target.value),
|
|
1102
|
-
className: "h-8 text-xs"
|
|
1046
|
+
className: "text-[10px] font-semibold text-muted-foreground tracking-widest uppercase select-none",
|
|
1047
|
+
style: { writingMode: "vertical-rl", transform: "rotate(180deg)" },
|
|
1048
|
+
children: "Playlist"
|
|
1103
1049
|
}
|
|
1104
|
-
),
|
|
1105
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1050
|
+
) }),
|
|
1051
|
+
playlist.length > 0 && /* @__PURE__ */ jsxRuntime.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 })
|
|
1052
|
+
] })
|
|
1053
|
+
) : (
|
|
1054
|
+
/* ── Full panel ── */
|
|
1055
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("h-full flex flex-col shrink-0 transition-[width] duration-200", SIZE_WIDTHS[size]), children: [
|
|
1056
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-3 py-2 border-b border-border shrink-0", children: [
|
|
1057
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
|
|
1058
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ListVideo, { className: "h-4 w-4 text-primary shrink-0" }),
|
|
1059
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-sm truncate", children: "Playlist" }),
|
|
1060
|
+
playlist.length > 0 && /* @__PURE__ */ jsxRuntime.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 })
|
|
1061
|
+
] }),
|
|
1062
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5 shrink-0", children: [
|
|
1063
|
+
playlist.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
1064
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1065
|
+
Button,
|
|
1066
|
+
{
|
|
1067
|
+
variant: "ghost",
|
|
1068
|
+
size: "icon",
|
|
1069
|
+
className: "h-7 w-7",
|
|
1070
|
+
onClick: () => core.exportPlaylist(playlist),
|
|
1071
|
+
"aria-label": "Export Playlist",
|
|
1072
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { className: "h-3.5 w-3.5" })
|
|
1073
|
+
}
|
|
1074
|
+
) }),
|
|
1075
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Export as M3U8" }) })
|
|
1076
|
+
] }),
|
|
1077
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
1078
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1079
|
+
Button,
|
|
1080
|
+
{
|
|
1081
|
+
variant: "ghost",
|
|
1082
|
+
size: "icon",
|
|
1083
|
+
className: "h-7 w-7",
|
|
1084
|
+
onClick: () => m3uInputRef.current?.click(),
|
|
1085
|
+
"aria-label": "Import Playlist",
|
|
1086
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Upload, { className: "h-3.5 w-3.5" })
|
|
1087
|
+
}
|
|
1088
|
+
) }),
|
|
1089
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Import M3U/M3U8" }) })
|
|
1090
|
+
] }),
|
|
1091
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
1092
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1093
|
+
Button,
|
|
1094
|
+
{
|
|
1095
|
+
variant: "ghost",
|
|
1096
|
+
size: "icon",
|
|
1097
|
+
className: cn("h-7 w-7", isPinned && "text-primary bg-primary/10"),
|
|
1098
|
+
onClick: onTogglePin,
|
|
1099
|
+
"aria-label": isPinned ? "Unpin Playlist" : "Pin Playlist",
|
|
1100
|
+
children: isPinned ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pin, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PinOff, { className: "h-3.5 w-3.5" })
|
|
1101
|
+
}
|
|
1102
|
+
) }),
|
|
1103
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: isPinned ? "Unpin (allow auto-hide on play)" : "Pin (keep open while playing)" }) })
|
|
1104
|
+
] }),
|
|
1105
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
1106
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1107
|
+
Button,
|
|
1108
|
+
{
|
|
1109
|
+
variant: "ghost",
|
|
1110
|
+
size: "icon",
|
|
1111
|
+
className: "h-7 w-7",
|
|
1112
|
+
onClick: () => onSizeChange(NEXT_SIZE[size]),
|
|
1113
|
+
"aria-label": "Resize Playlist",
|
|
1114
|
+
children: size === "lg" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Minimize2, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Maximize2, { className: "h-3.5 w-3.5" })
|
|
1115
|
+
}
|
|
1116
|
+
) }),
|
|
1117
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: size === "lg" ? "Make smaller" : "Make larger" }) })
|
|
1118
|
+
] }),
|
|
1119
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
1120
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1121
|
+
Button,
|
|
1122
|
+
{
|
|
1123
|
+
variant: "ghost",
|
|
1124
|
+
size: "icon",
|
|
1125
|
+
className: "h-7 w-7",
|
|
1126
|
+
onClick: onToggle,
|
|
1127
|
+
"aria-label": "Collapse Playlist",
|
|
1128
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-4 w-4" })
|
|
1129
|
+
}
|
|
1130
|
+
) }),
|
|
1131
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Collapse" }) })
|
|
1132
|
+
] })
|
|
1133
|
+
] })
|
|
1134
|
+
] }),
|
|
1135
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-3 space-y-2 border-b border-border shrink-0", children: [
|
|
1136
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1.5", children: [
|
|
1137
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Button, { onClick: () => fileInputRef.current?.click(), className: "flex-1 h-8 text-xs", children: [
|
|
1138
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.FilePlus, { className: "mr-1.5 h-3.5 w-3.5" }),
|
|
1139
|
+
" Add Files"
|
|
1140
|
+
] }),
|
|
1141
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
1142
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1143
|
+
Button,
|
|
1144
|
+
{
|
|
1145
|
+
variant: "outline",
|
|
1146
|
+
size: "icon",
|
|
1147
|
+
className: "h-8 w-8 shrink-0",
|
|
1148
|
+
onClick: () => folderInputRef.current?.click(),
|
|
1149
|
+
"aria-label": "Open Folder",
|
|
1150
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FolderOpen, { className: "h-3.5 w-3.5" })
|
|
1151
|
+
}
|
|
1152
|
+
) }),
|
|
1153
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Open Folder" }) })
|
|
1154
|
+
] })
|
|
1155
|
+
] }),
|
|
1156
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1157
|
+
"input",
|
|
1109
1158
|
{
|
|
1110
|
-
type: "
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
setMagnetError(null);
|
|
1117
|
-
},
|
|
1118
|
-
"aria-label": "Add magnet link",
|
|
1119
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Link2, { className: "h-3.5 w-3.5" })
|
|
1159
|
+
type: "file",
|
|
1160
|
+
ref: fileInputRef,
|
|
1161
|
+
className: "hidden",
|
|
1162
|
+
multiple: true,
|
|
1163
|
+
accept: "video/*,.mkv,.avi,.mov,.wmv,.flv,.webm,.vtt,.srt",
|
|
1164
|
+
onChange: (e) => e.target.files && onFilesAdded(e.target.files)
|
|
1120
1165
|
}
|
|
1121
|
-
)
|
|
1122
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Add Magnet Link" }) })
|
|
1123
|
-
] })
|
|
1124
|
-
] }),
|
|
1125
|
-
showMagnet && showMagnetInput && /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleMagnetSubmit, className: "space-y-1.5", children: [
|
|
1126
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1.5", children: [
|
|
1166
|
+
),
|
|
1127
1167
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1128
|
-
|
|
1168
|
+
"input",
|
|
1129
1169
|
{
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
disabled: torrentStatus.status === "loading-metadata"
|
|
1170
|
+
type: "file",
|
|
1171
|
+
ref: folderInputRef,
|
|
1172
|
+
className: "hidden",
|
|
1173
|
+
multiple: true,
|
|
1174
|
+
accept: "video/*",
|
|
1175
|
+
webkitdirectory: "",
|
|
1176
|
+
onChange: handleFolderSelect
|
|
1138
1177
|
}
|
|
1139
1178
|
),
|
|
1140
1179
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1141
|
-
|
|
1180
|
+
"input",
|
|
1142
1181
|
{
|
|
1143
|
-
type: "
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
"aria-label": "Load magnet link",
|
|
1149
|
-
children: torrentStatus.status === "loading-metadata" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Globe, { className: "h-3.5 w-3.5" })
|
|
1182
|
+
type: "file",
|
|
1183
|
+
ref: m3uInputRef,
|
|
1184
|
+
className: "hidden",
|
|
1185
|
+
accept: ".m3u,.m3u8",
|
|
1186
|
+
onChange: handleM3USelect
|
|
1150
1187
|
}
|
|
1151
|
-
)
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1188
|
+
),
|
|
1189
|
+
/* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleStreamUrlSubmit, className: "flex gap-1.5", children: [
|
|
1190
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1191
|
+
Input,
|
|
1192
|
+
{
|
|
1193
|
+
type: "url",
|
|
1194
|
+
placeholder: "Enter stream URL",
|
|
1195
|
+
value: streamUrl,
|
|
1196
|
+
onChange: (e) => setStreamUrl(e.target.value),
|
|
1197
|
+
className: "h-8 text-xs"
|
|
1198
|
+
}
|
|
1199
|
+
),
|
|
1200
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { type: "submit", size: "icon", variant: "secondary", className: "h-8 w-8 shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Link, { className: "h-3.5 w-3.5" }) }),
|
|
1201
|
+
showMagnet && /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
1202
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1203
|
+
Button,
|
|
1204
|
+
{
|
|
1205
|
+
type: "button",
|
|
1206
|
+
size: "icon",
|
|
1207
|
+
variant: showMagnetInput ? "default" : "outline",
|
|
1208
|
+
className: "h-8 w-8 shrink-0",
|
|
1209
|
+
onClick: () => {
|
|
1210
|
+
setShowMagnetInput((v) => !v);
|
|
1211
|
+
setMagnetError(null);
|
|
1212
|
+
},
|
|
1213
|
+
"aria-label": "Add magnet link",
|
|
1214
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Link2, { className: "h-3.5 w-3.5" })
|
|
1215
|
+
}
|
|
1216
|
+
) }),
|
|
1217
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Add Magnet Link" }) })
|
|
1218
|
+
] })
|
|
1219
|
+
] }),
|
|
1220
|
+
showMagnet && showMagnetInput && /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleMagnetSubmit, className: "space-y-1.5", children: [
|
|
1221
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1.5", children: [
|
|
1222
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1223
|
+
Input,
|
|
1224
|
+
{
|
|
1225
|
+
placeholder: "magnet:?xt=urn:btih:\u2026",
|
|
1226
|
+
value: magnetUri,
|
|
1227
|
+
onChange: (e) => {
|
|
1228
|
+
setMagnetUri(e.target.value);
|
|
1229
|
+
setMagnetError(null);
|
|
1230
|
+
},
|
|
1231
|
+
className: "h-8 text-xs font-mono",
|
|
1232
|
+
disabled: torrentStatus.status === "loading-metadata"
|
|
1233
|
+
}
|
|
1234
|
+
),
|
|
1235
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1236
|
+
Button,
|
|
1237
|
+
{
|
|
1238
|
+
type: "submit",
|
|
1239
|
+
size: "icon",
|
|
1240
|
+
variant: "default",
|
|
1241
|
+
className: "h-8 w-8 shrink-0",
|
|
1242
|
+
disabled: !magnetUri.trim() || torrentStatus.status === "loading-metadata",
|
|
1243
|
+
"aria-label": "Load magnet link",
|
|
1244
|
+
children: torrentStatus.status === "loading-metadata" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Globe, { className: "h-3.5 w-3.5" })
|
|
1245
|
+
}
|
|
1246
|
+
)
|
|
1247
|
+
] }),
|
|
1248
|
+
torrentStatus.status === "loading-metadata" && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-[11px] text-muted-foreground flex items-center gap-1", children: [
|
|
1249
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-3 w-3 animate-spin" }),
|
|
1250
|
+
"Fetching torrent info\u2026"
|
|
1251
|
+
] }),
|
|
1252
|
+
magnetError && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-[11px] text-destructive flex items-center gap-1", children: [
|
|
1253
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "h-3 w-3 shrink-0" }),
|
|
1254
|
+
magnetError
|
|
1255
|
+
] })
|
|
1256
|
+
] }),
|
|
1257
|
+
showMagnet && torrentStatus.status === "ready" && (torrentStatus.progress >= 1 ? /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-[10px] text-emerald-500 flex items-center gap-1", children: [
|
|
1258
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { className: "h-3 w-3 shrink-0" }),
|
|
1259
|
+
"Download ready \u2014 hover a file to save it"
|
|
1260
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
1261
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full bg-muted rounded-full h-1 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1262
|
+
"div",
|
|
1263
|
+
{
|
|
1264
|
+
className: "bg-emerald-500 h-1 rounded-full transition-all duration-500",
|
|
1265
|
+
style: { width: `${Math.round(torrentStatus.progress * 100)}%` }
|
|
1266
|
+
}
|
|
1267
|
+
) }),
|
|
1268
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-[10px] text-muted-foreground flex items-center justify-between", children: [
|
|
1269
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
1270
|
+
"\u2193 ",
|
|
1271
|
+
formatBytes(torrentStatus.downloadSpeed),
|
|
1272
|
+
"/s \xB7 ",
|
|
1273
|
+
torrentStatus.numPeers,
|
|
1274
|
+
" peer",
|
|
1275
|
+
torrentStatus.numPeers !== 1 ? "s" : ""
|
|
1276
|
+
] }),
|
|
1277
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
1278
|
+
Math.round(torrentStatus.progress * 100),
|
|
1279
|
+
"%"
|
|
1280
|
+
] })
|
|
1281
|
+
] })
|
|
1282
|
+
] })),
|
|
1283
|
+
playlist.length > 1 && /* @__PURE__ */ jsxRuntime.jsxs(Select, { value: sortKey, onValueChange: handleSort, children: [
|
|
1284
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { className: "h-7 text-xs", "aria-label": "Sort playlist", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: "Sort by\u2026" }) }),
|
|
1285
|
+
/* @__PURE__ */ jsxRuntime.jsxs(SelectContent, { children: [
|
|
1286
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: "name-asc", children: "Name A\u2013Z" }),
|
|
1287
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: "name-desc", children: "Name Z\u2013A" }),
|
|
1288
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: "duration-asc", children: "Shortest first" }),
|
|
1289
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: "duration-desc", children: "Longest first" })
|
|
1290
|
+
] })
|
|
1291
|
+
] })
|
|
1156
1292
|
] }),
|
|
1157
|
-
|
|
1158
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1159
|
-
|
|
1160
|
-
] })
|
|
1161
|
-
|
|
1162
|
-
showMagnet && torrentStatus.status === "ready" && (torrentStatus.progress >= 1 ? /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-[10px] text-emerald-500 flex items-center gap-1", children: [
|
|
1163
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { className: "h-3 w-3 shrink-0" }),
|
|
1164
|
-
"Download ready \u2014 hover a file to save it"
|
|
1165
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
1166
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full bg-muted rounded-full h-1 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1167
|
-
"div",
|
|
1293
|
+
/* @__PURE__ */ jsxRuntime.jsx(ScrollArea, { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-2 space-y-0.5", children: playlist.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center text-xs text-muted-foreground py-10 px-2", children: [
|
|
1294
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { children: "Your playlist is empty." }),
|
|
1295
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { children: "Add files or a stream URL to get started." })
|
|
1296
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx(core$1.DndContext, { collisionDetection: core$1.closestCenter, onDragEnd: handleDragEnd, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1297
|
+
sortable.SortableContext,
|
|
1168
1298
|
{
|
|
1169
|
-
|
|
1170
|
-
|
|
1299
|
+
items: playlist.map((i) => i.id),
|
|
1300
|
+
strategy: sortable.verticalListSortingStrategy,
|
|
1301
|
+
children: playlist.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1302
|
+
SortablePlaylistItem,
|
|
1303
|
+
{
|
|
1304
|
+
item,
|
|
1305
|
+
index,
|
|
1306
|
+
isActive: index === currentVideoIndex,
|
|
1307
|
+
downloadReady: item.source === "torrent" && torrentStatus.progress >= 1,
|
|
1308
|
+
onSelect: onSelectVideo,
|
|
1309
|
+
onRemove: onRemoveItem
|
|
1310
|
+
},
|
|
1311
|
+
item.id
|
|
1312
|
+
))
|
|
1171
1313
|
}
|
|
1172
|
-
) })
|
|
1173
|
-
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-[10px] text-muted-foreground flex items-center justify-between", children: [
|
|
1174
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
1175
|
-
"\u2193 ",
|
|
1176
|
-
formatBytes(torrentStatus.downloadSpeed),
|
|
1177
|
-
"/s \xB7 ",
|
|
1178
|
-
torrentStatus.numPeers,
|
|
1179
|
-
" peer",
|
|
1180
|
-
torrentStatus.numPeers !== 1 ? "s" : ""
|
|
1181
|
-
] }),
|
|
1182
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
1183
|
-
Math.round(torrentStatus.progress * 100),
|
|
1184
|
-
"%"
|
|
1185
|
-
] })
|
|
1186
|
-
] })
|
|
1187
|
-
] })),
|
|
1188
|
-
playlist.length > 1 && /* @__PURE__ */ jsxRuntime.jsxs(Select, { value: sortKey, onValueChange: handleSort, children: [
|
|
1189
|
-
/* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { className: "h-7 text-xs", "aria-label": "Sort playlist", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: "Sort by\u2026" }) }),
|
|
1190
|
-
/* @__PURE__ */ jsxRuntime.jsxs(SelectContent, { children: [
|
|
1191
|
-
/* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: "name-asc", children: "Name A\u2013Z" }),
|
|
1192
|
-
/* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: "name-desc", children: "Name Z\u2013A" }),
|
|
1193
|
-
/* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: "duration-asc", children: "Shortest first" }),
|
|
1194
|
-
/* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: "duration-desc", children: "Longest first" })
|
|
1195
|
-
] })
|
|
1314
|
+
) }) }) })
|
|
1196
1315
|
] })
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { children: "Your playlist is empty." }),
|
|
1200
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { children: "Add files or a stream URL to get started." })
|
|
1201
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsx(core$1.DndContext, { collisionDetection: core$1.closestCenter, onDragEnd: handleDragEnd, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1202
|
-
sortable.SortableContext,
|
|
1203
|
-
{
|
|
1204
|
-
items: playlist.map((i) => i.id),
|
|
1205
|
-
strategy: sortable.verticalListSortingStrategy,
|
|
1206
|
-
children: playlist.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1207
|
-
SortablePlaylistItem,
|
|
1208
|
-
{
|
|
1209
|
-
item,
|
|
1210
|
-
index,
|
|
1211
|
-
isActive: index === currentVideoIndex,
|
|
1212
|
-
downloadReady: item.source === "torrent" && torrentStatus.progress >= 1,
|
|
1213
|
-
onSelect: onSelectVideo,
|
|
1214
|
-
onRemove: onRemoveItem
|
|
1215
|
-
},
|
|
1216
|
-
item.id
|
|
1217
|
-
))
|
|
1218
|
-
}
|
|
1219
|
-
) }) }) })
|
|
1220
|
-
] })
|
|
1316
|
+
)
|
|
1317
|
+
}
|
|
1221
1318
|
) });
|
|
1222
1319
|
};
|
|
1223
1320
|
var playlist_panel_default = PlaylistPanel;
|
|
@@ -1310,7 +1407,7 @@ function PlayerErrorDisplay({ error, onRetry, onSkip, onDismiss }) {
|
|
|
1310
1407
|
] })
|
|
1311
1408
|
] });
|
|
1312
1409
|
}
|
|
1313
|
-
function
|
|
1410
|
+
function formatTime4(seconds) {
|
|
1314
1411
|
if (isNaN(seconds) || seconds === 0) return "\u2014";
|
|
1315
1412
|
const h = Math.floor(seconds / 3600);
|
|
1316
1413
|
const m = Math.floor(seconds % 3600 / 60);
|
|
@@ -1333,7 +1430,7 @@ function VideoInfoPanel({ metadata, onClose }) {
|
|
|
1333
1430
|
const rows = [
|
|
1334
1431
|
["File", metadata.filename ? metadata.filename.split("/").pop() ?? metadata.filename : "\u2014"],
|
|
1335
1432
|
["Size", formatSize(metadata.fileSize)],
|
|
1336
|
-
["Duration",
|
|
1433
|
+
["Duration", formatTime4(metadata.duration)],
|
|
1337
1434
|
["Container", metadata.container || "\u2014"],
|
|
1338
1435
|
["Resolution", metadata.width && metadata.height ? `${metadata.width} \xD7 ${metadata.height}` : "\u2014"],
|
|
1339
1436
|
["Frame Rate", metadata.frameRate ? `${metadata.frameRate} fps` : "\u2014"],
|
|
@@ -1371,7 +1468,7 @@ function VideoInfoPanel({ metadata, onClose }) {
|
|
|
1371
1468
|
}
|
|
1372
1469
|
var Dialog = DialogPrimitive__namespace.Root;
|
|
1373
1470
|
var DialogPortal = DialogPrimitive__namespace.Portal;
|
|
1374
|
-
var DialogOverlay =
|
|
1471
|
+
var DialogOverlay = React11__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1375
1472
|
DialogPrimitive__namespace.Overlay,
|
|
1376
1473
|
{
|
|
1377
1474
|
ref,
|
|
@@ -1383,7 +1480,7 @@ var DialogOverlay = React10__namespace.forwardRef(({ className, ...props }, ref)
|
|
|
1383
1480
|
}
|
|
1384
1481
|
));
|
|
1385
1482
|
DialogOverlay.displayName = DialogPrimitive__namespace.Overlay.displayName;
|
|
1386
|
-
var DialogContent =
|
|
1483
|
+
var DialogContent = React11__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(DialogPortal, { children: [
|
|
1387
1484
|
/* @__PURE__ */ jsxRuntime.jsx(DialogOverlay, {}),
|
|
1388
1485
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1389
1486
|
DialogPrimitive__namespace.Content,
|
|
@@ -1419,7 +1516,7 @@ var DialogHeader = ({
|
|
|
1419
1516
|
}
|
|
1420
1517
|
);
|
|
1421
1518
|
DialogHeader.displayName = "DialogHeader";
|
|
1422
|
-
var DialogTitle =
|
|
1519
|
+
var DialogTitle = React11__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1423
1520
|
DialogPrimitive__namespace.Title,
|
|
1424
1521
|
{
|
|
1425
1522
|
ref,
|
|
@@ -1431,7 +1528,7 @@ var DialogTitle = React10__namespace.forwardRef(({ className, ...props }, ref) =
|
|
|
1431
1528
|
}
|
|
1432
1529
|
));
|
|
1433
1530
|
DialogTitle.displayName = DialogPrimitive__namespace.Title.displayName;
|
|
1434
|
-
var DialogDescription =
|
|
1531
|
+
var DialogDescription = React11__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1435
1532
|
DialogPrimitive__namespace.Description,
|
|
1436
1533
|
{
|
|
1437
1534
|
ref,
|
|
@@ -1540,8 +1637,8 @@ function toast({ ...props }) {
|
|
|
1540
1637
|
};
|
|
1541
1638
|
}
|
|
1542
1639
|
function useToast() {
|
|
1543
|
-
const [state, setState] =
|
|
1544
|
-
|
|
1640
|
+
const [state, setState] = React11__namespace.useState(memoryState);
|
|
1641
|
+
React11__namespace.useEffect(() => {
|
|
1545
1642
|
listeners.push(setState);
|
|
1546
1643
|
return () => {
|
|
1547
1644
|
const index = listeners.indexOf(setState);
|
|
@@ -1561,10 +1658,10 @@ function ShortcutSettingsDialog({
|
|
|
1561
1658
|
onSave,
|
|
1562
1659
|
onClose
|
|
1563
1660
|
}) {
|
|
1564
|
-
const [editing, setEditing] =
|
|
1565
|
-
const [capturing, setCapturing] =
|
|
1661
|
+
const [editing, setEditing] = React11.useState(shortcuts);
|
|
1662
|
+
const [capturing, setCapturing] = React11.useState(null);
|
|
1566
1663
|
const { toast: toast2 } = useToast();
|
|
1567
|
-
|
|
1664
|
+
React11.useEffect(() => {
|
|
1568
1665
|
if (!capturing) return;
|
|
1569
1666
|
const handler = (e) => {
|
|
1570
1667
|
e.preventDefault();
|
|
@@ -1643,8 +1740,8 @@ function ShortcutSettingsDialog({
|
|
|
1643
1740
|
] }) });
|
|
1644
1741
|
}
|
|
1645
1742
|
function SubtitleOverlay({ videoRef, activeSubtitle }) {
|
|
1646
|
-
const [cueText, setCueText] =
|
|
1647
|
-
|
|
1743
|
+
const [cueText, setCueText] = React11.useState("");
|
|
1744
|
+
React11.useEffect(() => {
|
|
1648
1745
|
const video = videoRef.current;
|
|
1649
1746
|
if (!video || activeSubtitle === "-1") {
|
|
1650
1747
|
setCueText("");
|
|
@@ -1737,7 +1834,7 @@ function SubtitleOverlay({ videoRef, activeSubtitle }) {
|
|
|
1737
1834
|
}
|
|
1738
1835
|
var AlertDialog = AlertDialogPrimitive__namespace.Root;
|
|
1739
1836
|
var AlertDialogPortal = AlertDialogPrimitive__namespace.Portal;
|
|
1740
|
-
var AlertDialogOverlay =
|
|
1837
|
+
var AlertDialogOverlay = React11__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1741
1838
|
AlertDialogPrimitive__namespace.Overlay,
|
|
1742
1839
|
{
|
|
1743
1840
|
className: cn(
|
|
@@ -1749,7 +1846,7 @@ var AlertDialogOverlay = React10__namespace.forwardRef(({ className, ...props },
|
|
|
1749
1846
|
}
|
|
1750
1847
|
));
|
|
1751
1848
|
AlertDialogOverlay.displayName = AlertDialogPrimitive__namespace.Overlay.displayName;
|
|
1752
|
-
var AlertDialogContent =
|
|
1849
|
+
var AlertDialogContent = React11__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogPortal, { children: [
|
|
1753
1850
|
/* @__PURE__ */ jsxRuntime.jsx(AlertDialogOverlay, {}),
|
|
1754
1851
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1755
1852
|
AlertDialogPrimitive__namespace.Content,
|
|
@@ -1792,7 +1889,7 @@ var AlertDialogFooter = ({
|
|
|
1792
1889
|
}
|
|
1793
1890
|
);
|
|
1794
1891
|
AlertDialogFooter.displayName = "AlertDialogFooter";
|
|
1795
|
-
var AlertDialogTitle =
|
|
1892
|
+
var AlertDialogTitle = React11__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1796
1893
|
AlertDialogPrimitive__namespace.Title,
|
|
1797
1894
|
{
|
|
1798
1895
|
ref,
|
|
@@ -1801,7 +1898,7 @@ var AlertDialogTitle = React10__namespace.forwardRef(({ className, ...props }, r
|
|
|
1801
1898
|
}
|
|
1802
1899
|
));
|
|
1803
1900
|
AlertDialogTitle.displayName = AlertDialogPrimitive__namespace.Title.displayName;
|
|
1804
|
-
var AlertDialogDescription =
|
|
1901
|
+
var AlertDialogDescription = React11__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1805
1902
|
AlertDialogPrimitive__namespace.Description,
|
|
1806
1903
|
{
|
|
1807
1904
|
ref,
|
|
@@ -1810,7 +1907,7 @@ var AlertDialogDescription = React10__namespace.forwardRef(({ className, ...prop
|
|
|
1810
1907
|
}
|
|
1811
1908
|
));
|
|
1812
1909
|
AlertDialogDescription.displayName = AlertDialogPrimitive__namespace.Description.displayName;
|
|
1813
|
-
var AlertDialogAction =
|
|
1910
|
+
var AlertDialogAction = React11__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1814
1911
|
AlertDialogPrimitive__namespace.Action,
|
|
1815
1912
|
{
|
|
1816
1913
|
ref,
|
|
@@ -1819,7 +1916,7 @@ var AlertDialogAction = React10__namespace.forwardRef(({ className, ...props },
|
|
|
1819
1916
|
}
|
|
1820
1917
|
));
|
|
1821
1918
|
AlertDialogAction.displayName = AlertDialogPrimitive__namespace.Action.displayName;
|
|
1822
|
-
var AlertDialogCancel =
|
|
1919
|
+
var AlertDialogCancel = React11__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1823
1920
|
AlertDialogPrimitive__namespace.Cancel,
|
|
1824
1921
|
{
|
|
1825
1922
|
ref,
|
|
@@ -1834,16 +1931,16 @@ var AlertDialogCancel = React10__namespace.forwardRef(({ className, ...props },
|
|
|
1834
1931
|
AlertDialogCancel.displayName = AlertDialogPrimitive__namespace.Cancel.displayName;
|
|
1835
1932
|
var MAX_RETRIES = 3;
|
|
1836
1933
|
var LightBirdPlayer = () => {
|
|
1837
|
-
const videoRef =
|
|
1838
|
-
const containerRef =
|
|
1839
|
-
const canvasRef =
|
|
1840
|
-
const subtitleInputRef =
|
|
1841
|
-
const playerRef =
|
|
1842
|
-
const subtitleFilesMapRef =
|
|
1843
|
-
const retryCountRef =
|
|
1844
|
-
const retryTimerRef =
|
|
1845
|
-
const streamStallDetectorRef =
|
|
1846
|
-
const isStreamRef =
|
|
1934
|
+
const videoRef = React11.useRef(null);
|
|
1935
|
+
const containerRef = React11.useRef(null);
|
|
1936
|
+
const canvasRef = React11.useRef(null);
|
|
1937
|
+
const subtitleInputRef = React11.useRef(null);
|
|
1938
|
+
const playerRef = React11.useRef(null);
|
|
1939
|
+
const subtitleFilesMapRef = React11.useRef(/* @__PURE__ */ new Map());
|
|
1940
|
+
const retryCountRef = React11.useRef(0);
|
|
1941
|
+
const retryTimerRef = React11.useRef(null);
|
|
1942
|
+
const streamStallDetectorRef = React11.useRef(null);
|
|
1943
|
+
const isStreamRef = React11.useRef(false);
|
|
1847
1944
|
const { toast: toast2 } = useToast();
|
|
1848
1945
|
const playlist = react.usePlaylist();
|
|
1849
1946
|
const playback = react.useVideoPlayback(videoRef);
|
|
@@ -1871,24 +1968,26 @@ var LightBirdPlayer = () => {
|
|
|
1871
1968
|
const { chapters, currentChapter, goToChapter } = react.useChapters(videoRef, playerRef);
|
|
1872
1969
|
const magnetLinkEnabled = reactSdk.useBooleanFlagValue(core.FLAG_MAGNET_LINK, true);
|
|
1873
1970
|
const magnet = react.useMagnet();
|
|
1874
|
-
const [disclaimerPendingUri, setDisclaimerPendingUri] =
|
|
1875
|
-
const [shortcuts, setShortcuts] =
|
|
1876
|
-
const [showShortcutsHelp, setShowShortcutsHelp] =
|
|
1877
|
-
const [showShortcutsDialog, setShowShortcutsDialog] =
|
|
1878
|
-
const [showInfo, setShowInfo] =
|
|
1879
|
-
const progressEstimatorRef =
|
|
1880
|
-
const [audioTracks, setAudioTracks] =
|
|
1881
|
-
const [activeAudioTrack, setActiveAudioTrack] =
|
|
1882
|
-
const [isLoading, setIsLoading] =
|
|
1883
|
-
const [loadingMessage, setLoadingMessage] =
|
|
1884
|
-
const [processingProgress, setProcessingProgress] =
|
|
1885
|
-
const [processingEta, setProcessingEta] =
|
|
1886
|
-
const [processingThroughput, setProcessingThroughput] =
|
|
1887
|
-
const [playerError, setPlayerError] =
|
|
1888
|
-
const [cancellableProcessing, setCancellableProcessing] =
|
|
1889
|
-
const [mediaThumbnail, setMediaThumbnail] =
|
|
1890
|
-
const [tracksLoading, setTracksLoading] =
|
|
1891
|
-
const
|
|
1971
|
+
const [disclaimerPendingUri, setDisclaimerPendingUri] = React11.useState(null);
|
|
1972
|
+
const [shortcuts, setShortcuts] = React11.useState(() => core.loadShortcuts());
|
|
1973
|
+
const [showShortcutsHelp, setShowShortcutsHelp] = React11.useState(false);
|
|
1974
|
+
const [showShortcutsDialog, setShowShortcutsDialog] = React11.useState(false);
|
|
1975
|
+
const [showInfo, setShowInfo] = React11.useState(false);
|
|
1976
|
+
const progressEstimatorRef = React11.useRef(null);
|
|
1977
|
+
const [audioTracks, setAudioTracks] = React11.useState([]);
|
|
1978
|
+
const [activeAudioTrack, setActiveAudioTrack] = React11.useState("0");
|
|
1979
|
+
const [isLoading, setIsLoading] = React11.useState(false);
|
|
1980
|
+
const [loadingMessage, setLoadingMessage] = React11.useState("");
|
|
1981
|
+
const [processingProgress, setProcessingProgress] = React11.useState(0);
|
|
1982
|
+
const [processingEta, setProcessingEta] = React11.useState(null);
|
|
1983
|
+
const [processingThroughput, setProcessingThroughput] = React11.useState(null);
|
|
1984
|
+
const [playerError, setPlayerError] = React11.useState(null);
|
|
1985
|
+
const [cancellableProcessing, setCancellableProcessing] = React11.useState(false);
|
|
1986
|
+
const [mediaThumbnail, setMediaThumbnail] = React11.useState(null);
|
|
1987
|
+
const [tracksLoading, setTracksLoading] = React11.useState(false);
|
|
1988
|
+
const abLoopCycleRef = React11.useRef(() => {
|
|
1989
|
+
});
|
|
1990
|
+
const shortcutHandlers = React11.useMemo(() => ({
|
|
1892
1991
|
"play-pause": () => playback.togglePlay(),
|
|
1893
1992
|
"seek-forward-5": () => {
|
|
1894
1993
|
const el = videoRef.current;
|
|
@@ -1937,9 +2036,13 @@ var LightBirdPlayer = () => {
|
|
|
1937
2036
|
const prev = chapters[cur.index - 1];
|
|
1938
2037
|
if (prev) el.currentTime = prev.startTime;
|
|
1939
2038
|
}
|
|
1940
|
-
}
|
|
2039
|
+
},
|
|
2040
|
+
"frame-step-forward": () => playback.frameStep("forward"),
|
|
2041
|
+
"frame-step-backward": () => playback.frameStep("backward"),
|
|
2042
|
+
"loop-toggle": () => playback.toggleLoop(),
|
|
2043
|
+
"ab-loop-cycle": () => abLoopCycleRef.current()
|
|
1941
2044
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1942
|
-
}), [playback.togglePlay, playback.seek, playback.setVolume, playback.toggleMute, fullscreen.toggle, chapters, currentChapter]);
|
|
2045
|
+
}), [playback.togglePlay, playback.seek, playback.setVolume, playback.toggleMute, playback.frameStep, playback.toggleLoop, fullscreen.toggle, chapters, currentChapter]);
|
|
1943
2046
|
react.useKeyboardShortcuts(shortcuts, shortcutHandlers);
|
|
1944
2047
|
const stopStallDetection = () => {
|
|
1945
2048
|
if (streamStallDetectorRef.current) {
|
|
@@ -1976,11 +2079,11 @@ var LightBirdPlayer = () => {
|
|
|
1976
2079
|
retryTimerRef.current = null;
|
|
1977
2080
|
}
|
|
1978
2081
|
};
|
|
1979
|
-
const [playlistOpen, setPlaylistOpen] =
|
|
1980
|
-
const [playlistPinned, setPlaylistPinned] =
|
|
1981
|
-
const [playlistSize, setPlaylistSize] =
|
|
1982
|
-
const wasAutoHiddenRef =
|
|
1983
|
-
const processFile =
|
|
2082
|
+
const [playlistOpen, setPlaylistOpen] = React11.useState(true);
|
|
2083
|
+
const [playlistPinned, setPlaylistPinned] = React11.useState(false);
|
|
2084
|
+
const [playlistSize, setPlaylistSize] = React11.useState("md");
|
|
2085
|
+
const wasAutoHiddenRef = React11.useRef(false);
|
|
2086
|
+
const processFile = React11.useCallback(async (file, subtitleFiles = []) => {
|
|
1984
2087
|
setIsLoading(true);
|
|
1985
2088
|
setLoadingMessage("Initializing player...");
|
|
1986
2089
|
setProcessingProgress(0);
|
|
@@ -2056,7 +2159,7 @@ var LightBirdPlayer = () => {
|
|
|
2056
2159
|
setProcessingThroughput(null);
|
|
2057
2160
|
}
|
|
2058
2161
|
}, [subtitles, toast2]);
|
|
2059
|
-
const handleCancelProcessing =
|
|
2162
|
+
const handleCancelProcessing = React11.useCallback(() => {
|
|
2060
2163
|
playerRef.current?.cancel?.();
|
|
2061
2164
|
playerRef.current = null;
|
|
2062
2165
|
setCancellableProcessing(false);
|
|
@@ -2064,7 +2167,7 @@ var LightBirdPlayer = () => {
|
|
|
2064
2167
|
setLoadingMessage("");
|
|
2065
2168
|
setProcessingProgress(0);
|
|
2066
2169
|
}, []);
|
|
2067
|
-
const loadVideo =
|
|
2170
|
+
const loadVideo = React11.useCallback((index) => {
|
|
2068
2171
|
const item = playlist.playlist[index];
|
|
2069
2172
|
if (!item) return;
|
|
2070
2173
|
playlist.selectItem(index);
|
|
@@ -2087,14 +2190,14 @@ var LightBirdPlayer = () => {
|
|
|
2087
2190
|
processFile(item.file, subs);
|
|
2088
2191
|
}
|
|
2089
2192
|
}, [playlist.playlist, playlist.selectItem, subtitles, processFile]);
|
|
2090
|
-
const handleSkipToNext =
|
|
2193
|
+
const handleSkipToNext = React11.useCallback(() => {
|
|
2091
2194
|
setPlayerError(null);
|
|
2092
2195
|
clearRetryTimer();
|
|
2093
2196
|
if (playlist.currentIndex !== null && playlist.playlist.length > 1) {
|
|
2094
2197
|
loadVideo((playlist.currentIndex + 1) % playlist.playlist.length);
|
|
2095
2198
|
}
|
|
2096
2199
|
}, [playlist.currentIndex, playlist.playlist.length, loadVideo]);
|
|
2097
|
-
const handleRetry =
|
|
2200
|
+
const handleRetry = React11.useCallback(() => {
|
|
2098
2201
|
setPlayerError(null);
|
|
2099
2202
|
clearRetryTimer();
|
|
2100
2203
|
retryCountRef.current = 0;
|
|
@@ -2102,11 +2205,11 @@ var LightBirdPlayer = () => {
|
|
|
2102
2205
|
videoRef.current.load();
|
|
2103
2206
|
}
|
|
2104
2207
|
}, []);
|
|
2105
|
-
const handleDismissError =
|
|
2208
|
+
const handleDismissError = React11.useCallback(() => {
|
|
2106
2209
|
setPlayerError(null);
|
|
2107
2210
|
clearRetryTimer();
|
|
2108
2211
|
}, []);
|
|
2109
|
-
|
|
2212
|
+
React11.useEffect(() => {
|
|
2110
2213
|
const el = videoRef.current;
|
|
2111
2214
|
if (!el) return;
|
|
2112
2215
|
const onError = () => {
|
|
@@ -2128,7 +2231,7 @@ var LightBirdPlayer = () => {
|
|
|
2128
2231
|
el.addEventListener("error", onError);
|
|
2129
2232
|
return () => el.removeEventListener("error", onError);
|
|
2130
2233
|
}, [playlist.currentIndex, playlist.playlist.length]);
|
|
2131
|
-
|
|
2234
|
+
React11.useEffect(() => {
|
|
2132
2235
|
const el = videoRef.current;
|
|
2133
2236
|
if (!el) return;
|
|
2134
2237
|
const onEnded = () => {
|
|
@@ -2143,14 +2246,14 @@ var LightBirdPlayer = () => {
|
|
|
2143
2246
|
el.addEventListener("ended", onEnded);
|
|
2144
2247
|
return () => el.removeEventListener("ended", onEnded);
|
|
2145
2248
|
}, [playback.loop, playlist.currentIndex, playlist.playlist.length, loadVideo]);
|
|
2146
|
-
|
|
2249
|
+
React11.useEffect(() => {
|
|
2147
2250
|
return () => {
|
|
2148
2251
|
playerRef.current?.destroy();
|
|
2149
2252
|
clearRetryTimer();
|
|
2150
2253
|
stopStallDetection();
|
|
2151
2254
|
};
|
|
2152
2255
|
}, []);
|
|
2153
|
-
|
|
2256
|
+
React11.useEffect(() => {
|
|
2154
2257
|
const el = videoRef.current;
|
|
2155
2258
|
if (!el || !playlist.currentItem) {
|
|
2156
2259
|
setMediaThumbnail(null);
|
|
@@ -2169,7 +2272,7 @@ var LightBirdPlayer = () => {
|
|
|
2169
2272
|
el.removeEventListener("loadeddata", onLoadedData);
|
|
2170
2273
|
};
|
|
2171
2274
|
}, [playlist.currentItem]);
|
|
2172
|
-
|
|
2275
|
+
React11.useEffect(() => {
|
|
2173
2276
|
if (playback.isPlaying) {
|
|
2174
2277
|
if (!playlistPinned) {
|
|
2175
2278
|
setPlaylistOpen((current) => {
|
|
@@ -2205,7 +2308,7 @@ var LightBirdPlayer = () => {
|
|
|
2205
2308
|
await processFile(validVideoFiles[0], subtitleFiles);
|
|
2206
2309
|
}
|
|
2207
2310
|
};
|
|
2208
|
-
const handleFolderFilesAdded =
|
|
2311
|
+
const handleFolderFilesAdded = React11.useCallback(
|
|
2209
2312
|
async (files) => {
|
|
2210
2313
|
const prevLen = playlist.playlist.length;
|
|
2211
2314
|
await playlist.addFiles(files);
|
|
@@ -2217,16 +2320,16 @@ var LightBirdPlayer = () => {
|
|
|
2217
2320
|
},
|
|
2218
2321
|
[playlist, processFile]
|
|
2219
2322
|
);
|
|
2220
|
-
const handleRemoveItem =
|
|
2323
|
+
const handleRemoveItem = React11.useCallback((index) => {
|
|
2221
2324
|
playlist.removeItem(index);
|
|
2222
2325
|
}, [playlist]);
|
|
2223
|
-
const handleReorder =
|
|
2326
|
+
const handleReorder = React11.useCallback(
|
|
2224
2327
|
(newPlaylist) => {
|
|
2225
2328
|
playlist.reorderItems(newPlaylist);
|
|
2226
2329
|
},
|
|
2227
2330
|
[playlist]
|
|
2228
2331
|
);
|
|
2229
|
-
const handleImportM3U =
|
|
2332
|
+
const handleImportM3U = React11.useCallback(
|
|
2230
2333
|
(items) => {
|
|
2231
2334
|
items.forEach((item) => {
|
|
2232
2335
|
playlist.appendItem({ ...item, id: crypto.randomUUID() });
|
|
@@ -2242,7 +2345,7 @@ var LightBirdPlayer = () => {
|
|
|
2242
2345
|
},
|
|
2243
2346
|
[playlist, subtitles]
|
|
2244
2347
|
);
|
|
2245
|
-
const handleAddStream =
|
|
2348
|
+
const handleAddStream = React11.useCallback((url, name) => {
|
|
2246
2349
|
const newIndex = playlist.playlist.length;
|
|
2247
2350
|
const newItem = {
|
|
2248
2351
|
id: crypto.randomUUID(),
|
|
@@ -2261,7 +2364,7 @@ var LightBirdPlayer = () => {
|
|
|
2261
2364
|
startStallDetection();
|
|
2262
2365
|
}
|
|
2263
2366
|
}, [playlist, subtitles]);
|
|
2264
|
-
const handleAddMagnet =
|
|
2367
|
+
const handleAddMagnet = React11.useCallback(async (uri) => {
|
|
2265
2368
|
if (!core.hasAcceptedDisclaimer()) {
|
|
2266
2369
|
setDisclaimerPendingUri(uri);
|
|
2267
2370
|
return false;
|
|
@@ -2283,7 +2386,7 @@ var LightBirdPlayer = () => {
|
|
|
2283
2386
|
}
|
|
2284
2387
|
return true;
|
|
2285
2388
|
}, [magnet, playlist, subtitles, toast2]);
|
|
2286
|
-
const handleDisclaimerAccepted =
|
|
2389
|
+
const handleDisclaimerAccepted = React11.useCallback(async () => {
|
|
2287
2390
|
core.acceptDisclaimer();
|
|
2288
2391
|
const uri = disclaimerPendingUri;
|
|
2289
2392
|
setDisclaimerPendingUri(null);
|
|
@@ -2295,7 +2398,7 @@ var LightBirdPlayer = () => {
|
|
|
2295
2398
|
}
|
|
2296
2399
|
}
|
|
2297
2400
|
}, [disclaimerPendingUri, handleAddMagnet, toast2]);
|
|
2298
|
-
const handleSubtitleChange =
|
|
2401
|
+
const handleSubtitleChange = React11.useCallback(async (id) => {
|
|
2299
2402
|
subtitles.switchSubtitle(id);
|
|
2300
2403
|
if (playerRef.current) {
|
|
2301
2404
|
try {
|
|
@@ -2305,7 +2408,7 @@ var LightBirdPlayer = () => {
|
|
|
2305
2408
|
}
|
|
2306
2409
|
}
|
|
2307
2410
|
}, [subtitles]);
|
|
2308
|
-
const handleAudioTrackChange =
|
|
2411
|
+
const handleAudioTrackChange = React11.useCallback(async (id) => {
|
|
2309
2412
|
if (!playerRef.current) return;
|
|
2310
2413
|
try {
|
|
2311
2414
|
setIsLoading(true);
|
|
@@ -2320,7 +2423,7 @@ var LightBirdPlayer = () => {
|
|
|
2320
2423
|
setLoadingMessage("");
|
|
2321
2424
|
}
|
|
2322
2425
|
}, [toast2]);
|
|
2323
|
-
const captureScreenshot =
|
|
2426
|
+
const captureScreenshot = React11.useCallback(() => {
|
|
2324
2427
|
const video = videoRef.current;
|
|
2325
2428
|
const canvas = canvasRef.current;
|
|
2326
2429
|
if (!video || !canvas) return;
|
|
@@ -2337,49 +2440,52 @@ var LightBirdPlayer = () => {
|
|
|
2337
2440
|
a.click();
|
|
2338
2441
|
toast2({ title: "Screenshot Saved" });
|
|
2339
2442
|
}, [toast2]);
|
|
2340
|
-
const handleABLoopCycle =
|
|
2443
|
+
const handleABLoopCycle = React11.useCallback(() => {
|
|
2341
2444
|
if (abLoop.pointA === null) abLoop.setPointA();
|
|
2342
2445
|
else if (abLoop.pointB === null) abLoop.setPointB();
|
|
2343
2446
|
else abLoop.clear();
|
|
2344
2447
|
}, [abLoop.pointA, abLoop.pointB, abLoop.setPointA, abLoop.setPointB, abLoop.clear]);
|
|
2345
|
-
|
|
2448
|
+
React11.useEffect(() => {
|
|
2449
|
+
abLoopCycleRef.current = handleABLoopCycle;
|
|
2450
|
+
}, [handleABLoopCycle]);
|
|
2451
|
+
const abLoopState = React11.useMemo(
|
|
2346
2452
|
() => ({ pointA: abLoop.pointA, pointB: abLoop.pointB, isLooping: abLoop.isLooping }),
|
|
2347
2453
|
[abLoop.pointA, abLoop.pointB, abLoop.isLooping]
|
|
2348
2454
|
);
|
|
2349
|
-
const handleNext =
|
|
2455
|
+
const handleNext = React11.useCallback(() => {
|
|
2350
2456
|
if (playlist.currentIndex !== null && playlist.playlist.length > 1) {
|
|
2351
2457
|
loadVideo((playlist.currentIndex + 1) % playlist.playlist.length);
|
|
2352
2458
|
}
|
|
2353
2459
|
}, [playlist.currentIndex, playlist.playlist.length, loadVideo]);
|
|
2354
|
-
const handlePrevious =
|
|
2460
|
+
const handlePrevious = React11.useCallback(() => {
|
|
2355
2461
|
if (playlist.currentIndex !== null && playlist.playlist.length > 1) {
|
|
2356
2462
|
loadVideo((playlist.currentIndex - 1 + playlist.playlist.length) % playlist.playlist.length);
|
|
2357
2463
|
}
|
|
2358
2464
|
}, [playlist.currentIndex, playlist.playlist.length, loadVideo]);
|
|
2359
|
-
const handleSubtitleUpload =
|
|
2465
|
+
const handleSubtitleUpload = React11.useCallback(() => {
|
|
2360
2466
|
subtitleInputRef.current?.click();
|
|
2361
2467
|
}, []);
|
|
2362
|
-
const handleSelectVideo =
|
|
2468
|
+
const handleSelectVideo = React11.useCallback((index) => {
|
|
2363
2469
|
loadVideo(index);
|
|
2364
2470
|
}, [loadVideo]);
|
|
2365
2471
|
const handlePlaylistToggle = () => {
|
|
2366
2472
|
wasAutoHiddenRef.current = false;
|
|
2367
2473
|
setPlaylistOpen((v) => !v);
|
|
2368
2474
|
};
|
|
2369
|
-
const handleMediaPlay =
|
|
2475
|
+
const handleMediaPlay = React11.useCallback(() => {
|
|
2370
2476
|
const el = videoRef.current;
|
|
2371
2477
|
if (el) el.play().catch(() => {
|
|
2372
2478
|
});
|
|
2373
2479
|
}, []);
|
|
2374
|
-
const handleMediaPause =
|
|
2480
|
+
const handleMediaPause = React11.useCallback(() => {
|
|
2375
2481
|
const el = videoRef.current;
|
|
2376
2482
|
if (el) el.pause();
|
|
2377
2483
|
}, []);
|
|
2378
|
-
const handleMediaSeekForward =
|
|
2484
|
+
const handleMediaSeekForward = React11.useCallback(() => {
|
|
2379
2485
|
const el = videoRef.current;
|
|
2380
2486
|
if (el) playback.seek(el.currentTime + 10);
|
|
2381
2487
|
}, [playback.seek]);
|
|
2382
|
-
const handleMediaSeekBackward =
|
|
2488
|
+
const handleMediaSeekBackward = React11.useCallback(() => {
|
|
2383
2489
|
const el = videoRef.current;
|
|
2384
2490
|
if (el) playback.seek(el.currentTime - 10);
|
|
2385
2491
|
}, [playback.seek]);
|
|
@@ -2470,9 +2576,11 @@ var LightBirdPlayer = () => {
|
|
|
2470
2576
|
)
|
|
2471
2577
|
}
|
|
2472
2578
|
),
|
|
2473
|
-
|
|
2579
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2474
2580
|
player_controls_default,
|
|
2475
2581
|
{
|
|
2582
|
+
isDisabled: !playlist.currentItem,
|
|
2583
|
+
videoRef,
|
|
2476
2584
|
isPlaying: playback.isPlaying,
|
|
2477
2585
|
progress: playback.progress,
|
|
2478
2586
|
duration: playback.duration,
|
|
@@ -2541,9 +2649,17 @@ var LightBirdPlayer = () => {
|
|
|
2541
2649
|
}
|
|
2542
2650
|
}
|
|
2543
2651
|
),
|
|
2544
|
-
!playlist.currentItem && !isLoading && !loadingMessage && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "
|
|
2545
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2546
|
-
|
|
2652
|
+
!playlist.currentItem && !isLoading && !loadingMessage && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center px-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center text-center max-w-sm", children: [
|
|
2653
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2654
|
+
"div",
|
|
2655
|
+
{
|
|
2656
|
+
"aria-hidden": true,
|
|
2657
|
+
className: "flex h-16 w-16 items-center justify-center rounded-full bg-accent/10 ring-1 ring-accent/30",
|
|
2658
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Film, { className: "h-7 w-7", style: { color: "hsl(var(--accent))" } })
|
|
2659
|
+
}
|
|
2660
|
+
),
|
|
2661
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-5 text-lg font-medium text-foreground", children: "No video loaded" }),
|
|
2662
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: "Drop files anywhere, or pick a source from the playlist panel." })
|
|
2547
2663
|
] }) })
|
|
2548
2664
|
]
|
|
2549
2665
|
}
|
|
@@ -2596,7 +2712,7 @@ var LightBirdPlayer = () => {
|
|
|
2596
2712
|
] });
|
|
2597
2713
|
};
|
|
2598
2714
|
var lightbird_player_default = LightBirdPlayer;
|
|
2599
|
-
var PlayerErrorBoundary = class extends
|
|
2715
|
+
var PlayerErrorBoundary = class extends React11.Component {
|
|
2600
2716
|
constructor() {
|
|
2601
2717
|
super(...arguments);
|
|
2602
2718
|
this.state = { hasError: false };
|
|
@@ -2626,7 +2742,7 @@ var PlayerErrorBoundary = class extends React10.Component {
|
|
|
2626
2742
|
}
|
|
2627
2743
|
};
|
|
2628
2744
|
var ToastProvider = ToastPrimitives__namespace.Provider;
|
|
2629
|
-
var ToastViewport =
|
|
2745
|
+
var ToastViewport = React11__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2630
2746
|
ToastPrimitives__namespace.Viewport,
|
|
2631
2747
|
{
|
|
2632
2748
|
ref,
|
|
@@ -2652,7 +2768,7 @@ var toastVariants = classVarianceAuthority.cva(
|
|
|
2652
2768
|
}
|
|
2653
2769
|
}
|
|
2654
2770
|
);
|
|
2655
|
-
var Toast =
|
|
2771
|
+
var Toast = React11__namespace.forwardRef(({ className, variant, ...props }, ref) => {
|
|
2656
2772
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2657
2773
|
ToastPrimitives__namespace.Root,
|
|
2658
2774
|
{
|
|
@@ -2663,7 +2779,7 @@ var Toast = React10__namespace.forwardRef(({ className, variant, ...props }, ref
|
|
|
2663
2779
|
);
|
|
2664
2780
|
});
|
|
2665
2781
|
Toast.displayName = ToastPrimitives__namespace.Root.displayName;
|
|
2666
|
-
var ToastAction =
|
|
2782
|
+
var ToastAction = React11__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2667
2783
|
ToastPrimitives__namespace.Action,
|
|
2668
2784
|
{
|
|
2669
2785
|
ref,
|
|
@@ -2675,7 +2791,7 @@ var ToastAction = React10__namespace.forwardRef(({ className, ...props }, ref) =
|
|
|
2675
2791
|
}
|
|
2676
2792
|
));
|
|
2677
2793
|
ToastAction.displayName = ToastPrimitives__namespace.Action.displayName;
|
|
2678
|
-
var ToastClose =
|
|
2794
|
+
var ToastClose = React11__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2679
2795
|
ToastPrimitives__namespace.Close,
|
|
2680
2796
|
{
|
|
2681
2797
|
ref,
|
|
@@ -2689,7 +2805,7 @@ var ToastClose = React10__namespace.forwardRef(({ className, ...props }, ref) =>
|
|
|
2689
2805
|
}
|
|
2690
2806
|
));
|
|
2691
2807
|
ToastClose.displayName = ToastPrimitives__namespace.Close.displayName;
|
|
2692
|
-
var ToastTitle =
|
|
2808
|
+
var ToastTitle = React11__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2693
2809
|
ToastPrimitives__namespace.Title,
|
|
2694
2810
|
{
|
|
2695
2811
|
ref,
|
|
@@ -2698,7 +2814,7 @@ var ToastTitle = React10__namespace.forwardRef(({ className, ...props }, ref) =>
|
|
|
2698
2814
|
}
|
|
2699
2815
|
));
|
|
2700
2816
|
ToastTitle.displayName = ToastPrimitives__namespace.Title.displayName;
|
|
2701
|
-
var ToastDescription =
|
|
2817
|
+
var ToastDescription = React11__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2702
2818
|
ToastPrimitives__namespace.Description,
|
|
2703
2819
|
{
|
|
2704
2820
|
ref,
|
|
@@ -2725,7 +2841,7 @@ function Toaster() {
|
|
|
2725
2841
|
}
|
|
2726
2842
|
var started = false;
|
|
2727
2843
|
function FeatureFlagsProvider({ children }) {
|
|
2728
|
-
|
|
2844
|
+
React11.useEffect(() => {
|
|
2729
2845
|
if (started) return;
|
|
2730
2846
|
started = true;
|
|
2731
2847
|
core.initFeatureFlags().catch(console.error);
|