Skip to content

Commit 9cf4c19

Browse files
committed
refactor: enhance error handling and add support for large file translations in subtitle processing
1 parent be7d96a commit 9cf4c19

File tree

7 files changed

+571
-99
lines changed

7 files changed

+571
-99
lines changed

package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/routes/subtitle.js

Lines changed: 100 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { Router } from 'express';
22
import { join } from 'path';
3-
import { translateDocument } from '../services/deepl.js';
3+
import { translateDocument, translateLargeDocument } from '../services/deepl.js';
44
import { extractSubtitles } from '../services/ffmpeg.js';
55
import { DATA_DIR, MOVIES_DIR } from '../utils/paths.js';
6+
import fs from 'fs';
67

78
const router = Router();
9+
const MAX_FILE_SIZE = 130 * 1024; // 130KB as a safe threshold (below the 150KB limit)
810

911
router.post('/translate_subtitle', async (req, res) => {
1012
try {
@@ -20,31 +22,89 @@ router.post('/translate_subtitle', async (req, res) => {
2022
if (!process.env.DEEPL_API_KEY) {
2123
return res.status(400).json({ message: 'DeepL API key not configured' });
2224
}
25+
26+
let subtitlePath;
2327
if (type === 'series') {
24-
const subtitlePath = join(DATA_DIR, serie, season, srt_path);
25-
const translatedPath = await translateDocument(
28+
subtitlePath = join(DATA_DIR, serie, season, srt_path);
29+
} else {
30+
subtitlePath = join(MOVIES_DIR, serie, srt_path);
31+
}
32+
33+
// Check the file size
34+
const stats = fs.statSync(subtitlePath);
35+
let translatedPath;
36+
37+
if (stats.size > MAX_FILE_SIZE) {
38+
console.log(`Large SRT file detected (${Math.round(stats.size/1024)}KB). Using large file translation method.`);
39+
translatedPath = await translateLargeDocument(
2640
subtitlePath,
2741
source_lang,
2842
target_lang
2943
);
30-
31-
res.json({ path: translatedPath });
32-
}
33-
else
34-
{
35-
const subtitlePath = join(MOVIES_DIR, serie, srt_path);
36-
const translatedPath = await translateDocument(
44+
} else {
45+
translatedPath = await translateDocument(
3746
subtitlePath,
3847
source_lang,
3948
target_lang
4049
);
41-
42-
res.json({ path: translatedPath });
4350
}
51+
52+
res.json({ path: translatedPath });
4453
} catch (error) {
45-
res.status(500).json({
46-
message: error.message,
54+
console.error('Error in subtitle translation:', error);
55+
56+
// Format status code based on the error
57+
let statusCode = 500;
58+
if (error.status) {
59+
statusCode = error.status;
60+
}
61+
62+
// Specific handling for size errors
63+
if (error.message && error.message.includes('exceeds the size limit')) {
64+
return res.status(statusCode).json({
65+
message: 'The SRT file is too large to translate in a single operation. Please try again so it can be processed in smaller parts.',
66+
originalError: error.toString(),
67+
isLargeFile: true
68+
});
69+
}
70+
71+
// Authentication errors
72+
if (error.response && error.response.status === 403) {
73+
return res.status(403).json({
74+
message: 'Authentication failed with DeepL API. Please check your API key.',
75+
originalError: error.toString()
76+
});
77+
}
78+
79+
// API limit errors
80+
if (error.message && (error.message.includes('quota') || error.message.includes('limit'))) {
81+
return res.status(statusCode).json({
82+
message: 'DeepL API quota exceeded. Please try again later or upgrade your DeepL subscription.',
83+
originalError: error.toString()
84+
});
85+
}
86+
87+
// Handle DeepL-specific errors with more details
88+
if (error.deeplRequest) {
89+
// This is a enhanced error from our DeepL service
90+
return res.status(statusCode).json({
91+
message: `DeepL translation failed: ${error.message}`,
92+
originalError: error.toString(),
93+
apiDetails: error.apiDetails || null,
94+
deeplRequest: error.deeplRequest,
95+
status: error.status || 500,
96+
stack: error.stack
97+
});
98+
}
99+
100+
// Format a user-friendly error message with technical details
101+
const userMessage = 'Translation failed: ' + (error.message || 'Unknown error');
102+
103+
res.status(statusCode).json({
104+
message: userMessage,
47105
originalError: error.toString(),
106+
apiDetails: error.apiDetails || null,
107+
status: error.status || 500,
48108
stack: error.stack
49109
});
50110
}
@@ -61,26 +121,37 @@ router.post('/extract_subtitles', async (req, res) => {
61121
return res.status(400).json({ error: 'Missing parameters' });
62122
}
63123

124+
let videoPath, outputPath;
64125
if (type === 'series') {
65-
const videoPath = join(DATA_DIR, serie, season, video_file);
126+
videoPath = join(DATA_DIR, serie, season, video_file);
66127
const videoFileName = video_file.substring(0, video_file.lastIndexOf('.'));
67-
const outputPath = join(DATA_DIR, serie, season, `${videoFileName}.${source_lang.toLowerCase()}.srt`);
68-
69-
await extractSubtitles(videoPath, outputPath);
70-
res.json({ path: outputPath });
71-
}
72-
else
73-
{
74-
const videoPath = join(MOVIES_DIR, serie, video_file);
128+
outputPath = join(DATA_DIR, serie, season, `${videoFileName}.${source_lang.toLowerCase()}.srt`);
129+
} else {
130+
videoPath = join(MOVIES_DIR, serie, video_file);
75131
const videoFileName = video_file.substring(0, video_file.lastIndexOf('.'));
76-
const outputPath = join(MOVIES_DIR, serie, `${videoFileName}.${source_lang.toLowerCase()}.srt`);
77-
78-
await extractSubtitles(videoPath, outputPath);
79-
res.json({ path: outputPath });
132+
outputPath = join(MOVIES_DIR, serie, `${videoFileName}.${source_lang.toLowerCase()}.srt`);
80133
}
134+
135+
await extractSubtitles(videoPath, outputPath);
136+
res.json({ path: outputPath });
81137
} catch (error) {
82-
res.status(500).json({
83-
message: error.message,
138+
console.error('Error extracting subtitles:', error);
139+
140+
// Format a user-friendly error message with technical details
141+
let userMessage = 'Failed to extract subtitles: ' + (error.message || 'Unknown error');
142+
let statusCode = 500;
143+
144+
// Handle specific ffmpeg errors
145+
if (error.message && error.message.includes('No such file')) {
146+
userMessage = 'Video file not found or inaccessible';
147+
statusCode = 404;
148+
} else if (error.message && error.message.includes('No subtitle')) {
149+
userMessage = 'No subtitles found in this video file';
150+
statusCode = 422;
151+
}
152+
153+
res.status(statusCode).json({
154+
message: userMessage,
84155
originalError: error.toString(),
85156
stack: error.stack
86157
});

0 commit comments

Comments
 (0)