Spaces:
Running
Running
| /** | |
| * Video Subtitle OCR Application | |
| * Extracts hard-coded subtitles from videos using transformers.js OCR | |
| */ | |
| import { pipeline, env } from 'https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.8.0'; | |
| // Configure transformers.js | |
| env.allowLocalModels = false; | |
| env.useBrowserCache = true; | |
| // ============================================ | |
| // STATE MANAGEMENT | |
| // ============================================ | |
| const state = { | |
| // Video | |
| videoFile: null, | |
| videoDuration: 0, | |
| videoWidth: 0, | |
| videoHeight: 0, | |
| // Subtitle Box | |
| subtitleBox: { | |
| x: 10, // percentage | |
| y: 75, // percentage | |
| width: 80, // percentage | |
| height: 15, // percentage | |
| aspectRatio: 5.33 | |
| }, | |
| isDragging: false, | |
| isResizing: false, | |
| resizeHandle: null, | |
| dragStart: { x: 0, y: 0, boxX: 0, boxY: 0 }, | |
| // Language | |
| selectedLanguage: 'eng', | |
| // Time Range | |
| startTime: 0, | |
| endTime: 0, | |
| // Processing | |
| ocrPipeline: null, | |
| isProcessing: false, | |
| processingCancelled: false, | |
| // Results | |
| subtitles: [], | |
| currentSubtitleIndex: -1 | |
| }; | |
| // Language configurations for OCR | |
| const LANGUAGE_CONFIGS = { | |
| eng: { name: 'English', code: 'en' }, | |
| jpn: { name: 'Japanese', code: 'ja' }, | |
| kor: { name: 'Korean', code: 'ko' }, | |
| chi_sim: { name: 'Chinese (Simplified)', code: 'zh' }, | |
| chi_tra: { name: 'Chinese (Traditional)', code: 'zh-Hant' }, | |
| tha: { name: 'Thai', code: 'th' } | |
| }; | |
| // ============================================ | |
| // DOM ELEMENTS | |
| // ============================================ | |
| const elements = { | |
| // Pages | |
| setupPage: document.getElementById('setup-page'), | |
| processingPage: document.getElementById('processing-page'), | |
| resultsPage: document.getElementById('results-page'), | |
| // Upload | |
| uploadZone: document.getElementById('upload-zone'), | |
| videoInput: document.getElementById('video-input'), | |
| // Preview | |
| previewSection: document.getElementById('preview-section'), | |
| videoPreview: document.getElementById('video-preview'), | |
| subtitleOverlay: document.getElementById('subtitle-overlay'), | |
| subtitleBox: document.getElementById('subtitle-box'), | |
| // Controls | |
| xPosition: document.getElementById('x-position'), | |
| yPosition: document.getElementById('y-position'), | |
| width: document.getElementById('width'), | |
| height: document.getElementById('height'), | |
| xValue: document.getElementById('x-value'), | |
| yValue: document.getElementById('y-value'), | |
| widthValue: document.getElementById('width-value'), | |
| heightValue: document.getElementById('height-value'), | |
| aspectLock: document.getElementById('aspect-lock'), | |
| resetBox: document.getElementById('reset-box'), | |
| autoDetect: document.getElementById('auto-detect'), | |
| // Language | |
| languageSection: document.getElementById('language-section'), | |
| langButtons: document.querySelectorAll('.lang-btn'), | |
| // Time | |
| timeSection: document.getElementById('time-section'), | |
| startHours: document.getElementById('start-hours'), | |
| startMinutes: document.getElementById('start-minutes'), | |
| startSeconds: document.getElementById('start-seconds'), | |
| startMs: document.getElementById('start-ms'), | |
| endHours: document.getElementById('end-hours'), | |
| endMinutes: document.getElementById('end-minutes'), | |
| endSeconds: document.getElementById('end-seconds'), | |
| endMs: document.getElementById('end-ms'), | |
| setStartCurrent: document.getElementById('set-start-current'), | |
| setEndCurrent: document.getElementById('set-end-current'), | |
| timeRangeSlider: document.getElementById('time-range-slider'), | |
| rangeFill: document.getElementById('range-fill'), | |
| startThumb: document.getElementById('start-thumb'), | |
| endThumb: document.getElementById('end-thumb'), | |
| sliderStartTime: document.getElementById('slider-start-time'), | |
| sliderEndTime: document.getElementById('slider-end-time'), | |
| // Process | |
| processSection: document.getElementById('process-section'), | |
| processBtn: document.getElementById('process-btn'), | |
| // Processing | |
| progressFill: document.getElementById('progress-fill'), | |
| progressText: document.getElementById('progress-text'), | |
| progressPercent: document.getElementById('progress-percent'), | |
| frames |