mirror of
https://github.com/GuillemCastro/spotify-dl.git
synced 2024-11-10 05:20:25 +01:00
merged with upstream
This commit is contained in:
commit
4b4861e0f8
6 changed files with 157 additions and 87 deletions
36
.github/workflows/rust.yml
vendored
36
.github/workflows/rust.yml
vendored
|
@ -3,8 +3,6 @@ name: Rust
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "master" ]
|
branches: [ "master" ]
|
||||||
pull_request:
|
|
||||||
branches: [ "master" ]
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
|
@ -12,16 +10,36 @@ env:
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
platform: [macos-latest, ubuntu-latest, windows-latest]
|
||||||
|
include:
|
||||||
|
- platform: macos-latest
|
||||||
|
target: macos-x86_64
|
||||||
|
- platform: ubuntu-latest
|
||||||
|
target: linux-x86_64
|
||||||
|
- platform: windows-latest
|
||||||
|
target: windows-x86_64
|
||||||
|
# platform: [ubuntu-latest]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Install Dependencies
|
- name: Setup cmake
|
||||||
run: sudo apt install libflac-dev build-essential pkg-config alsa libasound2-dev
|
uses: jwlawson/actions-setup-cmake@v1.13
|
||||||
|
- name: install Rust stable
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
- if: matrix.platform == 'ubuntu-latest'
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libasound2-dev gcc alsa
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cargo build --verbose --release
|
run: |
|
||||||
- name: Run tests
|
cargo build --verbose --release
|
||||||
run: cargo test --verbose
|
- name: Rename binary
|
||||||
|
run: |
|
||||||
|
mv target/release/spotify-dl* target/release/spotify-dl.${{ matrix.target }}
|
||||||
- uses: "marvinpinto/action-automatic-releases@latest"
|
- uses: "marvinpinto/action-automatic-releases@latest"
|
||||||
with:
|
with:
|
||||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
@ -30,4 +48,4 @@ jobs:
|
||||||
title: "Latest Build"
|
title: "Latest Build"
|
||||||
files: |
|
files: |
|
||||||
LICENSE
|
LICENSE
|
||||||
target/release/spotify-dl
|
target/release/spotify-dl*
|
||||||
|
|
52
Cargo.lock
generated
52
Cargo.lock
generated
|
@ -51,6 +51,15 @@ dependencies = [
|
||||||
"opaque-debug",
|
"opaque-debug",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "0.7.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alsa"
|
name = "alsa"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
|
@ -261,6 +270,15 @@ dependencies = [
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cmake"
|
||||||
|
version = "0.1.49"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "db34956e100b30725f2eb215f90d4871051239535632f84fea3bc92722c66b7c"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "combine"
|
name = "combine"
|
||||||
version = "4.6.4"
|
version = "4.6.4"
|
||||||
|
@ -466,19 +484,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flac-bound"
|
name = "flac-bound"
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "750099df417963398b7d52cfbdbda53aa20c64d553d33cacedc872fa30cb8a00"
|
checksum = "d438dc79612e982e62d0d86a1fd434b4f536cddf156ce02063cd56fc3d8d426c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"flac-sys",
|
"libflac-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "flac-sys"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b5ccca1896065c6c3927147467ab3d042565607c01194e36560e84344a02c5c0"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.0.24"
|
version = "1.0.24"
|
||||||
|
@ -952,6 +964,16 @@ version = "0.2.126"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libflac-sys"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "358a7beee1a8a2b28876814d6dbf06126255b062a59f02779143ec68cb9e54e7"
|
||||||
|
dependencies = [
|
||||||
|
"cmake",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
version = "0.7.3"
|
version = "0.7.3"
|
||||||
|
@ -1700,18 +1722,20 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.5.6"
|
version = "1.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1"
|
checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
"regex-syntax",
|
"regex-syntax",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.6.26"
|
version = "0.6.28"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
|
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "remove_dir_all"
|
name = "remove_dir_all"
|
||||||
|
@ -1900,7 +1924,7 @@ dependencies = [
|
||||||
"futures-state-stream",
|
"futures-state-stream",
|
||||||
"indicatif",
|
"indicatif",
|
||||||
"librespot",
|
"librespot",
|
||||||
"pkg-config",
|
"regex",
|
||||||
"rpassword",
|
"rpassword",
|
||||||
"structopt",
|
"structopt",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
|
|
@ -3,7 +3,7 @@ name = "spotify-dl"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Guillem Castro <guillemcastro4@gmail.com>"]
|
authors = ["Guillem Castro <guillemcastro4@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
links = "FLAC"
|
#links = "FLAC"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
futures = "0.1"
|
futures = "0.1"
|
||||||
|
@ -13,11 +13,12 @@ rpassword = "5.0"
|
||||||
indicatif = "0.15.0"
|
indicatif = "0.15.0"
|
||||||
librespot = "0.3.1"
|
librespot = "0.3.1"
|
||||||
tokio = { version = "1.18.2", features = ["rt"] }
|
tokio = { version = "1.18.2", features = ["rt"] }
|
||||||
flac-bound = { version = "0.2.0" }
|
flac-bound = { version = "0.3.0", default-features = false, features = ["libflac-noogg"] }
|
||||||
audiotags = "0.2.7182"
|
audiotags = "0.2.7182"
|
||||||
|
regex = "1.7.1"
|
||||||
|
|
||||||
[package.metadata.deb]
|
[package.metadata.deb]
|
||||||
depends="libflac-dev"
|
depends="libflac-dev"
|
||||||
|
|
||||||
[build-dependencies]
|
# [build-dependencies]
|
||||||
pkg-config = "0.3.16"
|
# pkg-config = "0.3.16"
|
||||||
|
|
|
@ -32,21 +32,23 @@ spotify-dl 0.1.0
|
||||||
A commandline utility to download music directly from Spotify
|
A commandline utility to download music directly from Spotify
|
||||||
|
|
||||||
USAGE:
|
USAGE:
|
||||||
spotify-dl [OPTIONS] --username <username> [tracks]...
|
spotify-dl [FLAGS] [OPTIONS] --username <username> [tracks]...
|
||||||
|
|
||||||
FLAGS:
|
FLAGS:
|
||||||
-h, --help Prints help information
|
-h, --help Prints help information
|
||||||
|
-o, --ordered Download songs in the order they are in the playlist, prfixing the filename with the track number
|
||||||
-V, --version Prints version information
|
-V, --version Prints version information
|
||||||
|
|
||||||
OPTIONS:
|
OPTIONS:
|
||||||
-d, --destination <destination> The directory where the songs will be downloaded [default: .]
|
-d, --destination <destination> The directory where the songs will be downloaded [default: .]
|
||||||
|
-p, --password <password> Your Spotify password
|
||||||
-u, --username <username> Your Spotify username
|
-u, --username <username> Your Spotify username
|
||||||
|
|
||||||
ARGS:
|
ARGS:
|
||||||
<tracks>... A list of Spotify URIs (songs, podcasts or playlists)
|
<tracks>... A list of Spotify URIs (songs, podcasts or playlists)
|
||||||
```
|
```
|
||||||
|
|
||||||
Songs and playlists must be passed as Spotify URIs (e.g. `spotify:track:123456789abcdefghABCDEF` for songs and `spotify:playlist:123456789abcdefghABCDEF` for playlists).
|
Songs and playlists must be passed as Spotify URIs or URLs (e.g. `spotify:track:123456789abcdefghABCDEF` for songs and `spotify:playlist:123456789abcdefghABCDEF` for playlists or `https://open.spotify.com/playlist/123456789abcdefghABCDEF?si=1234567890`).
|
||||||
|
|
||||||
## Disclaimer
|
## Disclaimer
|
||||||
|
|
||||||
|
|
29
build.rs
29
build.rs
|
@ -1,29 +0,0 @@
|
||||||
use std::env;
|
|
||||||
use std::fs;
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let library = pkg_config::Config::new().probe("flac").unwrap();
|
|
||||||
let profile = env::var_os("PROFILE").unwrap();
|
|
||||||
|
|
||||||
let lib_flac_from = String::from(
|
|
||||||
if env::consts::OS == "macos"{
|
|
||||||
"libFLAC.dylib"
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
"libFLAC.so"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
let lib_flac_to = String::from(
|
|
||||||
if env::consts::OS == "macos"{
|
|
||||||
"libFLAC.dylib"
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
"libflac.so"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
let from = library.link_paths.get(0).unwrap().join(lib_flac_from);
|
|
||||||
let to = Path::new("target").join(profile).join("deps").join(lib_flac_to);
|
|
||||||
fs::copy(from, to).unwrap();
|
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
|
||||||
}
|
|
112
src/main.rs
112
src/main.rs
|
@ -2,26 +2,30 @@ mod file_sink;
|
||||||
|
|
||||||
extern crate rpassword;
|
extern crate rpassword;
|
||||||
|
|
||||||
use std::{path::PathBuf};
|
use std::path::PathBuf;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use librespot::{core::authentication::Credentials, metadata::Playlist};
|
|
||||||
use librespot::core::config::SessionConfig;
|
use librespot::core::config::SessionConfig;
|
||||||
use librespot::core::session::Session;
|
use librespot::core::session::Session;
|
||||||
use librespot::core::spotify_id::SpotifyId;
|
use librespot::core::spotify_id::SpotifyId;
|
||||||
use librespot::playback::config::PlayerConfig;
|
use librespot::playback::config::PlayerConfig;
|
||||||
|
use librespot::{core::authentication::Credentials, metadata::Playlist};
|
||||||
|
|
||||||
|
use librespot::playback::audio_backend::Open;
|
||||||
use librespot::playback::player::Player;
|
use librespot::playback::player::Player;
|
||||||
use librespot::playback::audio_backend::{Open};
|
|
||||||
|
|
||||||
use librespot::metadata::{Track, Artist, Metadata, Album};
|
use librespot::metadata::{Album, Artist, Metadata, Track};
|
||||||
|
|
||||||
|
use regex::Regex;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use indicatif::{ProgressBar, ProgressStyle};
|
use indicatif::{ProgressBar, ProgressStyle};
|
||||||
|
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, StructOpt)]
|
||||||
#[structopt(name = "spotify-dl", about = "A commandline utility to download music directly from Spotify")]
|
#[structopt(
|
||||||
|
name = "spotify-dl",
|
||||||
|
about = "A commandline utility to download music directly from Spotify"
|
||||||
|
)]
|
||||||
struct Opt {
|
struct Opt {
|
||||||
#[structopt(help = "A list of Spotify URIs (songs, podcasts or playlists)")]
|
#[structopt(help = "A list of Spotify URIs (songs, podcasts or playlists)")]
|
||||||
tracks: Vec<String>,
|
tracks: Vec<String>,
|
||||||
|
@ -29,8 +33,19 @@ struct Opt {
|
||||||
username: String,
|
username: String,
|
||||||
#[structopt(short = "p", long = "password", help = "Your Spotify password")]
|
#[structopt(short = "p", long = "password", help = "Your Spotify password")]
|
||||||
password: Option<String>,
|
password: Option<String>,
|
||||||
#[structopt(short = "d", long = "destination", default_value = ".", help = "The directory where the songs will be downloaded")]
|
#[structopt(
|
||||||
destination: String
|
short = "d",
|
||||||
|
long = "destination",
|
||||||
|
default_value = ".",
|
||||||
|
help = "The directory where the songs will be downloaded"
|
||||||
|
)]
|
||||||
|
destination: String,
|
||||||
|
#[structopt(
|
||||||
|
short = "o",
|
||||||
|
long = "ordered",
|
||||||
|
help = "Prefixing the filename with its index in the playlist"
|
||||||
|
)]
|
||||||
|
ordered: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -42,7 +57,9 @@ pub struct TrackMetadata {
|
||||||
|
|
||||||
async fn create_session(credentials: Credentials) -> Session {
|
async fn create_session(credentials: Credentials) -> Session {
|
||||||
let session_config = SessionConfig::default();
|
let session_config = SessionConfig::default();
|
||||||
let session = Session::connect(session_config, credentials, None).await.unwrap();
|
let session = Session::connect(session_config, credentials, None)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
session
|
session
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +74,12 @@ fn make_filename_compatible(filename: &str) -> String {
|
||||||
clean
|
clean
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn download_tracks(session: &Session, destination: PathBuf, tracks: Vec<SpotifyId>) {
|
async fn download_tracks(
|
||||||
|
session: &Session,
|
||||||
|
destination: PathBuf,
|
||||||
|
tracks: Vec<SpotifyId>,
|
||||||
|
ordered: bool,
|
||||||
|
) {
|
||||||
let player_config = PlayerConfig::default();
|
let player_config = PlayerConfig::default();
|
||||||
let bar_style = ProgressStyle::default_bar()
|
let bar_style = ProgressStyle::default_bar()
|
||||||
.template("[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} (ETA: {eta}) {msg}")
|
.template("[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} (ETA: {eta}) {msg}")
|
||||||
|
@ -66,14 +88,14 @@ async fn download_tracks(session: &Session, destination: PathBuf, tracks: Vec<Sp
|
||||||
bar.set_style(bar_style);
|
bar.set_style(bar_style);
|
||||||
bar.enable_steady_tick(500);
|
bar.enable_steady_tick(500);
|
||||||
|
|
||||||
for track in tracks {
|
for (i, track) in tracks.iter().enumerate() {
|
||||||
let track_item = Track::get(&session, track).await.unwrap();
|
let track_item = Track::get(&session, *track).await.unwrap();
|
||||||
let artist_name: String;
|
let artist_name: String;
|
||||||
|
|
||||||
let mut metadata = TrackMetadata {
|
let mut metadata = TrackMetadata {
|
||||||
artists: Vec::new(),
|
artists: Vec::new(),
|
||||||
track_name: track_item.name,
|
track_name: track_item.name,
|
||||||
album: Album::get(session, track_item.album).await.unwrap().name
|
album: Album::get(session, track_item.album).await.unwrap().name,
|
||||||
};
|
};
|
||||||
if track_item.artists.len() > 1 {
|
if track_item.artists.len() > 1 {
|
||||||
let mut tmp: String = String::new();
|
let mut tmp: String = String::new();
|
||||||
|
@ -84,19 +106,27 @@ async fn download_tracks(session: &Session, destination: PathBuf, tracks: Vec<Sp
|
||||||
tmp.push_str(", ");
|
tmp.push_str(", ");
|
||||||
}
|
}
|
||||||
artist_name = String::from(tmp.trim_end_matches(", "));
|
artist_name = String::from(tmp.trim_end_matches(", "));
|
||||||
}
|
} else {
|
||||||
else {
|
artist_name = Artist::get(&session, track_item.artists[0])
|
||||||
artist_name = Artist::get(&session, track_item.artists[0]).await.unwrap().name;
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.name;
|
||||||
metadata.artists.push(artist_name.clone());
|
metadata.artists.push(artist_name.clone());
|
||||||
}
|
}
|
||||||
let full_track_name = format!("{} - {}", artist_name, metadata.track_name);
|
let full_track_name = format!("{} - {}", artist_name, metadata.track_name);
|
||||||
let full_track_name_clean = make_filename_compatible(full_track_name.as_str());
|
let full_track_name_clean = make_filename_compatible(full_track_name.as_str());
|
||||||
let filename = format!("{}.flac", full_track_name_clean);
|
//let filename = format!("{}.flac", full_track_name_clean);
|
||||||
|
let filename: String;
|
||||||
|
if ordered {
|
||||||
|
filename = format!("{:03} - {}.flac", i + 1, full_track_name_clean);
|
||||||
|
} else {
|
||||||
|
filename = format!("{}.flac", full_track_name_clean);
|
||||||
|
}
|
||||||
let joined_path = destination.join(&filename);
|
let joined_path = destination.join(&filename);
|
||||||
let path = joined_path.to_str().unwrap();
|
let path = joined_path.to_str().unwrap();
|
||||||
bar.set_message(full_track_name_clean.as_str());
|
bar.set_message(full_track_name_clean.as_str());
|
||||||
|
|
||||||
|
|
||||||
let file_name = Path::new(path)
|
let file_name = Path::new(path)
|
||||||
.file_stem()
|
.file_stem()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -118,12 +148,16 @@ async fn download_tracks(session: &Session, destination: PathBuf, tracks: Vec<Sp
|
||||||
}
|
}
|
||||||
|
|
||||||
if !file_exists {
|
if !file_exists {
|
||||||
let mut file_sink = file_sink::FileSink::open(Some(path.to_owned()), librespot::playback::config::AudioFormat::S16);
|
let mut file_sink = file_sink::FileSink::open(
|
||||||
|
Some(path.to_owned()),
|
||||||
|
librespot::playback::config::AudioFormat::S16
|
||||||
|
);
|
||||||
file_sink.add_metadata(metadata);
|
file_sink.add_metadata(metadata);
|
||||||
let (mut player, _) = Player::new(player_config.clone(), session.clone(), None, move || {
|
let (mut player, _) =
|
||||||
Box::new(file_sink)
|
Player::new(player_config.clone(), session.clone(), None, move || {
|
||||||
});
|
Box::new(file_sink)
|
||||||
player.load(track, true, 0);
|
});
|
||||||
|
player.load(*track, true, 0);
|
||||||
player.await_end_of_track().await;
|
player.await_end_of_track().await;
|
||||||
player.stop();
|
player.stop();
|
||||||
bar.inc(1);
|
bar.inc(1);
|
||||||
|
@ -137,11 +171,12 @@ async fn download_tracks(session: &Session, destination: PathBuf, tracks: Vec<Sp
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
|
||||||
let opt = Opt::from_args();
|
let opt = Opt::from_args();
|
||||||
|
|
||||||
let username = opt.username;
|
let username = opt.username;
|
||||||
let password = opt.password.unwrap_or_else(|| rpassword::read_password_from_tty(Some("Password: ")).unwrap());
|
let password = opt
|
||||||
|
.password
|
||||||
|
.unwrap_or_else(|| rpassword::read_password_from_tty(Some("Password: ")).unwrap());
|
||||||
let credentials = Credentials::with_password(username, password);
|
let credentials = Credentials::with_password(username, password);
|
||||||
|
|
||||||
let session = create_session(credentials.clone()).await;
|
let session = create_session(credentials.clone()).await;
|
||||||
|
@ -149,7 +184,18 @@ async fn main() {
|
||||||
let mut tracks: Vec<SpotifyId> = Vec::new();
|
let mut tracks: Vec<SpotifyId> = Vec::new();
|
||||||
|
|
||||||
for track_url in opt.tracks {
|
for track_url in opt.tracks {
|
||||||
let track = SpotifyId::from_uri(track_url.as_str()).unwrap();
|
let track = SpotifyId::from_uri(track_url.as_str()).unwrap_or_else(|_| {
|
||||||
|
let regex = Regex::new(r"https://open.spotify.com/(\w+)/(.*)\?").unwrap();
|
||||||
|
|
||||||
|
let results = regex.captures(track_url.as_str()).unwrap();
|
||||||
|
let uri = format!(
|
||||||
|
"spotify:{}:{}",
|
||||||
|
results.get(1).unwrap().as_str(),
|
||||||
|
results.get(2).unwrap().as_str()
|
||||||
|
);
|
||||||
|
|
||||||
|
SpotifyId::from_uri(&uri).unwrap()
|
||||||
|
});
|
||||||
match &track.audio_type {
|
match &track.audio_type {
|
||||||
librespot::core::spotify_id::SpotifyAudioType::Track => {
|
librespot::core::spotify_id::SpotifyAudioType::Track => {
|
||||||
tracks.push(track);
|
tracks.push(track);
|
||||||
|
@ -160,7 +206,10 @@ async fn main() {
|
||||||
librespot::core::spotify_id::SpotifyAudioType::NonPlayable => {
|
librespot::core::spotify_id::SpotifyAudioType::NonPlayable => {
|
||||||
match Playlist::get(&session, track).await {
|
match Playlist::get(&session, track).await {
|
||||||
Ok(mut playlist) => {
|
Ok(mut playlist) => {
|
||||||
println!("Adding all songs from playlist {} (by {}) to the queue", &playlist.name, &playlist.user);
|
println!(
|
||||||
|
"Adding all songs from playlist {} (by {}) to the queue",
|
||||||
|
&playlist.name, &playlist.user
|
||||||
|
);
|
||||||
tracks.append(&mut playlist.tracks);
|
tracks.append(&mut playlist.tracks);
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
|
@ -171,6 +220,11 @@ async fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
download_tracks(&session, PathBuf::from(opt.destination), tracks).await;
|
download_tracks(
|
||||||
|
&session,
|
||||||
|
PathBuf::from(opt.destination),
|
||||||
|
tracks,
|
||||||
|
opt.ordered,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue