1
0
Fork 0
mirror of https://github.com/librespot-org/librespot.git synced 2025-10-03 09:49:31 +02:00

fix: add fallback logic for CDN urls (#1524)

This commit is contained in:
./lemon.sh 2025-08-08 16:32:20 +02:00 committed by GitHub
parent be37402421
commit 3a700f0020
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 81 additions and 25 deletions

View file

@ -78,6 +78,7 @@ impl CdnUrl {
Ok(cdn_url)
}
#[deprecated = "This function only returns the first valid URL. Use try_get_urls instead, which allows for fallback logic."]
pub fn try_get_url(&self) -> Result<&str, Error> {
if self.urls.is_empty() {
return Err(CdnUrlError::Unresolved.into());
@ -95,6 +96,34 @@ impl CdnUrl {
Err(CdnUrlError::Expired.into())
}
}
pub fn try_get_urls(&self) -> Result<Vec<&str>, Error> {
if self.urls.is_empty() {
return Err(CdnUrlError::Unresolved.into());
}
let now = Date::now_utc();
let urls: Vec<&str> = self
.urls
.iter()
.filter_map(|MaybeExpiringUrl(url, expiry)| match *expiry {
Some(expiry) => {
if now < expiry {
Some(url.as_str())
} else {
None
}
}
None => Some(url.as_str()),
})
.collect();
if urls.is_empty() {
Err(CdnUrlError::Expired.into())
} else {
Ok(urls)
}
}
}
impl TryFrom<CdnUrlMessage> for MaybeExpiringUrls {

View file

@ -6,7 +6,6 @@ use std::{
use crate::config::{os_version, OS};
use crate::{
apresolve::SocketAddress,
cdn_url::CdnUrl,
config::SessionConfig,
error::ErrorKind,
protocol::{
@ -27,7 +26,7 @@ use crate::{
use bytes::Bytes;
use data_encoding::HEXUPPER_PERMISSIVE;
use futures_util::future::IntoStream;
use http::header::HeaderValue;
use http::{header::HeaderValue, Uri};
use hyper::{
header::{HeaderName, ACCEPT, AUTHORIZATION, CONTENT_TYPE, RANGE},
HeaderMap, Method, Request,
@ -730,16 +729,19 @@ impl SpClient {
self.request(&Method::GET, &endpoint, None, None).await
}
pub fn stream_from_cdn(
pub fn stream_from_cdn<U>(
&self,
cdn_url: &CdnUrl,
cdn_url: U,
offset: usize,
length: usize,
) -> Result<IntoStream<ResponseFuture>, Error> {
let url = cdn_url.try_get_url()?;
) -> Result<IntoStream<ResponseFuture>, Error>
where
U: TryInto<Uri>,
<U as TryInto<Uri>>::Error: Into<http::Error>,
{
let req = Request::builder()
.method(&Method::GET)
.uri(url)
.uri(cdn_url)
.header(
RANGE,
HeaderValue::from_str(&format!("bytes={}-{}", offset, offset + length - 1))?,