loading a new Song

main
aprzn 2 years ago
parent 441cc8cb24
commit e865b51c73

7
Cargo.lock generated

@ -2030,6 +2030,7 @@ dependencies = [
"reqwest",
"rodio",
"thiserror",
"urlencoding",
]
[[package]]
@ -2797,6 +2798,12 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "urlencoding"
version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9"
[[package]]
name = "vcpkg"
version = "0.2.15"

@ -6,13 +6,14 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
eframe = {version = "0.20.1"}
rodio = {version = "0.16.0"}
home = {version = "0.5.4"}
base64 = {version = "0.21.0"}
flate2 = {version = "1.0.25"}
eframe = "0.20.1"
rodio = "0.16.0"
home = "0.5.4"
base64 = "0.21.0"
flate2 = "1.0.25"
gd_plist = {git = "https://github.com/Syudagye/gd-plist.git", version = "1.4.0"}
chrono = {version = "0.4.23"}
ordered-float = {version = "3.4.0"}
thiserror = {version = "1.0.38"}
chrono = "0.4.23"
ordered-float = "3.4.0"
thiserror = "1.0.38"
reqwest = {version = "0.11.14", features = [ "blocking" ]}
urlencoding = "2.1.2"

@ -21,6 +21,7 @@ pub enum Song {
Unknown,
}
#[derive(Clone)]
pub struct SongResponse([Option<String>; 9]);
struct Level {
@ -34,16 +35,20 @@ pub struct OuterLevel {
revision: Option<i64>, // k46
}
#[derive(Debug, Error)]
#[derive(Debug, Error, Clone)]
pub enum SongRequestError {
#[error("Request failed")]
ConnectionFailure(#[from] reqwest::Error),
ConnectionFailure,
#[error("Index is not an int?????")]
ParseFailure(#[from] ParseIntError),
#[error("Not a Newgrounds song")]
NotNewgrounds,
}
impl From<reqwest::Error> for SongRequestError {
fn from(_: reqwest::Error) -> Self {Self::ConnectionFailure}
}
impl Song {
pub fn get_response(&self) -> Result<SongResponse, SongRequestError> {
match self {
@ -60,7 +65,7 @@ impl Song {
.split("~|~")
.array_chunks()
.try_for_each(|[id, value]| -> Result<(), SongRequestError> {
out.0[id.parse::<usize>()?] = Some(value.into());
out.0[id.parse::<usize>()? - 1] = Some(value.into());
Ok(())
})
.map(|_| out)
@ -70,6 +75,36 @@ impl Song {
}
}
impl SongResponse {
pub fn id(&self) -> Option<i32> {
self.0[0].and_then(|s| s.parse().ok())
}
pub fn name(&self) -> Option<String> {
self.0[1]
}
pub fn artist_id(&self) -> Option<i32> {
self.0[2].and_then(|s| s.parse().ok())
}
pub fn artist_name(&self) -> Option<String> {
self.0[3]
}
pub fn size(&self) -> Option<i32> {
self.0[4].and_then(|s| s.parse().ok())
}
pub fn video_id(&self) -> Option<String> {
self.0[5]
}
pub fn youtube_url(&self) -> Option<String> {
self.0[6]
}
pub fn song_priority(&self) -> Option<i32> {
self.0[8].and_then(|s| s.parse().ok())
}
pub fn download_link(&self) -> Option<String> {
self.0[9].and_then(|url| urlencoding::decode(&url).ok().map(|url| url.into_owned()))
}
}
impl OuterLevel {
pub fn load_all() -> Vec<OuterLevel> {
let plist = get_local_level_plist();

@ -8,7 +8,9 @@ use eframe::egui;
use std::boxed::Box;
use std::collections::VecDeque;
use std::fs::File;
use std::io::Write;
use thiserror::Error;
use reqwest::blocking as req;
struct PipeDash {
msg_queue: VecDeque<Message>,
@ -59,12 +61,16 @@ struct Song {
enum SongError {
#[error("Not a Newgrounds song")]
NotNewgrounds,
#[error("Song mp3 not downloaded")]
#[error("Song mp3 couldn't be downloaded")]
MissingFile(#[from] std::io::Error),
#[error("Couldn't decode mp3 file")]
BrokenSong(#[from] rodio::decoder::DecoderError),
#[error("Couldn't access song data on servers")]
ServerError(#[from] gd::SongRequestError)
GdServerError(#[from] gd::SongRequestError),
#[error("Couldn't fetch song from Newgrounds")]
NgServerError(#[from] reqwest::Error),
#[error("Missing download link")]
MissingLink,
}
struct BeatRateWidget<'a> {
@ -105,19 +111,31 @@ impl Song {
pub fn try_new(gd_song: gd::Song) -> Result<Self, SongError> {
match &gd_song {
gd::Song::Newgrounds { id } => {
let song_data = gd_song.get_response()?;
file::open(gd::gd_path().join(format!("{}.mp3", id)))
.or_else(|_| {
let song_response = gd_song.get_response();
let song_path = gd::gd_path().join(format!("{}.mp3", id));
File::open(&song_path)
.or_else(|_| song_response
.clone()
.map_err(|e| e.into())
.and_then(|response: gd::SongResponse| -> Result<File, SongError> {
let song_blob = response.download_link()
.ok_or(SongError::MissingLink)
.and_then(|link| Ok(req::get(link)?.bytes()?))?;
let mut file = File::open(&song_path)?;
file.write(&song_blob);
Ok(file)
})
todo!("if file is missing, try to download it from the SongResponse. If that fails, return an error. Otherwise, return file info")
// Ok(Song {
// name: todo!(),
// id,
// decoder: rodio::Decoder::new_mp3(file)?
// })
},
_ => Err(SongError::NotNewgrounds)
)
.and_then(|file| {
let name = song_response
.ok()
.and_then(|response| response.name())
.unwrap_or("".into());
let decoder = rodio::Decoder::new_mp3(file)?;
Ok(Song {name, id: *id, decoder})
})
}
_ => Err(SongError::NotNewgrounds),
}
}
}

Loading…
Cancel
Save