jellyfin-tui-asio/src/main.rs
2025-06-20 15:45:52 +02:00

160 lines
5.3 KiB
Rust

use anyhow::Result;
use tracing::info;
use cpal::traits::{HostTrait, DeviceTrait};
use std::io::Write;
mod jellyfin;
mod audio;
mod config;
mod player;
mod tui;
#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt::init();
info!("Starting Jellyfin ASIO Client");
// Load config
let mut config = config::Config::load()?;
// Create Jellyfin client
let mut jellyfin = jellyfin::JellyfinClient::new(config.clone());
// Check if we need to authenticate
if !jellyfin.is_authenticated() {
println!("No saved session found. Please log in.");
// Get server URL if not set
if config.server_url.is_empty() {
print!("Enter Jellyfin server URL (e.g., https://jellyfin.example.com): ");
std::io::stdout().flush()?;
let mut url = String::new();
std::io::stdin().read_line(&mut url)?;
config.server_url = url.trim().to_string();
config.save()?;
jellyfin = jellyfin::JellyfinClient::new(config.clone());
}
print!("Enter your Jellyfin username: ");
std::io::stdout().flush()?;
let mut username = String::new();
std::io::stdin().read_line(&mut username)?;
username = username.trim().to_string();
print!("Enter your Jellyfin password: ");
std::io::stdout().flush()?;
let password = rpassword::read_password()?;
jellyfin.authenticate(&username, &password).await?
} else {
info!("Using saved session for {}", config.username.unwrap_or_else(|| "Unknown user".to_string()));
}
// List ASIO devices
list_asio_devices()?;
// Create player
let player = player::Player::new(jellyfin.clone())?;
// Create and run TUI
let mut tui = tui::Tui::new(jellyfin, player)?;
tui.run().await?;
Ok(())
}
async fn test_jellyfin_connection() -> Result<()> {
info!("Testing Jellyfin connection...");
let config = config::Config {
server_url: "https://jfin.mercurio.moe".to_string(),
..config::Config::default()
};
let mut client = jellyfin::JellyfinClient::new(config);
println!("Enter your Jellyfin username:");
let mut username = String::new();
std::io::stdin().read_line(&mut username)?;
username = username.trim().to_string();
println!("Enter your Jellyfin password:");
let password = rpassword::read_password()?;
client.authenticate(&username, &password).await?;
let items = client.get_music_library().await?;
info!("Your music library (first few items):");
for item in items.iter().take(10) {
let artist = item.artists.as_ref()
.and_then(|a| a.first())
.map(|s| s.as_str())
.unwrap_or("Unknown Artist");
let album = item.album.as_ref()
.map(|s| s.as_str())
.unwrap_or("Unknown Album");
info!(" {} - {} - {}", artist, album, item.name);
if items.iter().position(|i| i.id == item.id) == Some(0) {
let stream_url = client.get_stream_url(&item.id)?;
info!(" Stream URL: {}", stream_url);
println!("\nDo you want to play this track? (y/n):");
let mut input = String::new();
std::io::stdin().read_line(&mut input)?;
if input.trim().to_lowercase() == "y" {
let mut player = audio::AsioPlayer::new()?;
println!("Do you want to set a specific sample rate? (y/n):");
let mut rate_input = String::new();
std::io::stdin().read_line(&mut rate_input)?;
if rate_input.trim().to_lowercase() == "y" {
println!("Enter sample rate (e.g., 44100, 48000, 96000, 192000):");
let mut rate_str = String::new();
std::io::stdin().read_line(&mut rate_str)?;
if let Ok(sample_rate) = rate_str.trim().parse::<u32>() {
match player.set_sample_rate(sample_rate) {
Ok(_) => println!("Sample rate set to {} Hz", sample_rate),
Err(e) => println!("Failed to set sample rate: {}", e),
}
} else {
println!("Invalid sample rate, using automatic selection");
}
}
player.play_url(&stream_url).await?;
}
}
}
Ok(())
}
fn list_asio_devices() -> Result<()> {
info!("Enumerating ASIO devices...");
let host = cpal::host_from_id(cpal::HostId::Asio)?;
let devices = host.output_devices()?;
for (i, device) in devices.enumerate() {
let name = device.name()?;
info!("ASIO Device {}: {}", i, name);
let supported_configs = device.supported_output_configs()?;
for config in supported_configs {
info!(" Channels: {}, Sample Rate: {}-{} Hz, Format: {:?}",
config.channels(),
config.min_sample_rate().0,
config.max_sample_rate().0,
config.sample_format());
}
}
Ok(())
}