fix: allow downloading again

This commit is contained in:
oSumAtrIX 2023-06-17 01:22:04 +02:00
parent 38dcdb8651
commit aa3c09b112
No known key found for this signature in database
GPG key ID: A9B3094ACDB604B4
8 changed files with 1619 additions and 849 deletions

2285
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -7,7 +7,7 @@ panic = "abort"
[package]
name = "down_on_spot"
version = "0.2.3"
version = "0.2.4"
edition = "2021"
authors = ["exttex", "oSumAtrIX"]
build = "build.rs"
@ -16,20 +16,20 @@ build = "build.rs"
winres = "0.1"
[dependencies]
clap = "3.2"
clap = "4.2.1"
log = "0.4"
url = "2.2"
protobuf = "3.1"
id3 = "1.3"
dirs = "4.0"
dirs = "5.0.0"
chrono = "0.4"
lewton = "0.10"
futures = "0.3"
reqwest = "0.11"
colored = "2"
lame = "0.1"
aspotify = "0.7"
librespot = { git = "ssh://git@github.com/oSumAtrIX/free-librespot.git" }
aspotify = "0.7.1"
librespot = { git = "https://github.com/librespot-org/librespot.git", rev = "4d402e690c67457ca2d462670db39330bbceb4cf" }
async-std = { version = "1.12", features = ["attributes", "tokio1"] }
serde_json = "1.0"
async-stream = "0.3"
@ -42,4 +42,4 @@ tokio = { version = "1.20", features = ["fs"] }
OriginalFilename = "DownOnSpot.exe"
FileDescription = "Download songs from Spotify with Rust"
ProductName = "DownOnSpot"
ProductVersion = "0.2.3"
ProductVersion = "0.2.4"

View file

