@lightbird/ui 0.6.0 → 0.8.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 +852 -740
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +776 -664
- 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,15 @@ 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
|
|
1840
|
-
const
|
|
1841
|
-
const
|
|
1842
|
-
const
|
|
1843
|
-
const
|
|
1844
|
-
const
|
|
1845
|
-
const
|
|
1846
|
-
const isStreamRef = React10.useRef(false);
|
|
1934
|
+
const videoRef = React11.useRef(null);
|
|
1935
|
+
const containerRef = React11.useRef(null);
|
|
1936
|
+
const subtitleInputRef = React11.useRef(null);
|
|
1937
|
+
const playerRef = React11.useRef(null);
|
|
1938
|
+
const subtitleFilesMapRef = React11.useRef(/* @__PURE__ */ new Map());
|
|
1939
|
+
const retryCountRef = React11.useRef(0);
|
|
1940
|
+
const retryTimerRef = React11.useRef(null);
|
|
1941
|
+
const streamStallDetectorRef = React11.useRef(null);
|
|
1942
|
+
const isStreamRef = React11.useRef(false);
|
|
1847
1943
|
const { toast: toast2 } = useToast();
|
|
1848
1944
|
const playlist = react.usePlaylist();
|
|
1849
1945
|
const playback = react.useVideoPlayback(videoRef);
|
|
@@ -1871,24 +1967,26 @@ var LightBirdPlayer = () => {
|
|
|
1871
1967
|
const { chapters, currentChapter, goToChapter } = react.useChapters(videoRef, playerRef);
|
|
1872
1968
|
const magnetLinkEnabled = reactSdk.useBooleanFlagValue(core.FLAG_MAGNET_LINK, true);
|
|
1873
1969
|
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
|
|
1970
|
+
const [disclaimerPendingUri, setDisclaimerPendingUri] = React11.useState(null);
|
|
1971
|
+
const [shortcuts, setShortcuts] = React11.useState(() => core.loadShortcuts());
|
|
1972
|
+
const [showShortcutsHelp, setShowShortcutsHelp] = React11.useState(false);
|
|
1973
|
+
const [showShortcutsDialog, setShowShortcutsDialog] = React11.useState(false);
|
|
1974
|
+
const [showInfo, setShowInfo] = React11.useState(false);
|
|
1975
|
+
const progressEstimatorRef = React11.useRef(null);
|
|
1976
|
+
const [audioTracks, setAudioTracks] = React11.useState([]);
|
|
1977
|
+
const [activeAudioTrack, setActiveAudioTrack] = React11.useState("0");
|
|
1978
|
+
const [isLoading, setIsLoading] = React11.useState(false);
|
|
1979
|
+
const [loadingMessage, setLoadingMessage] = React11.useState("");
|
|
1980
|
+
const [processingProgress, setProcessingProgress] = React11.useState(0);
|
|
1981
|
+
const [processingEta, setProcessingEta] = React11.useState(null);
|
|
1982
|
+
const [processingThroughput, setProcessingThroughput] = React11.useState(null);
|
|
1983
|
+
const [playerError, setPlayerError] = React11.useState(null);
|
|
1984
|
+
const [cancellableProcessing, setCancellableProcessing] = React11.useState(false);
|
|
1985
|
+
const [mediaThumbnail, setMediaThumbnail] = React11.useState(null);
|
|
1986
|
+
const [tracksLoading, setTracksLoading] = React11.useState(false);
|
|
1987
|
+
const abLoopCycleRef = React11.useRef(() => {
|
|
1988
|
+
});
|
|
1989
|
+
const shortcutHandlers = React11.useMemo(() => ({
|
|
1892
1990
|
"play-pause": () => playback.togglePlay(),
|
|
1893
1991
|
"seek-forward-5": () => {
|
|
1894
1992
|
const el = videoRef.current;
|
|
@@ -1937,9 +2035,13 @@ var LightBirdPlayer = () => {
|
|
|
1937
2035
|
const prev = chapters[cur.index - 1];
|
|
1938
2036
|
if (prev) el.currentTime = prev.startTime;
|
|
1939
2037
|
}
|
|
1940
|
-
}
|
|
2038
|
+
},
|
|
2039
|
+
"frame-step-forward": () => playback.frameStep("forward"),
|
|
2040
|
+
"frame-step-backward": () => playback.frameStep("backward"),
|
|
2041
|
+
"loop-toggle": () => playback.toggleLoop(),
|
|
2042
|
+
"ab-loop-cycle": () => abLoopCycleRef.current()
|
|
1941
2043
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1942
|
-
}), [playback.togglePlay, playback.seek, playback.setVolume, playback.toggleMute, fullscreen.toggle, chapters, currentChapter]);
|
|
2044
|
+
}), [playback.togglePlay, playback.seek, playback.setVolume, playback.toggleMute, playback.frameStep, playback.toggleLoop, fullscreen.toggle, chapters, currentChapter]);
|
|
1943
2045
|
react.useKeyboardShortcuts(shortcuts, shortcutHandlers);
|
|
1944
2046
|
const stopStallDetection = () => {
|
|
1945
2047
|
if (streamStallDetectorRef.current) {
|
|
@@ -1976,11 +2078,11 @@ var LightBirdPlayer = () => {
|
|
|
1976
2078
|
retryTimerRef.current = null;
|
|
1977
2079
|
}
|
|
1978
2080
|
};
|
|
1979
|
-
const [playlistOpen, setPlaylistOpen] =
|
|
1980
|
-
const [playlistPinned, setPlaylistPinned] =
|
|
1981
|
-
const [playlistSize, setPlaylistSize] =
|
|
1982
|
-
const wasAutoHiddenRef =
|
|
1983
|
-
const processFile =
|
|
2081
|
+
const [playlistOpen, setPlaylistOpen] = React11.useState(true);
|
|
2082
|
+
const [playlistPinned, setPlaylistPinned] = React11.useState(false);
|
|
2083
|
+
const [playlistSize, setPlaylistSize] = React11.useState("md");
|
|
2084
|
+
const wasAutoHiddenRef = React11.useRef(false);
|
|
2085
|
+
const processFile = React11.useCallback(async (file, subtitleFiles = []) => {
|
|
1984
2086
|
setIsLoading(true);
|
|
1985
2087
|
setLoadingMessage("Initializing player...");
|
|
1986
2088
|
setProcessingProgress(0);
|
|
@@ -2056,7 +2158,7 @@ var LightBirdPlayer = () => {
|
|
|
2056
2158
|
setProcessingThroughput(null);
|
|
2057
2159
|
}
|
|
2058
2160
|
}, [subtitles, toast2]);
|
|
2059
|
-
const handleCancelProcessing =
|
|
2161
|
+
const handleCancelProcessing = React11.useCallback(() => {
|
|
2060
2162
|
playerRef.current?.cancel?.();
|
|
2061
2163
|
playerRef.current = null;
|
|
2062
2164
|
setCancellableProcessing(false);
|
|
@@ -2064,7 +2166,7 @@ var LightBirdPlayer = () => {
|
|
|
2064
2166
|
setLoadingMessage("");
|
|
2065
2167
|
setProcessingProgress(0);
|
|
2066
2168
|
}, []);
|
|
2067
|
-
const loadVideo =
|
|
2169
|
+
const loadVideo = React11.useCallback((index) => {
|
|
2068
2170
|
const item = playlist.playlist[index];
|
|
2069
2171
|
if (!item) return;
|
|
2070
2172
|
playlist.selectItem(index);
|
|
@@ -2087,14 +2189,14 @@ var LightBirdPlayer = () => {
|
|
|
2087
2189
|
processFile(item.file, subs);
|
|
2088
2190
|
}
|
|
2089
2191
|
}, [playlist.playlist, playlist.selectItem, subtitles, processFile]);
|
|
2090
|
-
const handleSkipToNext =
|
|
2192
|
+
const handleSkipToNext = React11.useCallback(() => {
|
|
2091
2193
|
setPlayerError(null);
|
|
2092
2194
|
clearRetryTimer();
|
|
2093
2195
|
if (playlist.currentIndex !== null && playlist.playlist.length > 1) {
|
|
2094
2196
|
loadVideo((playlist.currentIndex + 1) % playlist.playlist.length);
|
|
2095
2197
|
}
|
|
2096
2198
|
}, [playlist.currentIndex, playlist.playlist.length, loadVideo]);
|
|
2097
|
-
const handleRetry =
|
|
2199
|
+
const handleRetry = React11.useCallback(() => {
|
|
2098
2200
|
setPlayerError(null);
|
|
2099
2201
|
clearRetryTimer();
|
|
2100
2202
|
retryCountRef.current = 0;
|
|
@@ -2102,11 +2204,11 @@ var LightBirdPlayer = () => {
|
|
|
2102
2204
|
videoRef.current.load();
|
|
2103
2205
|
}
|
|
2104
2206
|
}, []);
|
|
2105
|
-
const handleDismissError =
|
|
2207
|
+
const handleDismissError = React11.useCallback(() => {
|
|
2106
2208
|
setPlayerError(null);
|
|
2107
2209
|
clearRetryTimer();
|
|
2108
2210
|
}, []);
|
|
2109
|
-
|
|
2211
|
+
React11.useEffect(() => {
|
|
2110
2212
|
const el = videoRef.current;
|
|
2111
2213
|
if (!el) return;
|
|
2112
2214
|
const onError = () => {
|
|
@@ -2128,7 +2230,7 @@ var LightBirdPlayer = () => {
|
|
|
2128
2230
|
el.addEventListener("error", onError);
|
|
2129
2231
|
return () => el.removeEventListener("error", onError);
|
|
2130
2232
|
}, [playlist.currentIndex, playlist.playlist.length]);
|
|
2131
|
-
|
|
2233
|
+
React11.useEffect(() => {
|
|
2132
2234
|
const el = videoRef.current;
|
|
2133
2235
|
if (!el) return;
|
|
2134
2236
|
const onEnded = () => {
|
|
@@ -2143,14 +2245,14 @@ var LightBirdPlayer = () => {
|
|
|
2143
2245
|
el.addEventListener("ended", onEnded);
|
|
2144
2246
|
return () => el.removeEventListener("ended", onEnded);
|
|
2145
2247
|
}, [playback.loop, playlist.currentIndex, playlist.playlist.length, loadVideo]);
|
|
2146
|
-
|
|
2248
|
+
React11.useEffect(() => {
|
|
2147
2249
|
return () => {
|
|
2148
2250
|
playerRef.current?.destroy();
|
|
2149
2251
|
clearRetryTimer();
|
|
2150
2252
|
stopStallDetection();
|
|
2151
2253
|
};
|
|
2152
2254
|
}, []);
|
|
2153
|
-
|
|
2255
|
+
React11.useEffect(() => {
|
|
2154
2256
|
const el = videoRef.current;
|
|
2155
2257
|
if (!el || !playlist.currentItem) {
|
|
2156
2258
|
setMediaThumbnail(null);
|
|
@@ -2169,7 +2271,7 @@ var LightBirdPlayer = () => {
|
|
|
2169
2271
|
el.removeEventListener("loadeddata", onLoadedData);
|
|
2170
2272
|
};
|
|
2171
2273
|
}, [playlist.currentItem]);
|
|
2172
|
-
|
|
2274
|
+
React11.useEffect(() => {
|
|
2173
2275
|
if (playback.isPlaying) {
|
|
2174
2276
|
if (!playlistPinned) {
|
|
2175
2277
|
setPlaylistOpen((current) => {
|
|
@@ -2205,7 +2307,7 @@ var LightBirdPlayer = () => {
|
|
|
2205
2307
|
await processFile(validVideoFiles[0], subtitleFiles);
|
|
2206
2308
|
}
|
|
2207
2309
|
};
|
|
2208
|
-
const handleFolderFilesAdded =
|
|
2310
|
+
const handleFolderFilesAdded = React11.useCallback(
|
|
2209
2311
|
async (files) => {
|
|
2210
2312
|
const prevLen = playlist.playlist.length;
|
|
2211
2313
|
await playlist.addFiles(files);
|
|
@@ -2217,16 +2319,16 @@ var LightBirdPlayer = () => {
|
|
|
2217
2319
|
},
|
|
2218
2320
|
[playlist, processFile]
|
|
2219
2321
|
);
|
|
2220
|
-
const handleRemoveItem =
|
|
2322
|
+
const handleRemoveItem = React11.useCallback((index) => {
|
|
2221
2323
|
playlist.removeItem(index);
|
|
2222
2324
|
}, [playlist]);
|
|
2223
|
-
const handleReorder =
|
|
2325
|
+
const handleReorder = React11.useCallback(
|
|
2224
2326
|
(newPlaylist) => {
|
|
2225
2327
|
playlist.reorderItems(newPlaylist);
|
|
2226
2328
|
},
|
|
2227
2329
|
[playlist]
|
|
2228
2330
|
);
|
|
2229
|
-
const handleImportM3U =
|
|
2331
|
+
const handleImportM3U = React11.useCallback(
|
|
2230
2332
|
(items) => {
|
|
2231
2333
|
items.forEach((item) => {
|
|
2232
2334
|
playlist.appendItem({ ...item, id: crypto.randomUUID() });
|
|
@@ -2242,7 +2344,7 @@ var LightBirdPlayer = () => {
|
|
|
2242
2344
|
},
|
|
2243
2345
|
[playlist, subtitles]
|
|
2244
2346
|
);
|
|
2245
|
-
const handleAddStream =
|
|
2347
|
+
const handleAddStream = React11.useCallback((url, name) => {
|
|
2246
2348
|
const newIndex = playlist.playlist.length;
|
|
2247
2349
|
const newItem = {
|
|
2248
2350
|
id: crypto.randomUUID(),
|
|
@@ -2261,7 +2363,7 @@ var LightBirdPlayer = () => {
|
|
|
2261
2363
|
startStallDetection();
|
|
2262
2364
|
}
|
|
2263
2365
|
}, [playlist, subtitles]);
|
|
2264
|
-
const handleAddMagnet =
|
|
2366
|
+
const handleAddMagnet = React11.useCallback(async (uri) => {
|
|
2265
2367
|
if (!core.hasAcceptedDisclaimer()) {
|
|
2266
2368
|
setDisclaimerPendingUri(uri);
|
|
2267
2369
|
return false;
|
|
@@ -2283,7 +2385,7 @@ var LightBirdPlayer = () => {
|
|
|
2283
2385
|
}
|
|
2284
2386
|
return true;
|
|
2285
2387
|
}, [magnet, playlist, subtitles, toast2]);
|
|
2286
|
-
const handleDisclaimerAccepted =
|
|
2388
|
+
const handleDisclaimerAccepted = React11.useCallback(async () => {
|
|
2287
2389
|
core.acceptDisclaimer();
|
|
2288
2390
|
const uri = disclaimerPendingUri;
|
|
2289
2391
|
setDisclaimerPendingUri(null);
|
|
@@ -2295,7 +2397,7 @@ var LightBirdPlayer = () => {
|
|
|
2295
2397
|
}
|
|
2296
2398
|
}
|
|
2297
2399
|
}, [disclaimerPendingUri, handleAddMagnet, toast2]);
|
|
2298
|
-
const handleSubtitleChange =
|
|
2400
|
+
const handleSubtitleChange = React11.useCallback(async (id) => {
|
|
2299
2401
|
subtitles.switchSubtitle(id);
|
|
2300
2402
|
if (playerRef.current) {
|
|
2301
2403
|
try {
|
|
@@ -2305,7 +2407,7 @@ var LightBirdPlayer = () => {
|
|
|
2305
2407
|
}
|
|
2306
2408
|
}
|
|
2307
2409
|
}, [subtitles]);
|
|
2308
|
-
const handleAudioTrackChange =
|
|
2410
|
+
const handleAudioTrackChange = React11.useCallback(async (id) => {
|
|
2309
2411
|
if (!playerRef.current) return;
|
|
2310
2412
|
try {
|
|
2311
2413
|
setIsLoading(true);
|
|
@@ -2320,66 +2422,67 @@ var LightBirdPlayer = () => {
|
|
|
2320
2422
|
setLoadingMessage("");
|
|
2321
2423
|
}
|
|
2322
2424
|
}, [toast2]);
|
|
2323
|
-
const captureScreenshot =
|
|
2425
|
+
const captureScreenshot = React11.useCallback(() => {
|
|
2324
2426
|
const video = videoRef.current;
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
a.download = `lightbird-screenshot-${(/* @__PURE__ */ new Date()).toISOString()}.png`;
|
|
2337
|
-
a.click();
|
|
2427
|
+
if (!video) return;
|
|
2428
|
+
const dataUrl = core.exportVideoFrame(video, { filter: video.style.filter });
|
|
2429
|
+
if (!dataUrl) {
|
|
2430
|
+
toast2({
|
|
2431
|
+
title: "Screenshot failed",
|
|
2432
|
+
description: "The frame could not be captured (the video may be cross-origin protected).",
|
|
2433
|
+
variant: "destructive"
|
|
2434
|
+
});
|
|
2435
|
+
return;
|
|
2436
|
+
}
|
|
2437
|
+
core.downloadDataUrl(dataUrl, core.frameExportFilename("png"));
|
|
2338
2438
|
toast2({ title: "Screenshot Saved" });
|
|
2339
2439
|
}, [toast2]);
|
|
2340
|
-
const handleABLoopCycle =
|
|
2440
|
+
const handleABLoopCycle = React11.useCallback(() => {
|
|
2341
2441
|
if (abLoop.pointA === null) abLoop.setPointA();
|
|
2342
2442
|
else if (abLoop.pointB === null) abLoop.setPointB();
|
|
2343
2443
|
else abLoop.clear();
|
|
2344
2444
|
}, [abLoop.pointA, abLoop.pointB, abLoop.setPointA, abLoop.setPointB, abLoop.clear]);
|
|
2345
|
-
|
|
2445
|
+
React11.useEffect(() => {
|
|
2446
|
+
abLoopCycleRef.current = handleABLoopCycle;
|
|
2447
|
+
}, [handleABLoopCycle]);
|
|
2448
|
+
const abLoopState = React11.useMemo(
|
|
2346
2449
|
() => ({ pointA: abLoop.pointA, pointB: abLoop.pointB, isLooping: abLoop.isLooping }),
|
|
2347
2450
|
[abLoop.pointA, abLoop.pointB, abLoop.isLooping]
|
|
2348
2451
|
);
|
|
2349
|
-
const handleNext =
|
|
2452
|
+
const handleNext = React11.useCallback(() => {
|
|
2350
2453
|
if (playlist.currentIndex !== null && playlist.playlist.length > 1) {
|
|
2351
2454
|
loadVideo((playlist.currentIndex + 1) % playlist.playlist.length);
|
|
2352
2455
|
}
|
|
2353
2456
|
}, [playlist.currentIndex, playlist.playlist.length, loadVideo]);
|
|
2354
|
-
const handlePrevious =
|
|
2457
|
+
const handlePrevious = React11.useCallback(() => {
|
|
2355
2458
|
if (playlist.currentIndex !== null && playlist.playlist.length > 1) {
|
|
2356
2459
|
loadVideo((playlist.currentIndex - 1 + playlist.playlist.length) % playlist.playlist.length);
|
|
2357
2460
|
}
|
|
2358
2461
|
}, [playlist.currentIndex, playlist.playlist.length, loadVideo]);
|
|
2359
|
-
const handleSubtitleUpload =
|
|
2462
|
+
const handleSubtitleUpload = React11.useCallback(() => {
|
|
2360
2463
|
subtitleInputRef.current?.click();
|
|
2361
2464
|
}, []);
|
|
2362
|
-
const handleSelectVideo =
|
|
2465
|
+
const handleSelectVideo = React11.useCallback((index) => {
|
|
2363
2466
|
loadVideo(index);
|
|
2364
2467
|
}, [loadVideo]);
|
|
2365
2468
|
const handlePlaylistToggle = () => {
|
|
2366
2469
|
wasAutoHiddenRef.current = false;
|
|
2367
2470
|
setPlaylistOpen((v) => !v);
|
|
2368
2471
|
};
|
|
2369
|
-
const handleMediaPlay =
|
|
2472
|
+
const handleMediaPlay = React11.useCallback(() => {
|
|
2370
2473
|
const el = videoRef.current;
|
|
2371
2474
|
if (el) el.play().catch(() => {
|
|
2372
2475
|
});
|
|
2373
2476
|
}, []);
|
|
2374
|
-
const handleMediaPause =
|
|
2477
|
+
const handleMediaPause = React11.useCallback(() => {
|
|
2375
2478
|
const el = videoRef.current;
|
|
2376
2479
|
if (el) el.pause();
|
|
2377
2480
|
}, []);
|
|
2378
|
-
const handleMediaSeekForward =
|
|
2481
|
+
const handleMediaSeekForward = React11.useCallback(() => {
|
|
2379
2482
|
const el = videoRef.current;
|
|
2380
2483
|
if (el) playback.seek(el.currentTime + 10);
|
|
2381
2484
|
}, [playback.seek]);
|
|
2382
|
-
const handleMediaSeekBackward =
|
|
2485
|
+
const handleMediaSeekBackward = React11.useCallback(() => {
|
|
2383
2486
|
const el = videoRef.current;
|
|
2384
2487
|
if (el) playback.seek(el.currentTime - 10);
|
|
2385
2488
|
}, [playback.seek]);
|
|
@@ -2410,7 +2513,6 @@ var LightBirdPlayer = () => {
|
|
|
2410
2513
|
crossOrigin: "anonymous"
|
|
2411
2514
|
}
|
|
2412
2515
|
),
|
|
2413
|
-
/* @__PURE__ */ jsxRuntime.jsx("canvas", { ref: canvasRef, className: "hidden" }),
|
|
2414
2516
|
/* @__PURE__ */ jsxRuntime.jsx(SubtitleOverlay, { videoRef, activeSubtitle: subtitles.activeSubtitle }),
|
|
2415
2517
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2416
2518
|
VideoOverlay,
|
|
@@ -2470,9 +2572,11 @@ var LightBirdPlayer = () => {
|
|
|
2470
2572
|
)
|
|
2471
2573
|
}
|
|
2472
2574
|
),
|
|
2473
|
-
|
|
2575
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2474
2576
|
player_controls_default,
|
|
2475
2577
|
{
|
|
2578
|
+
isDisabled: !playlist.currentItem,
|
|
2579
|
+
videoRef,
|
|
2476
2580
|
isPlaying: playback.isPlaying,
|
|
2477
2581
|
progress: playback.progress,
|
|
2478
2582
|
duration: playback.duration,
|
|
@@ -2541,9 +2645,17 @@ var LightBirdPlayer = () => {
|
|
|
2541
2645
|
}
|
|
2542
2646
|
}
|
|
2543
2647
|
),
|
|
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
|
-
|
|
2648
|
+
!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: [
|
|
2649
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2650
|
+
"div",
|
|
2651
|
+
{
|
|
2652
|
+
"aria-hidden": true,
|
|
2653
|
+
className: "flex h-16 w-16 items-center justify-center rounded-full bg-accent/10 ring-1 ring-accent/30",
|
|
2654
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Film, { className: "h-7 w-7", style: { color: "hsl(var(--accent))" } })
|
|
2655
|
+
}
|
|
2656
|
+
),
|
|
2657
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-5 text-lg font-medium text-foreground", children: "No video loaded" }),
|
|
2658
|
+
/* @__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
2659
|
] }) })
|
|
2548
2660
|
]
|
|
2549
2661
|
}
|
|
@@ -2596,7 +2708,7 @@ var LightBirdPlayer = () => {
|
|
|
2596
2708
|
] });
|
|
2597
2709
|
};
|
|
2598
2710
|
var lightbird_player_default = LightBirdPlayer;
|
|
2599
|
-
var PlayerErrorBoundary = class extends
|
|
2711
|
+
var PlayerErrorBoundary = class extends React11.Component {
|
|
2600
2712
|
constructor() {
|
|
2601
2713
|
super(...arguments);
|
|
2602
2714
|
this.state = { hasError: false };
|
|
@@ -2626,7 +2738,7 @@ var PlayerErrorBoundary = class extends React10.Component {
|
|
|
2626
2738
|
}
|
|
2627
2739
|
};
|
|
2628
2740
|
var ToastProvider = ToastPrimitives__namespace.Provider;
|
|
2629
|
-
var ToastViewport =
|
|
2741
|
+
var ToastViewport = React11__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2630
2742
|
ToastPrimitives__namespace.Viewport,
|
|
2631
2743
|
{
|
|
2632
2744
|
ref,
|
|
@@ -2652,7 +2764,7 @@ var toastVariants = classVarianceAuthority.cva(
|
|
|
2652
2764
|
}
|
|
2653
2765
|
}
|
|
2654
2766
|
);
|
|
2655
|
-
var Toast =
|
|
2767
|
+
var Toast = React11__namespace.forwardRef(({ className, variant, ...props }, ref) => {
|
|
2656
2768
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2657
2769
|
ToastPrimitives__namespace.Root,
|
|
2658
2770
|
{
|
|
@@ -2663,7 +2775,7 @@ var Toast = React10__namespace.forwardRef(({ className, variant, ...props }, ref
|
|
|
2663
2775
|
);
|
|
2664
2776
|
});
|
|
2665
2777
|
Toast.displayName = ToastPrimitives__namespace.Root.displayName;
|
|
2666
|
-
var ToastAction =
|
|
2778
|
+
var ToastAction = React11__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2667
2779
|
ToastPrimitives__namespace.Action,
|
|
2668
2780
|
{
|
|
2669
2781
|
ref,
|
|
@@ -2675,7 +2787,7 @@ var ToastAction = React10__namespace.forwardRef(({ className, ...props }, ref) =
|
|
|
2675
2787
|
}
|
|
2676
2788
|
));
|
|
2677
2789
|
ToastAction.displayName = ToastPrimitives__namespace.Action.displayName;
|
|
2678
|
-
var ToastClose =
|
|
2790
|
+
var ToastClose = React11__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2679
2791
|
ToastPrimitives__namespace.Close,
|
|
2680
2792
|
{
|
|
2681
2793
|
ref,
|
|
@@ -2689,7 +2801,7 @@ var ToastClose = React10__namespace.forwardRef(({ className, ...props }, ref) =>
|
|
|
2689
2801
|
}
|
|
2690
2802
|
));
|
|
2691
2803
|
ToastClose.displayName = ToastPrimitives__namespace.Close.displayName;
|
|
2692
|
-
var ToastTitle =
|
|
2804
|
+
var ToastTitle = React11__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2693
2805
|
ToastPrimitives__namespace.Title,
|
|
2694
2806
|
{
|
|
2695
2807
|
ref,
|
|
@@ -2698,7 +2810,7 @@ var ToastTitle = React10__namespace.forwardRef(({ className, ...props }, ref) =>
|
|
|
2698
2810
|
}
|
|
2699
2811
|
));
|
|
2700
2812
|
ToastTitle.displayName = ToastPrimitives__namespace.Title.displayName;
|
|
2701
|
-
var ToastDescription =
|
|
2813
|
+
var ToastDescription = React11__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2702
2814
|
ToastPrimitives__namespace.Description,
|
|
2703
2815
|
{
|
|
2704
2816
|
ref,
|
|
@@ -2725,7 +2837,7 @@ function Toaster() {
|
|
|
2725
2837
|
}
|
|
2726
2838
|
var started = false;
|
|
2727
2839
|
function FeatureFlagsProvider({ children }) {
|
|
2728
|
-
|
|
2840
|
+
React11.useEffect(() => {
|
|
2729
2841
|
if (started) return;
|
|
2730
2842
|
started = true;
|
|
2731
2843
|
core.initFeatureFlags().catch(console.error);
|