@@ -14,7 +14,12 @@ use config::Config;
1414use feed:: Feed ;
1515use info_hash:: InfoHash ;
1616use meta:: Meta ;
17- use rocket:: { State , http:: Status , response:: content:: RawXml , serde:: Serialize } ;
17+ use rocket:: {
18+ State ,
19+ http:: { ContentType , Header , Status } ,
20+ response:: { Responder , Response , content:: RawXml } ,
21+ serde:: Serialize ,
22+ } ;
1823use rocket_dyn_templates:: { Template , context} ;
1924use torrent:: Torrent ;
2025
@@ -182,9 +187,6 @@ fn info(
182187 }
183188}
184189
185- #[ derive( Responder ) ]
186- #[ response( status = 200 , content_type = "application/x-bittorrent" ) ]
187- pub struct TorrentFile ( Vec < u8 > ) ;
188190/// Return .torrent file with updated trackers @TODO resolve rank collision
189191#[ get( "/<filename>" , rank = 2 ) ]
190192fn torrent_file (
@@ -235,10 +237,25 @@ fn torrent_file(
235237 . to_vec ( ) ,
236238 ) ,
237239 ) ;
238- Ok ( TorrentFile ( serde_bencode:: to_bytes ( & b) . map_err ( |e| {
239- error ! ( "Could not encode torrent bytes: `{e}`" ) ;
240- Status :: InternalServerError
241- } ) ?) )
240+ Ok ( TorrentFile {
241+ name : format ! (
242+ "{}.torrent" ,
243+ if let Some ( Value :: Dict ( info) ) = b. get( "info" ) {
244+ if let Some ( Value :: Bytes ( name_bytes) ) = info. get( b"name" . as_slice( ) ) {
245+ String :: from_utf8( name_bytes. clone( ) )
246+ . unwrap_or( filename. id20( ) . as_string( ) )
247+ } else {
248+ filename. id20( ) . as_string( )
249+ }
250+ } else {
251+ filename. id20( ) . as_string( )
252+ }
253+ ) ,
254+ data : serde_bencode:: to_bytes ( & b) . map_err ( |e| {
255+ error ! ( "Could not encode torrent bytes: `{e}`" ) ;
256+ Status :: InternalServerError
257+ } ) ?,
258+ } )
242259 }
243260 None => Err ( Status :: NotFound ) ,
244261 }
@@ -342,3 +359,21 @@ fn rocket() -> _ {
342359}
343360
344361const S : & str = " • " ;
362+
363+ /// Downloadable .torrent bytes, with meta-info updated
364+ struct TorrentFile {
365+ name : String ,
366+ data : Vec < u8 > ,
367+ }
368+ impl < ' r > Responder < ' r , ' static > for TorrentFile {
369+ fn respond_to ( self , _: & ' r rocket:: request:: Request < ' _ > ) -> rocket:: response:: Result < ' static > {
370+ Response :: build ( )
371+ . header ( ContentType :: new ( "application" , "x-bittorrent" ) )
372+ . header ( Header :: new (
373+ "Content-Disposition" ,
374+ format ! ( "attachment; filename=\" {}\" " , self . name) ,
375+ ) )
376+ . sized_body ( self . data . len ( ) , std:: io:: Cursor :: new ( self . data ) )
377+ . ok ( )
378+ }
379+ }
0 commit comments