@geekapps/silo-elements-nextjs 0.3.7 → 0.3.9
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 +149 -4
- package/dist/MediaUploader.js.map +1 -1
- package/dist/VideoPlayer.js +10 -2
- package/dist/VideoPlayer.js.map +1 -1
- package/dist/VideoUploader.js +147 -2
- package/dist/VideoUploader.js.map +1 -1
- package/dist/index.js +159 -6
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +48 -48
- package/styles.css +1 -1
package/dist/index.js
CHANGED
|
@@ -163,7 +163,7 @@ var FORMATS = [
|
|
|
163
163
|
{ value: "png", label: "PNG", hint: "Sem perda" }
|
|
164
164
|
];
|
|
165
165
|
function ImageOptions({ value, onChange, style }) {
|
|
166
|
-
const
|
|
166
|
+
const fmt2 = value.format ?? "webp";
|
|
167
167
|
const optimize = value.optimize ?? true;
|
|
168
168
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2.5", style, children: [
|
|
169
169
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
@@ -174,7 +174,7 @@ function ImageOptions({ value, onChange, style }) {
|
|
|
174
174
|
type: "button",
|
|
175
175
|
onClick: () => onChange({ ...value, format: f.value }),
|
|
176
176
|
title: f.hint,
|
|
177
|
-
style:
|
|
177
|
+
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
178
|
children: f.label
|
|
179
179
|
},
|
|
180
180
|
f.value
|
|
@@ -698,6 +698,128 @@ function VideoOptions({ value, onChange, style }) {
|
|
|
698
698
|
)
|
|
699
699
|
] });
|
|
700
700
|
}
|
|
701
|
+
var CODEC_MESSAGES = {
|
|
702
|
+
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." },
|
|
703
|
+
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." },
|
|
704
|
+
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." },
|
|
705
|
+
"MPEG-1 Video": { title: "MPEG-1 n\xE3o \xE9 suportado", detail: "Formato muito antigo. Converta para H.264 antes de enviar." },
|
|
706
|
+
"MPEG-2 Video": { title: "MPEG-2 n\xE3o \xE9 suportado", detail: "Converta para H.264 antes de enviar." }
|
|
707
|
+
};
|
|
708
|
+
var CONTAINER_MESSAGES = {
|
|
709
|
+
FLV: { title: "Formato FLV n\xE3o suportado", detail: "Arquivos Flash Video n\xE3o s\xE3o aceitos. Converta para MP4 (H.264) antes de enviar." },
|
|
710
|
+
"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." },
|
|
711
|
+
RealMedia: { title: "Formato RealMedia n\xE3o suportado", detail: "Converta para MP4 (H.264) antes de enviar." },
|
|
712
|
+
ASF: { title: "Formato ASF/WMV n\xE3o suportado", detail: "Converta para MP4 (H.264) antes de enviar." }
|
|
713
|
+
};
|
|
714
|
+
function issueMessage(issue) {
|
|
715
|
+
if (issue.code === "unsupported_codec") {
|
|
716
|
+
return CODEC_MESSAGES[issue.codec] ?? { title: `Codec ${issue.codec} n\xE3o suportado`, detail: "Converta para H.264 ou H.265 antes de enviar." };
|
|
717
|
+
}
|
|
718
|
+
const key = Object.keys(CONTAINER_MESSAGES).find((k) => issue.container.includes(k));
|
|
719
|
+
return (key ? CONTAINER_MESSAGES[key] : null) ?? { title: `Container ${issue.container} n\xE3o suportado`, detail: "Converta para MP4 (H.264) antes de enviar." };
|
|
720
|
+
}
|
|
721
|
+
function fmt(n, unit) {
|
|
722
|
+
return `${n.toLocaleString("pt-BR")}${unit}`;
|
|
723
|
+
}
|
|
724
|
+
function fmtDuration(secs) {
|
|
725
|
+
const h = Math.floor(secs / 3600);
|
|
726
|
+
const m = Math.floor(secs % 3600 / 60);
|
|
727
|
+
const s = Math.floor(secs % 60);
|
|
728
|
+
if (h > 0) return `${h}h ${m}m ${s}s`;
|
|
729
|
+
if (m > 0) return `${m}m ${s}s`;
|
|
730
|
+
return `${s}s`;
|
|
731
|
+
}
|
|
732
|
+
function VideoCompatAlert({ info, issues, analyzing }) {
|
|
733
|
+
if (analyzing) {
|
|
734
|
+
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: [
|
|
735
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: 14 }, children: "\u{1F50D}" }),
|
|
736
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: "rgba(165,180,252,0.9)" }, children: "Analisando arquivo\u2026" })
|
|
737
|
+
] });
|
|
738
|
+
}
|
|
739
|
+
const v = info.video;
|
|
740
|
+
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
|
|
741
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", flexWrap: "wrap", gap: 6 }, children: [
|
|
742
|
+
v && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
743
|
+
/* @__PURE__ */ jsx(Chip, { label: v.codec || "?", color: "neutral" }),
|
|
744
|
+
v.width > 0 && v.height > 0 && /* @__PURE__ */ jsx(Chip, { label: `${v.width}\xD7${v.height}`, color: "neutral" }),
|
|
745
|
+
v.duration > 0 && /* @__PURE__ */ jsx(Chip, { label: fmtDuration(v.duration), color: "neutral" }),
|
|
746
|
+
v.frameRate > 0 && /* @__PURE__ */ jsx(Chip, { label: fmt(Math.round(v.frameRate), " fps"), color: "neutral" }),
|
|
747
|
+
v.hdr && /* @__PURE__ */ jsx(Chip, { label: "HDR", color: "purple" })
|
|
748
|
+
] }),
|
|
749
|
+
info.audio && /* @__PURE__ */ jsx(Chip, { label: info.audio.codec || "sem \xE1udio", color: "neutral" }),
|
|
750
|
+
/* @__PURE__ */ jsx(Chip, { label: formatBytes(info.fileSize), color: "neutral" })
|
|
751
|
+
] }),
|
|
752
|
+
issues.map((issue, i) => {
|
|
753
|
+
const msg = issueMessage(issue);
|
|
754
|
+
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: [
|
|
755
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: 14, flexShrink: 0 }, children: "\u{1F6AB}" }),
|
|
756
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
757
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: 12, fontWeight: 700, color: "#f87171", marginBottom: 2 }, children: msg.title }),
|
|
758
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: 11, color: "rgba(255,255,255,0.5)", lineHeight: 1.55 }, children: msg.detail })
|
|
759
|
+
] })
|
|
760
|
+
] }, i);
|
|
761
|
+
})
|
|
762
|
+
] });
|
|
763
|
+
}
|
|
764
|
+
function Chip({ label, color }) {
|
|
765
|
+
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)" };
|
|
766
|
+
return /* @__PURE__ */ jsx("span", { style: { ...s, fontSize: 11, fontWeight: 600, borderRadius: 6, padding: "2px 8px", display: "inline-block" }, children: label });
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// src/utils/videoInfo.ts
|
|
770
|
+
var UNSUPPORTED_CODECS = ["AV1", "VP8", "VP9", "MPEG-1 Video", "MPEG-2 Video", "Theora", "RealVideo"];
|
|
771
|
+
var UNSUPPORTED_CONTAINERS = ["FLV", "Flash Video", "RealMedia", "ASF"];
|
|
772
|
+
function getCompatIssues(info) {
|
|
773
|
+
const issues = [];
|
|
774
|
+
if (info.video && UNSUPPORTED_CODECS.includes(info.video.codec)) {
|
|
775
|
+
issues.push({ code: "unsupported_codec", codec: info.video.codec });
|
|
776
|
+
}
|
|
777
|
+
if (UNSUPPORTED_CONTAINERS.some((c) => info.container.includes(c))) {
|
|
778
|
+
issues.push({ code: "unsupported_container", container: info.container });
|
|
779
|
+
}
|
|
780
|
+
return issues;
|
|
781
|
+
}
|
|
782
|
+
async function analyzeVideoFile(file) {
|
|
783
|
+
const wasmUrl = `https://cdn.jsdelivr.net/npm/mediainfo.js@0.3.7/dist/MediaInfoModule.wasm`;
|
|
784
|
+
const { default: MediaInfoFactory } = await import('mediainfo.js');
|
|
785
|
+
const mediainfo = await MediaInfoFactory({ format: "object", locateFile: () => wasmUrl });
|
|
786
|
+
const getSize = () => file.size;
|
|
787
|
+
const readChunk = (chunkSize, offset) => new Promise((resolve, reject) => {
|
|
788
|
+
const reader = new FileReader();
|
|
789
|
+
reader.onload = (e) => resolve(new Uint8Array(e.target.result));
|
|
790
|
+
reader.onerror = reject;
|
|
791
|
+
reader.readAsArrayBuffer(file.slice(offset, offset + chunkSize));
|
|
792
|
+
});
|
|
793
|
+
const result = await mediainfo.analyzeData(getSize, readChunk);
|
|
794
|
+
mediainfo.close();
|
|
795
|
+
const tracks = result.media?.track ?? [];
|
|
796
|
+
const general = tracks.find((t) => t["@type"] === "General");
|
|
797
|
+
const video = tracks.find((t) => t["@type"] === "Video");
|
|
798
|
+
const audio = tracks.find((t) => t["@type"] === "Audio");
|
|
799
|
+
const str = (v) => typeof v === "string" ? v : "";
|
|
800
|
+
const num = (v) => parseFloat(str(v)) || 0;
|
|
801
|
+
const hdrFormats = str(video?.["HDR_Format"] ?? video?.["transfer_characteristics"]);
|
|
802
|
+
const isHdr = hdrFormats.length > 0 && !hdrFormats.toLowerCase().includes("bt.709") && !hdrFormats.toLowerCase().includes("sdr");
|
|
803
|
+
return {
|
|
804
|
+
container: str(general?.["Format"]) || file.type || "Unknown",
|
|
805
|
+
fileSize: file.size,
|
|
806
|
+
video: video ? {
|
|
807
|
+
codec: str(video["Format"]),
|
|
808
|
+
width: num(video["Width"]),
|
|
809
|
+
height: num(video["Height"]),
|
|
810
|
+
duration: num(video["Duration"]) / 1e3,
|
|
811
|
+
bitrate: num(video["BitRate"]),
|
|
812
|
+
frameRate: num(video["FrameRate"]),
|
|
813
|
+
hdr: isHdr,
|
|
814
|
+
colorSpace: str(video["colour_primaries"]) || str(video["ColorSpace"])
|
|
815
|
+
} : null,
|
|
816
|
+
audio: audio ? {
|
|
817
|
+
codec: str(audio["Format"]),
|
|
818
|
+
channels: num(audio["Channels"]),
|
|
819
|
+
sampleRate: num(audio["SamplingRate"])
|
|
820
|
+
} : null
|
|
821
|
+
};
|
|
822
|
+
}
|
|
701
823
|
var DEFAULT_VIDEO_OPTS = {
|
|
702
824
|
thumbnails: true,
|
|
703
825
|
storyboard: false,
|
|
@@ -742,6 +864,9 @@ function VideoUploader({
|
|
|
742
864
|
const [preview, setPreview] = useState(null);
|
|
743
865
|
const [videoOpts, setVideoOpts] = useState(() => createInitialVideoOpts(video));
|
|
744
866
|
const [stagedFile, setStagedFile] = useState(null);
|
|
867
|
+
const [videoInfo, setVideoInfo] = useState(null);
|
|
868
|
+
const [videoIssues, setVideoIssues] = useState([]);
|
|
869
|
+
const [analyzing, setAnalyzing] = useState(false);
|
|
745
870
|
const t = resolveTheme(theme);
|
|
746
871
|
const vars = themeToVars(t);
|
|
747
872
|
const doUpload = useCallback(async (file, opts) => {
|
|
@@ -758,8 +883,19 @@ function VideoUploader({
|
|
|
758
883
|
if (!file) return;
|
|
759
884
|
if (showVideoOptions) {
|
|
760
885
|
setVideoOpts(createInitialVideoOpts(video));
|
|
886
|
+
setVideoInfo(null);
|
|
887
|
+
setVideoIssues([]);
|
|
761
888
|
setStagedFile(file);
|
|
762
889
|
if (showPreview) setPreview(URL.createObjectURL(file));
|
|
890
|
+
setAnalyzing(true);
|
|
891
|
+
try {
|
|
892
|
+
const info = await analyzeVideoFile(file);
|
|
893
|
+
setVideoInfo(info);
|
|
894
|
+
setVideoIssues(getCompatIssues(info));
|
|
895
|
+
} catch {
|
|
896
|
+
} finally {
|
|
897
|
+
setAnalyzing(false);
|
|
898
|
+
}
|
|
763
899
|
} else {
|
|
764
900
|
await doUpload(file, video ?? videoOpts);
|
|
765
901
|
}
|
|
@@ -802,7 +938,11 @@ function VideoUploader({
|
|
|
802
938
|
),
|
|
803
939
|
showVideoOptions && stagedFile && !isUploading && state.status !== "done" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
804
940
|
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: [
|
|
941
|
+
(analyzing || videoInfo) && /* @__PURE__ */ jsxs("div", { className: "border border-slate-200 rounded-xl overflow-hidden", children: [
|
|
942
|
+
/* @__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" }),
|
|
943
|
+
/* @__PURE__ */ jsx("div", { className: "p-3.5", children: /* @__PURE__ */ jsx(VideoCompatAlert, { info: videoInfo, issues: videoIssues, analyzing }) })
|
|
944
|
+
] }),
|
|
945
|
+
videoIssues.length === 0 && /* @__PURE__ */ jsxs("div", { className: "border border-slate-200 rounded-xl overflow-hidden", children: [
|
|
806
946
|
/* @__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
947
|
/* @__PURE__ */ jsx(VideoOptions, { value: videoOpts, onChange: setVideoOpts, style: { padding: "12px 14px" } })
|
|
808
948
|
] }),
|
|
@@ -814,6 +954,8 @@ function VideoUploader({
|
|
|
814
954
|
onClick: () => {
|
|
815
955
|
setStagedFile(null);
|
|
816
956
|
setPreview(null);
|
|
957
|
+
setVideoInfo(null);
|
|
958
|
+
setVideoIssues([]);
|
|
817
959
|
setVideoOpts(createInitialVideoOpts(video));
|
|
818
960
|
},
|
|
819
961
|
children: "Cancelar"
|
|
@@ -822,10 +964,13 @@ function VideoUploader({
|
|
|
822
964
|
/* @__PURE__ */ jsx(
|
|
823
965
|
"button",
|
|
824
966
|
{
|
|
825
|
-
|
|
967
|
+
disabled: videoIssues.length > 0 || analyzing,
|
|
968
|
+
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
969
|
onClick: () => {
|
|
827
970
|
const f = stagedFile;
|
|
828
971
|
setStagedFile(null);
|
|
972
|
+
setVideoInfo(null);
|
|
973
|
+
setVideoIssues([]);
|
|
829
974
|
void doUpload(f, videoOpts);
|
|
830
975
|
setVideoOpts(createInitialVideoOpts(video));
|
|
831
976
|
},
|
|
@@ -2168,15 +2313,23 @@ function Video({
|
|
|
2168
2313
|
});
|
|
2169
2314
|
let mediaErrorAttempts = 0;
|
|
2170
2315
|
hls.on(Hls.Events.ERROR, (_, data) => {
|
|
2171
|
-
|
|
2316
|
+
const fragUrl = (data.frag?.url ?? data.url ?? "").slice(-80);
|
|
2317
|
+
const fragType = data.frag?.type ?? "?";
|
|
2318
|
+
console.debug("[Silo/hls] ERROR fatal=", data.fatal, "type=", data.type, "details=", data.details, "fragType=", fragType, "url=", fragUrl);
|
|
2172
2319
|
if (!data.fatal) return;
|
|
2320
|
+
if (data.details === Hls.ErrorDetails.FRAG_PARSING_ERROR && fragType === "audio") {
|
|
2321
|
+
console.warn("[Silo/hls] audio frag parse failed \u2014 disabling separate audio tracks and retrying");
|
|
2322
|
+
setAudioTracks([]);
|
|
2323
|
+
hls.recoverMediaError();
|
|
2324
|
+
return;
|
|
2325
|
+
}
|
|
2173
2326
|
if (data.type === Hls.ErrorTypes.MEDIA_ERROR && mediaErrorAttempts < 2) {
|
|
2174
2327
|
mediaErrorAttempts += 1;
|
|
2175
2328
|
console.debug("[Silo/hls] recoverMediaError attempt", mediaErrorAttempts);
|
|
2176
2329
|
hls.recoverMediaError();
|
|
2177
2330
|
return;
|
|
2178
2331
|
}
|
|
2179
|
-
console.error("[Silo] HLS playback failed", data.details);
|
|
2332
|
+
console.error("[Silo] HLS playback failed", data.details, "fragType=", fragType, "url=", fragUrl);
|
|
2180
2333
|
hls.destroy();
|
|
2181
2334
|
hlsRef.current = null;
|
|
2182
2335
|
setIsLoading(false);
|