use crate::{
devices::{Render, RenderSpec},
dlna,
error::Result,
streaming::{
get_local_ip, infer_subtitle_from_video, MediaStreamingServer, STREAMING_PORT_DEFAULT,
},
};
use clap::{Args, Parser, Subcommand};
use log::info;
use pretty_env_logger;
use std::env;
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
struct Cli {
#[clap(short, long, default_value_t = 5)]
timeout: u64,
#[clap(short, long)]
quiet: bool,
#[clap(short = 'b', long)]
debug: bool,
#[clap(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
List(List),
Play(Play),
}
impl Commands {
pub async fn run(&self, cli: &Cli) -> Result<()> {
self.setup_log(cli);
match self {
Self::List(list) => list.run(cli).await?,
Self::Play(play) => play.run(cli).await?,
}
Ok(())
}
fn setup_log(&self, cli: &Cli) {
let crabldna_log = env::var("CRABDLNA_LOG");
let log_level = if let Ok(crabldna_log) = &crabldna_log {
crabldna_log.as_str()
} else if cli.debug {
"debug"
} else if cli.quiet {
"warn"
} else {
"info"
};
env::set_var("RUST_LOG", log_level);
pretty_env_logger::init();
}
}
#[derive(Args)]
struct List;
impl List {
async fn run(&self, cli: &Cli) -> Result<()> {
info!("List devices");
for render in Render::discover(cli.timeout).await? {
println!("{}", render);
}
Ok(())
}
}
#[derive(Args)]
struct Play {
#[clap(short = 'H', long = "host")]
host: Option<String>,
#[clap(short = 'P', long = "port", default_value_t=STREAMING_PORT_DEFAULT)]
port: u32,
#[clap(short = 'q', long = "query-device")]
device_query: Option<String>,
#[clap(short, long = "device")]
device_url: Option<String>,
#[clap(short, long, parse(from_os_str), value_name = "FILE_SUBTITLE")]
subtitle: Option<std::path::PathBuf>,
#[clap(short, long)]
no_subtitle: bool,
#[clap(parse(from_os_str))]
file_video: std::path::PathBuf,
}
impl Play {
async fn run(&self, cli: &Cli) -> Result<()> {
let render = self.select_render(cli).await?;
let media_streaming_server = self.build_media_streaming_server().await?;
dlna::play(render, media_streaming_server).await
}
async fn select_render(&self, cli: &Cli) -> Result<Render> {
info!("Selecting render");
Render::new(if let Some(device_url) = &self.device_url {
RenderSpec::Location(device_url.to_owned())
} else if let Some(device_query) = &self.device_query {
RenderSpec::Query(cli.timeout, device_query.to_owned())
} else {
RenderSpec::First(cli.timeout)
})
.await
}
async fn build_media_streaming_server(&self) -> Result<MediaStreamingServer> {
info!("Building media streaming server");
let local_host_ip = get_local_ip().await?;
let host_ip = self.host.as_ref().unwrap_or(&local_host_ip);
let host_port = self.port;
let subtitle = match &self.no_subtitle {
false => self
.subtitle
.clone()
.or_else(|| infer_subtitle_from_video(&self.file_video)),
true => None,
};
MediaStreamingServer::new(&self.file_video, &subtitle, host_ip, &host_port)
}
}
pub async fn run() -> Result<()> {
let cli = Cli::parse();
cli.command.run(&cli).await
}