You've already forked flix
Throw away flix files in favor of a flix database
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
//! Collection entity
|
||||
|
||||
use flix_model::id::{CollectionId, LibraryId};
|
||||
|
||||
use seamantic::model::path::PathBytes;
|
||||
|
||||
use sea_orm::{
|
||||
ActiveModelBehavior, DeriveEntityModel, DerivePrimaryKey, DeriveRelation, EntityTrait,
|
||||
EnumIter, PrimaryKeyTrait, Related, RelationDef, RelationTrait,
|
||||
};
|
||||
|
||||
/// The database representation of a collection media folder
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_collections")]
|
||||
pub struct Model {
|
||||
/// The collection's ID
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: CollectionId,
|
||||
/// The collection's parent
|
||||
pub parent: Option<CollectionId>,
|
||||
/// The collection's slug
|
||||
pub slug: String,
|
||||
/// The collection's library ID
|
||||
pub library: LibraryId,
|
||||
/// The collection's directory
|
||||
pub directory: PathBytes,
|
||||
/// The collection's poster path
|
||||
pub relative_poster_path: Option<PathBytes>,
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
||||
/// Relation
|
||||
#[derive(Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
/// The parent collection of this collection
|
||||
#[sea_orm(
|
||||
belongs_to = "super::collections::Entity",
|
||||
from = "Column::Parent",
|
||||
to = "super::collections::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Parent,
|
||||
/// The library this collection belongs to
|
||||
#[sea_orm(
|
||||
belongs_to = "super::libraries::Entity",
|
||||
from = "Column::Library",
|
||||
to = "super::libraries::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Library,
|
||||
}
|
||||
|
||||
impl Related<super::collections::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Parent.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::libraries::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Library.def()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
//! Episode entity
|
||||
|
||||
use flix_model::id::{LibraryId, ShowId};
|
||||
|
||||
use seamantic::model::path::PathBytes;
|
||||
|
||||
use flix_model::numbers::{EpisodeNumber, SeasonNumber};
|
||||
use sea_orm::{
|
||||
ActiveModelBehavior, DeriveEntityModel, DerivePrimaryKey, DeriveRelation, EntityTrait,
|
||||
EnumIter, PrimaryKeyTrait, Related, RelationDef, RelationTrait,
|
||||
};
|
||||
|
||||
/// The database representation of a episode media folder
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_episodes")]
|
||||
pub struct Model {
|
||||
/// The episode's show's ID
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub show: ShowId,
|
||||
/// The episode's season's number
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub season: SeasonNumber,
|
||||
/// The episode's number
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub episode: EpisodeNumber,
|
||||
/// The episode's slug
|
||||
pub slug: String,
|
||||
/// The episode's library
|
||||
pub library: LibraryId,
|
||||
/// The episode's directory
|
||||
pub directory: PathBytes,
|
||||
/// The episode's media path
|
||||
pub relative_media_path: PathBytes,
|
||||
/// The episode's poster path
|
||||
pub relative_poster_path: Option<PathBytes>,
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
||||
/// Relation
|
||||
#[derive(Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
/// The library this episode belongs to
|
||||
#[sea_orm(
|
||||
belongs_to = "super::libraries::Entity",
|
||||
from = "Column::Library",
|
||||
to = "super::libraries::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Library,
|
||||
}
|
||||
|
||||
impl Related<super::libraries::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Library.def()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
//! Library entity
|
||||
|
||||
use flix_model::id::LibraryId;
|
||||
|
||||
use seamantic::model::path::PathBytes;
|
||||
|
||||
use sea_orm::{
|
||||
ActiveModelBehavior, DeriveEntityModel, DerivePrimaryKey, DeriveRelation, EntityTrait,
|
||||
EnumIter, PrimaryKeyTrait, Related, RelationDef, RelationTrait,
|
||||
};
|
||||
|
||||
/// The database representation of a library media folder
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_libraries")]
|
||||
pub struct Model {
|
||||
/// The library's ID
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: LibraryId,
|
||||
/// The library's directory
|
||||
pub directory: PathBytes,
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
||||
/// Relation
|
||||
#[derive(Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
/// All collections in this library
|
||||
#[sea_orm(has_many = "super::collections::Entity")]
|
||||
Collections,
|
||||
#[sea_orm(has_many = "super::movies::Entity")]
|
||||
/// All movies in this library
|
||||
Movies,
|
||||
#[sea_orm(has_many = "super::shows::Entity")]
|
||||
/// All shows in this library
|
||||
Shows,
|
||||
#[sea_orm(has_many = "super::seasons::Entity")]
|
||||
/// All seasons in this library
|
||||
Seasons,
|
||||
#[sea_orm(has_many = "super::episodes::Entity")]
|
||||
/// All episodes in this library
|
||||
Episodes,
|
||||
}
|
||||
|
||||
impl Related<super::collections::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Collections.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::movies::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Movies.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::shows::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Shows.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::seasons::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Seasons.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::episodes::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Episodes.def()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
//! This module contains entities for storing media file information
|
||||
|
||||
pub mod libraries;
|
||||
|
||||
pub mod collections;
|
||||
|
||||
pub mod movies;
|
||||
|
||||
pub mod episodes;
|
||||
pub mod seasons;
|
||||
pub mod shows;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::Path;
|
||||
|
||||
use flix_model::id::{CollectionId, LibraryId, MovieId, ShowId};
|
||||
|
||||
use sea_orm::ActiveModelTrait;
|
||||
use sea_orm::ActiveValue::{NotSet, Set};
|
||||
use sea_orm::sqlx::error::ErrorKind;
|
||||
use sea_orm_migration::MigratorTrait;
|
||||
|
||||
use crate::migration::Migrator;
|
||||
use crate::tests::new_memory_db;
|
||||
|
||||
use super::super::tests::get_error_kind;
|
||||
use super::super::tests::{
|
||||
make_flix_collection, make_flix_episode, make_flix_movie, make_flix_season, make_flix_show,
|
||||
};
|
||||
use super::super::tests::{noneable, notsettable};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_inserts() {
|
||||
let db = new_memory_db().await;
|
||||
Migrator::up(&db, None).await.expect("up");
|
||||
|
||||
// Libraries
|
||||
macro_rules! assert_library {
|
||||
($db:expr, $id:literal, Success $(; $($skip:ident),+)?) => {
|
||||
let model = assert_library!(@insert, $db, $id $(; $($skip),+)?)
|
||||
.expect("insert");
|
||||
|
||||
assert_eq!(model.id, LibraryId::from_raw($id));
|
||||
assert_eq!(model.directory, Path::new(concat!("/L/", $id)).to_owned().into());
|
||||
};
|
||||
($db:expr, $id:literal, $error:ident $(; $($skip:ident),+)?) => {
|
||||
let model = assert_library!(@insert, $db, $id $(; $($skip),+)?)
|
||||
.expect_err("insert");
|
||||
|
||||
assert_eq!(get_error_kind(model).expect("get_error_kind"), ErrorKind::$error);
|
||||
};
|
||||
(@insert, $db:expr, $id:literal $(; $($skip:ident),+)?) => {
|
||||
super::libraries::ActiveModel {
|
||||
id: notsettable!(id, LibraryId::from_raw($id) $(, $($skip),+)?),
|
||||
directory: notsettable!(directory, Path::new(concat!("/L/", $id)).to_owned().into() $(, $($skip),+)?),
|
||||
}.insert($db).await
|
||||
};
|
||||
}
|
||||
assert_library!(&db, 1, Success);
|
||||
assert_library!(&db, 1, UniqueViolation);
|
||||
assert_library!(&db, 2, Success);
|
||||
assert_library!(&db, 3, Success; id);
|
||||
assert_library!(&db, 4, NotNullViolation; directory);
|
||||
|
||||
// Collections
|
||||
macro_rules! assert_collection {
|
||||
($db:expr, $id:literal, $pid:expr, $lid:literal, Success $(; $($skip:ident),+)?) => {
|
||||
let model = assert_collection!(@insert, $db, $id, $pid, $lid $(; $($skip),+)?)
|
||||
.expect("insert");
|
||||
|
||||
assert_eq!(model.id, CollectionId::from_raw($id));
|
||||
assert_eq!(model.parent, $pid);
|
||||
assert_eq!(model.slug, concat!("C/", $id).to_string());
|
||||
assert_eq!(model.library, LibraryId::from_raw($lid));
|
||||
assert_eq!(model.directory, Path::new(concat!("/C/", $id)).to_owned().into());
|
||||
assert_eq!(model.relative_poster_path, noneable!(relative_poster_path, Path::new(concat!("C/Poster", $id)).to_owned().into() $(, $($skip),+)?));
|
||||
};
|
||||
($db:expr, $id:literal, $pid:expr, $lid:literal, $error:ident $(; $($skip:ident),+)?) => {
|
||||
let model = assert_collection!(@insert, $db, $id, $pid, $lid $(; $($skip),+)?)
|
||||
.expect_err("insert");
|
||||
|
||||
assert_eq!(get_error_kind(model).expect("get_error_kind"), ErrorKind::$error);
|
||||
};
|
||||
(@insert, $db:expr, $id:literal, $pid:expr, $lid:literal $(; $($skip:ident),+)?) => {
|
||||
super::collections::ActiveModel {
|
||||
id: notsettable!(id, CollectionId::from_raw($id) $(, $($skip),+)?),
|
||||
parent: notsettable!(parent, $pid $(, $($skip),+)?),
|
||||
slug: notsettable!(slug, concat!("C/", $id).to_string() $(, $($skip),+)?),
|
||||
library: notsettable!(library, LibraryId::from_raw($lid) $(, $($skip),+)?),
|
||||
directory: notsettable!(directory, Path::new(concat!("/C/", $id)).to_owned().into() $(, $($skip),+)?),
|
||||
relative_poster_path: notsettable!(relative_poster_path, Some(Path::new(concat!("C/Poster", $id)).to_owned().into()) $(, $($skip),+)?),
|
||||
}.insert($db).await
|
||||
};
|
||||
}
|
||||
assert_collection!(&db, 1, None, 0, ForeignKeyViolation);
|
||||
assert_collection!(
|
||||
&db,
|
||||
1,
|
||||
Some(CollectionId::from_raw(0)),
|
||||
1,
|
||||
ForeignKeyViolation
|
||||
);
|
||||
assert_collection!(&db, 1, None, 1, ForeignKeyViolation);
|
||||
make_flix_collection!(&db, 1);
|
||||
make_flix_collection!(&db, 2);
|
||||
make_flix_collection!(&db, 3);
|
||||
make_flix_collection!(&db, 4);
|
||||
make_flix_collection!(&db, 8);
|
||||
|
||||
assert_collection!(&db, 1, None, 1, Success);
|
||||
assert_collection!(&db, 1, None, 1, UniqueViolation);
|
||||
assert_collection!(&db, 2, None, 1, Success);
|
||||
assert_collection!(&db, 3, None, 1, Success; id);
|
||||
assert_collection!(&db, 4, None, 1, Success; parent);
|
||||
assert_collection!(&db, 5, None, 1, NotNullViolation; slug);
|
||||
assert_collection!(&db, 6, None, 1, NotNullViolation; library);
|
||||
assert_collection!(&db, 7, None, 1, NotNullViolation; directory);
|
||||
assert_collection!(&db, 8, None, 1, Success; relative_poster_path);
|
||||
|
||||
// Movies
|
||||
macro_rules! assert_movie {
|
||||
($db:expr, $id:literal, $pid:expr, $lid:literal, Success $(; $($skip:ident),+)?) => {
|
||||
let model = assert_movie!(@insert, $db, $id, $pid, $lid $(; $($skip),+)?)
|
||||
.expect("insert");
|
||||
|
||||
assert_eq!(model.id, MovieId::from_raw($id));
|
||||
assert_eq!(model.parent, $pid);
|
||||
assert_eq!(model.slug, concat!("M/", $id).to_string());
|
||||
assert_eq!(model.library, LibraryId::from_raw($lid));
|
||||
assert_eq!(model.directory, Path::new(concat!("/M/", $id)).to_owned().into());
|
||||
assert_eq!(model.relative_media_path, Path::new(concat!("M/Media", $id)).to_owned().into());
|
||||
assert_eq!(model.relative_poster_path, noneable!(relative_poster_path, Path::new(concat!("M/Poster", $id)).to_owned().into() $(, $($skip),+)?));
|
||||
};
|
||||
($db:expr, $id:literal, $pid:expr, $lid:literal, $error:ident $(; $($skip:ident),+)?) => {
|
||||
let model = assert_movie!(@insert, $db, $id, $pid, $lid $(; $($skip),+)?)
|
||||
.expect_err("insert");
|
||||
|
||||
assert_eq!(get_error_kind(model).expect("get_error_kind"), ErrorKind::$error);
|
||||
};
|
||||
(@insert, $db:expr, $id:literal, $pid:expr, $lid:literal $(; $($skip:ident),+)?) => {
|
||||
super::movies::ActiveModel {
|
||||
id: notsettable!(id, MovieId::from_raw($id) $(, $($skip),+)?),
|
||||
parent: notsettable!(parent, $pid $(, $($skip),+)?),
|
||||
slug: notsettable!(slug, concat!("M/", $id).to_string() $(, $($skip),+)?),
|
||||
library: notsettable!(library, LibraryId::from_raw($lid) $(, $($skip),+)?),
|
||||
directory: notsettable!(directory, Path::new(concat!("/M/", $id)).to_owned().into() $(, $($skip),+)?),
|
||||
relative_media_path: notsettable!(relative_media_path, Path::new(concat!("M/Media", $id)).to_owned().into() $(, $($skip),+)?),
|
||||
relative_poster_path: notsettable!(relative_poster_path, Some(Path::new(concat!("M/Poster", $id)).to_owned().into()) $(, $($skip),+)?),
|
||||
}.insert($db).await
|
||||
};
|
||||
}
|
||||
assert_movie!(&db, 1, None, 0, ForeignKeyViolation);
|
||||
assert_movie!(
|
||||
&db,
|
||||
1,
|
||||
Some(CollectionId::from_raw(0)),
|
||||
1,
|
||||
ForeignKeyViolation
|
||||
);
|
||||
assert_movie!(&db, 1, None, 1, ForeignKeyViolation);
|
||||
make_flix_movie!(&db, 1);
|
||||
make_flix_movie!(&db, 2);
|
||||
make_flix_movie!(&db, 3);
|
||||
make_flix_movie!(&db, 4);
|
||||
make_flix_movie!(&db, 9);
|
||||
|
||||
assert_movie!(&db, 1, None, 1, Success);
|
||||
assert_movie!(&db, 1, None, 1, UniqueViolation);
|
||||
assert_movie!(&db, 2, None, 1, Success);
|
||||
assert_movie!(&db, 3, None, 1, Success; id);
|
||||
assert_movie!(&db, 4, None, 1, Success; parent);
|
||||
assert_movie!(&db, 5, None, 1, NotNullViolation; slug);
|
||||
assert_movie!(&db, 6, None, 1, NotNullViolation; library);
|
||||
assert_movie!(&db, 7, None, 1, NotNullViolation; directory);
|
||||
assert_movie!(&db, 8, None, 1, NotNullViolation; relative_media_path);
|
||||
assert_movie!(&db, 9, None, 1, Success; relative_poster_path);
|
||||
|
||||
// Shows
|
||||
macro_rules! assert_show {
|
||||
($db:expr, $id:literal, $pid:expr, $lid:literal, Success $(; $($skip:ident),+)?) => {
|
||||
let model = assert_show!(@insert, $db, $id, $pid, $lid $(; $($skip),+)?)
|
||||
.expect("insert");
|
||||
|
||||
assert_eq!(model.id, ShowId::from_raw($id));
|
||||
assert_eq!(model.parent, $pid);
|
||||
assert_eq!(model.slug, concat!("S/", $id).to_string());
|
||||
assert_eq!(model.library, LibraryId::from_raw($lid));
|
||||
assert_eq!(model.directory, Path::new(concat!("/S/", $id)).to_owned().into());
|
||||
assert_eq!(model.relative_poster_path, noneable!(relative_poster_path, Path::new(concat!("S/Poster", $id)).to_owned().into() $(, $($skip),+)?));
|
||||
};
|
||||
($db:expr, $id:literal, $pid:expr, $lid:literal, $error:ident $(; $($skip:ident),+)?) => {
|
||||
let model = assert_show!(@insert, $db, $id, $pid, $lid $(; $($skip),+)?)
|
||||
.expect_err("insert");
|
||||
|
||||
assert_eq!(get_error_kind(model).expect("get_error_kind"), ErrorKind::$error);
|
||||
};
|
||||
(@insert, $db:expr, $id:literal, $pid:expr, $lid:literal $(; $($skip:ident),+)?) => {
|
||||
super::shows::ActiveModel {
|
||||
id: notsettable!(id, ShowId::from_raw($id) $(, $($skip),+)?),
|
||||
parent: notsettable!(parent, $pid $(, $($skip),+)?),
|
||||
slug: notsettable!(slug, concat!("S/", $id).to_string() $(, $($skip),+)?),
|
||||
library: notsettable!(library, LibraryId::from_raw($lid) $(, $($skip),+)?),
|
||||
directory: notsettable!(directory, Path::new(concat!("/S/", $id)).to_owned().into() $(, $($skip),+)?),
|
||||
relative_poster_path: notsettable!(relative_poster_path, Some(Path::new(concat!("S/Poster", $id)).to_owned().into()) $(, $($skip),+)?),
|
||||
}.insert($db).await
|
||||
};
|
||||
}
|
||||
assert_show!(&db, 1, None, 0, ForeignKeyViolation);
|
||||
assert_show!(
|
||||
&db,
|
||||
1,
|
||||
Some(CollectionId::from_raw(0)),
|
||||
1,
|
||||
ForeignKeyViolation
|
||||
);
|
||||
assert_show!(&db, 1, None, 1, ForeignKeyViolation);
|
||||
make_flix_show!(&db, 1);
|
||||
make_flix_show!(&db, 2);
|
||||
make_flix_show!(&db, 3);
|
||||
make_flix_show!(&db, 4);
|
||||
make_flix_show!(&db, 8);
|
||||
|
||||
assert_show!(&db, 1, None, 1, Success);
|
||||
assert_show!(&db, 1, None, 1, UniqueViolation);
|
||||
assert_show!(&db, 2, None, 1, Success);
|
||||
assert_show!(&db, 3, None, 1, Success; id);
|
||||
assert_show!(&db, 4, None, 1, Success; parent);
|
||||
assert_show!(&db, 5, None, 1, NotNullViolation; slug);
|
||||
assert_show!(&db, 6, None, 1, NotNullViolation; library);
|
||||
assert_show!(&db, 7, None, 1, NotNullViolation; directory);
|
||||
assert_show!(&db, 8, None, 1, Success; relative_poster_path);
|
||||
|
||||
// Seasons
|
||||
macro_rules! assert_season {
|
||||
($db:expr, $id:literal, $season:literal, $lid:literal, Success $(; $($skip:ident),+)?) => {
|
||||
let model = assert_season!(@insert, $db, $id, $season, $lid $(; $($skip),+)?)
|
||||
.expect("insert");
|
||||
|
||||
assert_eq!(model.show, ShowId::from_raw($id));
|
||||
assert_eq!(model.season, $season);
|
||||
assert_eq!(model.slug, concat!("S/S", $id).to_string());
|
||||
assert_eq!(model.library, LibraryId::from_raw($lid));
|
||||
assert_eq!(model.directory, Path::new(concat!("/S/S", $id)).to_owned().into());
|
||||
assert_eq!(model.relative_poster_path, noneable!(relative_poster_path, Path::new(concat!("S/S/Poster", $id)).to_owned().into() $(, $($skip),+)?));
|
||||
};
|
||||
($db:expr, $id:literal, $season:literal, $lid:literal, $error:ident $(; $($skip:ident),+)?) => {
|
||||
let model = assert_season!(@insert, $db, $id, $season, $lid $(; $($skip),+)?)
|
||||
.expect_err("insert");
|
||||
|
||||
assert_eq!(get_error_kind(model).expect("get_error_kind"), ErrorKind::$error);
|
||||
};
|
||||
(@insert, $db:expr, $id:literal, $season:literal, $lid:literal $(; $($skip:ident),+)?) => {
|
||||
super::seasons::ActiveModel {
|
||||
show: notsettable!(id, ShowId::from_raw($id) $(, $($skip),+)?),
|
||||
season: notsettable!(season, $season $(, $($skip),+)?),
|
||||
slug: notsettable!(slug, concat!("S/S", $id).to_string() $(, $($skip),+)?),
|
||||
library: notsettable!(library, LibraryId::from_raw($lid) $(, $($skip),+)?),
|
||||
directory: notsettable!(directory, Path::new(concat!("/S/S", $id)).to_owned().into() $(, $($skip),+)?),
|
||||
relative_poster_path: notsettable!(relative_poster_path, Some(Path::new(concat!("S/S/Poster", $id)).to_owned().into()) $(, $($skip),+)?),
|
||||
}.insert($db).await
|
||||
};
|
||||
}
|
||||
assert_season!(&db, 1, 1, 0, ForeignKeyViolation);
|
||||
assert_season!(&db, 1, 1, 1, ForeignKeyViolation);
|
||||
make_flix_season!(&db, 1, 1);
|
||||
make_flix_season!(&db, 1, 2);
|
||||
make_flix_season!(&db, 2, 1);
|
||||
make_flix_season!(&db, 3, 1);
|
||||
make_flix_season!(&db, 8, 1);
|
||||
|
||||
assert_season!(&db, 1, 1, 1, Success);
|
||||
assert_season!(&db, 1, 2, 1, Success);
|
||||
assert_season!(&db, 1, 1, 1, UniqueViolation);
|
||||
assert_season!(&db, 2, 1, 1, Success);
|
||||
assert_season!(&db, 3, 1, 1, Success; show);
|
||||
assert_season!(&db, 4, 1, 1, NotNullViolation; season);
|
||||
assert_season!(&db, 5, 1, 1, NotNullViolation; slug);
|
||||
assert_season!(&db, 6, 1, 1, NotNullViolation; library);
|
||||
assert_season!(&db, 7, 1, 1, NotNullViolation; directory);
|
||||
assert_season!(&db, 8, 1, 1, Success; relative_poster_path);
|
||||
|
||||
// Episodes
|
||||
macro_rules! assert_episode {
|
||||
($db:expr, $id:literal, $season:literal, $episode:literal, $lid:literal, Success $(; $($skip:ident),+)?) => {
|
||||
let model = assert_episode!(@insert, $db, $id, $season, $episode, $lid $(; $($skip),+)?)
|
||||
.expect("insert");
|
||||
|
||||
assert_eq!(model.show, ShowId::from_raw($id));
|
||||
assert_eq!(model.season, $season);
|
||||
assert_eq!(model.episode, $episode);
|
||||
assert_eq!(model.slug, concat!("S/S/E", $id).to_string());
|
||||
assert_eq!(model.library, LibraryId::from_raw($lid));
|
||||
assert_eq!(model.directory, Path::new(concat!("/S/S/E", $id)).to_owned().into());
|
||||
assert_eq!(model.relative_media_path, Path::new(concat!("E/Media", $id)).to_owned().into());
|
||||
assert_eq!(model.relative_poster_path, noneable!(relative_poster_path, Path::new(concat!("S/S/E/Poster", $id)).to_owned().into() $(, $($skip),+)?));
|
||||
};
|
||||
($db:expr, $id:literal, $season:literal, $episode:literal, $lid:literal, $error:ident $(; $($skip:ident),+)?) => {
|
||||
let model = assert_episode!(@insert, $db, $id, $season, $episode, $lid $(; $($skip),+)?)
|
||||
.expect_err("insert");
|
||||
|
||||
assert_eq!(get_error_kind(model).expect("get_error_kind"), ErrorKind::$error);
|
||||
};
|
||||
(@insert, $db:expr, $id:literal, $season:literal, $episode:literal, $lid:literal $(; $($skip:ident),+)?) => {
|
||||
super::episodes::ActiveModel {
|
||||
show: notsettable!(id, ShowId::from_raw($id) $(, $($skip),+)?),
|
||||
season: notsettable!(season, $season $(, $($skip),+)?),
|
||||
episode: notsettable!(episode, $episode $(, $($skip),+)?),
|
||||
slug: notsettable!(slug, concat!("S/S/E", $id).to_string() $(, $($skip),+)?),
|
||||
library: notsettable!(library, LibraryId::from_raw($lid) $(, $($skip),+)?),
|
||||
directory: notsettable!(directory, Path::new(concat!("/S/S/E", $id)).to_owned().into() $(, $($skip),+)?),
|
||||
relative_media_path: notsettable!(relative_media_path, Path::new(concat!("E/Media", $id)).to_owned().into() $(, $($skip),+)?),
|
||||
relative_poster_path: notsettable!(relative_poster_path, Some(Path::new(concat!("S/S/E/Poster", $id)).to_owned().into()) $(, $($skip),+)?),
|
||||
}.insert($db).await
|
||||
};
|
||||
}
|
||||
assert_episode!(&db, 1, 1, 1, 0, ForeignKeyViolation);
|
||||
assert_episode!(&db, 1, 1, 1, 1, ForeignKeyViolation);
|
||||
make_flix_episode!(&db, 1, 1, 1);
|
||||
make_flix_episode!(&db, 1, 1, 2);
|
||||
make_flix_episode!(&db, 2, 1, 1);
|
||||
make_flix_episode!(&db, 3, 1, 1);
|
||||
make_flix_show!(&db, 10);
|
||||
make_flix_season!(&db, 10, 1);
|
||||
make_flix_episode!(&db, 10, 1, 1);
|
||||
|
||||
assert_episode!(&db, 1, 1, 1, 1, Success);
|
||||
assert_episode!(&db, 1, 1, 2, 1, Success);
|
||||
assert_episode!(&db, 1, 1, 1, 1, UniqueViolation);
|
||||
assert_episode!(&db, 2, 1, 1, 1, Success);
|
||||
assert_episode!(&db, 3, 1, 1, 1, Success; show);
|
||||
assert_episode!(&db, 4, 1, 1, 1, NotNullViolation; season);
|
||||
assert_episode!(&db, 5, 1, 1, 1, NotNullViolation; episode);
|
||||
assert_episode!(&db, 6, 1, 1, 1, NotNullViolation; slug);
|
||||
assert_episode!(&db, 7, 1, 1, 1, NotNullViolation; library);
|
||||
assert_episode!(&db, 8, 1, 1, 1, NotNullViolation; directory);
|
||||
assert_episode!(&db, 9, 1, 1, 1, NotNullViolation; relative_media_path);
|
||||
assert_episode!(&db, 10, 1, 1, 1, Success; relative_poster_path);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
//! Movie entity
|
||||
|
||||
use flix_model::id::{CollectionId, LibraryId, MovieId};
|
||||
|
||||
use seamantic::model::path::PathBytes;
|
||||
|
||||
use sea_orm::{
|
||||
ActiveModelBehavior, DeriveEntityModel, DerivePrimaryKey, DeriveRelation, EntityTrait,
|
||||
EnumIter, PrimaryKeyTrait, Related, RelationDef, RelationTrait,
|
||||
};
|
||||
|
||||
/// The database representation of a movie media folder
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_movies")]
|
||||
pub struct Model {
|
||||
/// The movie's ID
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: MovieId,
|
||||
/// The movie's parent
|
||||
pub parent: Option<CollectionId>,
|
||||
/// The movie's slug
|
||||
pub slug: String,
|
||||
/// The movie's library
|
||||
pub library: LibraryId,
|
||||
/// The movie's directory
|
||||
pub directory: PathBytes,
|
||||
/// The movie's media path
|
||||
pub relative_media_path: PathBytes,
|
||||
/// The movie's poster path
|
||||
pub relative_poster_path: Option<PathBytes>,
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
||||
/// Relation
|
||||
#[derive(Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
/// The parent collection of this collection
|
||||
#[sea_orm(
|
||||
belongs_to = "super::collections::Entity",
|
||||
from = "Column::Parent",
|
||||
to = "super::collections::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Parent,
|
||||
/// The library this movie belongs to
|
||||
#[sea_orm(
|
||||
belongs_to = "super::libraries::Entity",
|
||||
from = "Column::Library",
|
||||
to = "super::libraries::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Library,
|
||||
}
|
||||
|
||||
impl Related<super::collections::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Parent.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::libraries::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Library.def()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
//! Season entity
|
||||
|
||||
use flix_model::id::{LibraryId, ShowId};
|
||||
|
||||
use seamantic::model::path::PathBytes;
|
||||
|
||||
use flix_model::numbers::SeasonNumber;
|
||||
use sea_orm::{
|
||||
ActiveModelBehavior, DeriveEntityModel, DerivePrimaryKey, DeriveRelation, EntityTrait,
|
||||
EnumIter, PrimaryKeyTrait, Related, RelationDef, RelationTrait,
|
||||
};
|
||||
|
||||
/// The database representation of a season media folder
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_seasons")]
|
||||
pub struct Model {
|
||||
/// The season's show's ID
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub show: ShowId,
|
||||
/// The season's number
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub season: SeasonNumber,
|
||||
/// The season's slug
|
||||
pub slug: String,
|
||||
/// The season's library
|
||||
pub library: LibraryId,
|
||||
/// The season's directory
|
||||
pub directory: PathBytes,
|
||||
/// The season's poster path
|
||||
pub relative_poster_path: Option<PathBytes>,
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
||||
/// Relation
|
||||
#[derive(Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
/// The library this season belongs to
|
||||
#[sea_orm(
|
||||
belongs_to = "super::libraries::Entity",
|
||||
from = "Column::Library",
|
||||
to = "super::libraries::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Library,
|
||||
}
|
||||
|
||||
impl Related<super::libraries::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Library.def()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
//! Show entity
|
||||
|
||||
use flix_model::id::{CollectionId, LibraryId, ShowId};
|
||||
|
||||
use seamantic::model::path::PathBytes;
|
||||
|
||||
use sea_orm::{
|
||||
ActiveModelBehavior, DeriveEntityModel, DerivePrimaryKey, DeriveRelation, EntityTrait,
|
||||
EnumIter, PrimaryKeyTrait, Related, RelationDef, RelationTrait,
|
||||
};
|
||||
|
||||
/// The database representation of a show media folder
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_shows")]
|
||||
pub struct Model {
|
||||
/// The show's ID
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: ShowId,
|
||||
/// The show's parent
|
||||
pub parent: Option<CollectionId>,
|
||||
/// The show's slug
|
||||
pub slug: String,
|
||||
/// The show's library
|
||||
pub library: LibraryId,
|
||||
/// The show's directory
|
||||
pub directory: PathBytes,
|
||||
/// The show's poster path
|
||||
pub relative_poster_path: Option<PathBytes>,
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
||||
/// Relation
|
||||
#[derive(Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
/// The parent collection of this collection
|
||||
#[sea_orm(
|
||||
belongs_to = "super::collections::Entity",
|
||||
from = "Column::Parent",
|
||||
to = "super::collections::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Parent,
|
||||
/// The library this show belongs to
|
||||
#[sea_orm(
|
||||
belongs_to = "super::libraries::Entity",
|
||||
from = "Column::Library",
|
||||
to = "super::libraries::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Library,
|
||||
}
|
||||
|
||||
impl Related<super::collections::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Parent.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::libraries::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Library.def()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user