./kaisetsu-app/src/components/FileUpload.tsx
'use client';
import { useCallback, useState } from 'react';
import { Upload, FileVideo, Loader2 } from 'lucide-react';
export type Genre = 'variety' | 'anime' | 'news';
const GENRE_OPTIONS: { value: Genre; label: string }[] = [
{ value: 'variety', label: 'バラエティ' },
{ value: 'anime', label: 'アニメ/ドラマ' },
{ value: 'news', label: '報道/ドキュメンタリー' },
];
interface FileUploadProps {
onFileSelect: (file: File, generateNarration: boolean, tcOffset: string, genre: Genre) => void;
isAnalyzing?: boolean;
progress?: number;
analysisStep?: string;
}
export default function FileUpload({
onFileSelect,
isAnalyzing = false,
progress = 0,
analysisStep = '解析中...'
}: FileUploadProps) {
const [tcOffset, setTcOffset] = useState('09;59;45;00');
const [genre, setGenre] = useState<Genre>('variety');
const handleDragOver = useCallback((e: React.DragEvent) => {
e.preventDefault();
}, []);
const handleDragLeave = useCallback((e: React.DragEvent) => {
e.preventDefault();
}, []);
const handleDrop = useCallback(
(e: React.DragEvent) => {
e.preventDefault();
const file = e.dataTransfer.files[0];
if (file && file.type.startsWith('video/')) {
onFileSelect(file, true, tcOffset, genre);
}
},
[onFileSelect, tcOffset, genre]
);
const handleFileInput = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) {
onFileSelect(file, true, tcOffset, genre);
}
},
[onFileSelect, tcOffset, genre]
);
if (isAnalyzing) {
return (
<div className="flex flex-col items-center justify-center min-h-[400px] bg-white rounded-2xl border border-gray-200 p-8">
<div className="relative w-24 h-24 mb-6">
<div className="absolute inset-0 rounded-full border-4 border-gray-200" />
<div
className="absolute inset-0 rounded-full border-4 border-gray-900 border-t-transparent animate-spin"
/>
<div className="absolute inset-2 bg-gray-900 rounded-full flex items-center justify-center">
<Loader2 className="w-8 h-8 text-white animate-spin" />
</div>
</div>
<h3 className="text-xl font-bold text-gray-900 mb-2">
動画を解析中...
</h3>
<p className="text-gray-500 mb-4">
{analysisStep}
</p>
<div className="w-64 h-2 bg-gray-200 rounded-full overflow-hidden">
<div
className="h-full bg-gray-900 rounded-full transition-all duration-300"
style={{ width: `${progress}%` }}
/>
</div>
<p className="text-sm text-gray-400 mt-2">{progress}% 完了</p>
</div>
);
}
return (
<div
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
className="flex flex-col items-center justify-center min-h-[400px] bg-white rounded-2xl border-2 border-dashed transition-all cursor-pointer border-gray-300 hover:border-gray-400"
>
<input
type="file"
accept="video/*"
onChange={handleFileInput}
className="hidden"
id="video-upload"
/>
<label htmlFor="video-upload" className="cursor-pointer text-center">
<div className="w-20 h-20 rounded-2xl mb-6 mx-auto flex items-center justify-center bg-gray-900">
<FileVideo className="w-10 h-10 text-white" />
</div>
<h3 className="text-xl font-bold text-gray-900 mb-2">
動画ファイルをドロップ
</h3>
<p className="text-gray-500 mb-4">
または クリックしてファイルを選択
</p>
<div className="flex items-center gap-2 justify-center text-xs text-gray-400">
<span className="px-2 py-1 bg-gray-100 rounded">MP4</span>
<span className="px-2 py-1 bg-gray-100 rounded">MOV</span>
<span className="px-2 py-1 bg-gray-100 rounded">AVI</span>
<span className="px-2 py-1 bg-gray-100 rounded">MKV</span>
</div>
</label>
<div className="mt-6 flex flex-col items-center gap-4" onClick={(e) => e.stopPropagation()}>
<div className="flex items-center gap-2">
{GENRE_OPTIONS.map((opt) => (
<button
key={opt.value}
onClick={() => setGenre(opt.value)}
className={`px-4 py-2 text-sm font-medium rounded-lg border transition-colors ${
genre === opt.value
? 'bg-gray-900 text-white border-gray-900'
: 'bg-white text-gray-600 border-gray-300 hover:border-gray-400'
}`}
>
{opt.label}
</button>
))}
</div>
<div className="flex items-center gap-3">
<label className="text-sm font-medium text-gray-700">TC初期値</label>
<input
type="text"
value={tcOffset}
onChange={(e) => setTcOffset(e.target.value)}
placeholder="10;00;00;00"
className="w-36 px-3 py-1.5 text-sm font-mono border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-gray-900 text-center"
/>
</div>
</div>
</div>
);
}