use clap::Subcommand;
use tail_fin_common::TailFinError;
use crate::session::{browser_session, print_json, Ctx};
#[derive(Subcommand)]
pub enum SpotifyAction {
Search { query: String },
Track { id: String },
Album { id: String },
Playlist { id: String },
Artist { id: String },
Discography { id: String },
Related { id: String },
Me,
Home,
Show { id: String },
Episode { id: String },
Audiobook { id: String },
User { username: String },
Library,
#[command(name = "recently-played")]
RecentlyPlayed,
#[command(name = "recent-searches")]
RecentSearches,
#[command(hide = true)]
Discover {
#[arg(default_value = "/")]
path: String,
},
}
pub async fn run(action: SpotifyAction, ctx: &Ctx) -> Result<(), TailFinError> {
let session = if let Some(ref host) = ctx.connect {
browser_session(host, ctx.headed).await?
} else {
crate::session::auto_launch_stealth("https://open.spotify.com", ctx.headed).await?
};
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
let client = tail_fin_spotify::SpotifyClient::new(session);
if let Some(ref cookies_flag) = ctx.cookies {
let path = if cookies_flag == "auto" {
crate::session::default_cookies_path("spotify")
} else {
std::path::PathBuf::from(cookies_flag)
};
eprintln!("Injecting auth cookies from {}...", path.display());
client.inject_cookies(&path).await?;
}
match action {
SpotifyAction::Search { query } => print_json(&client.search(&query).await?)?,
SpotifyAction::Track { id } => print_json(&client.track(&id).await?)?,
SpotifyAction::Album { id } => print_json(&client.album(&id).await?)?,
SpotifyAction::Playlist { id } => print_json(&client.playlist(&id).await?)?,
SpotifyAction::Artist { id } => print_json(&client.artist(&id).await?)?,
SpotifyAction::Discography { id } => print_json(&client.discography(&id).await?)?,
SpotifyAction::Related { id } => print_json(&client.related(&id).await?)?,
SpotifyAction::Me => print_json(&client.me().await?)?,
SpotifyAction::Home => print_json(&client.home().await?)?,
SpotifyAction::Show { id } => print_json(&client.show(&id).await?)?,
SpotifyAction::Episode { id } => print_json(&client.episode(&id).await?)?,
SpotifyAction::Audiobook { id } => print_json(&client.audiobook(&id).await?)?,
SpotifyAction::User { username } => print_json(&client.user(&username).await?)?,
SpotifyAction::Library => print_json(&client.library().await?)?,
SpotifyAction::RecentlyPlayed => print_json(&client.recently_played().await?)?,
SpotifyAction::RecentSearches => print_json(&client.recent_searches().await?)?,
SpotifyAction::Discover { path } => print_json(&client.discover(&path).await?)?,
}
Ok(())
}
pub struct Adapter;
impl crate::adapter::CliAdapter for Adapter {
fn name(&self) -> &'static str {
"spotify"
}
fn about(&self) -> &'static str {
"Spotify Web Player (browser + pathfinder capture)"
}
fn command(&self) -> clap::Command {
<SpotifyAction as clap::Subcommand>::augment_subcommands(
clap::Command::new("spotify")
.about("Spotify Web Player (browser + pathfinder capture)"),
)
}
fn dispatch<'a>(
&'a self,
matches: &'a clap::ArgMatches,
ctx: &'a crate::session::Ctx,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<(), TailFinError>> + Send + 'a>>
{
Box::pin(async move {
let action = <SpotifyAction as clap::FromArgMatches>::from_arg_matches(matches)
.map_err(|e| TailFinError::Api(e.to_string()))?;
run(action, ctx).await
})
}
}