spotify-cli 0.5.0

A command-line interface for Spotify
Documentation
use clap::Parser;
#[cfg(unix)]
use spotify_cli::cli::DaemonCommand;
use spotify_cli::cli::commands::{self, ArtistQuery, ArtistView};
use spotify_cli::cli::{
    AlbumCommand, AudiobookCommand, AuthCommand, CategoryCommand, ChapterCommand, Cli, Command,
    DevicesCommand, EpisodeCommand, FollowCommand, InfoCommand, LibraryCommand, PinCommand,
    PlayerCommand, PlaylistCommand, QueueCommand, ShowCommand, UserCommand, print_completions,
};
use spotify_cli::io::output::{print_human, print_json};
use spotify_cli::logging::{LogConfig, LogFormat};

#[tokio::main]
async fn main() {
    let cli = Cli::parse();

    // Initialize logging with configured format
    let log_format = cli.log_format.parse::<LogFormat>().unwrap_or_default();
    LogConfig::new(cli.verbose).format(log_format).init();

    let response = match cli.command {
        Command::Auth { command } => match command {
            AuthCommand::Login { force } => commands::auth_login(force).await,
            AuthCommand::Logout => commands::auth_logout().await,
            AuthCommand::Refresh => commands::auth_refresh().await,
            AuthCommand::Status => commands::auth_status().await,
        },
        Command::Player { command } => match command {
            PlayerCommand::Next => commands::player_next().await,
            PlayerCommand::Previous => commands::player_previous().await,
            PlayerCommand::Toggle => commands::player_toggle().await,
            PlayerCommand::Play { uri, pin } => {
                commands::player_play(uri.as_deref(), pin.as_deref()).await
            }
            PlayerCommand::Pause => commands::player_pause().await,
            PlayerCommand::Status { id_only } => commands::player_status(id_only.as_deref()).await,
            PlayerCommand::Devices { command } => match command {
                DevicesCommand::List => commands::player_devices_list().await,
                DevicesCommand::Transfer { device } => {
                    commands::player_devices_transfer(&device).await
                }
            },
            PlayerCommand::Queue { command } => match command {
                QueueCommand::List => commands::player_queue_list().await,
                QueueCommand::Add { uri, now_playing } => {
                    commands::player_queue_add(uri.as_deref(), now_playing).await
                }
            },
            PlayerCommand::Seek { position } => commands::player_seek(&position).await,
            PlayerCommand::Repeat { mode } => commands::player_repeat(&mode).await,
            PlayerCommand::Volume { percent } => commands::player_volume(percent).await,
            PlayerCommand::Shuffle { state } => commands::player_shuffle(&state).await,
            PlayerCommand::Recent => commands::player_recent().await,
        },
        Command::Pin { command } => match command {
            PinCommand::Add {
                resource_type,
                url_or_id,
                alias,
                tags,
            } => commands::pin_add(&resource_type, &url_or_id, &alias, tags.as_deref()).await,
            PinCommand::Remove { alias_or_id } => commands::pin_remove(&alias_or_id).await,
            PinCommand::List { resource_type } => {
                commands::pin_list(resource_type.as_deref()).await
            }
        },
        Command::Search {
            query,
            types,
            limit,
            pins_only,
            exact,
            artist,
            album,
            track,
            year,
            genre,
            isrc,
            upc,
            new,
            hipster,
            play,
            sort,
        } => {
            let options = commands::SearchOptions {
                limit,
                pins_only,
                exact,
                filters: commands::SearchFilters {
                    artist,
                    album,
                    track,
                    year,
                    genre,
                    isrc,
                    upc,
                    new,
                    hipster,
                },
                play,
                sort,
            };
            commands::search_command(&query, &types, options).await
        }
        Command::Playlist { command } => match command {
            PlaylistCommand::List { limit, offset } => commands::playlist_list(limit, offset).await,
            PlaylistCommand::Get { playlist } => commands::playlist_get(&playlist).await,
            PlaylistCommand::Create {
                name,
                description,
                public,
            } => commands::playlist_create(&name, description.as_deref(), public).await,
            PlaylistCommand::Add {
                playlist,
                uris,
                now_playing,
                position,
                dry_run,
            } => commands::playlist_add(&playlist, &uris, now_playing, position, dry_run).await,
            PlaylistCommand::Remove {
                playlist,
                uris,
                dry_run,
            } => commands::playlist_remove(&playlist, &uris, dry_run).await,
            PlaylistCommand::Edit {
                playlist,
                name,
                description,
                public,
                private,
            } => {
                let visibility = if public {
                    Some(true)
                } else if private {
                    Some(false)
                } else {
                    None
                };
                commands::playlist_edit(
                    &playlist,
                    name.as_deref(),
                    description.as_deref(),
                    visibility,
                )
                .await
            }
            PlaylistCommand::Reorder {
                playlist,
                from,
                to,
                count,
            } => commands::playlist_reorder(&playlist, from, to, count).await,
            PlaylistCommand::Follow { playlist, public } => {
                commands::playlist_follow(&playlist, public).await
            }
            PlaylistCommand::Unfollow { playlist } => commands::playlist_unfollow(&playlist).await,
            PlaylistCommand::Duplicate { playlist, name } => {
                commands::playlist_duplicate(&playlist, name.as_deref()).await
            }
            PlaylistCommand::Cover { playlist } => commands::playlist_cover(&playlist).await,
            PlaylistCommand::User { user_id } => commands::playlist_user(&user_id).await,
            PlaylistCommand::Deduplicate { playlist, dry_run } => {
                commands::playlist_deduplicate(&playlist, dry_run).await
            }
        },
        Command::Library { command } => match command {
            LibraryCommand::List { limit, offset } => commands::library_list(limit, offset).await,
            LibraryCommand::Save {
                ids,
                now_playing,
                dry_run,
            } => commands::library_save(&ids, now_playing, dry_run).await,
            LibraryCommand::Remove { ids, dry_run } => {
                commands::library_remove(&ids, dry_run).await
            }
            LibraryCommand::Check { ids } => commands::library_check(&ids).await,
        },
        Command::Info { command } => match command {
            InfoCommand::Track { id, id_only } => {
                commands::info_track(id.as_deref(), id_only).await
            }
            InfoCommand::Album { id, id_only } => {
                commands::info_album(id.as_deref(), id_only).await
            }
            InfoCommand::Artist {
                id,
                id_only,
                top_tracks,
                albums,
                related,
                market,
                limit,
                offset,
            } => {
                let view = if top_tracks {
                    ArtistView::TopTracks
                } else if albums {
                    ArtistView::Albums
                } else if related {
                    ArtistView::Related
                } else {
                    ArtistView::Details
                };
                let query = ArtistQuery::new()
                    .with_id(id)
                    .id_only(id_only)
                    .view(view)
                    .market(market)
                    .paginate(limit, offset);
                commands::info_artist(query).await
            }
        },
        Command::User { command } => match command {
            UserCommand::Profile => commands::user_profile().await,
            UserCommand::Top {
                item_type,
                range,
                limit,
            } => commands::user_top(&item_type, &range, limit).await,
            UserCommand::Get { user_id } => commands::user_get(&user_id).await,
        },
        Command::Show { command } => match command {
            ShowCommand::Get { id } => commands::show_get(&id).await,
            ShowCommand::Episodes { id, limit, offset } => {
                commands::show_episodes(&id, limit, offset).await
            }
            ShowCommand::List { limit, offset } => commands::show_list(limit, offset).await,
            ShowCommand::Save { ids } => commands::show_save(&ids).await,
            ShowCommand::Remove { ids } => commands::show_remove(&ids).await,
            ShowCommand::Check { ids } => commands::show_check(&ids).await,
        },
        Command::Episode { command } => match command {
            EpisodeCommand::Get { id } => commands::episode_get(&id).await,
            EpisodeCommand::List { limit, offset } => commands::episode_list(limit, offset).await,
            EpisodeCommand::Save { ids } => commands::episode_save(&ids).await,
            EpisodeCommand::Remove { ids } => commands::episode_remove(&ids).await,
            EpisodeCommand::Check { ids } => commands::episode_check(&ids).await,
        },
        Command::Audiobook { command } => match command {
            AudiobookCommand::Get { id } => commands::audiobook_get(&id).await,
            AudiobookCommand::Chapters { id, limit, offset } => {
                commands::audiobook_chapters(&id, limit, offset).await
            }
            AudiobookCommand::List { limit, offset } => {
                commands::audiobook_list(limit, offset).await
            }
            AudiobookCommand::Save { ids } => commands::audiobook_save(&ids).await,
            AudiobookCommand::Remove { ids } => commands::audiobook_remove(&ids).await,
            AudiobookCommand::Check { ids } => commands::audiobook_check(&ids).await,
        },
        Command::Album { command } => match command {
            AlbumCommand::List { limit, offset } => commands::album_list(limit, offset).await,
            AlbumCommand::Tracks { id, limit, offset } => {
                commands::album_tracks(&id, limit, offset).await
            }
            AlbumCommand::Save { ids } => commands::album_save(&ids).await,
            AlbumCommand::Remove { ids } => commands::album_remove(&ids).await,
            AlbumCommand::Check { ids } => commands::album_check(&ids).await,
            AlbumCommand::NewReleases { limit, offset } => {
                commands::album_new_releases(limit, offset).await
            }
        },
        Command::Chapter { command } => match command {
            ChapterCommand::Get { id } => commands::chapter_get(&id).await,
        },
        Command::Category { command } => match command {
            CategoryCommand::List { limit, offset } => commands::category_list(limit, offset).await,
            CategoryCommand::Get { id } => commands::category_get(&id).await,
            CategoryCommand::Playlists { id, limit, offset } => {
                commands::category_playlists(&id, limit, offset).await
            }
        },
        Command::Follow { command } => match command {
            FollowCommand::Artist { ids, dry_run } => commands::follow_artist(&ids, dry_run).await,
            FollowCommand::User { ids, dry_run } => commands::follow_user(&ids, dry_run).await,
            FollowCommand::UnfollowArtist { ids, dry_run } => {
                commands::unfollow_artist(&ids, dry_run).await
            }
            FollowCommand::UnfollowUser { ids, dry_run } => {
                commands::unfollow_user(&ids, dry_run).await
            }
            FollowCommand::List { limit } => commands::follow_list(limit).await,
            FollowCommand::CheckArtist { ids } => commands::follow_check_artist(&ids).await,
            FollowCommand::CheckUser { ids } => commands::follow_check_user(&ids).await,
        },
        Command::Markets => commands::markets_list().await,
        #[cfg(unix)]
        Command::Daemon { command } => match command {
            DaemonCommand::Start => commands::daemon_start().await,
            DaemonCommand::Stop => commands::daemon_stop().await,
            DaemonCommand::Status => commands::daemon_status().await,
            DaemonCommand::Run => commands::daemon_run().await,
        },
        Command::Completions { shell } => {
            print_completions(shell);
            return;
        }
    };

    if cli.json {
        print_json(&response);
    } else {
        print_human(&response);
    }
}