A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 174 lines 6.4 kB view raw
1use anyhow::Error; 2use clap::Command; 3use owo_colors::OwoColorize; 4use rockbox_library::audio_scan::scan_audio_files; 5use rockbox_library::{create_connection_pool, repo}; 6use rockbox_search::album::Album; 7use rockbox_search::artist::Artist; 8use rockbox_search::track::Track; 9use rockbox_search::{create_indexes, delete_all_documents, index_entity}; 10use std::thread::sleep; 11use std::time::Duration; 12use std::{env, ffi::CStr}; 13use std::{fs, thread}; 14 15#[no_mangle] 16pub extern "C" fn parse_args(argc: usize, argv: *const *const u8) -> i32 { 17 let string_array = unsafe { std::slice::from_raw_parts(argv, argc) }; 18 let args: Vec<&str> = string_array 19 .iter() 20 .map(|&ptr| { 21 let c_str = unsafe { CStr::from_ptr(ptr as *const std::ffi::c_char) }; 22 c_str 23 .to_str() 24 .unwrap_or("[Invalid UTF-8 or Non Null-Terminated String]") 25 }) 26 .collect(); 27 28 const VERSION: &str = match option_env!("TAG") { 29 Some(tag) => tag, 30 None => env!("CARGO_PKG_VERSION"), 31 }; 32 33 let banner = format!( 34 "{}\nA fork of the original Rockbox project, with a focus on modernization and more features.", 35 r#" 36 __________ __ ___. 37 Open \______ \ ____ ____ | | _\_ |__ _______ ___ 38 Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 39 Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 40 Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 41 \/ \/ \/ \/ \/ 42 "# 43 .yellow() 44 ); 45 let cli = Command::new("rockboxd").version(VERSION).about(&banner); 46 47 cli.get_matches_from(args); 48 49 thread::spawn(move || { 50 let home = env::var("HOME").unwrap(); 51 52 match fs::create_dir_all(format!("{}/Music", home)) { 53 Ok(_) => {} 54 Err(e) => { 55 eprintln!("Failed to create Music directory: {}", e); 56 } 57 } 58 59 let update_library = match env::var("ROCKBOX_UPDATE_LIBRARY") 60 .as_ref() 61 .map(|s| s.as_str()) 62 { 63 Ok("1") => true, 64 Ok("true") => true, 65 Ok(_) => false, 66 Err(_) => false, 67 }; 68 let path = rockbox_settings::get_music_dir().unwrap_or(format!("{}/Music", home)); 69 let rt = tokio::runtime::Runtime::new().unwrap(); 70 rt.block_on(async { 71 let pool = create_connection_pool().await?; 72 let tracks = repo::track::all(pool.clone()).await?; 73 if tracks.is_empty() || update_library { 74 scan_audio_files(pool.clone(), path.into()).await?; 75 let tracks = repo::track::all(pool.clone()).await?; 76 let albums = repo::album::all(pool.clone()).await?; 77 let artists = repo::artist::all(pool.clone()).await?; 78 let indexes = create_indexes()?; 79 let tracks_index = indexes.tracks.clone(); 80 let albums_index = indexes.albums.clone(); 81 let artists_index = indexes.artists.clone(); 82 83 thread::spawn(move || { 84 match delete_all_documents(&tracks_index) { 85 Ok(_) => {} 86 Err(e) => eprintln!("Error deleting all documents: {:?}", e), 87 } 88 for track in tracks { 89 index_entity::<Track>(&tracks_index, &track.into()).unwrap(); 90 } 91 }); 92 93 thread::spawn(move || { 94 match delete_all_documents(&albums_index) { 95 Ok(_) => {} 96 Err(e) => eprintln!("Error deleting all documents: {:?}", e), 97 } 98 for album in albums { 99 index_entity::<Album>(&albums_index, &album.into()).unwrap(); 100 } 101 }); 102 103 thread::spawn(move || { 104 match delete_all_documents(&artists_index) { 105 Ok(_) => {} 106 Err(e) => eprintln!("Error deleting all documents: {:?}", e), 107 } 108 for artist in artists { 109 index_entity::<Artist>(&artists_index, &artist.into()).unwrap(); 110 } 111 }); 112 } 113 Ok::<(), Error>(()) 114 }) 115 .unwrap(); 116 117 thread::spawn(move || { 118 sleep(Duration::from_secs(5)); 119 match rockbox_rocksky::register_rockbox() { 120 Ok(_) => println!("Successfully registered Rockbox with Rocksky server"), 121 Err(e) => eprintln!("Failed to register Rockbox with Rocksky server: {}", e), 122 }; 123 }); 124 125 const BANNER: &str = r#" 126 __________ __ ___. 127Open \______ \ ____ ____ | | _\_ |__ _______ ___ 128Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 129Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 130Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 131 \/ \/ \/ \/ \/ 132"#; 133 134 println!("{}", BANNER.yellow()); 135 136 let port = std::env::var("ROCKBOX_TCP_PORT").unwrap_or_else(|_| "6063".to_string()); 137 let addr = format!("0.0.0.0:{}", port); 138 139 println!( 140 "{} server is running on {}", 141 "Rockbox TCP".bright_purple(), 142 addr.bright_green() 143 ); 144 145 let graphql_port = env::var("ROCKBOX_GRAPHQL_PORT").unwrap_or("6062".to_string()); 146 let addr = format!("{}:{}", "0.0.0.0", graphql_port); 147 148 println!( 149 "{} server is running on {}", 150 "Rockbox GraphQL".bright_purple(), 151 addr.bright_green() 152 ); 153 154 let rockbox_port: u16 = std::env::var("ROCKBOX_PORT") 155 .unwrap_or_else(|_| "6061".to_string()) 156 .parse() 157 .expect("ROCKBOX_PORT must be a number"); 158 159 let host_and_port = format!("0.0.0.0:{}", rockbox_port); 160 161 println!( 162 "{} server is running on {}", 163 "Rockbox gRPC".bright_purple(), 164 host_and_port.bright_green() 165 ); 166 167 println!( 168 "Rockbox Web UI is running on {}", 169 "http://localhost:6062".bright_green() 170 ); 171 }); 172 173 return 0; 174}