Browse and listen to thousands of radio stations across the globe right from your terminal ๐ŸŒŽ ๐Ÿ“ป ๐ŸŽตโœจ
radio rust tokio web-radio command-line-tool tui

feat: add systemd service commands

Changed files
+179
src
+21
README.md
··· 146 146 tunein play s221580 147 147 ``` 148 148 149 + ## ๐Ÿง™ Systemd Service 150 + 151 + Tunein daemon can be started as a systemd service. To enable and start the service, run the following command: 152 + 153 + ```bash 154 + tunein service install 155 + ``` 156 + 157 + To disable and stop the service, run the following command: 158 + 159 + ```bash 160 + tunein service uninstall 161 + ``` 162 + 163 + To check the status of the service, run the following command: 164 + 165 + ```bash 166 + tunein service status 167 + ``` 168 + 169 + 149 170 ## API Documentation 150 171 [https://buf.build/tsiry/tuneinserverapis/docs/main:tunein.v1alpha1](https://buf.build/tsiry/tuneinserverapis/docs/main:tunein.v1alpha1) 151 172
+26
src/main.rs
··· 13 13 mod provider; 14 14 mod search; 15 15 mod server; 16 + mod service; 16 17 mod tags; 17 18 mod tui; 18 19 mod types; ··· 58 59 .about("Start the server") 59 60 .arg(arg!([port] "The port to listen on").default_value("8090")), 60 61 ) 62 + .subcommand( 63 + Command::new("service") 64 + .about("Manage systemd service for tunein-cli server") 65 + .subcommand( 66 + Command::new("install") 67 + .about("Install systemd service for tunein-cli server") 68 + ) 69 + .subcommand( 70 + Command::new("uninstall") 71 + .about("Uninstall systemd service for tunein-cli server") 72 + ) 73 + .subcommand( 74 + Command::new("status") 75 + .about("Check status of tunein-cli systemd service") 76 + ) 77 + ) 61 78 } 62 79 63 80 #[tokio::main] ··· 93 110 let port = port.parse::<u16>().unwrap(); 94 111 server::exec(port).await?; 95 112 } 113 + Some(("service", sub_m)) => match sub_m.subcommand() { 114 + Some(("install", _)) => service::install()?, 115 + Some(("uninstall", _)) => service::uninstall()?, 116 + Some(("status", _)) => service::status()?, 117 + _ => { 118 + println!("Invalid subcommand. Use `tunein service --help` for more information"); 119 + std::process::exit(1); 120 + } 121 + }, 96 122 _ => unreachable!(), 97 123 } 98 124
+11
src/server/mod.rs
··· 15 15 16 16 pub async fn exec(port: u16) -> Result<(), Error> { 17 17 let addr: SocketAddr = format!("0.0.0.0:{}", port).parse().unwrap(); 18 + println!( 19 + "{}", 20 + r#" 21 + ______ ____ _______ ____ 22 + /_ __/_ _____ ___ / _/__ / ___/ / / _/ 23 + / / / // / _ \/ -_)/ // _ \ / /__/ /___/ / 24 + /_/ \_,_/_//_/\__/___/_//_/ \___/____/___/ 25 + 26 + "# 27 + .bright_green() 28 + ); 18 29 println!("Listening on {}", addr.cyan()); 19 30 Server::builder() 20 31 .accept_http1(true)
+111
src/service.rs
··· 1 + use std::{path::Path, process::Command}; 2 + 3 + use anyhow::Error; 4 + 5 + const SERVICE_TEMPLATE: &str = include_str!("./systemd/tunein.service"); 6 + 7 + pub fn install() -> Result<(), Error> { 8 + if cfg!(not(target_os = "linux")) { 9 + println!("This command is only supported on Linux"); 10 + std::process::exit(1); 11 + } 12 + 13 + let home = std::env::var("HOME")?; 14 + let service_path: &str = &format!("{}/.config/systemd/user/tunein.service", home); 15 + std::fs::create_dir_all(format!("{}/.config/systemd/user", home)) 16 + .expect("Failed to create systemd user directory"); 17 + 18 + if Path::new(service_path).exists() { 19 + println!("Service file already exists. Nothing to install."); 20 + return Ok(()); 21 + } 22 + 23 + let tunein_path = std::env::current_exe()?; 24 + 25 + let service_template: &str = &SERVICE_TEMPLATE.replace( 26 + "ExecStart=/usr/bin/tunein", 27 + &format!("ExecStart={}", tunein_path.display()), 28 + ); 29 + 30 + std::fs::write(service_path, service_template).expect("Failed to write service file"); 31 + 32 + Command::new("systemctl") 33 + .arg("--user") 34 + .arg("daemon-reload") 35 + .status()?; 36 + 37 + Command::new("systemctl") 38 + .arg("--user") 39 + .arg("enable") 40 + .arg("tunein") 41 + .status()?; 42 + 43 + Command::new("systemctl") 44 + .arg("--user") 45 + .arg("start") 46 + .arg("tunein") 47 + .status()?; 48 + 49 + println!("โœ… Tunein service installed successfully!"); 50 + 51 + Ok(()) 52 + } 53 + 54 + pub fn uninstall() -> Result<(), Error> { 55 + if cfg!(not(target_os = "linux")) { 56 + println!("This command is only supported on Linux"); 57 + std::process::exit(1); 58 + } 59 + 60 + let home = std::env::var("HOME")?; 61 + let service_path: &str = &format!("{}/.config/systemd/user/tunein.service", home); 62 + 63 + if Path::new(service_path).exists() { 64 + Command::new("systemctl") 65 + .arg("--user") 66 + .arg("stop") 67 + .arg("tunein") 68 + .status()?; 69 + 70 + Command::new("systemctl") 71 + .arg("--user") 72 + .arg("disable") 73 + .arg("tunein") 74 + .status()?; 75 + 76 + std::fs::remove_file(service_path).expect("Failed to remove service file"); 77 + 78 + Command::new("systemctl") 79 + .arg("--user") 80 + .arg("daemon-reload") 81 + .status()?; 82 + 83 + println!("โœ… Tunein service uninstalled successfully!"); 84 + } else { 85 + println!("Service file does not exist. Nothing to uninstall."); 86 + } 87 + 88 + Ok(()) 89 + } 90 + 91 + pub fn status() -> Result<(), Error> { 92 + if cfg!(not(target_os = "linux")) { 93 + println!("This command is only supported on Linux"); 94 + std::process::exit(1); 95 + } 96 + 97 + let home = std::env::var("HOME")?; 98 + let service_path: &str = &format!("{}/.config/systemd/user/tunein.service", home); 99 + 100 + if Path::new(service_path).exists() { 101 + Command::new("systemctl") 102 + .arg("--user") 103 + .arg("status") 104 + .arg("tunein") 105 + .status()?; 106 + } else { 107 + println!("Service file does not exist. Tunein service is not installed."); 108 + } 109 + 110 + Ok(()) 111 + }
+10
src/systemd/tunein.service
··· 1 + [Unit] 2 + Description=TuneIn Server Daemon 3 + After=network.target 4 + 5 + [Service] 6 + ExecStart=/usr/bin/tunein server 7 + Restart=always 8 + 9 + [Install] 10 + WantedBy=default.target