diff --git a/src/gpodder/models.rs b/src/gpodder/models.rs index 1c9e550..3bbf229 100644 --- a/src/gpodder/models.rs +++ b/src/gpodder/models.rs @@ -1,5 +1,4 @@ use chrono::{DateTime, Utc}; -use serde::{Deserialize, Serialize}; #[derive(Clone)] pub struct User { @@ -8,8 +7,6 @@ pub struct User { pub password_hash: String, } -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "lowercase")] pub enum DeviceType { Desktop, Laptop, @@ -18,7 +15,6 @@ pub enum DeviceType { Other, } -#[derive(Serialize)] pub struct Device { pub id: String, pub caption: String, @@ -26,37 +22,28 @@ pub struct Device { pub subscriptions: i64, } -#[derive(Deserialize)] pub struct DevicePatch { pub caption: Option, pub r#type: Option, } -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "lowercase")] -#[serde(tag = "action")] pub enum EpisodeActionType { Download, Play { - #[serde(default)] started: Option, position: i32, - #[serde(default)] total: Option, }, Delete, New, } -#[derive(Serialize, Deserialize, Debug)] pub struct EpisodeAction { pub podcast: String, pub episode: String, pub timestamp: Option>, pub time_changed: DateTime, - #[serde(default)] pub device: Option, - #[serde(flatten)] pub action: EpisodeActionType, } diff --git a/src/server/gpodder/advanced/devices.rs b/src/server/gpodder/advanced/devices.rs index 397ddb3..859e1c1 100644 --- a/src/server/gpodder/advanced/devices.rs +++ b/src/server/gpodder/advanced/devices.rs @@ -12,6 +12,7 @@ use crate::{ gpodder::{ auth_middleware, format::{Format, StringWithFormat}, + models, }, Context, }, @@ -28,7 +29,7 @@ async fn get_devices( State(ctx): State, Path(username): Path, Extension(user): Extension, -) -> AppResult>> { +) -> AppResult>> { if username.format != Format::Json { return Err(AppError::NotFound); } @@ -41,7 +42,7 @@ async fn get_devices( tokio::task::spawn_blocking(move || ctx.store.devices_for_user(&user)) .await .unwrap() - .map(Json)?, + .map(|devices| Json(devices.into_iter().map(models::Device::from).collect()))?, ) } @@ -49,13 +50,13 @@ async fn post_device( State(ctx): State, Path((_username, id)): Path<(String, StringWithFormat)>, Extension(user): Extension, - Json(patch): Json, + Json(patch): Json, ) -> AppResult<()> { if id.format != Format::Json { return Err(AppError::NotFound); } - tokio::task::spawn_blocking(move || ctx.store.update_device_info(&user, &id, patch)) + tokio::task::spawn_blocking(move || ctx.store.update_device_info(&user, &id, patch.into())) .await .unwrap()?; diff --git a/src/server/gpodder/advanced/episodes.rs b/src/server/gpodder/advanced/episodes.rs index 22839c0..6c7ff32 100644 --- a/src/server/gpodder/advanced/episodes.rs +++ b/src/server/gpodder/advanced/episodes.rs @@ -14,6 +14,7 @@ use crate::{ gpodder::{ auth_middleware, format::{Format, StringWithFormat}, + models, models::UpdatedUrlsResponse, }, Context, @@ -33,7 +34,7 @@ async fn post_episode_actions( State(ctx): State, Path(username): Path, Extension(user): Extension, - Json(actions): Json>, + Json(actions): Json>, ) -> AppResult> { if username.format != Format::Json { return Err(AppError::NotFound); @@ -43,17 +44,18 @@ async fn post_episode_actions( return Err(AppError::BadRequest); } - Ok( - tokio::task::spawn_blocking(move || ctx.store.add_episode_actions(&user, actions)) - .await - .unwrap() - .map(|time_changed| { - Json(UpdatedUrlsResponse { - timestamp: time_changed.timestamp(), - update_urls: Vec::new(), - }) - })?, - ) + Ok(tokio::task::spawn_blocking(move || { + ctx.store + .add_episode_actions(&user, actions.into_iter().map(Into::into).collect()) + }) + .await + .unwrap() + .map(|time_changed| { + Json(UpdatedUrlsResponse { + timestamp: time_changed.timestamp(), + update_urls: Vec::new(), + }) + })?) } #[derive(Deserialize, Default)] @@ -68,7 +70,7 @@ struct FilterQuery { #[derive(Serialize)] struct EpisodeActionsResponse { timestamp: i64, - actions: Vec, + actions: Vec, } async fn get_episode_actions( @@ -104,7 +106,7 @@ async fn get_episode_actions( .map(|(ts, actions)| { Json(EpisodeActionsResponse { timestamp: ts.timestamp(), - actions, + actions: actions.into_iter().map(Into::into).collect(), }) })?) } diff --git a/src/server/gpodder/models.rs b/src/server/gpodder/models.rs index 2be8f87..b2268af 100644 --- a/src/server/gpodder/models.rs +++ b/src/server/gpodder/models.rs @@ -1,5 +1,8 @@ +use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; +use crate::gpodder; + #[derive(Deserialize, Debug)] pub struct SubscriptionDelta { pub add: Vec, @@ -18,3 +21,164 @@ pub struct UpdatedUrlsResponse { pub timestamp: i64, pub update_urls: Vec<(String, String)>, } + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum DeviceType { + Desktop, + Laptop, + Mobile, + Server, + Other, +} + +#[derive(Serialize)] +pub struct Device { + pub id: String, + pub caption: String, + pub r#type: DeviceType, + pub subscriptions: i64, +} + +#[derive(Deserialize)] +pub struct DevicePatch { + pub caption: Option, + pub r#type: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "lowercase")] +#[serde(tag = "action")] +pub enum EpisodeActionType { + Download, + Play { + #[serde(default)] + started: Option, + position: i32, + #[serde(default)] + total: Option, + }, + Delete, + New, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct EpisodeAction { + pub podcast: String, + pub episode: String, + pub timestamp: Option, + #[serde(default)] + pub device: Option, + #[serde(flatten)] + pub action: EpisodeActionType, +} + +impl From for DeviceType { + fn from(value: gpodder::DeviceType) -> Self { + match value { + gpodder::DeviceType::Other => Self::Other, + gpodder::DeviceType::Laptop => Self::Laptop, + gpodder::DeviceType::Mobile => Self::Mobile, + gpodder::DeviceType::Server => Self::Server, + gpodder::DeviceType::Desktop => Self::Desktop, + } + } +} + +impl From for gpodder::DeviceType { + fn from(value: DeviceType) -> Self { + match value { + DeviceType::Other => gpodder::DeviceType::Other, + DeviceType::Laptop => gpodder::DeviceType::Laptop, + DeviceType::Mobile => gpodder::DeviceType::Mobile, + DeviceType::Server => gpodder::DeviceType::Server, + DeviceType::Desktop => gpodder::DeviceType::Desktop, + } + } +} + +impl From for Device { + fn from(value: gpodder::Device) -> Self { + Self { + id: value.id, + caption: value.caption, + r#type: value.r#type.into(), + subscriptions: value.subscriptions, + } + } +} + +impl From for gpodder::DevicePatch { + fn from(value: DevicePatch) -> Self { + Self { + caption: value.caption, + r#type: value.r#type.map(Into::into), + } + } +} + +impl From for EpisodeActionType { + fn from(value: gpodder::EpisodeActionType) -> Self { + match value { + gpodder::EpisodeActionType::New => Self::New, + gpodder::EpisodeActionType::Delete => Self::Delete, + gpodder::EpisodeActionType::Download => Self::Download, + gpodder::EpisodeActionType::Play { + started, + position, + total, + } => Self::Play { + started, + position, + total, + }, + } + } +} + +impl From for gpodder::EpisodeActionType { + fn from(value: EpisodeActionType) -> Self { + match value { + EpisodeActionType::New => gpodder::EpisodeActionType::New, + EpisodeActionType::Delete => gpodder::EpisodeActionType::Delete, + EpisodeActionType::Download => gpodder::EpisodeActionType::Download, + EpisodeActionType::Play { + started, + position, + total, + } => gpodder::EpisodeActionType::Play { + started, + position, + total, + }, + } + } +} + +impl From for EpisodeAction { + fn from(value: gpodder::EpisodeAction) -> Self { + Self { + podcast: value.podcast, + episode: value.episode, + timestamp: value.timestamp.map(|ts| ts.timestamp()), + device: value.device, + action: value.action.into(), + } + } +} + +impl From for gpodder::EpisodeAction { + fn from(value: EpisodeAction) -> Self { + Self { + podcast: value.podcast, + episode: value.episode, + // TODO remove this unwrap + timestamp: value + .timestamp + .map(|ts| DateTime::from_timestamp(ts, 0).unwrap()), + device: value.device, + action: value.action.into(), + time_changed: DateTime::::MIN_UTC, + } + } +}