11use std:: {
22 fs:: OpenOptions , io:: ErrorKind , net:: TcpListener , num:: ParseIntError , path:: PathBuf ,
3- sync:: mpsc:: SyncSender , time:: Duration ,
3+ process :: Command as ProcessCommand , sync:: mpsc:: SyncSender , time:: Duration ,
44} ;
55
66use clap:: { Args , Parser } ;
@@ -150,9 +150,10 @@ struct TorrentInfo {
150150 magnet_link : Option < String > ,
151151}
152152
153- /// Vortex bittorrent client cli. Fast trackerless torrent downloads using modern io_uring techniques.
153+ /// Vortex bittorrent client cli. Fast trackerless torrent client using modern io_uring techniques.
154154#[ derive( Debug , Parser ) ]
155155#[ command( version, about, long_about = None ) ]
156+ #[ command( subcommand_negates_reqs = true ) ]
156157struct Cli {
157158 /// Port for the listener
158159 #[ arg( short, long) ]
@@ -175,12 +176,69 @@ struct Cli {
175176 /// Skip use of the DHT node cache and rebuild from bootstrap nodes
176177 #[ arg( long) ]
177178 skip_dht_cache : bool ,
179+ #[ command( subcommand) ]
180+ command : Option < CliCommand > ,
181+ }
182+
183+ #[ derive( Debug , clap:: Subcommand ) ]
184+ enum CliCommand {
185+ /// Register vortex-cli as the system-wide magnet link handler
186+ RegisterMagnetHandler ,
187+ }
188+
189+ fn register_magnet_handler ( ) -> color_eyre:: Result < ( ) > {
190+ let exe_path = std:: env:: current_exe ( )
191+ . wrap_err ( "Failed to determine current executable path" ) ?
192+ . canonicalize ( )
193+ . wrap_err ( "Failed to canonicalize executable path" ) ?;
194+
195+ let applications_dir = dirs:: data_dir ( )
196+ . wrap_err ( "Failed to determine data directory" ) ?
197+ . join ( "applications" ) ;
198+ std:: fs:: create_dir_all ( & applications_dir)
199+ . wrap_err ( "Failed to create applications directory" ) ?;
200+
201+ let desktop_file = applications_dir. join ( "vortex-cli.desktop" ) ;
202+ let desktop_contents = format ! (
203+ "[Desktop Entry]\n \
204+ Type=Application\n \
205+ Name=Vortex\n \
206+ Comment=Fast trackerless torrent client using modern io_uring techniques\n \
207+ Exec={} --magnet-link %u\n \
208+ MimeType=x-scheme-handler/magnet;\n \
209+ NoDisplay=true\n \
210+ Terminal=true\n ",
211+ exe_path. display( )
212+ ) ;
213+ std:: fs:: write ( & desktop_file, & desktop_contents)
214+ . wrap_err ( "Failed to write .desktop file for vorex-cli" ) ?;
215+
216+ let status = ProcessCommand :: new ( "xdg-mime" )
217+ . args ( [ "default" , "vortex-cli.desktop" , "x-scheme-handler/magnet" ] )
218+ . status ( )
219+ . wrap_err ( "Failed to run xdg-mime" ) ?;
220+ if !status. success ( ) {
221+ return Err ( eyre ! ( "xdg-mime exited with status {status}" ) ) ;
222+ }
223+
224+ // Best-effort to update the desktop database
225+ let _ = ProcessCommand :: new ( "update-desktop-database" )
226+ . arg ( & applications_dir)
227+ . status ( ) ;
228+
229+ println ! ( "Registered vortex-cli as the default magnet link handler." ) ;
230+ println ! ( "Desktop file written to: {}" , desktop_file. display( ) ) ;
231+ Ok ( ( ) )
178232}
179233
180234fn main ( ) -> color_eyre:: Result < ( ) > {
181235 color_eyre:: install ( ) ?;
182236 let cli = Cli :: parse ( ) ;
183237
238+ if let Some ( CliCommand :: RegisterMagnetHandler ) = cli. command {
239+ return register_magnet_handler ( ) ;
240+ }
241+
184242 // load or create config file (auto-creates if doesn't exist)
185243 let mut vortex_config =
186244 config:: load_or_create_config ( cli. config_file ) . wrap_err ( "Failed to load config" ) ?;
0 commit comments