feat: federate watch_medium in AP review objects

- add watch_medium field to ReviewObject (outbound + inbound)
- update_remote_review passes watch_medium to DB
- unknown values silently ignored on ingest
This commit is contained in:
2026-07-10 00:38:29 +02:00
parent f584bcd724
commit cde2f5aaae
5 changed files with 19 additions and 4 deletions

View File

@@ -49,6 +49,8 @@ pub struct ReviewObject {
pub(crate) rating: u8, pub(crate) rating: u8,
pub(crate) comment: Option<String>, pub(crate) comment: Option<String>,
pub(crate) watched_at: DateTime<Utc>, pub(crate) watched_at: DateTime<Utc>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub(crate) watch_medium: Option<String>,
/// Discriminator so Movies Diary instances detect this as a review Note. /// Discriminator so Movies Diary instances detect this as a review Note.
#[serde(default)] #[serde(default)]
pub(crate) review: bool, pub(crate) review: bool,
@@ -135,6 +137,7 @@ pub fn review_to_ap_object(review: &Review, input: ReviewApInput) -> ReviewObjec
rating: review.rating().value(), rating: review.rating().value(),
comment: comment_text, comment: comment_text,
watched_at: DateTime::from_naive_utc_and_offset(*review.watched_at(), Utc), watched_at: DateTime::from_naive_utc_and_offset(*review.watched_at(), Utc),
watch_medium: review.watch_medium().map(|wm| wm.to_string()),
review: true, review: true,
attachment, attachment,
tag, tag,

View File

@@ -25,6 +25,7 @@ pub trait RemoteReviewRepository: Send + Sync {
comment: Option<&str>, comment: Option<&str>,
watched_at: NaiveDateTime, watched_at: NaiveDateTime,
poster_url: Option<&str>, poster_url: Option<&str>,
watch_medium: Option<&str>,
) -> Result<()>; ) -> Result<()>;
async fn delete_by_actor(&self, actor_url: &str) -> Result<()>; async fn delete_by_actor(&self, actor_url: &str) -> Result<()>;

View File

@@ -120,6 +120,12 @@ impl ApObjectHandler for ReviewObjectHandler {
)); ));
let rating = Rating::new(obj.rating.min(5))?; let rating = Rating::new(obj.rating.min(5))?;
let comment = obj.comment.map(Comment::new).transpose()?; let comment = obj.comment.map(Comment::new).transpose()?;
let watch_medium = obj
.watch_medium
.as_deref()
.map(|s| s.parse())
.transpose()
.unwrap_or(None);
let review = domain::models::Review::from_persistence(domain::models::PersistedReview { let review = domain::models::Review::from_persistence(domain::models::PersistedReview {
id: review_id, id: review_id,
@@ -132,7 +138,7 @@ impl ApObjectHandler for ReviewObjectHandler {
source: ReviewSource::Remote { source: ReviewSource::Remote {
actor_url: actor_url_str, actor_url: actor_url_str,
}, },
watch_medium: None, watch_medium,
}); });
self.review_store self.review_store
@@ -189,6 +195,7 @@ impl ApObjectHandler for ReviewObjectHandler {
obj.comment.as_deref(), obj.comment.as_deref(),
obj.watched_at.naive_utc(), obj.watched_at.naive_utc(),
obj.poster_url.as_deref(), obj.poster_url.as_deref(),
obj.watch_medium.as_deref(),
) )
.await?; .await?;

View File

@@ -79,15 +79,17 @@ impl RemoteReviewRepository for PostgresFederationRepository {
comment: Option<&str>, comment: Option<&str>,
watched_at: chrono::NaiveDateTime, watched_at: chrono::NaiveDateTime,
poster_url: Option<&str>, poster_url: Option<&str>,
watch_medium: Option<&str>,
) -> Result<()> { ) -> Result<()> {
let watched_at_str = datetime_to_str(&watched_at); let watched_at_str = datetime_to_str(&watched_at);
sqlx::query( sqlx::query(
"UPDATE reviews SET rating = $1, comment = $2, watched_at = $3::timestamptz "UPDATE reviews SET rating = $1, comment = $2, watched_at = $3::timestamptz, watch_medium = $4
WHERE ap_id = $4 AND remote_actor_url = $5", WHERE ap_id = $5 AND remote_actor_url = $6",
) )
.bind(rating as i64) .bind(rating as i64)
.bind(comment) .bind(comment)
.bind(&watched_at_str) .bind(&watched_at_str)
.bind(watch_medium)
.bind(ap_id) .bind(ap_id)
.bind(actor_url) .bind(actor_url)
.execute(&self.pool) .execute(&self.pool)

View File

@@ -79,15 +79,17 @@ impl RemoteReviewRepository for SqliteFederationRepository {
comment: Option<&str>, comment: Option<&str>,
watched_at: chrono::NaiveDateTime, watched_at: chrono::NaiveDateTime,
poster_url: Option<&str>, poster_url: Option<&str>,
watch_medium: Option<&str>,
) -> Result<()> { ) -> Result<()> {
let watched_at_str = datetime_to_str(&watched_at); let watched_at_str = datetime_to_str(&watched_at);
sqlx::query( sqlx::query(
"UPDATE reviews SET rating = ?, comment = ?, watched_at = ? "UPDATE reviews SET rating = ?, comment = ?, watched_at = ?, watch_medium = ?
WHERE ap_id = ? AND remote_actor_url = ?", WHERE ap_id = ? AND remote_actor_url = ?",
) )
.bind(rating as i64) .bind(rating as i64)
.bind(comment) .bind(comment)
.bind(&watched_at_str) .bind(&watched_at_str)
.bind(watch_medium)
.bind(ap_id) .bind(ap_id)
.bind(actor_url) .bind(actor_url)
.execute(&self.pool) .execute(&self.pool)