import React, { useState, useRef, useEffect } from 'react';
import {
Upload,
Scissors,
Play,
Pause,
Download,
Clock,
FileVideo,
AlertCircle,
Terminal,
CheckCircle2
} from 'lucide-react';
const formatTime = (seconds) => {
const date = new Date(0);
date.setSeconds(seconds);
return date.toISOString().substr(11, 8);
};
export default function App() {
const [videoFile, setVideoFile] = useState(null);
const [videoUrl, setVideoUrl] = useState(null);
const [duration, setDuration] = useState(0);
const [splitDuration, setSplitDuration] = useState(1); // em minutos
const [segments, setSegments] = useState([]);
const [isPlaying, setIsPlaying] = useState(false);
const [currentTime, setCurrentTime] = useState(0);
const [activeSegmentIndex, setActiveSegmentIndex] = useState(null);
const [processingSegment, setProcessingSegment] = useState(null);
const [showTerminal, setShowTerminal] = useState(false);
const videoRef = useRef(null);
const mediaRecorderRef = useRef(null);
// Manipular upload de arquivo
const handleFileChange = (event) => {
const file = event.target.files[0];
if (file) {
const url = URL.createObjectURL(file);
setVideoFile(file);
setVideoUrl(url);
setSegments([]);
setActiveSegmentIndex(null);
}
};
// Carregar metadados do vídeo
const handleLoadedMetadata = () => {
if (videoRef.current) {
setDuration(videoRef.current.duration);
calculateSegments(videoRef.current.duration, splitDuration);
}
};
// Calcular segmentos quando a duração ou o tempo de corte muda
const calculateSegments = (totalDuration, minutes) => {
if (!totalDuration || !minutes) return;
const secondsPerSegment = minutes * 60;
const numberOfSegments = Math.ceil(totalDuration / secondsPerSegment);
const newSegments = [];
for (let i = 0; i < numberOfSegments; i++) {
const start = i * secondsPerSegment;
const end = Math.min((i + 1) * secondsPerSegment, totalDuration);
newSegments.push({
id: i,
label: `Parte ${i + 1}`,
start,
end,
duration: end - start
});
}
setSegments(newSegments);
};
useEffect(() => {
calculateSegments(duration, splitDuration);
}, [splitDuration, duration]);
// Atualizar tempo atual
const handleTimeUpdate = () => {
if (videoRef.current) {
const current = videoRef.current.currentTime;
setCurrentTime(current);
// Lógica para parar ao fim do segmento se estivermos reproduzindo apenas um segmento
if (activeSegmentIndex !== null) {
const segment = segments[activeSegmentIndex];
if (current >= segment.end) {
videoRef.current.pause();
setIsPlaying(false);
// Opcional: resetar para o início do segmento ou sair do modo segmento
}
}
}
};
// Reproduzir um segmento específico
const playSegment = (index) => {
if (videoRef.current) {
const segment = segments[index];
videoRef.current.currentTime = segment.start;
setActiveSegmentIndex(index);
videoRef.current.play();
setIsPlaying(true);
}
};
// Função para exportar (gravar) um segmento
const exportSegment = async (segment) => {
if (!videoRef.current) return;
setProcessingSegment(segment.id);
const video = videoRef.current;
// Setup para gravação
video.currentTime = segment.start;
// Esperar o seek
await new Promise(r => setTimeout(r, 500));
try {
// Tenta capturar o stream do elemento de vídeo
// Nota: captureStream é experimental em alguns navegadores, mas funciona bem no Chrome/FF
const stream = video.captureStream ? video.captureStream() : video.mozCaptureStream();
const mimeType = MediaRecorder.isTypeSupported('video/webm;codecs=vp9')
? 'video/webm;codecs=vp9'
: 'video/webm';
const mediaRecorder = new MediaRecorder(stream, { mimeType });
const chunks = [];
mediaRecorder.ondataavailable = (e) => {
if (e.data.size > 0) chunks.push(e.data);
};
mediaRecorder.onstop = () => {
const blob = new Blob(chunks, { type: mimeType });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${videoFile.name.split('.')[0]}_parte_${segment.id + 1}.webm`;
a.click();
URL.revokeObjectURL(url);
setProcessingSegment(null);
setActiveSegmentIndex(null);
};
mediaRecorder.start();
video.play();
// Monitorar o tempo para parar a gravação
const checkTime = () => {
if (video.currentTime >= segment.end) {
mediaRecorder.stop();
video.pause();
video.removeEventListener('timeupdate', checkTime);
}
};
video.addEventListener('timeupdate', checkTime);
} catch (err) {
console.error("Erro ao gravar:", err);
alert("Seu navegador não suporta a captura direta de vídeo (MediaRecorder API).");
setProcessingSegment(null);
}
};
// Gerar comando FFmpeg
const generateFFmpegCommand = () => {
if (!videoFile) return "";
let cmd = `# Execute isso no seu terminal para cortar sem re-codificar (super rápido):\n\n`;
segments.forEach(seg => {
const start = new Date(seg.start * 1000).toISOString().substr(11, 8);
const duration = seg.duration.toFixed(2);
cmd += `ffmpeg -i "${videoFile.name}" -ss ${start} -t ${duration} -c copy "${videoFile.name.split('.')[0]}_part${seg.id + 1}.mp4"\n`;
});
return cmd;
};
return (
{/* Header */}
{/* Coluna da Esquerda: Upload e Player */}
{/* Área de Upload ou Player */}
{!videoUrl ? (
) : (
{/* Botão de Trocar Vídeo */}
{videoFile.name}
{formatTime(duration)} de duração
)}
{/* Coluna da Direita: Configuração e Lista */}
{/* Configuração de Divisão */}
Configuração
O vídeo será dividido logicamente em {segments.length} partes.
{/* Lista de Segmentos */}
Segmentos
{segments.length === 0 ? (
Carregue um vídeo para ver os segmentos.
) : (
segments.map((segment) => (
{segment.id + 1}
{segment.label}
{formatTime(segment.start)} - {formatTime(segment.end)}
))
)}
{/* Área Avançada: Comandos FFmpeg */}
{showTerminal && segments.length > 0 && (
{generateFFmpegCommand()}
Copie e cole este código no seu terminal para cortar o vídeo original instantaneamente sem re-codificar.
)}
);
}