Skip to content
Snippets Groups Projects
Verified Commit 518c2e3a authored by Janne Mareike Koschinski's avatar Janne Mareike Koschinski
Browse files

Provide meta information about episodes/shows for titles

parent f0a827e4
Branches
No related tags found
No related merge requests found
...@@ -2,12 +2,13 @@ use serde::{Deserialize, Serialize}; ...@@ -2,12 +2,13 @@ use serde::{Deserialize, Serialize};
use crate::config::Config; use crate::config::Config;
use crate::models::*; use crate::models::*;
use crate::dto_helpers::absolute_url; use crate::dto_helpers::{absolute_url, UrlKind};
#[derive(Serialize, Deserialize, PartialEq, Debug)] #[derive(Serialize, Deserialize, PartialEq, Debug)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct TitleDto { pub struct TitleDto {
pub ids: TitleIdDto, pub ids: TitleIdDto,
pub kind: String,
pub original_language: Option<String>, pub original_language: Option<String>,
pub runtime: Option<i32>, pub runtime: Option<i32>,
pub year_start: Option<i32>, pub year_start: Option<i32>,
...@@ -45,6 +46,7 @@ impl TitleDto { ...@@ -45,6 +46,7 @@ impl TitleDto {
tmdb: src.tmdb_id, tmdb: src.tmdb_id,
tvdb: src.tvdb_id, tvdb: src.tvdb_id,
}, },
kind: src.kind,
original_language: src.original_language, original_language: src.original_language,
runtime: src.runtime, runtime: src.runtime,
year_start: src.year_start, year_start: src.year_start,
...@@ -204,7 +206,7 @@ impl ImageDto { ...@@ -204,7 +206,7 @@ impl ImageDto {
Ok(ImageDto { Ok(ImageDto {
kind: src.kind, kind: src.kind,
mime: src.mime, mime: src.mime,
src: absolute_url(config, src.src)?, src: absolute_url(config, UrlKind::Static,src.src)?,
}) })
} }
} }
...@@ -224,7 +226,7 @@ impl MediaDto { ...@@ -224,7 +226,7 @@ impl MediaDto {
mime: src.mime, mime: src.mime,
codecs: src.codecs, codecs: src.codecs,
languages: src.languages, languages: src.languages,
src: absolute_url(config, src.src)?, src: absolute_url(config, UrlKind::Static, src.src)?,
}) })
} }
} }
...@@ -262,7 +264,7 @@ impl SubtitleDto { ...@@ -262,7 +264,7 @@ impl SubtitleDto {
language: src.language, language: src.language,
region: src.region, region: src.region,
specifier: src.specifier, specifier: src.specifier,
src: absolute_url(config, src.src)?, src: absolute_url(config, UrlKind::Static,src.src)?,
}) })
} }
} }
...@@ -273,3 +275,11 @@ pub struct GenreWithTitlesDto { ...@@ -273,3 +275,11 @@ pub struct GenreWithTitlesDto {
pub genre: GenreDto, pub genre: GenreDto,
pub titles: Vec<TitleDto>, pub titles: Vec<TitleDto>,
} }
#[derive(Serialize, Deserialize, PartialEq, Debug)]
#[serde(rename_all = "camelCase")]
pub struct TitleMetaDto {
pub title: TitleDto,
pub show: Option<TitleDto>,
pub episodes: Vec<EpisodeDto>,
}
\ No newline at end of file
use diesel::*; use diesel::*;
use url::ParseError;
use uuid::Uuid;
use crate::config::Config; use crate::config::Config;
use crate::dto::*; use crate::dto::*;
use crate::models::*; use crate::models::*;
use crate::schema::*; use crate::schema::*;
use url::ParseError;
pub fn load_episodes(db: &PgConnection, config: &Config, title_id: Uuid) -> QueryResult<Vec<EpisodeDto>> {
let titles: Vec<(TitleEpisode, Title)> = title_episodes::table
.filter(title_episodes::show_id.eq(title_id))
.inner_join(titles::table.on(titles::id.eq(title_episodes::episode_id)))
.load::<(TitleEpisode, Title)>(db)?;
let (episodes, titles): (Vec<TitleEpisode>, Vec<Title>) = titles.into_iter().unzip();
let titles = load_titles(db, config, titles)?;
Ok(episodes.into_iter().zip(titles)
.map(|(episode, title)| EpisodeDto::of(episode, title))
.collect::<Vec<_>>())
}
pub fn load_show(db: &PgConnection, config: &Config, title_id: Uuid) -> QueryResult<TitleDto> {
let show = title_episodes::table
.filter(title_episodes::episode_id.eq(title_id))
.inner_join(titles::table.on(titles::id.eq(title_episodes::show_id)))
.select(titles::all_columns)
.first::<Title>(db)?;
load_title(db, config, show)
}
pub fn load_title(db: &PgConnection, config: &Config, title: Title) -> QueryResult<TitleDto> { pub fn load_title(db: &PgConnection, config: &Config, title: Title) -> QueryResult<TitleDto> {
let title_names: Vec<TitleName> = TitleName::belonging_to(&title) let title_names: Vec<TitleName> = TitleName::belonging_to(&title)
...@@ -129,10 +151,19 @@ fn process_title( ...@@ -129,10 +151,19 @@ fn process_title(
}).collect::<Vec<_>>(), }).collect::<Vec<_>>(),
preview preview
.and_then(|preview| preview.src) .and_then(|preview| preview.src)
.and_then(|preview| absolute_url(config, preview).ok()) .and_then(|preview| absolute_url(config, UrlKind::Static, preview).ok()),
) )
} }
pub fn absolute_url(config: &Config, url: impl Into<String>) -> Result<String, ParseError> { pub enum UrlKind {
config.static_url.join(url.into().as_str()).map(url::Url::into_string) Static,
Api,
}
pub fn absolute_url(config: &Config, url_kind: UrlKind, url: impl Into<String>) -> Result<String, ParseError> {
let base_url = match url_kind {
UrlKind::Static => &config.static_url,
UrlKind::Api => &config.api_url
};
base_url.join(url.into().as_str()).map(url::Url::into_string)
} }
\ No newline at end of file
...@@ -19,7 +19,7 @@ use url::Url; ...@@ -19,7 +19,7 @@ use url::Url;
use media_backend::config::Config; use media_backend::config::Config;
use media_backend::dto::*; use media_backend::dto::*;
use media_backend::dto_helpers::{load_title, load_titles}; use media_backend::dto_helpers::{load_title, load_titles, load_episodes, load_show};
use media_backend::models::*; use media_backend::models::*;
use media_backend::param_helpers::ParamUuid; use media_backend::param_helpers::ParamUuid;
...@@ -62,7 +62,7 @@ fn list_titles(db: MediaflixConnection, config: State<Config>) -> QueryResult<Js ...@@ -62,7 +62,7 @@ fn list_titles(db: MediaflixConnection, config: State<Config>) -> QueryResult<Js
} }
#[get("/api/v1/titles/<title_id>")] #[get("/api/v1/titles/<title_id>")]
fn get_title(db: MediaflixConnection, config: State<Config>, title_id: ParamUuid) -> QueryResult<Json<TitleDto>> { fn get_title(db: MediaflixConnection, config: State<Config>, title_id: ParamUuid) -> QueryResult<Json<TitleMetaDto>> {
use media_backend::schema::*; use media_backend::schema::*;
let title = load_title( let title = load_title(
&db.0, &db.0,
...@@ -71,21 +71,25 @@ fn get_title(db: MediaflixConnection, config: State<Config>, title_id: ParamUuid ...@@ -71,21 +71,25 @@ fn get_title(db: MediaflixConnection, config: State<Config>, title_id: ParamUuid
.find(title_id.uuid()) .find(title_id.uuid())
.first::<Title>(&db.0)?, .first::<Title>(&db.0)?,
)?; )?;
Ok(Json(title)) match title.kind.as_str() {
"episode" => {
let show = load_show(&db.0, &config, title.ids.uuid)?;
let episodes = load_episodes(&db.0, &config, show.ids.uuid)?;
Ok(Json(TitleMetaDto { title, show: Some(show), episodes }))
}
"show" => {
let episodes = load_episodes(&db.0, &config, title.ids.uuid)?;
Ok(Json(TitleMetaDto { title, show: None, episodes }))
}
_ => {
Ok(Json(TitleMetaDto { title, show: None, episodes: vec![] }))
}
}
} }
#[get("/api/v1/titles/<title_id>/episodes")] #[get("/api/v1/titles/<title_id>/episodes")]
fn list_episodes(db: MediaflixConnection, config: State<Config>, title_id: ParamUuid) -> QueryResult<Json<Vec<EpisodeDto>>> { fn list_episodes(db: MediaflixConnection, config: State<Config>, title_id: ParamUuid) -> QueryResult<Json<Vec<EpisodeDto>>> {
use media_backend::schema::*; Ok(Json(load_episodes(&db.0, &config, title_id.uuid())?))
let titles: Vec<(TitleEpisode, Title)> = title_episodes::table
.filter(title_episodes::show_id.eq(title_id.uuid()))
.inner_join(titles::table.on(titles::id.eq(title_episodes::episode_id)))
.load::<(TitleEpisode, Title)>(&db.0)?;
let (episodes, titles): (Vec<TitleEpisode>, Vec<Title>) = titles.into_iter().unzip();
let titles = load_titles(&db.0, &config, titles)?;
Ok(Json(episodes.into_iter().zip(titles)
.map(|(episode, title)| EpisodeDto::of(episode, title))
.collect::<Vec<_>>()))
} }
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
...@@ -96,12 +100,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> { ...@@ -96,12 +100,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
api_url: Url::parse(env::var("MEDIAFLIX_API_URL")?.as_str())?, api_url: Url::parse(env::var("MEDIAFLIX_API_URL")?.as_str())?,
static_url: Url::parse(env::var("MEDIAFLIX_STATIC_URL")?.as_str())?, static_url: Url::parse(env::var("MEDIAFLIX_STATIC_URL")?.as_str())?,
}) })
.attach(MediaflixConnection::fairing())
.attach(rocket_cors::CorsOptions {
allowed_headers: AllowedHeaders::some(&["Authorization", "Accept", "Accept-Language"]),
allow_credentials: true,
..Default::default()
}.to_cors()?)
.mount( .mount(
"/", "/",
rocket::routes![ rocket::routes![
...@@ -112,6 +110,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> { ...@@ -112,6 +110,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
], ],
) )
.mount("/static", StaticFiles::from(env::var("MEDIAFLIX_PATH")?)) .mount("/static", StaticFiles::from(env::var("MEDIAFLIX_PATH")?))
.attach(MediaflixConnection::fairing())
.attach(rocket_cors::CorsOptions {
allowed_headers: AllowedHeaders::some(&["Authorization", "Accept", "Accept-Language"]),
allow_credentials: true,
..Default::default()
}.to_cors()?)
.launch(); .launch();
Ok(()) Ok(())
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment