Throw away flix files in favor of a flix database

This commit is contained in:
2025-09-18 22:41:34 -06:00
parent ba9c3fa03d
commit 06110b91a1
117 changed files with 8645 additions and 1054 deletions
@@ -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()
}
}
+58
View File
@@ -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()
}
}
+73
View File
@@ -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()
}
}
+340
View File
@@ -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);
}
}
+68
View File
@@ -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()
}
}
+53
View File
@@ -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()
}
}
+66
View File
@@ -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()
}
}