arcanus commited on
Commit
a9ffeb4
·
verified ·
1 Parent(s): ba59f5e

Create subtitle.js

Browse files
Files changed (1) hide show
  1. static/js/subtitle.js +317 -0
static/js/subtitle.js ADDED
@@ -0,0 +1,317 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const SubtitleManager = {
2
+ subtitles: [],
3
+
4
+ addSubtitle: function(start, end, text) {
5
+ this.subtitles.push({
6
+ start: start,
7
+ end: end,
8
+ text: text
9
+ });
10
+ this.sortSubtitles();
11
+ this.renderSubtitleList();
12
+ this.updateParentEditor();
13
+ },
14
+
15
+ loadSubtitles: function(content) {
16
+ this.subtitles = []; // Clear existing subtitles
17
+ if (!content || typeof content !== 'string') {
18
+ console.error("Invalid subtitle content:", content);
19
+ return;
20
+ }
21
+ console.log("Loading subtitles from content:", content);
22
+
23
+ try {
24
+ // Parse VTT format
25
+ if (content.includes('WEBVTT')) {
26
+ console.log("Detected VTT format");
27
+ this.parseVTT(content);
28
+ } else {
29
+ // Assume SRT format
30
+ console.log("Assuming SRT format");
31
+ this.parseSRT(content);
32
+ }
33
+ console.log("Parsed subtitles:", this.subtitles);
34
+
35
+ if (this.subtitles.length === 0) {
36
+ console.warn("No subtitles were parsed from the content");
37
+ }
38
+
39
+ this.renderSubtitleList();
40
+ TimelineManager.render(); // Update timeline after loading subtitles
41
+ } catch (error) {
42
+ console.error("Error parsing subtitles:", error);
43
+ }
44
+ },
45
+
46
+ parseVTT: function(content) {
47
+ this.subtitles = [];
48
+ const lines = content.split('\n');
49
+ let current = null;
50
+
51
+ for (let line of lines) {
52
+ line = line.trim();
53
+
54
+ if (line.includes('-->')) {
55
+ const [start, end] = line.split('-->').map(this.timeToSeconds);
56
+ current = { start, end, text: '' };
57
+ } else if (current && line && !line.includes('WEBVTT')) {
58
+ current.text += line + '\n';
59
+ } else if (line === '' && current) {
60
+ current.text = current.text.trim();
61
+ this.subtitles.push(current);
62
+ current = null;
63
+ }
64
+ }
65
+ },
66
+
67
+ parseSRT: function(content) {
68
+ console.log("Starting SRT parsing");
69
+ this.subtitles = [];
70
+
71
+ // Normalize line endings and split into blocks
72
+ const normalizedContent = content.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
73
+ const blocks = normalizedContent.split('\n\n').filter(block => block.trim());
74
+
75
+ console.log(`Found ${blocks.length} subtitle blocks`);
76
+
77
+ for (let i = 0; i < blocks.length; i++) {
78
+ try {
79
+ const lines = blocks[i].split('\n').map(line => line.trim()).filter(line => line);
80
+ console.log(`Block ${i + 1} has ${lines.length} lines:`, lines);
81
+
82
+ // Skip invalid blocks
83
+ if (lines.length < 2) {
84
+ console.warn(`Block ${i + 1} has insufficient lines, skipping`);
85
+ continue;
86
+ }
87
+
88
+ // Find time line
89
+ const timeLine = lines.find(line => line.includes('-->'));
90
+ if (!timeLine) {
91
+ console.warn(`Block ${i + 1} has no time line, skipping`);
92
+ continue;
93
+ }
94
+
95
+ // Parse time values
96
+ const [startStr, endStr] = timeLine.split('-->').map(t => t.trim());
97
+ console.log(`Time values - Start: "${startStr}", End: "${endStr}"`);
98
+
99
+ const start = this.timeToSeconds(startStr);
100
+ const end = this.timeToSeconds(endStr);
101
+
102
+ if (isNaN(start) || isNaN(end)) {
103
+ console.warn(`Block ${i + 1} has invalid time values:`, { start, end });
104
+ continue;
105
+ }
106
+
107
+ console.log(`Converted times - Start: ${start}s, End: ${end}s`);
108
+
109
+ // Get text lines (exclude index number and time line)
110
+ const textLines = lines.filter(line =>
111
+ !line.includes('-->') &&
112
+ !line.match(/^\d+$/) &&
113
+ line.trim().length > 0
114
+ );
115
+
116
+ const text = textLines.join('\n').trim();
117
+ console.log(`Extracted text: "${text}"`);
118
+
119
+ if (text) {
120
+ this.subtitles.push({ start, end, text });
121
+ console.log(`Successfully added subtitle ${i + 1}`);
122
+ } else {
123
+ console.warn(`Block ${i + 1} has no text content, skipping`);
124
+ }
125
+ } catch (error) {
126
+ console.error(`Error parsing block ${i + 1}:`, error);
127
+ }
128
+ }
129
+
130
+ this.subtitles.sort((a, b) => a.start - b.start);
131
+ console.log(`Successfully parsed ${this.subtitles.length} subtitles`);
132
+ },
133
+
134
+ timeToSeconds: function(timeStr) {
135
+ try {
136
+ timeStr = timeStr.trim().replace(',', '.'); // Convert SRT format to decimal
137
+ console.log(`Processing time string: "${timeStr}"`);
138
+
139
+ // Handle missing milliseconds
140
+ if (!timeStr.includes('.')) {
141
+ timeStr += '.000';
142
+ }
143
+
144
+ const [time, ms] = timeStr.split('.');
145
+ const [hours, minutes, seconds] = time.split(':').map(str => {
146
+ const num = Number(str);
147
+ if (isNaN(num)) {
148
+ throw new Error(`Invalid time component: "${str}"`);
149
+ }
150
+ return num;
151
+ });
152
+
153
+ const msSeconds = ms ? Number('0.' + ms) : 0;
154
+ if (isNaN(msSeconds)) {
155
+ throw new Error(`Invalid milliseconds: "${ms}"`);
156
+ }
157
+
158
+ const totalSeconds = hours * 3600 + minutes * 60 + seconds + msSeconds;
159
+ console.log(`Converted "${timeStr}" to ${totalSeconds} seconds`);
160
+ return totalSeconds;
161
+
162
+ } catch (error) {
163
+ console.error(`Error converting time "${timeStr}":`, error);
164
+ return NaN;
165
+ }
166
+ },
167
+
168
+ secondsToTime: function(seconds) {
169
+ const date = new Date(seconds * 1000);
170
+ const hh = date.getUTCHours();
171
+ const mm = date.getUTCMinutes();
172
+ const ss = date.getUTCSeconds();
173
+ const ms = date.getUTCMilliseconds();
174
+ return `${hh.toString().padStart(2, '0')}:${mm.toString().padStart(2, '0')}:${ss.toString().padStart(2, '0')}.${ms.toString().padStart(3, '0')}`;
175
+ },
176
+
177
+ sortSubtitles: function() {
178
+ this.subtitles.sort((a, b) => a.start - b.start);
179
+ },
180
+
181
+ onTimelineUpdate: null,
182
+
183
+ setTimelineUpdateCallback: function(callback) {
184
+ this.onTimelineUpdate = callback;
185
+ },
186
+
187
+ renderSubtitleList: function() {
188
+ const listElement = document.getElementById('subtitle-list');
189
+ if (!listElement) return;
190
+
191
+ listElement.innerHTML = '';
192
+ this.sortSubtitles();
193
+
194
+ this.subtitles.forEach((subtitle, index) => {
195
+ const subtitleElement = document.createElement('div');
196
+ subtitleElement.className = 'subtitle-item';
197
+ subtitleElement.innerHTML = `
198
+ <div class="d-flex justify-content-between align-items-center">
199
+ <small class="text-muted">
200
+ ${this.secondsToTime(subtitle.start)} - ${this.secondsToTime(subtitle.end)}
201
+ </small>
202
+ <div>
203
+ <button class="btn btn-sm btn-primary edit-subtitle" data-index="${index}">
204
+ <i class="fas fa-edit"></i>
205
+ </button>
206
+ <button class="btn btn-sm btn-danger delete-subtitle" data-index="${index}">
207
+ <i class="fas fa-trash"></i>
208
+ </button>
209
+ </div>
210
+ </div>
211
+ <div class="mt-1">${subtitle.text}</div>
212
+ `;
213
+
214
+ // Add click handlers
215
+ const editBtn = subtitleElement.querySelector('.edit-subtitle');
216
+ const deleteBtn = subtitleElement.querySelector('.delete-subtitle');
217
+
218
+ editBtn.addEventListener('click', () => {
219
+ const modal = new bootstrap.Modal(document.getElementById('editSubtitleModal'));
220
+ const textArea = document.getElementById('subtitleText');
221
+ textArea.value = subtitle.text;
222
+
223
+ const saveHandler = () => {
224
+ subtitle.text = textArea.value;
225
+ this.renderSubtitleList();
226
+ this.updateParentEditor();
227
+ modal.hide();
228
+ document.getElementById('saveSubtitle').removeEventListener('click', saveHandler);
229
+ };
230
+
231
+ document.getElementById('saveSubtitle').addEventListener('click', saveHandler);
232
+ modal.show();
233
+ });
234
+
235
+ deleteBtn.addEventListener('click', () => {
236
+ if (confirm('Are you sure you want to delete this subtitle?')) {
237
+ this.subtitles.splice(index, 1);
238
+ this.renderSubtitleList();
239
+ TimelineManager.render();
240
+ this.updateParentEditor();
241
+ }
242
+ });
243
+
244
+ listElement.appendChild(subtitleElement);
245
+ });
246
+
247
+ TimelineManager.render();
248
+ },
249
+
250
+ updateParentEditor: function() {
251
+ const srt = this.exportSRT();
252
+
253
+ // Aktualizace editoru v rodičovském okně
254
+ window.parent.postMessage({
255
+ type: 'updateSubtitles',
256
+ content: srt
257
+ }, '*');
258
+
259
+ // Získání video_path ze sessionStorage a úprava cesty
260
+ const video_path = window.parent.sessionStorage.getItem('video_path');
261
+ if (!video_path) {
262
+ console.error('video_path není k dispozici v sessionStorage');
263
+ return;
264
+ }
265
+
266
+ // Odstranění '\video.mp4' a 'exports\' z cesty
267
+ const project_path = video_path.replace('\\video.mp4', '').replace('exports\\', '');
268
+ console.log('Ukládám titulky do projektu:', project_path);
269
+
270
+ // Okamžité uložení do souboru
271
+ fetch('/save_srt', {
272
+ method: 'POST',
273
+ headers: {
274
+ 'Content-Type': 'application/json'
275
+ },
276
+ body: JSON.stringify({
277
+ srt_content: srt,
278
+ project_name: project_path
279
+ })
280
+ })
281
+ .then(response => response.json())
282
+ .then(data => {
283
+ if (!data.success) {
284
+ console.error('Chyba při ukládání titulků:', data.error);
285
+ } else {
286
+ console.log('Titulky byly úspěšně uloženy do:', project_path + '/titulky.srt');
287
+ }
288
+ })
289
+ .catch(error => {
290
+ console.error('Chyba při komunikaci se serverem:', error);
291
+ });
292
+ },
293
+
294
+ exportVTT: function() {
295
+ let output = 'WEBVTT\n\n';
296
+ this.subtitles.forEach((subtitle, index) => {
297
+ output += `${index + 1}\n`;
298
+ output += `${this.secondsToTime(subtitle.start)} --> ${this.secondsToTime(subtitle.end)}\n`;
299
+ output += `${subtitle.text}\n\n`;
300
+ });
301
+ return output;
302
+ },
303
+
304
+ exportSRT: function() {
305
+ let output = '';
306
+ this.subtitles.forEach((subtitle, index) => {
307
+ output += `${index + 1}\n`;
308
+ output += `${this.secondsToTime(subtitle.start).replace('.', ',')} --> ${this.secondsToTime(subtitle.end).replace('.', ',')}\n`;
309
+ output += `${subtitle.text}\n\n`;
310
+ });
311
+ return output;
312
+ },
313
+
314
+ getSubtitles: function() {
315
+ return this.subtitles;
316
+ }
317
+ };