@@ -13,6 +13,7 @@ use http::{header::CONTENT_TYPE, StatusCode};
1313use log:: { debug, error, info, warn} ;
1414use teloxide:: net:: Download ;
1515use teloxide:: prelude:: Requester ;
16+ use base64:: Engine ;
1617
1718use shared:: file_storage:: { get_file_metadata, list_all_files, FileMetadata } ;
1819use crate :: config:: Config ;
@@ -219,6 +220,9 @@ async fn files_id(
219220 }
220221 }
221222
223+ // Check if auto-close is requested via ?close=1
224+ let auto_close = params. get ( "close" ) . is_some ( ) ;
225+
222226 // Determine content type, allow force download via ?dl=1
223227 let force_download = params. get ( "dl" ) . is_some ( ) ;
224228 let content_type = if force_download {
@@ -234,6 +238,65 @@ async fn files_id(
234238 info ! ( "Serving file: {} ({} bytes) with content type: {}" ,
235239 metadata. file_name, file_bytes. len( ) , content_type) ;
236240
241+ // If auto-close requested, return HTML with auto-download and close script
242+ if auto_close {
243+ let html = format ! (
244+ r#"<!DOCTYPE html>
245+ <html>
246+ <head>
247+ <title>Downloading {}</title>
248+ <style>
249+ body {{ font-family: Arial, sans-serif; text-align: center; padding: 50px; }}
250+ .loader {{ border: 5px solid #f3f3f3; border-top: 5px solid #3498db;
251+ border-radius: 50%; width: 50px; height: 50px;
252+ animation: spin 1s linear infinite; margin: 20px auto; }}
253+ @keyframes spin {{ 0% {{ transform: rotate(0deg); }} 100% {{ transform: rotate(360deg); }} }}
254+ </style>
255+ </head>
256+ <body>
257+ <h2>Downloading {}</h2>
258+ <div class="loader"></div>
259+ <p>Your download will begin shortly...</p>
260+ <p><small>This window will close automatically.</small></p>
261+ <script>
262+ // Create blob from base64 data and trigger download
263+ const base64Data = "{}";
264+ const byteCharacters = atob(base64Data);
265+ const byteNumbers = new Array(byteCharacters.length);
266+ for (let i = 0; i < byteCharacters.length; i++) {{
267+ byteNumbers[i] = byteCharacters.charCodeAt(i);
268+ }}
269+ const byteArray = new Uint8Array(byteNumbers);
270+ const blob = new Blob([byteArray], {{ type: '{}' }});
271+ const url = window.URL.createObjectURL(blob);
272+ const a = document.createElement('a');
273+ a.href = url;
274+ a.download = "{}";
275+ document.body.appendChild(a);
276+ a.click();
277+ window.URL.revokeObjectURL(url);
278+
279+ // Close window after 2 seconds
280+ setTimeout(function() {{
281+ window.close();
282+ }}, 2000);
283+ </script>
284+ </body>
285+ </html>"# ,
286+ metadata. file_name,
287+ metadata. file_name,
288+ base64:: engine:: general_purpose:: STANDARD . encode( & file_bytes) ,
289+ content_type,
290+ metadata. file_name
291+ ) ;
292+
293+ return Ok ( Response :: builder ( )
294+ . status ( StatusCode :: OK )
295+ . header ( CONTENT_TYPE , "text/html; charset=utf-8" )
296+ . body ( html. into ( ) )
297+ . unwrap ( ) ) ;
298+ }
299+
237300 Ok ( Response :: builder ( )
238301 . status ( StatusCode :: OK )
239302 . header ( CONTENT_TYPE , content_type)
0 commit comments