@geekapps/silo-elements-nextjs 0.3.6 → 0.3.8
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/MediaUploader.js +147 -4
- package/dist/MediaUploader.js.map +1 -1
- package/dist/VideoPlayer.js +47 -4
- package/dist/VideoPlayer.js.map +1 -1
- package/dist/VideoUploader.js +145 -2
- package/dist/VideoUploader.js.map +1 -1
- package/dist/index.js +194 -8
- package/dist/index.js.map +1 -1
- package/dist/styles.css +2 -2
- package/package.json +48 -48
- package/styles.css +2 -2
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import React, { useState, useRef, useCallback, useMemo, useEffect } from 'react'
|
|
|
2
2
|
import { useMultipartUpload, useBatchUpload, useSignedUrl, useFileStatus } from '@geekapps/silo-nextjs';
|
|
3
3
|
export { SiloProvider, useBatchUpload, useFileStatus, useMultipartUpload, useSignedUrl } from '@geekapps/silo-nextjs';
|
|
4
4
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
5
|
+
import MediaInfoFactory from 'mediainfo.js';
|
|
5
6
|
import gsap from 'gsap';
|
|
6
7
|
import { Pause, Play, Captions as Captions$1, Settings, Minimize, Maximize, VolumeX, Volume2 } from 'lucide-react';
|
|
7
8
|
|
|
@@ -163,7 +164,7 @@ var FORMATS = [
|
|
|
163
164
|
{ value: "png", label: "PNG", hint: "Sem perda" }
|
|
164
165
|
];
|
|
165
166
|
function ImageOptions({ value, onChange, style }) {
|
|
166
|
-
const
|
|
167
|
+
const fmt2 = value.format ?? "webp";
|
|
167
168
|
const optimize = value.optimize ?? true;
|
|
168
169
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2.5", style, children: [
|
|
169
170
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
@@ -174,7 +175,7 @@ function ImageOptions({ value, onChange, style }) {
|
|
|
174
175
|
type: "button",
|
|
175
176
|
onClick: () => onChange({ ...value, format: f.value }),
|
|
176
177
|
title: f.hint,
|
|
177
|
-
style:
|
|
178
|
+
style: fmt2 === f.value ? { border: "1px solid #6366f1", background: "#6366f1", color: "#fff", padding: "4px 12px", borderRadius: 6, fontSize: 12, fontWeight: 600, cursor: "pointer" } : { border: "1px solid rgba(255,255,255,0.2)", background: "rgba(255,255,255,0.06)", color: "rgba(255,255,255,0.8)", padding: "4px 12px", borderRadius: 6, fontSize: 12, fontWeight: 600, cursor: "pointer" },
|
|
178
179
|
children: f.label
|
|
179
180
|
},
|
|
180
181
|
f.value
|
|
@@ -698,6 +699,125 @@ function VideoOptions({ value, onChange, style }) {
|
|
|
698
699
|
)
|
|
699
700
|
] });
|
|
700
701
|
}
|
|
702
|
+
var CODEC_MESSAGES = {
|
|
703
|
+
AV1: { title: "AV1 n\xE3o \xE9 suportado como entrada", detail: "O pipeline de transcodifica\xE7\xE3o n\xE3o aceita arquivos AV1. Converta para H.264 ou H.265 antes de enviar." },
|
|
704
|
+
VP9: { title: "VP9 n\xE3o \xE9 suportado como entrada", detail: "Arquivos WebM/VP9 n\xE3o s\xE3o aceitos. Converta para H.264 ou H.265 antes de enviar." },
|
|
705
|
+
VP8: { title: "VP8 n\xE3o \xE9 suportado como entrada", detail: "Arquivos WebM/VP8 n\xE3o s\xE3o aceitos. Converta para H.264 antes de enviar." },
|
|
706
|
+
"MPEG-1 Video": { title: "MPEG-1 n\xE3o \xE9 suportado", detail: "Formato muito antigo. Converta para H.264 antes de enviar." },
|
|
707
|
+
"MPEG-2 Video": { title: "MPEG-2 n\xE3o \xE9 suportado", detail: "Converta para H.264 antes de enviar." }
|
|
708
|
+
};
|
|
709
|
+
var CONTAINER_MESSAGES = {
|
|
710
|
+
FLV: { title: "Formato FLV n\xE3o suportado", detail: "Arquivos Flash Video n\xE3o s\xE3o aceitos. Converta para MP4 (H.264) antes de enviar." },
|
|
711
|
+
"Flash Video": { title: "Formato FLV n\xE3o suportado", detail: "Arquivos Flash Video n\xE3o s\xE3o aceitos. Converta para MP4 (H.264) antes de enviar." },
|
|
712
|
+
RealMedia: { title: "Formato RealMedia n\xE3o suportado", detail: "Converta para MP4 (H.264) antes de enviar." },
|
|
713
|
+
ASF: { title: "Formato ASF/WMV n\xE3o suportado", detail: "Converta para MP4 (H.264) antes de enviar." }
|
|
714
|
+
};
|
|
715
|
+
function issueMessage(issue) {
|
|
716
|
+
if (issue.code === "unsupported_codec") {
|
|
717
|
+
return CODEC_MESSAGES[issue.codec] ?? { title: `Codec ${issue.codec} n\xE3o suportado`, detail: "Converta para H.264 ou H.265 antes de enviar." };
|
|
718
|
+
}
|
|
719
|
+
const key = Object.keys(CONTAINER_MESSAGES).find((k) => issue.container.includes(k));
|
|
720
|
+
return (key ? CONTAINER_MESSAGES[key] : null) ?? { title: `Container ${issue.container} n\xE3o suportado`, detail: "Converta para MP4 (H.264) antes de enviar." };
|
|
721
|
+
}
|
|
722
|
+
function fmt(n, unit) {
|
|
723
|
+
return `${n.toLocaleString("pt-BR")}${unit}`;
|
|
724
|
+
}
|
|
725
|
+
function fmtDuration(secs) {
|
|
726
|
+
const h = Math.floor(secs / 3600);
|
|
727
|
+
const m = Math.floor(secs % 3600 / 60);
|
|
728
|
+
const s = Math.floor(secs % 60);
|
|
729
|
+
if (h > 0) return `${h}h ${m}m ${s}s`;
|
|
730
|
+
if (m > 0) return `${m}m ${s}s`;
|
|
731
|
+
return `${s}s`;
|
|
732
|
+
}
|
|
733
|
+
function VideoCompatAlert({ info, issues, analyzing }) {
|
|
734
|
+
if (analyzing) {
|
|
735
|
+
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8, padding: "10px 14px", background: "rgba(99,102,241,0.06)", border: "1px solid rgba(99,102,241,0.18)", borderRadius: 10 }, children: [
|
|
736
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: 14 }, children: "\u{1F50D}" }),
|
|
737
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: "rgba(165,180,252,0.9)" }, children: "Analisando arquivo\u2026" })
|
|
738
|
+
] });
|
|
739
|
+
}
|
|
740
|
+
const v = info.video;
|
|
741
|
+
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
|
|
742
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", flexWrap: "wrap", gap: 6 }, children: [
|
|
743
|
+
v && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
744
|
+
/* @__PURE__ */ jsx(Chip, { label: v.codec || "?", color: "neutral" }),
|
|
745
|
+
v.width > 0 && v.height > 0 && /* @__PURE__ */ jsx(Chip, { label: `${v.width}\xD7${v.height}`, color: "neutral" }),
|
|
746
|
+
v.duration > 0 && /* @__PURE__ */ jsx(Chip, { label: fmtDuration(v.duration), color: "neutral" }),
|
|
747
|
+
v.frameRate > 0 && /* @__PURE__ */ jsx(Chip, { label: fmt(Math.round(v.frameRate), " fps"), color: "neutral" }),
|
|
748
|
+
v.hdr && /* @__PURE__ */ jsx(Chip, { label: "HDR", color: "purple" })
|
|
749
|
+
] }),
|
|
750
|
+
info.audio && /* @__PURE__ */ jsx(Chip, { label: info.audio.codec || "sem \xE1udio", color: "neutral" }),
|
|
751
|
+
/* @__PURE__ */ jsx(Chip, { label: formatBytes(info.fileSize), color: "neutral" })
|
|
752
|
+
] }),
|
|
753
|
+
issues.map((issue, i) => {
|
|
754
|
+
const msg = issueMessage(issue);
|
|
755
|
+
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8, padding: "10px 14px", background: "rgba(239,68,68,0.07)", border: "1px solid rgba(239,68,68,0.22)", borderRadius: 10 }, children: [
|
|
756
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: 14, flexShrink: 0 }, children: "\u{1F6AB}" }),
|
|
757
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
758
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: 12, fontWeight: 700, color: "#f87171", marginBottom: 2 }, children: msg.title }),
|
|
759
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: 11, color: "rgba(255,255,255,0.5)", lineHeight: 1.55 }, children: msg.detail })
|
|
760
|
+
] })
|
|
761
|
+
] }, i);
|
|
762
|
+
})
|
|
763
|
+
] });
|
|
764
|
+
}
|
|
765
|
+
function Chip({ label, color }) {
|
|
766
|
+
const s = color === "purple" ? { background: "rgba(168,85,247,0.12)", border: "1px solid rgba(168,85,247,0.25)", color: "#c084fc" } : { background: "rgba(255,255,255,0.05)", border: "1px solid rgba(255,255,255,0.1)", color: "rgba(255,255,255,0.6)" };
|
|
767
|
+
return /* @__PURE__ */ jsx("span", { style: { ...s, fontSize: 11, fontWeight: 600, borderRadius: 6, padding: "2px 8px", display: "inline-block" }, children: label });
|
|
768
|
+
}
|
|
769
|
+
var UNSUPPORTED_CODECS = ["AV1", "VP8", "VP9", "MPEG-1 Video", "MPEG-2 Video", "Theora", "RealVideo"];
|
|
770
|
+
var UNSUPPORTED_CONTAINERS = ["FLV", "Flash Video", "RealMedia", "ASF"];
|
|
771
|
+
function getCompatIssues(info) {
|
|
772
|
+
const issues = [];
|
|
773
|
+
if (info.video && UNSUPPORTED_CODECS.includes(info.video.codec)) {
|
|
774
|
+
issues.push({ code: "unsupported_codec", codec: info.video.codec });
|
|
775
|
+
}
|
|
776
|
+
if (UNSUPPORTED_CONTAINERS.some((c) => info.container.includes(c))) {
|
|
777
|
+
issues.push({ code: "unsupported_container", container: info.container });
|
|
778
|
+
}
|
|
779
|
+
return issues;
|
|
780
|
+
}
|
|
781
|
+
async function analyzeVideoFile(file) {
|
|
782
|
+
const wasmUrl = `https://cdn.jsdelivr.net/npm/mediainfo.js@0.3.7/dist/MediaInfoModule.wasm`;
|
|
783
|
+
const mediainfo = await MediaInfoFactory({ format: "object", locateFile: () => wasmUrl });
|
|
784
|
+
const getSize = () => file.size;
|
|
785
|
+
const readChunk = (chunkSize, offset) => new Promise((resolve, reject) => {
|
|
786
|
+
const reader = new FileReader();
|
|
787
|
+
reader.onload = (e) => resolve(new Uint8Array(e.target.result));
|
|
788
|
+
reader.onerror = reject;
|
|
789
|
+
reader.readAsArrayBuffer(file.slice(offset, offset + chunkSize));
|
|
790
|
+
});
|
|
791
|
+
const result = await mediainfo.analyzeData(getSize, readChunk);
|
|
792
|
+
mediainfo.close();
|
|
793
|
+
const tracks = result.media?.track ?? [];
|
|
794
|
+
const general = tracks.find((t) => t["@type"] === "General");
|
|
795
|
+
const video = tracks.find((t) => t["@type"] === "Video");
|
|
796
|
+
const audio = tracks.find((t) => t["@type"] === "Audio");
|
|
797
|
+
const str = (v) => typeof v === "string" ? v : "";
|
|
798
|
+
const num = (v) => parseFloat(str(v)) || 0;
|
|
799
|
+
const hdrFormats = str(video?.["HDR_Format"] ?? video?.["transfer_characteristics"]);
|
|
800
|
+
const isHdr = hdrFormats.length > 0 && !hdrFormats.toLowerCase().includes("bt.709") && !hdrFormats.toLowerCase().includes("sdr");
|
|
801
|
+
return {
|
|
802
|
+
container: str(general?.["Format"]) || file.type || "Unknown",
|
|
803
|
+
fileSize: file.size,
|
|
804
|
+
video: video ? {
|
|
805
|
+
codec: str(video["Format"]),
|
|
806
|
+
width: num(video["Width"]),
|
|
807
|
+
height: num(video["Height"]),
|
|
808
|
+
duration: num(video["Duration"]) / 1e3,
|
|
809
|
+
bitrate: num(video["BitRate"]),
|
|
810
|
+
frameRate: num(video["FrameRate"]),
|
|
811
|
+
hdr: isHdr,
|
|
812
|
+
colorSpace: str(video["colour_primaries"]) || str(video["ColorSpace"])
|
|
813
|
+
} : null,
|
|
814
|
+
audio: audio ? {
|
|
815
|
+
codec: str(audio["Format"]),
|
|
816
|
+
channels: num(audio["Channels"]),
|
|
817
|
+
sampleRate: num(audio["SamplingRate"])
|
|
818
|
+
} : null
|
|
819
|
+
};
|
|
820
|
+
}
|
|
701
821
|
var DEFAULT_VIDEO_OPTS = {
|
|
702
822
|
thumbnails: true,
|
|
703
823
|
storyboard: false,
|
|
@@ -742,6 +862,9 @@ function VideoUploader({
|
|
|
742
862
|
const [preview, setPreview] = useState(null);
|
|
743
863
|
const [videoOpts, setVideoOpts] = useState(() => createInitialVideoOpts(video));
|
|
744
864
|
const [stagedFile, setStagedFile] = useState(null);
|
|
865
|
+
const [videoInfo, setVideoInfo] = useState(null);
|
|
866
|
+
const [videoIssues, setVideoIssues] = useState([]);
|
|
867
|
+
const [analyzing, setAnalyzing] = useState(false);
|
|
745
868
|
const t = resolveTheme(theme);
|
|
746
869
|
const vars = themeToVars(t);
|
|
747
870
|
const doUpload = useCallback(async (file, opts) => {
|
|
@@ -758,8 +881,19 @@ function VideoUploader({
|
|
|
758
881
|
if (!file) return;
|
|
759
882
|
if (showVideoOptions) {
|
|
760
883
|
setVideoOpts(createInitialVideoOpts(video));
|
|
884
|
+
setVideoInfo(null);
|
|
885
|
+
setVideoIssues([]);
|
|
761
886
|
setStagedFile(file);
|
|
762
887
|
if (showPreview) setPreview(URL.createObjectURL(file));
|
|
888
|
+
setAnalyzing(true);
|
|
889
|
+
try {
|
|
890
|
+
const info = await analyzeVideoFile(file);
|
|
891
|
+
setVideoInfo(info);
|
|
892
|
+
setVideoIssues(getCompatIssues(info));
|
|
893
|
+
} catch {
|
|
894
|
+
} finally {
|
|
895
|
+
setAnalyzing(false);
|
|
896
|
+
}
|
|
763
897
|
} else {
|
|
764
898
|
await doUpload(file, video ?? videoOpts);
|
|
765
899
|
}
|
|
@@ -802,7 +936,11 @@ function VideoUploader({
|
|
|
802
936
|
),
|
|
803
937
|
showVideoOptions && stagedFile && !isUploading && state.status !== "done" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
804
938
|
preview && /* @__PURE__ */ jsx("div", { className: "border border-slate-200 rounded-xl overflow-hidden bg-black", children: /* @__PURE__ */ jsx("video", { src: preview, className: "w-full max-h-[200px] block", muted: true, playsInline: true, controls: true }) }),
|
|
805
|
-
/* @__PURE__ */ jsxs("div", { className: "border border-slate-200 rounded-xl overflow-hidden", children: [
|
|
939
|
+
(analyzing || videoInfo) && /* @__PURE__ */ jsxs("div", { className: "border border-slate-200 rounded-xl overflow-hidden", children: [
|
|
940
|
+
/* @__PURE__ */ jsx("div", { className: "px-3.5 py-2 bg-slate-100 text-[12px] font-bold text-slate-500 tracking-[0.04em]", children: "\u{1F4CB} Informa\xE7\xF5es do arquivo" }),
|
|
941
|
+
/* @__PURE__ */ jsx("div", { className: "p-3.5", children: /* @__PURE__ */ jsx(VideoCompatAlert, { info: videoInfo, issues: videoIssues, analyzing }) })
|
|
942
|
+
] }),
|
|
943
|
+
videoIssues.length === 0 && /* @__PURE__ */ jsxs("div", { className: "border border-slate-200 rounded-xl overflow-hidden", children: [
|
|
806
944
|
/* @__PURE__ */ jsx("div", { className: "px-3.5 py-2 bg-slate-100 text-[12px] font-bold text-slate-500 tracking-[0.04em]", children: "\u{1F3AC} Configura\xE7\xF5es de v\xEDdeo" }),
|
|
807
945
|
/* @__PURE__ */ jsx(VideoOptions, { value: videoOpts, onChange: setVideoOpts, style: { padding: "12px 14px" } })
|
|
808
946
|
] }),
|
|
@@ -814,6 +952,8 @@ function VideoUploader({
|
|
|
814
952
|
onClick: () => {
|
|
815
953
|
setStagedFile(null);
|
|
816
954
|
setPreview(null);
|
|
955
|
+
setVideoInfo(null);
|
|
956
|
+
setVideoIssues([]);
|
|
817
957
|
setVideoOpts(createInitialVideoOpts(video));
|
|
818
958
|
},
|
|
819
959
|
children: "Cancelar"
|
|
@@ -822,10 +962,13 @@ function VideoUploader({
|
|
|
822
962
|
/* @__PURE__ */ jsx(
|
|
823
963
|
"button",
|
|
824
964
|
{
|
|
825
|
-
|
|
965
|
+
disabled: videoIssues.length > 0 || analyzing,
|
|
966
|
+
className: "flex-1 inline-flex items-center justify-center gap-1.5 text-sm font-bold py-2.5 px-4 rounded-xl border-transparent bg-indigo-500 text-white cursor-pointer hover:opacity-90 disabled:opacity-40 disabled:cursor-not-allowed",
|
|
826
967
|
onClick: () => {
|
|
827
968
|
const f = stagedFile;
|
|
828
969
|
setStagedFile(null);
|
|
970
|
+
setVideoInfo(null);
|
|
971
|
+
setVideoIssues([]);
|
|
829
972
|
void doUpload(f, videoOpts);
|
|
830
973
|
setVideoOpts(createInitialVideoOpts(video));
|
|
831
974
|
},
|
|
@@ -1565,7 +1708,12 @@ function Video({
|
|
|
1565
1708
|
const best = sorted.find((e) => e.px >= targetPx) ?? sorted[sorted.length - 1];
|
|
1566
1709
|
setPoster(best.url);
|
|
1567
1710
|
}, [parsed.thumbnailUrls, parsed.thumbnailSrc, playerWidth]);
|
|
1568
|
-
const
|
|
1711
|
+
const rawActiveSource = parsed.sources[sourceIndex] ?? parsed.sources[0] ?? null;
|
|
1712
|
+
const activeSourceRef = useRef(null);
|
|
1713
|
+
if (rawActiveSource?.src !== activeSourceRef.current?.src || rawActiveSource?.type !== activeSourceRef.current?.type) {
|
|
1714
|
+
activeSourceRef.current = rawActiveSource;
|
|
1715
|
+
}
|
|
1716
|
+
const activeSource = activeSourceRef.current;
|
|
1569
1717
|
const progressPercent = duration ? currentTime / duration * 100 : 0;
|
|
1570
1718
|
const bufferedPercent = duration ? bufferedTime / duration * 100 : 0;
|
|
1571
1719
|
const destroyMediaEngines = useCallback(() => {
|
|
@@ -1813,7 +1961,9 @@ function Video({
|
|
|
1813
1961
|
}, 60 * 1e3);
|
|
1814
1962
|
}
|
|
1815
1963
|
};
|
|
1816
|
-
const onWaiting = () =>
|
|
1964
|
+
const onWaiting = () => {
|
|
1965
|
+
if (video.played.length > 0) setIsLoading(true);
|
|
1966
|
+
};
|
|
1817
1967
|
const onCanPlay = () => setIsLoading(false);
|
|
1818
1968
|
const onEnded = () => setControlsVisible(true);
|
|
1819
1969
|
video.addEventListener("timeupdate", syncTime);
|
|
@@ -2098,6 +2248,7 @@ function Video({
|
|
|
2098
2248
|
const HlsModule = await import('hls.js');
|
|
2099
2249
|
if (cancelled) return;
|
|
2100
2250
|
const Hls = HlsModule.default;
|
|
2251
|
+
console.debug("[Silo/hls] Hls.isSupported()=", Hls.isSupported());
|
|
2101
2252
|
if (Hls.isSupported()) {
|
|
2102
2253
|
const hls = new Hls({
|
|
2103
2254
|
enableWorker: true,
|
|
@@ -2106,10 +2257,12 @@ function Video({
|
|
|
2106
2257
|
maxBufferSize: 20 * 1e3 * 1e3
|
|
2107
2258
|
});
|
|
2108
2259
|
hlsRef.current = hls;
|
|
2260
|
+
console.debug("[Silo/hls] loadSource", activeSource.src);
|
|
2109
2261
|
hls.loadSource(activeSource.src);
|
|
2110
2262
|
hls.attachMedia(video);
|
|
2111
2263
|
hls.on(Hls.Events.MANIFEST_PARSED, (_, data) => {
|
|
2112
2264
|
const levels = data.levels ?? hls.levels ?? [];
|
|
2265
|
+
console.debug("[Silo/hls] MANIFEST_PARSED levels=", levels.length, "audioTracks=", hls.audioTracks?.length ?? 0);
|
|
2113
2266
|
setQualities([
|
|
2114
2267
|
AUTO_QUALITY,
|
|
2115
2268
|
...levels.map((level, index) => ({
|
|
@@ -2120,6 +2273,7 @@ function Video({
|
|
|
2120
2273
|
}))
|
|
2121
2274
|
]);
|
|
2122
2275
|
const tracks = hls.audioTracks ?? [];
|
|
2276
|
+
console.debug("[Silo/hls] audio tracks:", tracks.map((t) => ({ name: t.name, lang: t.lang, url: t.url })));
|
|
2123
2277
|
if (tracks.length > 1) {
|
|
2124
2278
|
setAudioTracks(
|
|
2125
2279
|
tracks.map((t, i) => ({
|
|
@@ -2133,6 +2287,7 @@ function Video({
|
|
|
2133
2287
|
});
|
|
2134
2288
|
hls.on(Hls.Events.AUDIO_TRACKS_UPDATED, (_, data) => {
|
|
2135
2289
|
const tracks = data.audioTracks ?? [];
|
|
2290
|
+
console.debug("[Silo/hls] AUDIO_TRACKS_UPDATED", tracks.length);
|
|
2136
2291
|
if (tracks.length > 1) {
|
|
2137
2292
|
setAudioTracks(
|
|
2138
2293
|
tracks.map((t, i) => ({
|
|
@@ -2142,18 +2297,49 @@ function Video({
|
|
|
2142
2297
|
);
|
|
2143
2298
|
}
|
|
2144
2299
|
});
|
|
2300
|
+
hls.on(Hls.Events.LEVEL_LOADED, (_, data) => {
|
|
2301
|
+
console.debug("[Silo/hls] LEVEL_LOADED level=", data.level, "fragments=", data.details?.fragments?.length);
|
|
2302
|
+
});
|
|
2303
|
+
hls.on(Hls.Events.AUDIO_TRACK_LOADED, (_, data) => {
|
|
2304
|
+
console.debug("[Silo/hls] AUDIO_TRACK_LOADED track=", data.id, "fragments=", data.details?.fragments?.length);
|
|
2305
|
+
});
|
|
2306
|
+
hls.on(Hls.Events.FRAG_LOADING, (_, data) => {
|
|
2307
|
+
console.debug("[Silo/hls] FRAG_LOADING", data.frag?.type, data.frag?.url?.slice(-60));
|
|
2308
|
+
});
|
|
2309
|
+
hls.on(Hls.Events.FRAG_LOADED, (_, data) => {
|
|
2310
|
+
console.debug("[Silo/hls] FRAG_LOADED", data.frag?.type, data.frag?.sn);
|
|
2311
|
+
});
|
|
2312
|
+
let mediaErrorAttempts = 0;
|
|
2145
2313
|
hls.on(Hls.Events.ERROR, (_, data) => {
|
|
2314
|
+
const fragUrl = (data.frag?.url ?? data.url ?? "").slice(-80);
|
|
2315
|
+
const fragType = data.frag?.type ?? "?";
|
|
2316
|
+
console.debug("[Silo/hls] ERROR fatal=", data.fatal, "type=", data.type, "details=", data.details, "fragType=", fragType, "url=", fragUrl);
|
|
2146
2317
|
if (!data.fatal) return;
|
|
2147
|
-
if (data.
|
|
2318
|
+
if (data.details === Hls.ErrorDetails.FRAG_PARSING_ERROR && fragType === "audio") {
|
|
2319
|
+
console.warn("[Silo/hls] audio frag parse failed \u2014 disabling separate audio tracks and retrying");
|
|
2320
|
+
setAudioTracks([]);
|
|
2321
|
+
hls.recoverMediaError();
|
|
2322
|
+
return;
|
|
2323
|
+
}
|
|
2324
|
+
if (data.type === Hls.ErrorTypes.MEDIA_ERROR && mediaErrorAttempts < 2) {
|
|
2325
|
+
mediaErrorAttempts += 1;
|
|
2326
|
+
console.debug("[Silo/hls] recoverMediaError attempt", mediaErrorAttempts);
|
|
2148
2327
|
hls.recoverMediaError();
|
|
2149
2328
|
return;
|
|
2150
2329
|
}
|
|
2151
|
-
console.error("[Silo] HLS playback failed");
|
|
2330
|
+
console.error("[Silo] HLS playback failed", data.details, "fragType=", fragType, "url=", fragUrl);
|
|
2331
|
+
hls.destroy();
|
|
2332
|
+
hlsRef.current = null;
|
|
2152
2333
|
setIsLoading(false);
|
|
2153
2334
|
});
|
|
2335
|
+
video.addEventListener("waiting", () => console.debug("[Silo/hls] video:waiting readyState=", video.readyState, "played=", video.played.length));
|
|
2336
|
+
video.addEventListener("canplay", () => console.debug("[Silo/hls] video:canplay readyState=", video.readyState));
|
|
2337
|
+
video.addEventListener("stalled", () => console.debug("[Silo/hls] video:stalled"));
|
|
2338
|
+
video.addEventListener("suspend", () => console.debug("[Silo/hls] video:suspend"));
|
|
2154
2339
|
return;
|
|
2155
2340
|
}
|
|
2156
2341
|
if (video.canPlayType("application/vnd.apple.mpegurl")) {
|
|
2342
|
+
console.debug("[Silo/hls] using native HLS (Safari)");
|
|
2157
2343
|
video.src = activeSource.src;
|
|
2158
2344
|
video.load();
|
|
2159
2345
|
setIsLoading(false);
|