11import { Router } from 'express' ;
22import { join } from 'path' ;
3- import { translateDocument } from '../services/deepl.js' ;
3+ import { translateDocument , translateLargeDocument } from '../services/deepl.js' ;
44import { extractSubtitles } from '../services/ffmpeg.js' ;
55import { DATA_DIR , MOVIES_DIR } from '../utils/paths.js' ;
6+ import fs from 'fs' ;
67
78const router = Router ( ) ;
9+ const MAX_FILE_SIZE = 130 * 1024 ; // 130KB as a safe threshold (below the 150KB limit)
810
911router . 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