@@ -22,24 +22,84 @@ function extractFileId(driveUrl) {
2222
2323async function downloadFile ( fileId , progressCallback = null ) {
2424 try {
25- const downloadUrl = `https://drive.google.com/uc?export=download&id=${ fileId } ` ;
26-
2725 const tempFilename = `temp_${ Date . now ( ) } _${ Math . random ( ) . toString ( 36 ) . substr ( 2 , 9 ) } ` ;
2826 const tempPath = path . join ( paths . videos , tempFilename ) ;
2927
30- const response = await axios ( {
31- method : 'GET' ,
32- url : downloadUrl ,
33- responseType : 'stream' ,
34- timeout : 300000 ,
35- headers : {
36- 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
28+ let response ;
29+ let retryCount = 0 ;
30+ const maxRetries = 3 ;
31+ let downloadUrl = `https://drive.google.com/uc?export=download&id=${ fileId } &confirm=t` ;
32+
33+ while ( retryCount < maxRetries ) {
34+ try {
35+ console . log ( `Attempting download from: ${ downloadUrl } ` ) ;
36+
37+ const headResponse = await axios . head ( downloadUrl , {
38+ timeout : 30000 ,
39+ maxRedirects : 10 ,
40+ headers : {
41+ 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
42+ }
43+ } ) ;
44+
45+ const contentType = headResponse . headers [ 'content-type' ] || '' ;
46+ console . log ( `Content-Type: ${ contentType } ` ) ;
47+
48+ if ( contentType . includes ( 'text/html' ) ) {
49+ console . log ( 'Received HTML response, trying alternative download method...' ) ;
50+ downloadUrl = `https://drive.usercontent.google.com/download?id=${ fileId } &export=download&authuser=0&confirm=t` ;
51+
52+ if ( retryCount === 1 ) {
53+ downloadUrl = `https://docs.google.com/uc?export=download&id=${ fileId } &confirm=t` ;
54+ }
55+
56+ retryCount ++ ;
57+ if ( retryCount >= maxRetries ) {
58+ throw new Error ( 'File appears to be private or requires additional authentication. Please ensure the file is publicly accessible.' ) ;
59+ }
60+ continue ;
61+ }
62+
63+ response = await axios ( {
64+ method : 'GET' ,
65+ url : downloadUrl ,
66+ responseType : 'stream' ,
67+ timeout : 600000 ,
68+ maxRedirects : 10 ,
69+ headers : {
70+ 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' ,
71+ 'Accept' : '*/*' ,
72+ 'Accept-Language' : 'en-US,en;q=0.9' ,
73+ 'Accept-Encoding' : 'gzip, deflate, br' ,
74+ 'Connection' : 'keep-alive' ,
75+ 'Upgrade-Insecure-Requests' : '1'
76+ }
77+ } ) ;
78+ break ;
79+ } catch ( error ) {
80+ retryCount ++ ;
81+ console . log ( `Download attempt ${ retryCount } failed:` , error . message ) ;
82+
83+ if ( retryCount >= maxRetries ) {
84+ throw error ;
85+ }
86+
87+ if ( error . code === 'ENOTFOUND' || error . code === 'ECONNRESET' ) {
88+ downloadUrl = `https://drive.usercontent.google.com/download?id=${ fileId } &export=download&authuser=0&confirm=t` ;
89+ }
90+
91+ await new Promise ( resolve => setTimeout ( resolve , 2000 * retryCount ) ) ;
3792 }
38- } ) ;
93+ }
3994
4095 if ( response . status !== 200 ) {
4196 throw new Error ( `HTTP ${ response . status } : Failed to download file` ) ;
4297 }
98+
99+ const responseContentType = response . headers [ 'content-type' ] || '' ;
100+ if ( responseContentType . includes ( 'text/html' ) ) {
101+ throw new Error ( 'Received HTML page instead of video file. The file might be private or require additional permissions.' ) ;
102+ }
43103
44104 const totalSize = parseInt ( response . headers [ 'content-length' ] || '0' ) ;
45105 let downloadedSize = 0 ;
@@ -78,7 +138,57 @@ async function downloadFile(fileId, progressCallback = null) {
78138
79139 if ( fileSize === 0 ) {
80140 fs . unlinkSync ( tempPath ) ;
81- reject ( new Error ( 'Downloaded file is empty. File might be private or not accessible.' ) ) ;
141+ reject ( new Error ( 'Downloaded file is empty. The file might be private, not accessible, or the link is invalid.' ) ) ;
142+ return ;
143+ }
144+
145+ if ( fileSize < 1024 ) {
146+ fs . unlinkSync ( tempPath ) ;
147+ reject ( new Error ( 'Downloaded file is too small to be a valid video. Please check if the Google Drive link is correct and the file is publicly accessible.' ) ) ;
148+ return ;
149+ }
150+
151+ const buffer = Buffer . alloc ( 512 ) ;
152+ const fd = fs . openSync ( tempPath , 'r' ) ;
153+ fs . readSync ( fd , buffer , 0 , 512 , 0 ) ;
154+ fs . closeSync ( fd ) ;
155+
156+ const fileHeader = buffer . toString ( 'utf8' , 0 , 100 ) . toLowerCase ( ) ;
157+
158+ if ( fileHeader . includes ( '<!doctype html' ) || fileHeader . includes ( '<html' ) || fileHeader . includes ( '<head>' ) ) {
159+ fs . unlinkSync ( tempPath ) ;
160+ reject ( new Error ( 'Downloaded content is an HTML page, not a video file. The file might be private, require authentication, or the sharing settings are incorrect.' ) ) ;
161+ return ;
162+ }
163+
164+ const validVideoHeaders = [
165+ [ 0x00 , 0x00 , 0x00 , 0x18 , 0x66 , 0x74 , 0x79 , 0x70 ] ,
166+ [ 0x00 , 0x00 , 0x00 , 0x1C , 0x66 , 0x74 , 0x79 , 0x70 ] ,
167+ [ 0x00 , 0x00 , 0x00 , 0x20 , 0x66 , 0x74 , 0x79 , 0x70 ] ,
168+ [ 0x1A , 0x45 , 0xDF , 0xA3 ] ,
169+ [ 0x00 , 0x00 , 0x01 , 0xBA ] ,
170+ [ 0x00 , 0x00 , 0x01 , 0xB3 ] ,
171+ [ 0x46 , 0x4C , 0x56 , 0x01 ]
172+ ] ;
173+
174+ let isValidVideo = false ;
175+ for ( const header of validVideoHeaders ) {
176+ let matches = true ;
177+ for ( let i = 0 ; i < header . length && i < buffer . length ; i ++ ) {
178+ if ( buffer [ i ] !== header [ i ] ) {
179+ matches = false ;
180+ break ;
181+ }
182+ }
183+ if ( matches ) {
184+ isValidVideo = true ;
185+ break ;
186+ }
187+ }
188+
189+ if ( ! isValidVideo && ! buffer . includes ( Buffer . from ( 'ftyp' ) ) ) {
190+ fs . unlinkSync ( tempPath ) ;
191+ reject ( new Error ( 'Downloaded file does not appear to be a valid video format. Please ensure the Google Drive link points to a video file and is publicly accessible.' ) ) ;
82192 return ;
83193 }
84194
@@ -123,18 +233,26 @@ async function downloadFile(fileId, progressCallback = null) {
123233
124234 if ( error . response ) {
125235 if ( error . response . status === 403 ) {
126- throw new Error ( 'File is private or sharing is disabled. Please make sure the file is publicly accessible.' ) ;
236+ throw new Error ( 'File is private or sharing is disabled. Please make sure the file is publicly accessible and try again .' ) ;
127237 } else if ( error . response . status === 404 ) {
128- throw new Error ( 'File not found. Please check the Google Drive URL.' ) ;
238+ throw new Error ( 'File not found. Please check the Google Drive URL and ensure the file exists.' ) ;
239+ } else if ( error . response . status === 429 ) {
240+ throw new Error ( 'Too many requests. Please wait a few minutes and try again.' ) ;
241+ } else if ( error . response . status >= 500 ) {
242+ throw new Error ( 'Google Drive server error. Please try again later.' ) ;
129243 } else {
130- throw new Error ( `HTTP ${ error . response . status } : ${ error . response . statusText } ` ) ;
244+ throw new Error ( `Download failed with HTTP ${ error . response . status } . Please try again or check if the file is accessible. ` ) ;
131245 }
132246 } else if ( error . code === 'ENOTFOUND' ) {
133- throw new Error ( 'Network error: Unable to connect to Google Drive ' ) ;
247+ throw new Error ( 'Network connection failed. Please check your internet connection and try again. ' ) ;
134248 } else if ( error . code === 'ETIMEDOUT' ) {
135- throw new Error ( 'Download timeout: File might be too large or connection is slow' ) ;
249+ throw new Error ( 'Download timeout. The file might be too large or your connection is slow. Please try again.' ) ;
250+ } else if ( error . code === 'ECONNRESET' || error . code === 'ECONNREFUSED' ) {
251+ throw new Error ( 'Connection was reset. Please check your internet connection and try again.' ) ;
252+ } else if ( error . code === 'ECONNABORTED' ) {
253+ throw new Error ( 'Download was interrupted. Please try again.' ) ;
136254 } else {
137- throw new Error ( `Download failed: ${ error . message } ` ) ;
255+ throw new Error ( `Download failed: ${ error . message } . Please try again or check your internet connection. ` ) ;
138256 }
139257 }
140258}
0 commit comments