Add stock info, fix sunrise sorting
This commit is contained in:
parent
3d0cf7dafb
commit
6ddf5d7201
7 changed files with 66 additions and 10 deletions
|
@ -70,6 +70,7 @@ impl MusicSearchStore for ProStudioMasters {
|
||||||
},
|
},
|
||||||
}).into(),
|
}).into(),
|
||||||
price: StorePrice { cents: 0, currency: StoreCurrency::Unknown },
|
price: StorePrice { cents: 0, currency: StoreCurrency::Unknown },
|
||||||
|
stock: crate::StoreStock::Available,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,7 +161,8 @@ impl std::convert::Into<StoreEntry> for ArtistSearchResult {
|
||||||
image: Some(self.image)
|
image: Some(self.image)
|
||||||
}).into(),
|
}).into(),
|
||||||
store: "7digital".to_owned(),
|
store: "7digital".to_owned(),
|
||||||
price: StorePrice { cents: 0, currency: StoreCurrency::Unknown }
|
price: StorePrice { cents: 0, currency: StoreCurrency::Unknown },
|
||||||
|
stock: crate::StoreStock::Available,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -200,7 +201,8 @@ impl std::convert::Into<StoreEntry> for ReleaseSearchResult {
|
||||||
}
|
}
|
||||||
}).into(),
|
}).into(),
|
||||||
store: "7digital".to_owned(),
|
store: "7digital".to_owned(),
|
||||||
price: StorePrice { cents: 0, currency: StoreCurrency::Unknown }
|
price: StorePrice { cents: 0, currency: StoreCurrency::Unknown },
|
||||||
|
stock: crate::StoreStock::Available,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -255,7 +257,8 @@ impl std::convert::Into<StoreEntry> for TrackSearchResult {
|
||||||
}),
|
}),
|
||||||
}).into(),
|
}).into(),
|
||||||
store: "7digital".to_owned(),
|
store: "7digital".to_owned(),
|
||||||
price: StorePrice { cents: 0, currency: StoreCurrency::Unknown }
|
price: StorePrice { cents: 0, currency: StoreCurrency::Unknown },
|
||||||
|
stock: crate::StoreStock::Available,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,8 @@ pub struct StoreEntry {
|
||||||
pub extra: StoreExtra,
|
pub extra: StoreExtra,
|
||||||
/// Price information
|
/// Price information
|
||||||
pub price: StorePrice,
|
pub price: StorePrice,
|
||||||
|
/// Stock information
|
||||||
|
pub stock: StoreStock,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A store identifier
|
/// A store identifier
|
||||||
|
@ -61,6 +63,50 @@ impl std::fmt::Display for StoreId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Stock of a search result
|
||||||
|
pub enum StoreStock {
|
||||||
|
/// Out of stock
|
||||||
|
SoldOut,
|
||||||
|
/// In stock, but amount unknown or not applicable (for digital items)
|
||||||
|
Available,
|
||||||
|
/// In stock with <count> available
|
||||||
|
Count(u64),
|
||||||
|
/// Stock is unknown
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StoreStock {
|
||||||
|
/// Store definitely has at least 1 of the item
|
||||||
|
pub fn has_stock(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Available => true,
|
||||||
|
Self::Count(n) => *n > 0,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Store is definitely out of stock of the item
|
||||||
|
pub fn is_oos(&self) -> bool {
|
||||||
|
matches!(self, Self::SoldOut)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Store does not know the stock of the item
|
||||||
|
pub fn is_unknown(&self) -> bool {
|
||||||
|
matches!(self, Self::Unknown)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for StoreStock {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::SoldOut => write!(f, "OOS"),
|
||||||
|
Self::Available => write!(f, "InStock"),
|
||||||
|
Self::Count(n) => write!(f, "InStock:{}", n),
|
||||||
|
Self::Unknown => write!(f, "???"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Store price information
|
/// Store price information
|
||||||
pub struct StorePrice {
|
pub struct StorePrice {
|
||||||
/// Price of store result, in 100ths of a dollar
|
/// Price of store result, in 100ths of a dollar
|
||||||
|
|
|
@ -195,6 +195,8 @@ impl std::convert::Into<StoreEntry> for ProductInfo {
|
||||||
cents: (self.salePrice * 100.0) as _,
|
cents: (self.salePrice * 100.0) as _,
|
||||||
currency: StoreCurrency::Cad,
|
currency: StoreCurrency::Cad,
|
||||||
},
|
},
|
||||||
|
stock: crate::StoreStock::Unknown, // TODO do query to API endpoint that provides this info
|
||||||
|
// e.g. https://www.bestbuy.ca/ecomm-api/availability/products?accept=application/vnd.bestbuy.simpleproduct.v1+json&accept-language=en-CA&locations=940|928|972|639|627|224|975&postalCode=K1R&skus=M2234719
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use serde::Deserialize;
|
||||||
use super::GeneralSearchStore;
|
use super::GeneralSearchStore;
|
||||||
use crate::audio::{AudioExtra, music::{MusicExtra, MusicMediumType}};
|
use crate::audio::{AudioExtra, music::{MusicExtra, MusicMediumType}};
|
||||||
use crate::video::{VideoExtra, VideoFormat};
|
use crate::video::{VideoExtra, VideoFormat};
|
||||||
use crate::{MuniteError, StoreEntry, StorePrice, StoreCurrency, StoreType, StoreId, StoreExtra};
|
use crate::{MuniteError, StoreEntry, StorePrice, StoreCurrency, StoreType, StoreId, StoreExtra, StoreStock};
|
||||||
|
|
||||||
/// BestBuy Canada store client
|
/// BestBuy Canada store client
|
||||||
pub struct SunriseRecords {
|
pub struct SunriseRecords {
|
||||||
|
@ -24,11 +24,12 @@ impl SunriseRecords {
|
||||||
.query(&[
|
.query(&[
|
||||||
("page", &page.to_string() as &str),
|
("page", &page.to_string() as &str),
|
||||||
("limit", &page_size.to_string() as &str),
|
("limit", &page_size.to_string() as &str),
|
||||||
(&format!("controls[{}]", page), query),
|
("controls[1]", query),
|
||||||
("sortBy", sort_by),
|
("sortBy", sort_by),
|
||||||
])
|
])
|
||||||
//.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; rv:108.0) Gecko/20100101 Firefox/108.0")
|
//.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; rv:108.0) Gecko/20100101 Firefox/108.0")
|
||||||
.header("Accept", "application/json, text/javascript, */*; q=0.01")
|
.header("Accept", "application/json, text/javascript, */*; q=0.01")
|
||||||
|
.header("DNT", "1")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_store(&self, query: &str, page: u64, page_size: u64, sort_by: &str) -> RequestBuilder {
|
fn search_store(&self, query: &str, page: u64, page_size: u64, sort_by: &str) -> RequestBuilder {
|
||||||
|
@ -54,9 +55,9 @@ const MAX_PAGE_SIZE: u64 = 48; // Anything larger is clamped back down to this v
|
||||||
impl GeneralSearchStore for SunriseRecords {
|
impl GeneralSearchStore for SunriseRecords {
|
||||||
async fn search(&self, s: String, category: StoreType) -> crate::MuniteResult<Vec<StoreEntry>> {
|
async fn search(&self, s: String, category: StoreType) -> crate::MuniteResult<Vec<StoreEntry>> {
|
||||||
let result = match category {
|
let result = match category {
|
||||||
StoreType::General => self.search_store(&s, 1, MAX_PAGE_SIZE, "stock_desc").send().await,
|
StoreType::General => self.search_store(&s, 1, MAX_PAGE_SIZE, "relevance_desc").send().await,
|
||||||
StoreType::Video(_) => self.search_store(&s, 1, MAX_PAGE_SIZE, "stock_desc").send().await,
|
StoreType::Video(_) => self.search_store(&s, 1, MAX_PAGE_SIZE, "relevance_desc").send().await,
|
||||||
StoreType::Audio(_) => self.search_store(&s, 1, MAX_PAGE_SIZE, "stock_desc").send().await,
|
StoreType::Audio(_) => self.search_store(&s, 1, MAX_PAGE_SIZE, "relevance_desc").send().await,
|
||||||
}.map_err(|e| MuniteError::Http(e))?;
|
}.map_err(|e| MuniteError::Http(e))?;
|
||||||
let json_data: SearchResultsResponse = result.json().await.map_err(|e| MuniteError::Http(e))?;
|
let json_data: SearchResultsResponse = result.json().await.map_err(|e| MuniteError::Http(e))?;
|
||||||
let mut output = Vec::with_capacity(json_data.items.len());
|
let mut output = Vec::with_capacity(json_data.items.len());
|
||||||
|
@ -130,7 +131,7 @@ struct ProductInfo {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct StockInfo {
|
struct StockInfo {
|
||||||
isBackordered: bool,
|
isBackordered: bool,
|
||||||
available: u64,
|
available: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case, dead_code)]
|
#[allow(non_snake_case, dead_code)]
|
||||||
|
@ -186,6 +187,7 @@ impl std::convert::Into<StoreEntry> for ProductInfo {
|
||||||
cents: (dollar_price * 100.0) as _,
|
cents: (dollar_price * 100.0) as _,
|
||||||
currency: StoreCurrency::Cad,
|
currency: StoreCurrency::Cad,
|
||||||
},
|
},
|
||||||
|
stock: if self.stock.available <= 0 || self.stock.isBackordered { StoreStock::SoldOut } else { StoreStock::Count(self.stock.available as _) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ mod entry;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod search;
|
mod search;
|
||||||
|
|
||||||
pub use entry::{StoreEntry, StoreExtra, StoreType, StorePrice, StoreCurrency, StoreId};
|
pub use entry::{StoreEntry, StoreExtra, StoreType, StorePrice, StoreCurrency, StoreId, StoreStock};
|
||||||
pub use errors::{MuniteError, MuniteResult};
|
pub use errors::{MuniteError, MuniteResult};
|
||||||
|
|
||||||
pub use search::StoreSearch;
|
pub use search::StoreSearch;
|
||||||
|
|
|
@ -49,6 +49,7 @@ impl CinemaOne {
|
||||||
if let Some(format) = formats.tag("li").class("active").find() {
|
if let Some(format) = formats.tag("li").class("active").find() {
|
||||||
let format_info = match format.text().trim() as &str {
|
let format_info = match format.text().trim() as &str {
|
||||||
"4K" => VideoFormat::BlurayUhd,
|
"4K" => VideoFormat::BlurayUhd,
|
||||||
|
"UHD" => VideoFormat::BlurayUhd, // idk why there's a distinction
|
||||||
"3D BLU-RAY" => VideoFormat::Bluray3d,
|
"3D BLU-RAY" => VideoFormat::Bluray3d,
|
||||||
"BLU-RAY" => VideoFormat::Bluray,
|
"BLU-RAY" => VideoFormat::Bluray,
|
||||||
"DVD" => VideoFormat::Dvd,
|
"DVD" => VideoFormat::Dvd,
|
||||||
|
@ -74,6 +75,7 @@ impl CinemaOne {
|
||||||
info: None, // TODO
|
info: None, // TODO
|
||||||
}.into(),
|
}.into(),
|
||||||
price: price_info,
|
price: price_info,
|
||||||
|
stock: crate::StoreStock::Available,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue