@geekapps/silo-elements-nextjs 0.3.7 → 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 +10 -2
- package/dist/VideoPlayer.js.map +1 -1
- package/dist/VideoUploader.js +145 -2
- package/dist/VideoUploader.js.map +1 -1
- package/dist/index.js +157 -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/MediaUploader.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useState, useCallback, useRef } from 'react';
|
|
2
2
|
import { useMultipartUpload, useBatchUpload } from '@geekapps/silo-nextjs';
|
|
3
3
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
4
|
+
import MediaInfoFactory from 'mediainfo.js';
|
|
4
5
|
|
|
5
6
|
// src/utils/theme.ts
|
|
6
7
|
var defaultTheme = {
|
|
@@ -158,7 +159,7 @@ var FORMATS = [
|
|
|
158
159
|
{ value: "png", label: "PNG", hint: "Sem perda" }
|
|
159
160
|
];
|
|
160
161
|
function ImageOptions({ value, onChange, style }) {
|
|
161
|
-
const
|
|
162
|
+
const fmt2 = value.format ?? "webp";
|
|
162
163
|
const optimize = value.optimize ?? true;
|
|
163
164
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2.5", style, children: [
|
|
164
165
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
@@ -169,7 +170,7 @@ function ImageOptions({ value, onChange, style }) {
|
|
|
169
170
|
type: "button",
|
|
170
171
|
onClick: () => onChange({ ...value, format: f.value }),
|
|
171
172
|
title: f.hint,
|
|
172
|
-
style:
|
|
173
|
+
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" },
|
|
173
174
|
children: f.label
|
|
174
175
|
},
|
|
175
176
|
f.value
|
|
@@ -693,6 +694,125 @@ function VideoOptions({ value, onChange, style }) {
|
|
|
693
694
|
)
|
|
694
695
|
] });
|
|
695
696
|
}
|
|
697
|
+
var CODEC_MESSAGES = {
|
|
698
|
+
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." },
|
|
699
|
+
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." },
|
|
700
|
+
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." },
|
|
701
|
+
"MPEG-1 Video": { title: "MPEG-1 n\xE3o \xE9 suportado", detail: "Formato muito antigo. Converta para H.264 antes de enviar." },
|
|
702
|
+
"MPEG-2 Video": { title: "MPEG-2 n\xE3o \xE9 suportado", detail: "Converta para H.264 antes de enviar." }
|
|
703
|
+
};
|
|
704
|
+
var CONTAINER_MESSAGES = {
|
|
705
|
+
FLV: { title: "Formato FLV n\xE3o suportado", detail: "Arquivos Flash Video n\xE3o s\xE3o aceitos. Converta para MP4 (H.264) antes de enviar." },
|
|
706
|
+
"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." },
|
|
707
|
+
RealMedia: { title: "Formato RealMedia n\xE3o suportado", detail: "Converta para MP4 (H.264) antes de enviar." },
|
|
708
|
+
ASF: { title: "Formato ASF/WMV n\xE3o suportado", detail: "Converta para MP4 (H.264) antes de enviar." }
|
|
709
|
+
};
|
|
710
|
+
function issueMessage(issue) {
|
|
711
|
+
if (issue.code === "unsupported_codec") {
|
|
712
|
+
return CODEC_MESSAGES[issue.codec] ?? { title: `Codec ${issue.codec} n\xE3o suportado`, detail: "Converta para H.264 ou H.265 antes de enviar." };
|
|
713
|
+
}
|
|
714
|
+
const key = Object.keys(CONTAINER_MESSAGES).find((k) => issue.container.includes(k));
|
|
715
|
+
return (key ? CONTAINER_MESSAGES[key] : null) ?? { title: `Container ${issue.container} n\xE3o suportado`, detail: "Converta para MP4 (H.264) antes de enviar." };
|
|
716
|
+
}
|
|
717
|
+
function fmt(n, unit) {
|
|
718
|
+
return `${n.toLocaleString("pt-BR")}${unit}`;
|
|
719
|
+
}
|
|
720
|
+
function fmtDuration(secs) {
|
|
721
|
+
const h = Math.floor(secs / 3600);
|
|
722
|
+
const m = Math.floor(secs % 3600 / 60);
|
|
723
|
+
const s = Math.floor(secs % 60);
|
|
724
|
+
if (h > 0) return `${h}h ${m}m ${s}s`;
|
|
725
|
+
if (m > 0) return `${m}m ${s}s`;
|
|
726
|
+
return `${s}s`;
|
|
727
|
+
}
|
|
728
|
+
function VideoCompatAlert({ info, issues, analyzing }) {
|
|
729
|
+
if (analyzing) {
|
|
730
|
+
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: [
|
|
731
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: 14 }, children: "\u{1F50D}" }),
|
|
732
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: "rgba(165,180,252,0.9)" }, children: "Analisando arquivo\u2026" })
|
|
733
|
+
] });
|
|
734
|
+
}
|
|
735
|
+
const v = info.video;
|
|
736
|
+
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
|
|
737
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", flexWrap: "wrap", gap: 6 }, children: [
|
|
738
|
+
v && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
739
|
+
/* @__PURE__ */ jsx(Chip, { label: v.codec || "?", color: "neutral" }),
|
|
740
|
+
v.width > 0 && v.height > 0 && /* @__PURE__ */ jsx(Chip, { label: `${v.width}\xD7${v.height}`, color: "neutral" }),
|
|
741
|
+
v.duration > 0 && /* @__PURE__ */ jsx(Chip, { label: fmtDuration(v.duration), color: "neutral" }),
|
|
742
|
+
v.frameRate > 0 && /* @__PURE__ */ jsx(Chip, { label: fmt(Math.round(v.frameRate), " fps"), color: "neutral" }),
|
|
743
|
+
v.hdr && /* @__PURE__ */ jsx(Chip, { label: "HDR", color: "purple" })
|
|
744
|
+
] }),
|
|
745
|
+
info.audio && /* @__PURE__ */ jsx(Chip, { label: info.audio.codec || "sem \xE1udio", color: "neutral" }),
|
|
746
|
+
/* @__PURE__ */ jsx(Chip, { label: formatBytes(info.fileSize), color: "neutral" })
|
|
747
|
+
] }),
|
|
748
|
+
issues.map((issue, i) => {
|
|
749
|
+
const msg = issueMessage(issue);
|
|
750
|
+
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: [
|
|
751
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: 14, flexShrink: 0 }, children: "\u{1F6AB}" }),
|
|
752
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
753
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: 12, fontWeight: 700, color: "#f87171", marginBottom: 2 }, children: msg.title }),
|
|
754
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: 11, color: "rgba(255,255,255,0.5)", lineHeight: 1.55 }, children: msg.detail })
|
|
755
|
+
] })
|
|
756
|
+
] }, i);
|
|
757
|
+
})
|
|
758
|
+
] });
|
|
759
|
+
}
|
|
760
|
+
function Chip({ label, color }) {
|
|
761
|
+
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)" };
|
|
762
|
+
return /* @__PURE__ */ jsx("span", { style: { ...s, fontSize: 11, fontWeight: 600, borderRadius: 6, padding: "2px 8px", display: "inline-block" }, children: label });
|
|
763
|
+
}
|
|
764
|
+
var UNSUPPORTED_CODECS = ["AV1", "VP8", "VP9", "MPEG-1 Video", "MPEG-2 Video", "Theora", "RealVideo"];
|
|
765
|
+
var UNSUPPORTED_CONTAINERS = ["FLV", "Flash Video", "RealMedia", "ASF"];
|
|
766
|
+
function getCompatIssues(info) {
|
|
767
|
+
const issues = [];
|
|
768
|
+
if (info.video && UNSUPPORTED_CODECS.includes(info.video.codec)) {
|
|
769
|
+
issues.push({ code: "unsupported_codec", codec: info.video.codec });
|
|
770
|
+
}
|
|
771
|
+
if (UNSUPPORTED_CONTAINERS.some((c) => info.container.includes(c))) {
|
|
772
|
+
issues.push({ code: "unsupported_container", container: info.container });
|
|
773
|
+
}
|
|
774
|
+
return issues;
|
|
775
|
+
}
|
|
776
|
+
async function analyzeVideoFile(file) {
|
|
777
|
+
const wasmUrl = `https://cdn.jsdelivr.net/npm/mediainfo.js@0.3.7/dist/MediaInfoModule.wasm`;
|
|
778
|
+
const mediainfo = await MediaInfoFactory({ format: "object", locateFile: () => wasmUrl });
|
|
779
|
+
const getSize = () => file.size;
|
|
780
|
+
const readChunk = (chunkSize, offset) => new Promise((resolve, reject) => {
|
|
781
|
+
const reader = new FileReader();
|
|
782
|
+
reader.onload = (e) => resolve(new Uint8Array(e.target.result));
|
|
783
|
+
reader.onerror = reject;
|
|
784
|
+
reader.readAsArrayBuffer(file.slice(offset, offset + chunkSize));
|
|
785
|
+
});
|
|
786
|
+
const result = await mediainfo.analyzeData(getSize, readChunk);
|
|
787
|
+
mediainfo.close();
|
|
788
|
+
const tracks = result.media?.track ?? [];
|
|
789
|
+
const general = tracks.find((t) => t["@type"] === "General");
|
|
790
|
+
const video = tracks.find((t) => t["@type"] === "Video");
|
|
791
|
+
const audio = tracks.find((t) => t["@type"] === "Audio");
|
|
792
|
+
const str = (v) => typeof v === "string" ? v : "";
|
|
793
|
+
const num = (v) => parseFloat(str(v)) || 0;
|
|
794
|
+
const hdrFormats = str(video?.["HDR_Format"] ?? video?.["transfer_characteristics"]);
|
|
795
|
+
const isHdr = hdrFormats.length > 0 && !hdrFormats.toLowerCase().includes("bt.709") && !hdrFormats.toLowerCase().includes("sdr");
|
|
796
|
+
return {
|
|
797
|
+
container: str(general?.["Format"]) || file.type || "Unknown",
|
|
798
|
+
fileSize: file.size,
|
|
799
|
+
video: video ? {
|
|
800
|
+
codec: str(video["Format"]),
|
|
801
|
+
width: num(video["Width"]),
|
|
802
|
+
height: num(video["Height"]),
|
|
803
|
+
duration: num(video["Duration"]) / 1e3,
|
|
804
|
+
bitrate: num(video["BitRate"]),
|
|
805
|
+
frameRate: num(video["FrameRate"]),
|
|
806
|
+
hdr: isHdr,
|
|
807
|
+
colorSpace: str(video["colour_primaries"]) || str(video["ColorSpace"])
|
|
808
|
+
} : null,
|
|
809
|
+
audio: audio ? {
|
|
810
|
+
codec: str(audio["Format"]),
|
|
811
|
+
channels: num(audio["Channels"]),
|
|
812
|
+
sampleRate: num(audio["SamplingRate"])
|
|
813
|
+
} : null
|
|
814
|
+
};
|
|
815
|
+
}
|
|
696
816
|
var DEFAULT_VIDEO_OPTS = {
|
|
697
817
|
thumbnails: true,
|
|
698
818
|
storyboard: false,
|
|
@@ -737,6 +857,9 @@ function VideoUploader({
|
|
|
737
857
|
const [preview, setPreview] = useState(null);
|
|
738
858
|
const [videoOpts, setVideoOpts] = useState(() => createInitialVideoOpts(video));
|
|
739
859
|
const [stagedFile, setStagedFile] = useState(null);
|
|
860
|
+
const [videoInfo, setVideoInfo] = useState(null);
|
|
861
|
+
const [videoIssues, setVideoIssues] = useState([]);
|
|
862
|
+
const [analyzing, setAnalyzing] = useState(false);
|
|
740
863
|
const t = resolveTheme(theme);
|
|
741
864
|
const vars = themeToVars(t);
|
|
742
865
|
const doUpload = useCallback(async (file, opts) => {
|
|
@@ -753,8 +876,19 @@ function VideoUploader({
|
|
|
753
876
|
if (!file) return;
|
|
754
877
|
if (showVideoOptions) {
|
|
755
878
|
setVideoOpts(createInitialVideoOpts(video));
|
|
879
|
+
setVideoInfo(null);
|
|
880
|
+
setVideoIssues([]);
|
|
756
881
|
setStagedFile(file);
|
|
757
882
|
if (showPreview) setPreview(URL.createObjectURL(file));
|
|
883
|
+
setAnalyzing(true);
|
|
884
|
+
try {
|
|
885
|
+
const info = await analyzeVideoFile(file);
|
|
886
|
+
setVideoInfo(info);
|
|
887
|
+
setVideoIssues(getCompatIssues(info));
|
|
888
|
+
} catch {
|
|
889
|
+
} finally {
|
|
890
|
+
setAnalyzing(false);
|
|
891
|
+
}
|
|
758
892
|
} else {
|
|
759
893
|
await doUpload(file, video ?? videoOpts);
|
|
760
894
|
}
|
|
@@ -797,7 +931,11 @@ function VideoUploader({
|
|
|
797
931
|
),
|
|
798
932
|
showVideoOptions && stagedFile && !isUploading && state.status !== "done" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
799
933
|
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 }) }),
|
|
800
|
-
/* @__PURE__ */ jsxs("div", { className: "border border-slate-200 rounded-xl overflow-hidden", children: [
|
|
934
|
+
(analyzing || videoInfo) && /* @__PURE__ */ jsxs("div", { className: "border border-slate-200 rounded-xl overflow-hidden", children: [
|
|
935
|
+
/* @__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" }),
|
|
936
|
+
/* @__PURE__ */ jsx("div", { className: "p-3.5", children: /* @__PURE__ */ jsx(VideoCompatAlert, { info: videoInfo, issues: videoIssues, analyzing }) })
|
|
937
|
+
] }),
|
|
938
|
+
videoIssues.length === 0 && /* @__PURE__ */ jsxs("div", { className: "border border-slate-200 rounded-xl overflow-hidden", children: [
|
|
801
939
|
/* @__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" }),
|
|
802
940
|
/* @__PURE__ */ jsx(VideoOptions, { value: videoOpts, onChange: setVideoOpts, style: { padding: "12px 14px" } })
|
|
803
941
|
] }),
|
|
@@ -809,6 +947,8 @@ function VideoUploader({
|
|
|
809
947
|
onClick: () => {
|
|
810
948
|
setStagedFile(null);
|
|
811
949
|
setPreview(null);
|
|
950
|
+
setVideoInfo(null);
|
|
951
|
+
setVideoIssues([]);
|
|
812
952
|
setVideoOpts(createInitialVideoOpts(video));
|
|
813
953
|
},
|
|
814
954
|
children: "Cancelar"
|
|
@@ -817,10 +957,13 @@ function VideoUploader({
|
|
|
817
957
|
/* @__PURE__ */ jsx(
|
|
818
958
|
"button",
|
|
819
959
|
{
|
|
820
|
-
|
|
960
|
+
disabled: videoIssues.length > 0 || analyzing,
|
|
961
|
+
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",
|
|
821
962
|
onClick: () => {
|
|
822
963
|
const f = stagedFile;
|
|
823
964
|
setStagedFile(null);
|
|
965
|
+
setVideoInfo(null);
|
|
966
|
+
setVideoIssues([]);
|
|
824
967
|
void doUpload(f, videoOpts);
|
|
825
968
|
setVideoOpts(createInitialVideoOpts(video));
|
|
826
969
|
},
|