diff --git a/src/dto.rs b/src/dto.rs index aa69ad7a692b405e525a2879d8cd70ab03eba338..fbdc64f546ba542fe08363a4b200c1f615d8d4d7 100644 --- a/src/dto.rs +++ b/src/dto.rs @@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize}; use crate::config::Config; use crate::models::*; +use crate::dto_helpers::absolute_url; #[derive(Serialize, Deserialize, PartialEq, Debug)] #[serde(rename_all = "camelCase")] @@ -19,6 +20,7 @@ pub struct TitleDto { pub images: Vec<ImageDto>, pub media: Vec<MediaDto>, pub subtitles: Vec<SubtitleDto>, + pub preview: Option<String>, pub created_at: chrono::DateTime<chrono::Utc>, pub updated_at: chrono::DateTime<chrono::Utc>, } @@ -34,6 +36,7 @@ impl TitleDto { images: Vec<ImageDto>, media: Vec<MediaDto>, subtitles: Vec<SubtitleDto>, + preview: Option<String> ) -> Self { TitleDto { ids: TitleIdDto { @@ -54,6 +57,7 @@ impl TitleDto { images, media, subtitles, + preview, created_at: src.created_at, updated_at: src.updated_at, } @@ -200,7 +204,7 @@ impl ImageDto { Ok(ImageDto { kind: src.kind, mime: src.mime, - src: config.static_url.join(src.src.as_str())?.into_string(), + src: absolute_url(config, src.src)?, }) } } @@ -220,7 +224,7 @@ impl MediaDto { mime: src.mime, codecs: src.codecs, languages: src.languages, - src: config.static_url.join(src.src.as_str())?.into_string(), + src: absolute_url(config, src.src)?, }) } } @@ -258,7 +262,7 @@ impl SubtitleDto { language: src.language, region: src.region, specifier: src.specifier, - src: config.static_url.join(src.src.as_str())?.into_string(), + src: absolute_url(config, src.src)?, }) } } diff --git a/src/dto_helpers.rs b/src/dto_helpers.rs index c23cdd923119fcbf621fc735c6cb7d82fa5133ae..ca55297c38e666f08cdd3b601b8c594cc6fca115 100644 --- a/src/dto_helpers.rs +++ b/src/dto_helpers.rs @@ -4,6 +4,7 @@ use crate::config::Config; use crate::dto::*; use crate::models::*; use crate::schema::*; +use url::ParseError; pub fn load_title(db: &PgConnection, config: &Config, title: Title) -> QueryResult<TitleDto> { let title_names: Vec<TitleName> = TitleName::belonging_to(&title) @@ -24,11 +25,14 @@ pub fn load_title(db: &PgConnection, config: &Config, title: Title) -> QueryResu .load::<TitleMedium>(db)?; let title_subtitles: Vec<TitleSubtitle> = TitleSubtitle::belonging_to(&title) .load::<TitleSubtitle>(db)?; + let title_preview: TitlePreview = TitlePreview::belonging_to(&title) + .first::<TitlePreview>(db)?; Ok(process_title( config, title, title_names, title_descriptions, title_cast, title_genres, title_ratings, title_images, title_media, title_subtitles, + title_preview, )) } @@ -59,6 +63,8 @@ pub fn load_titles(db: &PgConnection, config: &Config, titles: Vec<Title>) -> Qu let title_subtitles: Vec<Vec<TitleSubtitle>> = TitleSubtitle::belonging_to(&titles) .load::<TitleSubtitle>(db)? .grouped_by(&titles); + let title_preview: Vec<TitlePreview> = TitlePreview::belonging_to(&titles) + .load::<TitlePreview>(db)?; Ok(titles.into_iter() .zip(title_names) .zip(title_descriptions) @@ -68,15 +74,17 @@ pub fn load_titles(db: &PgConnection, config: &Config, titles: Vec<Title>) -> Qu .zip(title_images) .zip(title_media) .zip(title_subtitles) + .zip(title_preview) .map(|tuple| { - let ((((((((title, akas), descriptions), + let (((((((((title, akas), descriptions), cast), genres), ratings), - images), media), subtitles) = tuple; + images), media), subtitles), preview) = tuple; process_title( config, title, akas, descriptions, cast, genres, ratings, images, media, subtitles, + preview, ) }).collect::<Vec<TitleDto>>()) } @@ -86,6 +94,7 @@ fn process_title( title: Title, names: Vec<TitleName>, descriptions: Vec<TitleDescription>, cast: Vec<(TitleCast, Person)>, genres: Vec<(TitleGenre, Genre)>, ratings: Vec<TitleRating>, images: Vec<TitleImage>, media: Vec<TitleMedium>, subtitles: Vec<TitleSubtitle>, + preview: TitlePreview, ) -> TitleDto { TitleDto::of( title, @@ -114,5 +123,10 @@ fn process_title( subtitles.into_iter().filter_map(|src| { SubtitleDto::of(src, &config).ok() }).collect::<Vec<_>>(), + preview.src.and_then(|preview| absolute_url(config, preview).ok()) ) +} + +pub fn absolute_url(config: &Config, url: impl Into<String>) -> Result<String, ParseError> { + config.static_url.join(url.into().as_str()).map(url::Url::into_string) } \ No newline at end of file diff --git a/src/models.rs b/src/models.rs index 3573ad982022932b9e57aa9cbfd0dceb451566c0..c4e00933ba13b789ef0bde5352aa75d5bdc0fa0e 100644 --- a/src/models.rs +++ b/src/models.rs @@ -148,6 +148,17 @@ pub struct TitleSubtitle { pub title_id: uuid::Uuid, } +#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)] +#[belongs_to(Title, foreign_key = "title_id")] +#[table_name = "title_previews"] +pub struct TitlePreview { + pub id: uuid::Uuid, + pub src: Option<String>, + pub created_at: chrono::DateTime<chrono::Utc>, + pub updated_at: chrono::DateTime<chrono::Utc>, + pub title_id: uuid::Uuid, +} + #[derive(Identifiable, Queryable, PartialEq, Debug)] #[table_name = "titles"] pub struct Title { diff --git a/src/schema.rs b/src/schema.rs index f20a6d78c4fbf53a513d1cff0a5fd1faf151a293..8a53a071ffc0d31b4a7fb75a2ebd47b627776187 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -131,6 +131,16 @@ table! { } } +table! { + title_previews (id) { + id -> Uuid, + src -> Nullable<Text>, + created_at -> Timestamptz, + updated_at -> Timestamptz, + title_id -> Uuid, + } +} + table! { titles (id) { id -> Uuid, @@ -156,6 +166,7 @@ joinable!(title_media -> titles (title_id)); joinable!(title_names -> titles (title_id)); joinable!(title_ratings -> titles (title_id)); joinable!(title_subtitles -> titles (title_id)); +joinable!(title_previews -> titles (title_id)); allow_tables_to_appear_in_same_query!( genres,