use std::collections::HashSet;
use axum::Json;
use axum::extract::State;
use entertainarr_domain::podcast::entity::ListPodcastEpisodeResponse;
use entertainarr_domain::podcast::prelude::PodcastService;
use serde_qs::axum::QsQuery;
use crate::entity::podcast::{PodcastDocument, PodcastEntity, PodcastRelationships};
use crate::entity::podcast_episode::{
ListPodcastEpisodeParams, PodcastEpisodeDocument, PodcastEpisodeInclude,
PodcastEpisodeProgressEntity, PodcastEpisodeRelation, PodcastEpisodeRelationships,
};
use crate::entity::podcast_subscription::PodcastSubscriptionEntity;
use crate::entity::task::TaskEntity;
use crate::entity::{ApiResource, Couple, Relation};
use crate::server::extractor::user::CurrentUser;
use crate::server::handler::error::ApiErrorResponse;
use crate::server::handler::prelude::FromDomainResponse;
pub async fn handle<S>(
State(state): State<S>,
CurrentUser(user_id): CurrentUser,
QsQuery(params): QsQuery<ListPodcastEpisodeParams>,
) -> Result<Json<ApiResource<Vec<PodcastEpisodeDocument>, PodcastEpisodeRelation>>, ApiErrorResponse>
where
S: crate::server::prelude::ServerState,
{
let response = state
.podcast_service()
.list_podcast_episode(
entertainarr_domain::podcast::entity::ListPodcastEpisodeParams {
user_id,
filter: entertainarr_domain::podcast::entity::ListPodcastEpisodeFilter {
podcast_ids: &[],
filtered: params.filter.filtered,
subscribed: params.filter.subscribed,
watched: params.filter.watched,
},
sort: params.sort.into(),
page: params.page.into(),
},
)
.await
.map_err(|err| {
tracing::error!(error = ?err, "unable to list podcast episodes");
ApiErrorResponse::internal()
})?;
Ok(Json(ApiResource::from_response(response, params.include)))
}
impl FromDomainResponse<ListPodcastEpisodeResponse, PodcastEpisodeInclude>
for ApiResource<Vec<PodcastEpisodeDocument>, PodcastEpisodeRelation>
{
fn from_response(
response: ListPodcastEpisodeResponse,
required: HashSet<PodcastEpisodeInclude>,
) -> Self {
let data = response
.episodes
.into_iter()
.map(|episode| PodcastEpisodeDocument {
id: episode.id,
kind: Default::default(),
relationships: PodcastEpisodeRelationships {
podcast: Relation {
data: Some(PodcastEntity::new(episode.podcast_id)),
meta: (),
},
progress: Relation {
data: response.progresses.get(&episode.id).map(|p| {
PodcastEpisodeProgressEntity::new(Couple(
p.podcast_episode_id,
p.user_id,
))
}),
meta: (),
},
},
attributes: episode.into(),
})
.collect();
let mut includes = Vec::with_capacity(
response.podcasts.len()
+ response.progresses.len()
+ response.subscriptions.len()
+ response.synchronizations.len(),
);
if required.contains(&PodcastEpisodeInclude::Podcast) {
includes.extend(response.podcasts.into_values().map(|podcast| {
PodcastEpisodeRelation::Podcast(PodcastDocument {
id: podcast.id,
kind: Default::default(),
relationships: PodcastRelationships {
subscription: Relation {
data: response.subscriptions.get(&podcast.id).map(|s| {
PodcastSubscriptionEntity::new(Couple(s.podcast_id, s.user_id))
}),
meta: None,
},
synchronization: Relation {
data: response
.synchronizations
.get(&podcast.id)
.map(|s| TaskEntity::new(s.id)),
meta: (),
},
},
attributes: podcast.into(),
})
}));
}
if required.contains(&PodcastEpisodeInclude::PodcastEpisodeProgress) {
includes.extend(
response.progresses.into_values().map(|progress| {
PodcastEpisodeRelation::PodcastEpisodeProgress(progress.into())
}),
);
}
ApiResource { data, includes }
}
}