use rust loaders and refactor http fetch

This commit is contained in:
psykose 2024-11-19 13:20:40 +01:00
parent f7033ffb5d
commit 4e078a857a
Signed by: psykose
SSH key fingerprint: SHA256:pRMVjV3kRB6zl+wNx+sV8KoMnPqQAW6v8dNCxsCGZv8
6 changed files with 359 additions and 169 deletions

319
Cargo.lock generated
View file

@ -29,6 +29,18 @@ dependencies = [
"zeroize",
]
[[package]]
name = "ahash"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "aho-corasick"
version = "1.1.3"
@ -38,21 +50,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "anyhow"
version = "1.0.93"
@ -248,12 +245,14 @@ dependencies = [
"gtk4",
"http-cache",
"http-cache-reqwest",
"image",
"libadwaita",
"oo7",
"rand",
"reqwest",
"reqwest-middleware",
"serde",
"serde_json",
"tokio",
"tracing",
"tracing-subscriber",
@ -268,6 +267,19 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "av-data"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fca67ba5d317924c02180c576157afd54babe48a76ebc66ce6d34bb8ba08308e"
dependencies = [
"byte-slice-cast",
"bytes",
"num-derive",
"num-rational",
"num-traits",
]
[[package]]
name = "backtrace"
version = "0.3.74"
@ -310,7 +322,7 @@ version = "0.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f"
dependencies = [
"bitflags",
"bitflags 2.6.0",
"cexpr",
"clang-sys",
"itertools",
@ -324,12 +336,27 @@ dependencies = [
"syn",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "bitreader"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "886559b1e163d56c765bc3a985febb4eee8009f625244511d8ee3c432e08c066"
dependencies = [
"cfg-if",
]
[[package]]
name = "block"
version = "0.1.6"
@ -373,6 +400,12 @@ version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "byte-slice-cast"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c"
[[package]]
name = "bytemuck"
version = "1.19.0"
@ -385,6 +418,12 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "byteorder-lite"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]]
name = "bytes"
version = "1.8.0"
@ -424,7 +463,7 @@ version = "0.20.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7fa699e1d7ae691001a811dda5ef0e3e42e1d4119b26426352989df9e94e3e6"
dependencies = [
"bitflags",
"bitflags 2.6.0",
"cairo-sys-rs",
"glib",
"libc",
@ -438,7 +477,7 @@ checksum = "428290f914b9b86089f60f5d8a9f6e440508e1bcff23b25afd51502b0a2da88f"
dependencies = [
"glib-sys",
"libc",
"system-deps",
"system-deps 7.0.3",
]
[[package]]
@ -468,6 +507,16 @@ dependencies = [
"nom",
]
[[package]]
name = "cfg-expr"
version = "0.15.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02"
dependencies = [
"smallvec",
"target-lexicon",
]
[[package]]
name = "cfg-expr"
version = "0.17.1"
@ -496,13 +545,8 @@ version = "0.4.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"serde",
"wasm-bindgen",
"windows-targets",
]
[[package]]
@ -596,6 +640,28 @@ dependencies = [
"typenum",
]
[[package]]
name = "dav1d"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d4b54a40baf633a71c6f0fb49494a7e4ee7bc26f3e727212b6cb915aa1ea1e1"
dependencies = [
"av-data",
"bitflags 2.6.0",
"dav1d-sys",
"static_assertions",
]
[[package]]
name = "dav1d-sys"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ecb1c5e8f4dc438eedc1b534a54672fb0e0a56035dae6b50162787bd2c50e95"
dependencies = [
"libc",
"system-deps 6.2.2",
]
[[package]]
name = "deranged"
version = "0.3.11"
@ -706,12 +772,30 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "fallible_collections"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a88c69768c0a15262df21899142bc6df9b9b823546d4b4b9a7bc2d6c448ec6fd"
dependencies = [
"hashbrown 0.13.2",
]
[[package]]
name = "fastrand"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
[[package]]
name = "fdeflate"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07c6f4c64c1d33a3111c4466f7365ebdcc37c5bd1ea0d62aae2e3d722aacbedb"
dependencies = [
"simd-adler32",
]
[[package]]
name = "field-offset"
version = "0.3.6"
@ -871,7 +955,7 @@ dependencies = [
"glib-sys",
"gobject-sys",
"libc",
"system-deps",
"system-deps 7.0.3",
]
[[package]]
@ -903,7 +987,7 @@ dependencies = [
"libc",
"pango-sys",
"pkg-config",
"system-deps",
"system-deps 7.0.3",
]
[[package]]
@ -981,7 +1065,7 @@ dependencies = [
"glib-sys",
"gobject-sys",
"libc",
"system-deps",
"system-deps 7.0.3",
"windows-sys 0.52.0",
]
@ -991,7 +1075,7 @@ version = "0.20.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86bd3e4ee7998ab5a135d900db56930cc19ad16681adf245daff54f618b9d5e1"
dependencies = [
"bitflags",
"bitflags 2.6.0",
"futures-channel",
"futures-core",
"futures-executor",
@ -1035,7 +1119,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d0b1827e8621fc42c0dfb228e5d57ff6a71f9699e666ece8113f979ad87c2de"
dependencies = [
"libc",
"system-deps",
"system-deps 7.0.3",
]
[[package]]
@ -1052,7 +1136,7 @@ checksum = "a4c674d2ff8478cf0ec29d2be730ed779fef54415a2fb4b565c52def62696462"
dependencies = [
"glib-sys",
"libc",
"system-deps",
"system-deps 7.0.3",
]
[[package]]
@ -1075,7 +1159,7 @@ dependencies = [
"glib-sys",
"libc",
"pkg-config",
"system-deps",
"system-deps 7.0.3",
]
[[package]]
@ -1106,7 +1190,7 @@ dependencies = [
"graphene-sys",
"libc",
"pango-sys",
"system-deps",
"system-deps 7.0.3",
]
[[package]]
@ -1158,7 +1242,7 @@ dependencies = [
"gsk4-sys",
"libc",
"pango-sys",
"system-deps",
"system-deps 7.0.3",
]
[[package]]
@ -1180,6 +1264,15 @@ dependencies = [
"tracing",
]
[[package]]
name = "hashbrown"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
dependencies = [
"ahash",
]
[[package]]
name = "hashbrown"
version = "0.15.1"
@ -1386,29 +1479,6 @@ dependencies = [
"tracing",
]
[[package]]
name = "iana-time-zone"
version = "0.1.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core 0.52.0",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "icu_collections"
version = "1.5.0"
@ -1548,6 +1618,33 @@ dependencies = [
"icu_properties",
]
[[package]]
name = "image"
version = "0.25.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b"
dependencies = [
"bytemuck",
"byteorder-lite",
"dav1d",
"image-webp",
"mp4parse",
"num-traits",
"png",
"zune-core",
"zune-jpeg",
]
[[package]]
name = "image-webp"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e031e8e3d94711a9ccb5d6ea357439ef3dcbed361798bd4071dc4d9793fbe22f"
dependencies = [
"byteorder-lite",
"quick-error",
]
[[package]]
name = "indexmap"
version = "2.6.0"
@ -1555,7 +1652,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
dependencies = [
"equivalent",
"hashbrown",
"hashbrown 0.15.1",
]
[[package]]
@ -1635,7 +1732,7 @@ dependencies = [
"gtk4-sys",
"libc",
"pango-sys",
"system-deps",
"system-deps 7.0.3",
]
[[package]]
@ -1776,6 +1873,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
dependencies = [
"adler2",
"simd-adler32",
]
[[package]]
@ -1790,13 +1888,27 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "mp4parse"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63a35203d3c6ce92d5251c77520acb2e57108c88728695aa883f70023624c570"
dependencies = [
"bitreader",
"byteorder",
"fallible_collections",
"log",
"num-traits",
"static_assertions",
]
[[package]]
name = "nix"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
dependencies = [
"bitflags",
"bitflags 2.6.0",
"cfg-if",
"cfg_aliases",
"libc",
@ -1880,6 +1992,17 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "num-derive"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "num-integer"
version = "0.1.46"
@ -2040,7 +2163,7 @@ dependencies = [
"glib-sys",
"gobject-sys",
"libc",
"system-deps",
"system-deps 7.0.3",
]
[[package]]
@ -2094,6 +2217,19 @@ version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
[[package]]
name = "png"
version = "0.17.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0"
dependencies = [
"bitflags 1.3.2",
"crc32fast",
"fdeflate",
"flate2",
"miniz_oxide",
]
[[package]]
name = "polling"
version = "3.7.4"
@ -2152,6 +2288,12 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "quick-error"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quinn"
version = "0.11.6"
@ -2397,11 +2539,11 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.38.40"
version = "0.38.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0"
checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6"
dependencies = [
"bitflags",
"bitflags 2.6.0",
"errno",
"libc",
"linux-raw-sys",
@ -2481,9 +2623,9 @@ dependencies = [
[[package]]
name = "schannel"
version = "0.1.26"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1"
checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d"
dependencies = [
"windows-sys 0.59.0",
]
@ -2494,7 +2636,7 @@ version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
dependencies = [
"bitflags",
"bitflags 2.6.0",
"core-foundation",
"core-foundation-sys",
"libc",
@ -2638,6 +2780,12 @@ dependencies = [
"libc",
]
[[package]]
name = "simd-adler32"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "slab"
version = "0.4.9"
@ -2735,13 +2883,26 @@ dependencies = [
"syn",
]
[[package]]
name = "system-deps"
version = "6.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349"
dependencies = [
"cfg-expr 0.15.8",
"heck",
"pkg-config",
"toml",
"version-compare",
]
[[package]]
name = "system-deps"
version = "7.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66d23aaf9f331227789a99e8de4c91bf46703add012bdfd45fdecdfb2975a005"
dependencies = [
"cfg-expr",
"cfg-expr 0.17.1",
"heck",
"pkg-config",
"toml",
@ -3270,16 +3431,7 @@ version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6"
dependencies = [
"windows-core 0.58.0",
"windows-targets",
]
[[package]]
name = "windows-core"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-core",
"windows-targets",
]
@ -3706,6 +3858,21 @@ dependencies = [
"syn",
]
[[package]]
name = "zune-core"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
[[package]]
name = "zune-jpeg"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16099418600b4d8f028622f73ff6e3deaabdff330fb9a2a131dea781ee8b0768"
dependencies = [
"zune-core",
]
[[package]]
name = "zvariant"
version = "4.2.0"

View file

@ -7,7 +7,7 @@ edition = "2021"
adw = { version = "0.7.0", package = "libadwaita", features = ["v1_6"] }
async-channel = "2.3.1"
bytes = "1.8.0"
chrono = { version = "0.4.38", features = ["serde"] }
chrono = { version = "0.4.38", features = ["serde", "std"], default-features = false }
color-thief = "0.2.2"
event-listener = "5.3.1"
futures = "0.3.31"
@ -19,6 +19,12 @@ http-cache = { version = "0.20.0", default-features = false, features = [
"manager-cacache",
] }
http-cache-reqwest = "0.15.0"
image = { version = "0.25.5", default-features = false, features = [
"avif-native",
"jpeg",
"png",
"webp",
] }
oo7 = "0.3.3"
rand = "0.8"
reqwest = { version = "0.12.9", default-features = false, features = [
@ -28,8 +34,13 @@ reqwest = { version = "0.12.9", default-features = false, features = [
"http2",
"rustls-tls-native-roots",
] }
reqwest-middleware = { version = "0.4.0", features = ["json", "http2", "charset"] }
reqwest-middleware = { version = "0.4.0", features = [
"json",
"http2",
"charset",
] }
serde = { version = "1.0.214", features = ["derive"] }
serde_json = "1.0.133"
tokio = { version = "1", features = ["rt-multi-thread"] }
tracing = { version = "0.1.40", default-features = false, features = [
"attributes",

View file

@ -98,12 +98,12 @@ impl Song {
*thumbnail_loading = Some(glib::spawn_future_local(async move {
let _permit = SEM.acquire().await.unwrap();
let bytes = match api
.cover_art(&id, Some(50 * scale_factor)) // see pixel-size in
// play_queue_song.blp
let pixbuf = match api
// see pixel-size in play_queue_song.blp
.cover_art(&id, Some(50 * scale_factor))
.await
{
Ok(bytes) => bytes,
Ok(pixbuf) => pixbuf,
Err(err) => {
event!(
Level::ERROR,
@ -113,13 +113,14 @@ impl Song {
}
};
event!(target: "audrey::thumbnail_loader", Level::DEBUG, id = id, "fetched thumbnail");
let song = match song_weak.upgrade() {
None => return,
Some(song) => song,
};
song.set_thumbnail(
gdk::Texture::from_bytes(&glib::Bytes::from_owned(bytes)).unwrap(),
);
let texture = gdk::Texture::for_pixbuf(&pixbuf);
song.set_thumbnail(texture);
}));
}
}

View file

@ -3,8 +3,9 @@ pub mod schema;
mod album_list;
pub use album_list::AlbumListType;
use adw::glib;
use bytes::Bytes;
use gtk::glib;
use image::ImageReader;
use rand::Rng;
use tracing::{event, Level};
@ -23,6 +24,8 @@ fn runtime() -> &'static tokio::runtime::Runtime {
pub enum Error {
UrlParseError(url::ParseError),
ReqwestError(reqwest_middleware::Error),
JsonDecodeError(serde_json::Error),
ImageDecodeError(image::error::ImageError),
SubsonicError(schema::Error),
OtherError(&'static str),
}
@ -33,6 +36,8 @@ impl fmt::Display for Error {
match self {
Self::UrlParseError(err) => fmt::Display::fmt(err, f),
Self::ReqwestError(err) => fmt::Display::fmt(err, f),
Self::ImageDecodeError(err) => fmt::Display::fmt(err, f),
Self::JsonDecodeError(err) => fmt::Display::fmt(err, f),
Self::SubsonicError(err) => fmt::Display::fmt(&err.message, f),
Self::OtherError(err) => fmt::Display::fmt(err, f),
}
@ -63,6 +68,12 @@ impl From<reqwest::Error> for Error {
}
}
impl From<serde_json::Error> for Error {
fn from(err: serde_json::Error) -> Self {
Self::JsonDecodeError(err)
}
}
#[derive(Clone)]
pub struct Client {
client: reqwest_middleware::ClientWithMiddleware,
@ -123,10 +134,7 @@ impl Client {
let http_cache = Cache(HttpCache {
mode: CacheMode::default(),
manager: CACacheManager {
path: cache_dir,
..Default::default()
},
manager: CACacheManager { path: cache_dir },
options: HttpCacheOptions {
cache_options: Some(CacheOptions {
ignore_cargo_cult: true,
@ -147,10 +155,7 @@ impl Client {
Ok(Client { client, base_url })
}
async fn send<T: serde::de::DeserializeOwned + Send + 'static>(
&self,
request: reqwest_middleware::RequestBuilder,
) -> Result<T, Error> {
async fn send(&self, request: reqwest_middleware::RequestBuilder) -> Result<Bytes, Error> {
// FIXME: is an entire channel per request overkill? maybe pool them?
let (sender, receiver) = async_channel::bounded(1);
@ -161,16 +166,27 @@ impl Client {
let future = request.send();
runtime().spawn(async move {
// wrap this logic in a fn so we can use ?
async fn perform<T: serde::de::DeserializeOwned + Send + 'static>(
async fn perform(
response: Result<reqwest::Response, reqwest_middleware::Error>,
) -> Result<schema::SubsonicResponseOuter<T>, reqwest_middleware::Error> {
) -> Result<Bytes, Error> {
let response = response?.error_for_status()?;
let xcache = response.headers()[http_cache::XCACHE].to_str().ok();
event!(target: "audrey::http_request", Level::DEBUG, url=response.url().as_str(), status=response.status().as_str(), cache_status=xcache);
let content_type = response.headers()[reqwest::header::CONTENT_TYPE]
.to_str()
.ok();
event!(
name: "response",
target: "audrey::http",
Level::DEBUG,
url=response.url().as_str(),
status=response.status().as_str(),
cache_status=xcache,
content_type=content_type
);
response
.json::<schema::SubsonicResponseOuter<T>>()
.bytes()
.await
.map_err(|e| reqwest_middleware::Error::Reqwest(e))
.map_err(|e| Error::ReqwestError(reqwest_middleware::Error::Reqwest(e)))
}
sender
@ -179,16 +195,10 @@ impl Client {
.expect("could not send subsonic response back to the main loop");
});
let response = receiver
receiver
.recv()
.await
.expect("could not receive subsonic response from tokio")?
.subsonic_response;
match response {
schema::SubsonicResponse::Ok { inner } => Ok(inner),
schema::SubsonicResponse::Failed { error } => Err(Error::SubsonicError(error)),
}
.expect("could not receive subsonic response from tokio")
}
fn url(&self, path: &[&str], query: &[(&str, &str)]) -> url::Url {
@ -201,22 +211,40 @@ impl Client {
url
}
async fn get<T: serde::de::DeserializeOwned + Send + 'static>(
async fn get_bytes(&self, url: url::Url) -> Result<Bytes, Error> {
self.send(self.client.get(url)).await
}
async fn get_json<T: serde::de::DeserializeOwned + Send + 'static>(
&self,
path: &[&str],
query: &[(&str, &str)],
url: url::Url,
) -> Result<T, Error> {
self.send(self.client.get(self.url(path, query))).await
let bytes = self.get_bytes(url).await?;
serde_json::from_slice::<T>(&bytes).map_err(Error::JsonDecodeError)
}
async fn get_subsonic<T: serde::de::DeserializeOwned + Send + 'static>(
&self,
url: url::Url,
) -> Result<T, Error> {
let response = self
.get_json::<schema::SubsonicResponseOuter<T>>(url)
.await?;
match response.subsonic_response {
schema::SubsonicResponse::Ok { inner } => Ok(inner),
schema::SubsonicResponse::Failed { error } => Err(Error::SubsonicError(error)),
}
}
pub async fn ping(&self) -> Result<(), Error> {
self.get(&["rest", "ping"], &[]).await
let url = self.url(&["rest", "ping"], &[]);
self.get_bytes(url).await.map(|_| ())
}
pub async fn random_songs(&self, size: u32) -> Result<Vec<schema::Child>, Error> {
self.get::<schema::RandomSongsOuter>(
&["rest", "getRandomSongs"],
&[("size", &size.to_string())],
self.get_subsonic::<schema::RandomSongsOuter>(
self.url(&["rest", "getRandomSongs"], &[("size", &size.to_string())]),
)
.await
.map(|response| response.random_songs.song)
@ -232,53 +260,40 @@ impl Client {
}
}
pub async fn cover_art(&self, id: &str, size: Option<u32>) -> Result<Bytes, Error> {
let (sender, receiver) = async_channel::bounded(1);
pub async fn cover_art(
&self,
id: &str,
size: Option<u32>,
) -> Result<gtk::gdk_pixbuf::Pixbuf, Error> {
let url = self.cover_art_url(id, size);
let cover_art_bytes = self.get_bytes(url).await?;
let image = ImageReader::new(std::io::BufReader::new(std::io::Cursor::new(
cover_art_bytes,
)))
.with_guessed_format()
.map_err(|e| Error::ImageDecodeError(image::ImageError::IoError(e)))?
.decode()
.map_err(Error::ImageDecodeError)?;
event!(target: "audrey::http_request_cover_art", Level::DEBUG, url = url.as_str());
event!(Level::DEBUG, "loaded image: {:?}", image.color());
let width = image.width();
let height = image.height();
// 8bpc rgba -> 32 -> 4 bytes
let stride = width * 4;
// gtk only supports 8bpc; maybe in the future we should handle hdr >8bpc too
let image = image.into_rgba8().into_vec();
let future = self.client.get(url).send();
runtime().spawn(async move {
async fn perform(
response: Result<reqwest::Response, reqwest_middleware::Error>,
) -> Result<Bytes, Error> {
let response = response?.error_for_status()?;
let xcache = response.headers()[http_cache::XCACHE].to_str().ok();
event!(target: "audrey::http_request_cover_art", Level::DEBUG, url=response.url().as_str(), status=response.status().as_str(), cache_status=xcache);
if response.headers()[reqwest::header::CONTENT_TYPE]
.to_str()
.expect("bad content type")
.starts_with("text/json")
{
match response
.json::<schema::SubsonicResponseOuter<()>>()
.await?
.subsonic_response
{
schema::SubsonicResponse::Failed { error } => {
Err(Error::SubsonicError(error))
}
_ => panic!("unlimited error but no error"),
}
} else {
Ok(response.bytes().await?)
}
}
if let Err(async_channel::SendError(_)) = sender.send(perform(future.await).await).await
{
event!(
Level::INFO,
"could not send cover art bytes to main loop (task cancelled?)"
let pixbuf = gtk::gdk_pixbuf::Pixbuf::from_bytes(
&glib::Bytes::from_owned(image),
gtk::gdk_pixbuf::Colorspace::Rgb,
true,
8,
// TODO: bubble up these to fail image loads instead of crashing
width.try_into().unwrap(),
height.try_into().unwrap(),
stride.try_into().unwrap(),
);
}
});
receiver
.recv()
.await
.expect("could not receive cover art bytes from tokio")
Ok(pixbuf)
}
pub fn stream_url(&self, id: &str) -> url::Url {

View file

@ -24,14 +24,14 @@ impl Client {
) -> Result<Vec<schema::AlbumID3>, Error> {
match type_ {
AlbumListType::Newest => {
self.get::<schema::AlbumList2Outer>(
self.get_subsonic::<schema::AlbumList2Outer>(self.url(
&["rest", "getAlbumList2"],
&[
("type", "newest"),
("size", &size.to_string()),
("offset", &offset.to_string()),
],
)
))
.await
}

View file

@ -663,11 +663,11 @@ mod imp {
.loading_cover_handle
.replace(Some(glib::spawn_future_local(async move {
let api = window.imp().api.borrow().as_ref().unwrap().clone();
let bytes = match api
let pixbuf = match api
.cover_art(&song_id, None) // full size
.await
{
Ok(bytes) => bytes,
Ok(pixbuf) => pixbuf,
Err(err) => {
event!(
Level::ERROR,
@ -679,13 +679,9 @@ mod imp {
match window.song() {
Some(song) if song.id() == song_id => {
let loader = gtk::gdk_pixbuf::PixbufLoader::new();
loader.write(&bytes).unwrap();
loader.close().unwrap();
let pixbuf = loader.pixbuf().unwrap();
let texture = gdk::Texture::for_pixbuf(&pixbuf);
window.set_playing_cover_art(Some(&texture.clone().into()));
window.set_playing_cover_art(Some(&texture.into()));
// from amberol
let palette = color_thief::get_palette(