From 70a990df0675a9b369392c1de0f73f0e1a5797f1 Mon Sep 17 00:00:00 2001 From: sarendsen Date: Sun, 13 Aug 2023 17:19:40 +0200 Subject: [PATCH] fix: rework pagination --- Makefile | 2 +- src/models.rs | 3 + src/plex_client.rs | 68 ++++++++++++++---- src/routes.rs | 17 ++--- src/transform.rs | 173 +++++++++++++++++++++++++++++++++++---------- 5 files changed, 197 insertions(+), 66 deletions(-) diff --git a/Makefile b/Makefile index d5fe395..6efdb34 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ docker-run: run: - REPLEX_REDIRECT_STREAMS=0 REPLEX_DISABLE_RELATED=1 REPLEX_DISABLE_LEAF_COUNT=0 REPLEX_DISABLE_USER_STATE=1 REPLEX_ENABLE_CONSOLE=0 REPLEX_CACHE_TTL=0 REPLEX_HOST=https://46-4-30-217.01b0839de64b49138531cab1bf32f7c2.plex.direct:42405 RUST_LOG="info,replex=info" cargo watch -x run + REPLEX_INCLUDE_WATCHED=0 REPLEX_REDIRECT_STREAMS=0 REPLEX_DISABLE_RELATED=1 REPLEX_DISABLE_LEAF_COUNT=0 REPLEX_DISABLE_USER_STATE=1 REPLEX_ENABLE_CONSOLE=0 REPLEX_CACHE_TTL=0 REPLEX_HOST=https://46-4-30-217.01b0839de64b49138531cab1bf32f7c2.plex.direct:42405 RUST_LOG="info,replex=info" cargo watch -x run # run: # REPLEX_ENABLE_CONSOLE=0 REPLEX_CACHE_TTL=0 REPLEX_HOST=https://46-4-30-217.01b0839de64b49138531cab1bf32f7c2.plex.direct:42405 RUST_LOG="info" cargo run diff --git a/src/models.rs b/src/models.rs index 322c216..251a12a 100644 --- a/src/models.rs +++ b/src/models.rs @@ -251,6 +251,9 @@ pub struct MetaData { #[yaserde(attribute)] #[serde(skip_serializing_if = "Option::is_none")] pub index: Option, + #[yaserde(attribute)] + #[serde(skip_serializing_if = "Option::is_none")] + pub subtype: Option, #[yaserde(attribute, rename = "parentYear")] #[serde(skip_serializing_if = "Option::is_none")] pub parent_year: Option, diff --git a/src/plex_client.rs b/src/plex_client.rs index 735e6d9..49fb843 100644 --- a/src/plex_client.rs +++ b/src/plex_client.rs @@ -141,6 +141,7 @@ impl PlexClient { // we want guids for banners path = format!("{}&includeGuids=1", path); + // dbg!(&path); let resp = self.get(path).await.unwrap(); let container: MediaContainerWrapper = @@ -154,41 +155,80 @@ impl PlexClient { id: u32, offset: i32, limit: i32, - original_limit: i32 + original_limit: i32, ) -> anyhow::Result> { let config: Config = Config::figment().extract().unwrap(); let mut c = self .get_collection_children(id, Some(offset), Some(limit)) .await?; - + // dbg!(limit); + // dbg!(c.media_container.children_mut()) if !config.include_watched { + // dbg!(c.media_container.children_mut().len()); c.media_container.children_mut().retain(|x| !x.is_watched()); - + // dbg!(c.media_container.children_mut().len()); + // dbg!("-----"); + let children_lenght = c.media_container.children_mut().len() as i32; let total_size = c.media_container.total_size.unwrap(); if children_lenght < original_limit - && total_size > offset + limit && offset < total_size + && total_size > offset + limit + && offset < total_size { // load more - return self.load_collection_children_recursive( - id, - offset, - limit + 10, - original_limit - ).await; + return self + .load_collection_children_recursive( + id, + offset, + limit + 10, + original_limit, + ) + .await; } } Ok(c) } - pub async fn get_collection( &self, id: i32, ) -> Result> { - let res = self - .get(format!("/library/collections/{}", id)) - .await?; + let res = self.get(format!("/library/collections/{}", id)).await?; + + if res.status() == 404 { + return Err(salvo::http::StatusError::not_found().into()); + } + + let container: MediaContainerWrapper = + from_reqwest_response(res).await.unwrap(); + Ok(container) + } + + // theres actually a global endpoint https://plex.sjoerdarendsen.dev/library/all?show.collection=2042780&collection=2042780&X-Plex-Container-Start=0&X-Plex-Container-Size=72 + pub async fn get_collection_total_size_unwatched( + &self, + section_id: i32, + collection_index: i32, + r#type: String, + ) -> Result> { + let mut path = format!("/library/sections/{}/all?X-Plex-Container-Start=0&X-Plex-Container-Size=0", section_id); + // dbg!(&path); + + if r#type == "show" { + path = format!( + "{}&show.unwatchedLeaves=1&show.collection={}", + path, collection_index + ); + } + + if r#type == "movie" { + path = format!( + "{}&movie.unwatched=1&movie.collection={}", + path, collection_index + ); + } + // dbg!(&path); + let res = self.get(path).await?; if res.status() == 404 { return Err(salvo::http::StatusError::not_found().into()); diff --git a/src/routes.rs b/src/routes.rs index 471a0ec..057842e 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -251,7 +251,6 @@ pub async fn get_hubs_promoted( } } - let upstream_res = plex_client.request(req).await?; match upstream_res.status() { reqwest::StatusCode::OK => (), @@ -267,17 +266,6 @@ pub async fn get_hubs_promoted( let mut container: MediaContainerWrapper = from_reqwest_response(upstream_res).await?; - // match from_reqwest_response(upstream_res).await - // if upstream_res.status() == 500 - // let mut container: MediaContainerWrapper = - // match from_reqwest_response(upstream_res).await { - // Ok(r) => r, - // Err(error) => { - // tracing::error!(error = ?error, uri = ?req.uri(), "Failed to get plex response"); - // res.status_code(StatusCode::INTERNAL_SERVER_ERROR); - // return Ok(()) - // } - // }; container.content_type = content_type; TransformBuilder::new(plex_client, params.clone()) @@ -290,6 +278,7 @@ pub async fn get_hubs_promoted( .with_transform(HubKeyTransform) .apply_to(&mut container) .await; + res.render(container); Ok(()) } @@ -348,7 +337,7 @@ pub async fn get_collections_children( req: &mut Request, _depot: &mut Depot, res: &mut Response, -) { +) -> Result<(), anyhow::Error> { let params: PlexParams = req.extract().await.unwrap(); let collection_ids = req.param::("ids").unwrap(); let collection_ids: Vec = collection_ids @@ -391,7 +380,9 @@ pub async fn get_collections_children( .with_transform(UserStateTransform) .apply_to(&mut container) .await; + res.render(container); // TODO: FIx XML + Ok(()) } #[handler] diff --git a/src/transform.rs b/src/transform.rs index 3e5c89f..1988484 100644 --- a/src/transform.rs +++ b/src/transform.rs @@ -11,6 +11,7 @@ use futures_util::{ stream::{FuturesOrdered, FuturesUnordered}, StreamExt, }; +use std::collections::HashMap; use itertools::Itertools; use lazy_static::__Deref; use std::sync::Arc; @@ -402,29 +403,143 @@ impl Transform for LibraryMixTransform { ) { let config: Config = Config::figment().extract().unwrap(); let mut children: Vec = vec![]; - let mut total_size: i32 = 0; let collection_ids_len = self.collection_ids.clone().len() as i32; - let collection_offset = - (self.offset as f32 / collection_ids_len as f32).ceil() as i32; - let collection_limit = - (self.limit as f32 / collection_ids_len as f32).ceil() as i32; - let mut total_size_children_unfiltered: i32 = 0; + let mut total_size = 0; + let mut collections_total_sizes: HashMap = HashMap::with_capacity(collection_ids_len as usize); for id in self.collection_ids.clone() { - let mut c = plex_client.clone().get_cached( - plex_client.load_collection_children_recursive( - id, - self.offset, - collection_limit, - collection_limit, - ), - format!( - "collection:{}:{}:{}", - id, self.offset, collection_limit - ), - ).await.unwrap(); - - total_size += c.media_container.total_size.unwrap(); + // We need this for the total count. In theory it shouldnt have mattered. + // But IOS doesnt listen to changes in total size. Picks it from the first request. + // Which results in a loop + if !config.include_watched { + let mut collection = plex_client + .clone() + .get_cached( + plex_client.get_collection(id as i32), + format!("get_collection:{}", id), + ) + .await + .unwrap(); + + let collection_metadata = + collection.media_container.children_mut()[0].clone(); + let library_items = plex_client + .clone() + .get_cached( + plex_client.get_collection_total_size_unwatched( + collection + .media_container + .library_section_id + .unwrap() as i32, + collection_metadata.index.unwrap() as i32, + collection_metadata.subtype.unwrap(), + ), + format!( + "get_collection_total_size_unwatched:{}:{}", + collection + .media_container + .library_section_id + .unwrap() as i32, + id + ), + ) + .await + .unwrap(); + collections_total_sizes.insert(id, library_items.media_container.total_size.unwrap()); + total_size += library_items.media_container.total_size.unwrap(); + } else { + let c = plex_client + .clone() + .get_cached( + plex_client.get_collection_children( + id, + Some(0), + Some(0), + ), + format!( + "get_collection_children:{}:{}:{}", + id, 0, 0 + ), + ) + .await + .unwrap(); + collections_total_sizes.insert(id, c.media_container.total_size.unwrap()); + total_size += c.media_container.total_size.unwrap(); + } + } + + for id in self.collection_ids.clone() { + let percentage = { + let mut b = 0; + for s in collections_total_sizes.clone().into_iter() { + b += s.1; + } + let size = collections_total_sizes.get(&id); + // b / size + // dbg!(&b); + // dbg!(&size); + let x = ((size.unwrap() * 100 / b * 100) / 100) as f32; + x / 100.00 + }; + // dbg!(percentage); + let mut c = plex_client + .clone() + .get_cached( + plex_client.load_collection_children_recursive( + id, + (self.offset as f32 * percentage).ceil() as i32, + (self.limit as f32 * percentage).ceil() as i32, + (self.limit as f32 * percentage).ceil() as i32, + ), + format!( + "load_collection_children_recursive:{}:{}:{}", + id, self.offset, self.limit + ), + ) + .await + .unwrap(); + + // if !config.include_watched { + // // We need this for the total count. In theory it shouldnt have mattered. + // // But IOS doesnt listen to changes in total size. Picks it from the first request. + // // Which results in a loop + // let mut collection = plex_client + // .clone() + // .get_cached( + // plex_client.get_collection(id as i32), + // format!("collection:{}", id), + // ) + // .await + // .unwrap(); + + // let collection_metadata = + // collection.media_container.children_mut()[0].clone(); + // let library_items = plex_client + // .clone() + // .get_cached( + // plex_client.get_collection_total_size( + // collection + // .media_container + // .library_section_id + // .unwrap() as i32, + // collection_metadata.index.unwrap() as i32, + // collection_metadata.subtype.unwrap(), + // ), + // format!( + // "library_items:{}:{}", + // collection + // .media_container + // .library_section_id + // .unwrap() as i32, + // id + // ), + // ) + // .await + // .unwrap(); + // total_size += library_items.media_container.total_size.unwrap(); + // } else { + // total_size += c.media_container.total_size.unwrap(); + // } match children.is_empty() { false => { @@ -436,28 +551,10 @@ impl Transform for LibraryMixTransform { true => children.append(&mut c.media_container.children()), } } - //dbg!(total_size); - let children_count = children.len() as i32; - - // we dont know the total size because we filter stuff. We might already be at the end of the line - if children_count < self.limit { - // dbg!("yup"); - // dbg!(children_count); - // dbg!(total_size); - // dbg!("------"); - // total_size = self.limit + children_count; - total_size = self.offset + children_count; - //total_size = self.offset + children_count; - //dbg!(total_size); - } - // dbg!(self.limit); - //item.children_mut().truncate(self.limit as usize); item.total_size = Some(total_size); // always metadata library - children.truncate(self.limit as usize); item.metadata = children; - item.size = Some(self.limit); } }