@ -45,19 +45,6 @@ git clone https://github.com/oSumAtrIX/DownOnSpot.git
cd DownOnSpot
```
A [private ssh key](https://osumatrix.me/ucp?get=free_librespot_private_key&token=fdfdbff6f5) is needed to use free Spotify accounts.
Follow [this answer by DopeGhoti on stackexchange.com](https://unix.stackexchange.com/a/494485) on how to set up ssh with the required private key.
A sample `~/.ssh/config` file could look like this:
```text
Host github.com
IdentityFile ~/.ssh/free_librespot_private_key
```
If you do not want to use `free-librespot` (i.e. if you are using a paid Spotify account), then remove the git dependency of `free-librespot`.
For that, delete `git = "ssh://git@github.com/oSumAtrIX/free-librespot.git"` inside `Cargo.toml`.
For paid Spotify accounts, make sure to then add `librespot = "0.4.2"` in the `Cargo.toml` file instead.
`Nightly Rust` is required to build this project. Install it by following [rustup.rs](https://rustup.rs) instructions.
```bash
@ -80,6 +67,7 @@ $ down_on_spot.exe
Usage:
down_on_spot.exe (search_term | track_url | album_url | playlist_url | artist_url)
```
On OS X, the `settings.json` file is created globally for the logged in user and is located in `~/.config/down_on_spot/settings.json`.
Apart from your Spotify username and password, you will need to login in to the Spotify developer dashboard and [create a new private application](https://developer.spotify.com/dashboard/applications). Fill in the `client_id` and `client_secret` in your `settings.json` from your newly created app.
@ -115,7 +103,6 @@ Following variables are available for `path` and `filename_template` in the `set
- [@exttex](https://git.freezer.life/exttex)
- [@breuerfelix](https://github.com/breuerfelix)
- [@thatpix3l](https://github.com/thatpix3l)
- [@45ninjas](https://github.com/45ninjas)
## License

View file

@ -49,7 +49,6 @@ impl AudioConverter {
match format {
AudioFormat::Aac => todo!(),
AudioFormat::Mp4 => todo!(),
// Lewton decoder
AudioFormat::Ogg => {
let decoder = OggStreamReader::new(ReadWrap::new(Box::new(read)))?;

View file

@ -7,7 +7,8 @@ use librespot::audio::{AudioDecrypt, AudioFile};
use librespot::core::audio_key::AudioKey;
use librespot::core::session::Session;
use librespot::core::spotify_id::SpotifyId;
use librespot::metadata::{FileFormat, Metadata, Track};
use librespot::metadata::audio::AudioFileFormat;
use librespot::metadata::{Metadata, Track};
use sanitize_filename::sanitize;
use serde::{Deserialize, Serialize};
use std::io::Read;
@ -76,6 +77,7 @@ impl Downloader {
.into_iter()
.map(SearchResult::from)
.collect();
Ok(Some(results))
}
}
@ -473,12 +475,13 @@ impl DownloaderInternal {
}
async fn find_alternative(session: &Session, track: Track) -> Result<Track, SpotifyError> {
for alt in track.alternatives {
let t = Track::get(&session, alt).await?;
if t.available {
for alt in track.alternatives.0 {
let t = Track::get(&session, &alt).await?;
if !t.availability.is_empty() {
return Ok(t);
}
}
return Err(SpotifyError::Unavailable);
}
@ -492,12 +495,13 @@ impl DownloaderInternal {
job_id: i64,
) -> Result<(PathBuf, AudioFormat), SpotifyError> {
let id = SpotifyId::from_base62(id)?;
let mut track = Track::get(session, id).await?;
let track = Track::get(session, &id).await?;
// TODO: Fallback if unavailable, but no idea how to check if it's available yet
//if track.availability.is_empty() {
// track = DownloaderInternal::find_alternative(session, track).await?;
//}
// Fallback if unavailable
if !track.available {
track = DownloaderInternal::find_alternative(session, track).await?;
}
// Quality fallback
let mut quality = config.quality;
let (mut file_id, mut file_format) = (None, None);
@ -517,6 +521,7 @@ impl DownloaderInternal {
}
warn!("{} Falling back to: {:?}", id.to_base62().unwrap(), quality);
}
let file_id = file_id.ok_or(SpotifyError::Unavailable)?;
let file_format = file_format.unwrap();
@ -541,8 +546,8 @@ impl DownloaderInternal {
let path_clone = path.clone();
let key = session.audio_key().request(track.id, *file_id).await?;
let encrypted = AudioFile::open(session, *file_id, 1024 * 1024, true).await?;
let size = encrypted.get_stream_loader_controller().len();
let encrypted = AudioFile::open(session, *file_id, 1024 * 1024).await?;
let size = encrypted.get_stream_loader_controller()?.len();
// Download
let s = match config.convert_to_mp3 {
true => {
@ -591,7 +596,7 @@ impl DownloaderInternal {
) -> impl Stream<Item = Result<usize, SpotifyError>> {
try_stream! {
let mut file = File::create(path).await?;
let mut decrypted = AudioDecrypt::new(key, encrypted);
let mut decrypted = AudioDecrypt::new(Some(key), encrypted);
// Skip (i guess encrypted shit)
let mut skip: [u8; 0xa7] = [0; 0xa7];
let mut decrypted = tokio::task::spawn_blocking(move || {
@ -629,7 +634,7 @@ impl DownloaderInternal {
) -> impl Stream<Item = Result<usize, SpotifyError>> {
try_stream! {
let mut file = File::create(path).await?;
let mut decrypted = AudioDecrypt::new(key, encrypted);
let mut decrypted = AudioDecrypt::new(Some(key), encrypted);
// Skip (i guess encrypted shit)
let mut skip: [u8; 0xa7] = [0; 0xa7];
let decrypted = tokio::task::spawn_blocking(move || {
@ -669,8 +674,7 @@ pub enum AudioFormat {
Ogg,
Aac,
Mp3,
Mp4,
Unknown,
Flac,
}
impl AudioFormat {
@ -680,50 +684,48 @@ impl AudioFormat {
AudioFormat::Ogg => "ogg",
AudioFormat::Aac => "m4a",
AudioFormat::Mp3 => "mp3",
AudioFormat::Mp4 => "mp4",
AudioFormat::Unknown => "",
AudioFormat::Flac => "flac",
}
.to_string()
}
}
impl From<FileFormat> for AudioFormat {
fn from(f: FileFormat) -> Self {
impl From<AudioFileFormat> for AudioFormat {
fn from(f: AudioFileFormat) -> Self {
match f {
FileFormat::OGG_VORBIS_96 => Self::Ogg,
FileFormat::OGG_VORBIS_160 => Self::Ogg,
FileFormat::OGG_VORBIS_320 => Self::Ogg,
FileFormat::MP3_256 => Self::Mp3,
FileFormat::MP3_320 => Self::Mp3,
FileFormat::MP3_160 => Self::Mp3,
FileFormat::MP3_96 => Self::Mp3,
FileFormat::MP3_160_ENC => Self::Mp3,
FileFormat::MP4_128_DUAL => Self::Mp4,
FileFormat::OTHER3 => Self::Unknown,
FileFormat::AAC_160 => Self::Aac,
FileFormat::AAC_320 => Self::Aac,
FileFormat::MP4_128 => Self::Mp4,
FileFormat::OTHER5 => Self::Unknown,
AudioFileFormat::OGG_VORBIS_96 => Self::Ogg,
AudioFileFormat::OGG_VORBIS_160 => Self::Ogg,
AudioFileFormat::OGG_VORBIS_320 => Self::Ogg,
AudioFileFormat::MP3_256 => Self::Mp3,
AudioFileFormat::MP3_320 => Self::Mp3,
AudioFileFormat::MP3_160 => Self::Mp3,
AudioFileFormat::MP3_96 => Self::Mp3,
AudioFileFormat::MP3_160_ENC => Self::Mp3,
AudioFileFormat::AAC_24 => Self::Aac,
AudioFileFormat::AAC_48 => Self::Aac,
AudioFileFormat::FLAC_FLAC => Self::Flac,
}
}
}
impl Quality {
/// Get librespot FileFormat
pub fn get_file_formats(&self) -> Vec<FileFormat> {
/// Get librespot AudioFileFormat
pub fn get_file_formats(&self) -> Vec<AudioFileFormat> {
match self {
Self::Q320 => vec![
FileFormat::OGG_VORBIS_320,
FileFormat::AAC_320,
FileFormat::MP3_320,
AudioFileFormat::OGG_VORBIS_320,
AudioFileFormat::MP3_320,
AudioFileFormat::FLAC_FLAC,
],
Self::Q256 => vec![FileFormat::MP3_256],
Self::Q256 => vec![AudioFileFormat::MP3_256],
Self::Q160 => vec![
FileFormat::OGG_VORBIS_160,
FileFormat::AAC_160,
FileFormat::MP3_160,
AudioFileFormat::OGG_VORBIS_160,
AudioFileFormat::MP3_160,
AudioFileFormat::MP3_160_ENC,
AudioFileFormat::AAC_24,
AudioFileFormat::AAC_48,
],
Self::Q96 => vec![FileFormat::OGG_VORBIS_96, FileFormat::MP3_96],
Self::Q96 => vec![AudioFileFormat::OGG_VORBIS_96, AudioFileFormat::MP3_96],
}
}

View file

@ -1,3 +1,4 @@
use librespot::core::Error;
use std::fmt;
#[derive(Debug, Clone)]
@ -22,6 +23,12 @@ pub enum SpotifyError {
AlreadyDownloaded,
}
impl From<Error> for SpotifyError {
fn from(e: Error) -> Self {
Self::Error(e.to_string())
}
}
impl std::error::Error for SpotifyError {}
impl fmt::Display for SpotifyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -71,6 +78,8 @@ impl From<librespot::core::session::SessionError> for SpotifyError {
librespot::core::session::SessionError::AuthenticationError(_) => {
SpotifyError::AuthenticationError
}
librespot::core::session::SessionError::NotConnected => SpotifyError::Error("Connection error".to_string()),
librespot::core::session::SessionError::Packet(_) => SpotifyError::Error("Packet Error".to_string()),
}
}
}

View file

@ -15,6 +15,7 @@ use settings::Settings;
use spotify::Spotify;
use std::{
env,
os::windows::process,
time::{Duration, Instant},
};

View file

@ -28,12 +28,13 @@ impl Spotify {
) -> Result<Spotify, SpotifyError> {
// librespot
let credentials = Credentials::with_password(username, password);
let (session, _) = Session::connect(
let session = Session::new(
SessionConfig::default(),
credentials,
Some(Cache::new(Some(Path::new("credentials_cache")), None, None, None).unwrap()),
true,
)
);
session.connect(credentials, true)
.await?;
//aspotify