feat: add CLI commands to fetch user library and YTM releases/charts
This commit is contained in:
parent
27f64fc412
commit
a1b43ad70a
1 changed files with 215 additions and 0 deletions
215
cli/src/main.rs
215
cli/src/main.rs
|
|
@ -219,6 +219,86 @@ enum Commands {
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
search: Option<String>,
|
search: Option<String>,
|
||||||
},
|
},
|
||||||
|
/// Get the channels you subscribed to
|
||||||
|
Subscriptions {
|
||||||
|
/// Output format
|
||||||
|
#[clap(short, long, value_parser)]
|
||||||
|
format: Option<Format>,
|
||||||
|
/// Pretty-print output
|
||||||
|
#[clap(long)]
|
||||||
|
pretty: bool,
|
||||||
|
/// Limit the number of items to fetch
|
||||||
|
#[clap(short, long, default_value_t = 20)]
|
||||||
|
limit: usize,
|
||||||
|
/// Use YouTube Music
|
||||||
|
#[clap(short, long)]
|
||||||
|
music: bool,
|
||||||
|
/// Output YouTube subscription feed
|
||||||
|
#[clap(long)]
|
||||||
|
feed: bool,
|
||||||
|
},
|
||||||
|
/// Get the playlists from your library
|
||||||
|
Playlists {
|
||||||
|
/// Output format
|
||||||
|
#[clap(short, long, value_parser)]
|
||||||
|
format: Option<Format>,
|
||||||
|
/// Pretty-print output
|
||||||
|
#[clap(long)]
|
||||||
|
pretty: bool,
|
||||||
|
/// Limit the number of items to fetch
|
||||||
|
#[clap(short, long, default_value_t = 20)]
|
||||||
|
limit: usize,
|
||||||
|
/// Use YouTube Music
|
||||||
|
#[clap(short, long)]
|
||||||
|
music: bool,
|
||||||
|
},
|
||||||
|
/// Get the albums from your library
|
||||||
|
Albums {
|
||||||
|
/// Output format
|
||||||
|
#[clap(short, long, value_parser)]
|
||||||
|
format: Option<Format>,
|
||||||
|
/// Pretty-print output
|
||||||
|
#[clap(long)]
|
||||||
|
pretty: bool,
|
||||||
|
/// Limit the number of items to fetch
|
||||||
|
#[clap(short, long, default_value_t = 20)]
|
||||||
|
limit: usize,
|
||||||
|
},
|
||||||
|
/// Get the tracks from your library
|
||||||
|
Tracks {
|
||||||
|
/// Output format
|
||||||
|
#[clap(short, long, value_parser)]
|
||||||
|
format: Option<Format>,
|
||||||
|
/// Pretty-print output
|
||||||
|
#[clap(long)]
|
||||||
|
pretty: bool,
|
||||||
|
/// Limit the number of items to fetch
|
||||||
|
#[clap(short, long, default_value_t = 20)]
|
||||||
|
limit: usize,
|
||||||
|
},
|
||||||
|
/// Get the latest music releases
|
||||||
|
Releases {
|
||||||
|
/// Get latest music videos
|
||||||
|
#[clap(long)]
|
||||||
|
videos: bool,
|
||||||
|
/// Output format
|
||||||
|
#[clap(short, long, value_parser)]
|
||||||
|
format: Option<Format>,
|
||||||
|
/// Pretty-print output
|
||||||
|
#[clap(long)]
|
||||||
|
pretty: bool,
|
||||||
|
},
|
||||||
|
/// Get YouTube music charts
|
||||||
|
Charts {
|
||||||
|
/// Chart country
|
||||||
|
country: Option<String>,
|
||||||
|
/// Output format
|
||||||
|
#[clap(short, long, value_parser)]
|
||||||
|
format: Option<Format>,
|
||||||
|
/// Pretty-print output
|
||||||
|
#[clap(long)]
|
||||||
|
pretty: bool,
|
||||||
|
},
|
||||||
/// Get a YouTube visitor data cookie
|
/// Get a YouTube visitor data cookie
|
||||||
Vdata,
|
Vdata,
|
||||||
/// Log in using your Google account
|
/// Log in using your Google account
|
||||||
|
|
@ -409,6 +489,31 @@ fn print_entity(e: &impl YtEntity, with_type: bool) {
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fmt_print_entities<T: YtEntity + Serialize>(
|
||||||
|
items: &[T],
|
||||||
|
format: Option<Format>,
|
||||||
|
pretty: bool,
|
||||||
|
title: &str,
|
||||||
|
) {
|
||||||
|
match format {
|
||||||
|
Some(format) => print_data(&items, format, pretty),
|
||||||
|
None => {
|
||||||
|
print_h1(title);
|
||||||
|
print_entities(items, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt_print_tracks(tracks: &[TrackItem], format: Option<Format>, pretty: bool, title: &str) {
|
||||||
|
match format {
|
||||||
|
Some(format) => print_data(&tracks, format, pretty),
|
||||||
|
None => {
|
||||||
|
print_h1(title);
|
||||||
|
print_tracks(tracks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn print_tracks(tracks: &[TrackItem]) {
|
fn print_tracks(tracks: &[TrackItem]) {
|
||||||
for t in tracks {
|
for t in tracks {
|
||||||
if let Some(n) = t.track_nr {
|
if let Some(n) = t.track_nr {
|
||||||
|
|
@ -455,6 +560,7 @@ fn print_music_search<T: Serialize + YtEntity>(
|
||||||
match format {
|
match format {
|
||||||
Some(format) => print_data(data, format, pretty),
|
Some(format) => print_data(data, format, pretty),
|
||||||
None => {
|
None => {
|
||||||
|
print_h1("Music search");
|
||||||
if let Some(corr) = &data.corrected_query {
|
if let Some(corr) = &data.corrected_query {
|
||||||
anstream::println!("Did you mean `{}`?", corr.magenta());
|
anstream::println!("Did you mean `{}`?", corr.magenta());
|
||||||
}
|
}
|
||||||
|
|
@ -472,6 +578,10 @@ fn print_description(desc: Option<String>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn print_h1(title: &str) {
|
||||||
|
anstream::println!("{}", format!("{title}:").on_green().black());
|
||||||
|
}
|
||||||
|
|
||||||
fn print_h2(title: &str) {
|
fn print_h2(title: &str) {
|
||||||
anstream::println!("\n{}", format!("{title}:").green().underline());
|
anstream::println!("\n{}", format!("{title}:").green().underline());
|
||||||
}
|
}
|
||||||
|
|
@ -1276,6 +1386,7 @@ async fn run() -> anyhow::Result<()> {
|
||||||
match format {
|
match format {
|
||||||
Some(format) => print_data(&res, format, pretty),
|
Some(format) => print_data(&res, format, pretty),
|
||||||
None => {
|
None => {
|
||||||
|
print_h1("Search");
|
||||||
if let Some(corr) = res.corrected_query {
|
if let Some(corr) = res.corrected_query {
|
||||||
anstream::println!("Did you mean `{}`?", corr.magenta());
|
anstream::println!("Did you mean `{}`?", corr.magenta());
|
||||||
}
|
}
|
||||||
|
|
@ -1386,6 +1497,110 @@ async fn run() -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Commands::Subscriptions {
|
||||||
|
format,
|
||||||
|
pretty,
|
||||||
|
limit,
|
||||||
|
music,
|
||||||
|
feed,
|
||||||
|
} => {
|
||||||
|
if music {
|
||||||
|
let mut subscriptions = rp.query().music_saved_artists().await?;
|
||||||
|
subscriptions.extend_limit(rp.query(), limit).await?;
|
||||||
|
fmt_print_entities(&subscriptions.items, format, pretty, "Music artists");
|
||||||
|
} else if feed {
|
||||||
|
let mut feed = rp.query().subscription_feed().await?;
|
||||||
|
feed.extend_limit(rp.query(), limit).await?;
|
||||||
|
fmt_print_entities(&feed.items, format, pretty, "Feed");
|
||||||
|
} else {
|
||||||
|
let mut subscriptions = rp.query().subscriptions().await?;
|
||||||
|
subscriptions.extend_limit(rp.query(), limit).await?;
|
||||||
|
fmt_print_entities(&subscriptions.items, format, pretty, "Subscriptions");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Commands::Playlists {
|
||||||
|
format,
|
||||||
|
pretty,
|
||||||
|
limit,
|
||||||
|
music,
|
||||||
|
} => {
|
||||||
|
if music {
|
||||||
|
let mut playlists = rp.query().music_saved_playlists().await?;
|
||||||
|
playlists.extend_limit(rp.query(), limit).await?;
|
||||||
|
fmt_print_entities(&playlists.items, format, pretty, "Music playlists");
|
||||||
|
} else {
|
||||||
|
let mut playlists = rp.query().saved_playlists().await?;
|
||||||
|
playlists.extend_limit(rp.query(), limit).await?;
|
||||||
|
fmt_print_entities(&playlists.items, format, pretty, "Saved playlists");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Commands::Albums {
|
||||||
|
format,
|
||||||
|
pretty,
|
||||||
|
limit,
|
||||||
|
} => {
|
||||||
|
let mut albums = rp.query().music_saved_albums().await?;
|
||||||
|
albums.extend_limit(rp.query(), limit).await?;
|
||||||
|
fmt_print_entities(&albums.items, format, pretty, "Saved albums");
|
||||||
|
}
|
||||||
|
Commands::Tracks {
|
||||||
|
format,
|
||||||
|
pretty,
|
||||||
|
limit,
|
||||||
|
} => {
|
||||||
|
let mut tracks = rp.query().music_saved_tracks().await?;
|
||||||
|
tracks.extend_limit(rp.query(), limit).await?;
|
||||||
|
fmt_print_tracks(&tracks.items, format, pretty, "Saved tracks");
|
||||||
|
}
|
||||||
|
Commands::Releases {
|
||||||
|
videos,
|
||||||
|
format,
|
||||||
|
pretty,
|
||||||
|
} => {
|
||||||
|
if videos {
|
||||||
|
let releases = rp.query().music_new_videos().await?;
|
||||||
|
fmt_print_tracks(&releases, format, pretty, "New music videos");
|
||||||
|
} else {
|
||||||
|
let releases = rp.query().music_new_albums().await?;
|
||||||
|
fmt_print_entities(&releases, format, pretty, "New albums");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Commands::Charts {
|
||||||
|
country,
|
||||||
|
format,
|
||||||
|
pretty,
|
||||||
|
} => {
|
||||||
|
let country = match country {
|
||||||
|
Some(c) => Some(Country::from_str(&c)?),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
let charts = rp.query().music_charts(country).await?;
|
||||||
|
match format {
|
||||||
|
Some(format) => print_data(&charts, format, pretty),
|
||||||
|
None => {
|
||||||
|
print_h1("Music charts");
|
||||||
|
if let Some(plid) = &charts.top_playlist_id {
|
||||||
|
print_h2(&format!("Top tracks [{plid}]"));
|
||||||
|
} else {
|
||||||
|
print_h2("Top tracks");
|
||||||
|
}
|
||||||
|
print_tracks(&charts.top_tracks);
|
||||||
|
if let Some(plid) = &charts.trending_playlist_id {
|
||||||
|
print_h2(&format!("Trending [{plid}]"));
|
||||||
|
} else {
|
||||||
|
print_h2("Trending");
|
||||||
|
}
|
||||||
|
print_tracks(&charts.trending_tracks);
|
||||||
|
print_h2("Artists");
|
||||||
|
print_entities(&charts.artists, false);
|
||||||
|
|
||||||
|
if !charts.playlists.is_empty() {
|
||||||
|
print_h2("Playlists");
|
||||||
|
print_entities(&charts.playlists, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Commands::Vdata => {
|
Commands::Vdata => {
|
||||||
let vd = rp.query().get_visitor_data().await?;
|
let vd = rp.query().get_visitor_data().await?;
|
||||||
println!("{vd}");
|
println!("{vd}");
|
||||||
|
|
|
||||||
Reference in a new issue