use std::str::FromStr;
use axum::routing::{get, put};
use entertainarr_domain::podcast::entity::{PodcastEpisode, PodcastEpisodeProgress};
use crate::entity::podcast::PodcastEntity;
use crate::entity::podcast_episode::{
PodcastEpisodeAttributes, PodcastEpisodeDocument, PodcastEpisodeField, PodcastEpisodeInclude,
PodcastEpisodeProgressAttributes, PodcastEpisodeProgressDocument, PodcastEpisodeProgressEntity,
PodcastEpisodeRelationships,
};
use crate::entity::{Couple, Relation};
pub mod delete_progression;
pub mod find;
pub mod list;
pub mod list_by_podcast;
pub mod upsert_progression;
pub fn create<S>() -> axum::Router<S>
where
S: crate::server::prelude::ServerState + Clone,
{
axum::Router::new()
.route("/podcast-episodes", get(list::handle::<S>))
.route(
"/podcasts/{podcast_id}/episodes",
get(list_by_podcast::handle::<S>),
)
.route("/podcast-episodes/{episode_id}", get(find::handle::<S>))
.route(
"/users/me/podcast-episode-progresses",
put(upsert_progression::handle::<S>).delete(delete_progression::handle::<S>),
)
}
impl From<PodcastEpisode> for PodcastEpisodeDocument {
fn from(value: PodcastEpisode) -> Self {
Self {
id: value.id,
kind: Default::default(),
attributes: PodcastEpisodeAttributes {
guid: value.guid,
published_at: value.published_at,
title: value.title,
description: value.description,
link: value.link,
duration: value.duration.map(|v| v.as_secs()),
file_url: value.file_url,
file_size: value.file_size,
file_type: value.file_type,
created_at: value.created_at,
updated_at: value.updated_at,
},
relationships: PodcastEpisodeRelationships {
podcast: Relation {
data: Some(PodcastEntity {
id: value.podcast_id,
kind: Default::default(),
}),
meta: (),
},
progress: Relation {
data: None,
meta: (),
},
},
}
}
}
impl From<PodcastEpisode> for PodcastEpisodeAttributes {
fn from(value: PodcastEpisode) -> Self {
PodcastEpisodeAttributes {
guid: value.guid,
published_at: value.published_at,
title: value.title,
description: value.description,
link: value.link,
duration: value.duration.map(|v| v.as_secs()),
file_url: value.file_url,
file_size: value.file_size,
file_type: value.file_type,
created_at: value.created_at,
updated_at: value.updated_at,
}
}
}
impl PodcastEpisodeDocument {
pub fn maybe_with_progress(self, progress: Option<&PodcastEpisodeProgress>) -> Self {
match progress {
Some(value) => self.with_progress(value),
None => self,
}
}
pub fn with_progress(mut self, progress: &PodcastEpisodeProgress) -> Self {
self.relationships.progress.data = Some(PodcastEpisodeProgressEntity {
id: Couple(progress.podcast_episode_id, progress.user_id),
kind: Default::default(),
});
self
}
}
impl From<PodcastEpisodeField> for entertainarr_domain::podcast::entity::PodcastEpisodeField {
fn from(value: PodcastEpisodeField) -> Self {
match value {
PodcastEpisodeField::PublishedAt => {
entertainarr_domain::podcast::entity::PodcastEpisodeField::PublishedAt
}
}
}
}
#[derive(Debug)]
pub struct ParsePodcastEpisodeIncludeError;
impl std::fmt::Display for ParsePodcastEpisodeIncludeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("invalid podcast episode include")
}
}
impl FromStr for PodcastEpisodeInclude {
type Err = ParsePodcastEpisodeIncludeError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
match input {
"podcast" => Ok(Self::Podcast),
"podcast-episode-progress" => Ok(Self::PodcastEpisodeProgress),
other => {
tracing::warn!(value = other, "invalid podcast episode include");
Err(ParsePodcastEpisodeIncludeError)
}
}
}
}
impl From<PodcastEpisodeProgress> for PodcastEpisodeProgressDocument {
fn from(value: PodcastEpisodeProgress) -> Self {
PodcastEpisodeProgressDocument {
id: Couple(value.podcast_episode_id, value.user_id),
kind: Default::default(),
attributes: value.into(),
}
}
}
impl From<PodcastEpisodeProgress> for PodcastEpisodeProgressAttributes {
fn from(value: PodcastEpisodeProgress) -> Self {
PodcastEpisodeProgressAttributes {
progress: value.progress,
completed: value.completed,
created_at: value.created_at,
updated_at: value.updated_at,
}
}
}