|
|
import React from 'react'; |
|
|
|
|
|
export interface SubtitleSegment { |
|
|
id: number; |
|
|
startTime: string; |
|
|
endTime: string; |
|
|
duration: string; |
|
|
sourceText: string; |
|
|
targetText?: string; |
|
|
isCurrent?: boolean; |
|
|
} |
|
|
|
|
|
export interface VideoInfo { |
|
|
title: string; |
|
|
duration: string; |
|
|
totalSegments: number; |
|
|
currentSegment: number; |
|
|
} |
|
|
|
|
|
interface SubtitlingModuleProps { |
|
|
videoInfo: VideoInfo; |
|
|
subtitleSegments: SubtitleSegment[]; |
|
|
currentDisplayedSubtitle?: string; |
|
|
onSegmentClick?: (id: number) => void; |
|
|
getSegmentButtonClass?: (id: number) => string; |
|
|
onPlayPause?: () => void; |
|
|
isPlaying?: boolean; |
|
|
onSaveSegment?: (id: number, text: string) => void; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const SubtitlingModule: React.FC<SubtitlingModuleProps> = ({ |
|
|
videoInfo, |
|
|
subtitleSegments, |
|
|
currentDisplayedSubtitle, |
|
|
onSegmentClick, |
|
|
getSegmentButtonClass, |
|
|
onPlayPause, |
|
|
isPlaying, |
|
|
}) => { |
|
|
return ( |
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8"> |
|
|
<div className="bg-white rounded-xl shadow-lg border border-gray-100 p-6"> |
|
|
<div className="mb-4"> |
|
|
<h3 className="text-xl font-bold text-gray-900 mb-2">{videoInfo.title}</h3> |
|
|
</div> |
|
|
<div className="bg-black rounded-lg aspect-video overflow-hidden relative"> |
|
|
<div className="absolute inset-0 flex items-center justify-center text-white/70 text-sm">Video Placeholder</div> |
|
|
{currentDisplayedSubtitle && ( |
|
|
<div className="absolute bottom-8 left-1/2 transform -translate-x-1/2 z-10"> |
|
|
<div className="bg-black bg-opacity-80 text-white px-6 py-3 rounded-lg text-center max-w-lg"> |
|
|
<p className={`text-base font-medium leading-relaxed tracking-wide ${currentDisplayedSubtitle.length <= 42 ? 'whitespace-nowrap' : 'whitespace-pre-line'}`}> |
|
|
{currentDisplayedSubtitle} |
|
|
</p> |
|
|
</div> |
|
|
</div> |
|
|
)} |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div className="bg-white rounded-xl shadow-lg border border-gray-100 p-6"> |
|
|
<h3 className="text-xl font-bold text-gray-900 mb-4">Translation Workspace</h3> |
|
|
<div className="grid grid-cols-9 gap-1 mb-6"> |
|
|
{subtitleSegments.map((segment) => ( |
|
|
<button |
|
|
key={segment.id} |
|
|
onClick={() => onSegmentClick && onSegmentClick(segment.id)} |
|
|
className={`px-1 py-1 rounded text-xs w-full ${getSegmentButtonClass ? getSegmentButtonClass(segment.id) : ''}`} |
|
|
> |
|
|
{segment.id} |
|
|
</button> |
|
|
))} |
|
|
</div> |
|
|
<div className="flex items-center gap-2 mb-4"> |
|
|
<button onClick={onPlayPause} className="px-3 py-1.5 text-sm rounded-md border border-gray-300"> |
|
|
{isPlaying ? 'Pause' : 'Play'} |
|
|
</button> |
|
|
<div className="text-sm text-gray-600">{videoInfo.currentSegment}/{videoInfo.totalSegments}</div> |
|
|
</div> |
|
|
<div className="space-y-4"> |
|
|
{subtitleSegments.map(seg => ( |
|
|
<div key={seg.id} className="border border-gray-200 rounded-lg p-3"> |
|
|
<div className="text-xs text-gray-500 mb-1">{seg.startTime} → {seg.endTime} ({seg.duration})</div> |
|
|
<div className="text-sm text-gray-800 mb-2">{seg.sourceText}</div> |
|
|
<textarea className="w-full border border-gray-300 rounded p-2 text-sm" defaultValue={seg.targetText || ''} rows={2} /> |
|
|
</div> |
|
|
))} |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
); |
|
|
}; |
|
|
|
|
|
export default SubtitlingModule; |
|
|
|
|
|
|
|
|
|