Model refinement

This commit is contained in:
2025-10-01 19:34:07 -06:00
parent 06110b91a1
commit c0cbf019ba
28 changed files with 292 additions and 143 deletions
Generated
+8 -8
View File
@@ -489,7 +489,7 @@ checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959"
[[package]] [[package]]
name = "flix" name = "flix"
version = "0.0.9" version = "0.0.10"
dependencies = [ dependencies = [
"flix-db", "flix-db",
"flix-fs", "flix-fs",
@@ -499,7 +499,7 @@ dependencies = [
[[package]] [[package]]
name = "flix-cli" name = "flix-cli"
version = "0.0.9" version = "0.0.10"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
@@ -515,7 +515,7 @@ dependencies = [
[[package]] [[package]]
name = "flix-db" name = "flix-db"
version = "0.0.9" version = "0.0.10"
dependencies = [ dependencies = [
"chrono", "chrono",
"flix-model", "flix-model",
@@ -528,7 +528,7 @@ dependencies = [
[[package]] [[package]]
name = "flix-fs" name = "flix-fs"
version = "0.0.9" version = "0.0.10"
dependencies = [ dependencies = [
"async-stream", "async-stream",
"flix-model", "flix-model",
@@ -540,7 +540,7 @@ dependencies = [
[[package]] [[package]]
name = "flix-model" name = "flix-model"
version = "0.0.9" version = "0.0.10"
dependencies = [ dependencies = [
"seamantic", "seamantic",
"serde", "serde",
@@ -549,7 +549,7 @@ dependencies = [
[[package]] [[package]]
name = "flix-tmdb" name = "flix-tmdb"
version = "0.0.9" version = "0.0.10"
dependencies = [ dependencies = [
"chrono", "chrono",
"flix-model", "flix-model",
@@ -1961,9 +1961,9 @@ dependencies = [
[[package]] [[package]]
name = "seamantic" name = "seamantic"
version = "0.0.5" version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4b388ca1d150dd2f3cceff59e910543fe7c638f44a9cb6223ace53d06d38e" checksum = "f4672c35f48459f4c16151a51ec6a1f244406fdf08e2e8ad8bcc6c78867d18af"
dependencies = [ dependencies = [
"sea-orm", "sea-orm",
"sea-orm-migration", "sea-orm-migration",
+7 -7
View File
@@ -35,14 +35,14 @@ overflow-checks = true
strip = "debuginfo" strip = "debuginfo"
[workspace.dependencies] [workspace.dependencies]
flix = { path = "crates/flix", version = "=0.0.9", default-features = false } flix = { path = "crates/flix", version = "=0.0.10", default-features = false }
flix-cli = { path = "crates/cli", version = "=0.0.9", default-features = false } flix-cli = { path = "crates/cli", version = "=0.0.10", default-features = false }
flix-db = { path = "crates/db", version = "=0.0.9", default-features = false } flix-db = { path = "crates/db", version = "=0.0.10", default-features = false }
flix-fs = { path = "crates/fs", version = "=0.0.9", default-features = false } flix-fs = { path = "crates/fs", version = "=0.0.10", default-features = false }
flix-model = { path = "crates/model", version = "=0.0.9", default-features = false } flix-model = { path = "crates/model", version = "=0.0.10", default-features = false }
flix-tmdb = { path = "crates/tmdb", version = "=0.0.9", default-features = false } flix-tmdb = { path = "crates/tmdb", version = "=0.0.10", default-features = false }
seamantic = { version = "0.0.5", default-features = false } seamantic = { version = "0.0.6", default-features = false }
sea-orm = { version = "2.0.0-rc.7", default-features = false } sea-orm = { version = "2.0.0-rc.7", default-features = false }
sea-orm-migration = { version = "2.0.0-rc.7", default-features = false } sea-orm-migration = { version = "2.0.0-rc.7", default-features = false }
+1
View File
@@ -11,4 +11,5 @@ Libraries and tools for dealing with media metadata
- fmt: `cargo fmt --check` - fmt: `cargo fmt --check`
- docs: `RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features` - docs: `RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features`
- install: `cargo install --path crates/cli` - install: `cargo install --path crates/cli`
- semver: `cargo semver-checks --all-features`
- publish: `cargo publish --dry-run --workspace` - publish: `cargo publish --dry-run --workspace`
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "flix-cli" name = "flix-cli"
version = "0.0.9" version = "0.0.10"
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
description = "CLI for interacting with a flix database" description = "CLI for interacting with a flix database"
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "flix-db" name = "flix-db"
version = "0.0.9" version = "0.0.10"
categories = [] categories = []
description = "Types for storing persistent data about media" description = "Types for storing persistent data about media"
+31 -1
View File
@@ -25,7 +25,7 @@ pub struct Model {
/// The collection's directory /// The collection's directory
pub directory: PathBytes, pub directory: PathBytes,
/// The collection's poster path /// The collection's poster path
pub relative_poster_path: Option<PathBytes>, pub relative_poster_path: Option<String>,
} }
impl ActiveModelBehavior for ActiveModel {} impl ActiveModelBehavior for ActiveModel {}
@@ -51,6 +51,24 @@ pub enum Relation {
on_delete = "Cascade" on_delete = "Cascade"
)] )]
Library, Library,
/// The media info for this collection
#[sea_orm(
belongs_to = "super::super::info::collections::Entity",
from = "Column::Id",
to = "super::super::info::collections::Column::Id",
on_update = "Cascade",
on_delete = "Cascade"
)]
MediaInfo,
/// The watched info for this collection
#[sea_orm(
belongs_to = "super::super::watched::collections::Entity",
from = "Column::Id",
to = "super::super::watched::collections::Column::Id",
on_update = "Cascade",
on_delete = "Cascade"
)]
WatchInfo,
} }
impl Related<super::collections::Entity> for Entity { impl Related<super::collections::Entity> for Entity {
@@ -64,3 +82,15 @@ impl Related<super::libraries::Entity> for Entity {
Relation::Library.def() Relation::Library.def()
} }
} }
impl Related<super::super::info::collections::Entity> for Entity {
fn to() -> RelationDef {
Relation::MediaInfo.def()
}
}
impl Related<super::super::watched::collections::Entity> for Entity {
fn to() -> RelationDef {
Relation::WatchInfo.def()
}
}
+34 -2
View File
@@ -23,6 +23,8 @@ pub struct Model {
/// The episode's number /// The episode's number
#[sea_orm(primary_key, auto_increment = false)] #[sea_orm(primary_key, auto_increment = false)]
pub episode: EpisodeNumber, pub episode: EpisodeNumber,
/// The number of additional contained episodes
pub count: u8,
/// The episode's slug /// The episode's slug
pub slug: String, pub slug: String,
/// The episode's library /// The episode's library
@@ -30,9 +32,9 @@ pub struct Model {
/// The episode's directory /// The episode's directory
pub directory: PathBytes, pub directory: PathBytes,
/// The episode's media path /// The episode's media path
pub relative_media_path: PathBytes, pub relative_media_path: String,
/// The episode's poster path /// The episode's poster path
pub relative_poster_path: Option<PathBytes>, pub relative_poster_path: Option<String>,
} }
impl ActiveModelBehavior for ActiveModel {} impl ActiveModelBehavior for ActiveModel {}
@@ -49,6 +51,24 @@ pub enum Relation {
on_delete = "Cascade" on_delete = "Cascade"
)] )]
Library, Library,
/// The media info for this episode
#[sea_orm(
belongs_to = "super::super::info::seasons::Entity",
from = "(Column::Show, Column::Season)",
to = "(super::super::info::seasons::Column::Show, super::super::info::seasons::Column::Season)",
on_update = "Cascade",
on_delete = "Cascade"
)]
MediaInfo,
/// The watched info for this episode
#[sea_orm(
belongs_to = "super::super::watched::episodes::Entity",
from = "(Column::Show, Column::Season, Column::Episode)",
to = "(super::super::watched::episodes::Column::Show, super::super::watched::episodes::Column::Season, super::super::watched::episodes::Column::Episode)",
on_update = "Cascade",
on_delete = "Cascade"
)]
WatchInfo,
} }
impl Related<super::libraries::Entity> for Entity { impl Related<super::libraries::Entity> for Entity {
@@ -56,3 +76,15 @@ impl Related<super::libraries::Entity> for Entity {
Relation::Library.def() Relation::Library.def()
} }
} }
impl Related<super::super::info::episodes::Entity> for Entity {
fn to() -> RelationDef {
Relation::MediaInfo.def()
}
}
impl Related<super::super::watched::episodes::Entity> for Entity {
fn to() -> RelationDef {
Relation::WatchInfo.def()
}
}
+24 -22
View File
@@ -74,7 +74,7 @@ mod tests {
assert_eq!(model.slug, concat!("C/", $id).to_string()); assert_eq!(model.slug, concat!("C/", $id).to_string());
assert_eq!(model.library, LibraryId::from_raw($lid)); assert_eq!(model.library, LibraryId::from_raw($lid));
assert_eq!(model.directory, Path::new(concat!("/C/", $id)).to_owned().into()); 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),+)?)); assert_eq!(model.relative_poster_path, noneable!(relative_poster_path, concat!("C/Poster", $id).to_owned() $(, $($skip),+)?));
}; };
($db:expr, $id:literal, $pid:expr, $lid:literal, $error:ident $(; $($skip:ident),+)?) => { ($db:expr, $id:literal, $pid:expr, $lid:literal, $error:ident $(; $($skip:ident),+)?) => {
let model = assert_collection!(@insert, $db, $id, $pid, $lid $(; $($skip),+)?) let model = assert_collection!(@insert, $db, $id, $pid, $lid $(; $($skip),+)?)
@@ -89,7 +89,7 @@ mod tests {
slug: notsettable!(slug, concat!("C/", $id).to_string() $(, $($skip),+)?), slug: notsettable!(slug, concat!("C/", $id).to_string() $(, $($skip),+)?),
library: notsettable!(library, LibraryId::from_raw($lid) $(, $($skip),+)?), library: notsettable!(library, LibraryId::from_raw($lid) $(, $($skip),+)?),
directory: notsettable!(directory, Path::new(concat!("/C/", $id)).to_owned().into() $(, $($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),+)?), relative_poster_path: notsettable!(relative_poster_path, Some(concat!("C/Poster", $id).to_owned()) $(, $($skip),+)?),
}.insert($db).await }.insert($db).await
}; };
} }
@@ -129,8 +129,8 @@ mod tests {
assert_eq!(model.slug, concat!("M/", $id).to_string()); assert_eq!(model.slug, concat!("M/", $id).to_string());
assert_eq!(model.library, LibraryId::from_raw($lid)); assert_eq!(model.library, LibraryId::from_raw($lid));
assert_eq!(model.directory, Path::new(concat!("/M/", $id)).to_owned().into()); 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_media_path, concat!("M/Media", $id));
assert_eq!(model.relative_poster_path, noneable!(relative_poster_path, Path::new(concat!("M/Poster", $id)).to_owned().into() $(, $($skip),+)?)); assert_eq!(model.relative_poster_path, noneable!(relative_poster_path, concat!("M/Poster", $id).to_owned() $(, $($skip),+)?));
}; };
($db:expr, $id:literal, $pid:expr, $lid:literal, $error:ident $(; $($skip:ident),+)?) => { ($db:expr, $id:literal, $pid:expr, $lid:literal, $error:ident $(; $($skip:ident),+)?) => {
let model = assert_movie!(@insert, $db, $id, $pid, $lid $(; $($skip),+)?) let model = assert_movie!(@insert, $db, $id, $pid, $lid $(; $($skip),+)?)
@@ -145,8 +145,8 @@ mod tests {
slug: notsettable!(slug, concat!("M/", $id).to_string() $(, $($skip),+)?), slug: notsettable!(slug, concat!("M/", $id).to_string() $(, $($skip),+)?),
library: notsettable!(library, LibraryId::from_raw($lid) $(, $($skip),+)?), library: notsettable!(library, LibraryId::from_raw($lid) $(, $($skip),+)?),
directory: notsettable!(directory, Path::new(concat!("/M/", $id)).to_owned().into() $(, $($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_media_path: notsettable!(relative_media_path, concat!("M/Media", $id).to_owned() $(, $($skip),+)?),
relative_poster_path: notsettable!(relative_poster_path, Some(Path::new(concat!("M/Poster", $id)).to_owned().into()) $(, $($skip),+)?), relative_poster_path: notsettable!(relative_poster_path, Some(concat!("M/Poster", $id).to_owned()) $(, $($skip),+)?),
}.insert($db).await }.insert($db).await
}; };
} }
@@ -187,7 +187,7 @@ mod tests {
assert_eq!(model.slug, concat!("S/", $id).to_string()); assert_eq!(model.slug, concat!("S/", $id).to_string());
assert_eq!(model.library, LibraryId::from_raw($lid)); assert_eq!(model.library, LibraryId::from_raw($lid));
assert_eq!(model.directory, Path::new(concat!("/S/", $id)).to_owned().into()); 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),+)?)); assert_eq!(model.relative_poster_path, noneable!(relative_poster_path, concat!("S/Poster", $id).to_owned() $(, $($skip),+)?));
}; };
($db:expr, $id:literal, $pid:expr, $lid:literal, $error:ident $(; $($skip:ident),+)?) => { ($db:expr, $id:literal, $pid:expr, $lid:literal, $error:ident $(; $($skip:ident),+)?) => {
let model = assert_show!(@insert, $db, $id, $pid, $lid $(; $($skip),+)?) let model = assert_show!(@insert, $db, $id, $pid, $lid $(; $($skip),+)?)
@@ -202,7 +202,7 @@ mod tests {
slug: notsettable!(slug, concat!("S/", $id).to_string() $(, $($skip),+)?), slug: notsettable!(slug, concat!("S/", $id).to_string() $(, $($skip),+)?),
library: notsettable!(library, LibraryId::from_raw($lid) $(, $($skip),+)?), library: notsettable!(library, LibraryId::from_raw($lid) $(, $($skip),+)?),
directory: notsettable!(directory, Path::new(concat!("/S/", $id)).to_owned().into() $(, $($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),+)?), relative_poster_path: notsettable!(relative_poster_path, Some(concat!("S/Poster", $id).to_owned()) $(, $($skip),+)?),
}.insert($db).await }.insert($db).await
}; };
} }
@@ -242,7 +242,7 @@ mod tests {
assert_eq!(model.slug, concat!("S/S", $id).to_string()); assert_eq!(model.slug, concat!("S/S", $id).to_string());
assert_eq!(model.library, LibraryId::from_raw($lid)); assert_eq!(model.library, LibraryId::from_raw($lid));
assert_eq!(model.directory, Path::new(concat!("/S/S", $id)).to_owned().into()); 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),+)?)); assert_eq!(model.relative_poster_path, noneable!(relative_poster_path, concat!("S/S/Poster", $id).to_owned() $(, $($skip),+)?));
}; };
($db:expr, $id:literal, $season:literal, $lid:literal, $error:ident $(; $($skip:ident),+)?) => { ($db:expr, $id:literal, $season:literal, $lid:literal, $error:ident $(; $($skip:ident),+)?) => {
let model = assert_season!(@insert, $db, $id, $season, $lid $(; $($skip),+)?) let model = assert_season!(@insert, $db, $id, $season, $lid $(; $($skip),+)?)
@@ -257,7 +257,7 @@ mod tests {
slug: notsettable!(slug, concat!("S/S", $id).to_string() $(, $($skip),+)?), slug: notsettable!(slug, concat!("S/S", $id).to_string() $(, $($skip),+)?),
library: notsettable!(library, LibraryId::from_raw($lid) $(, $($skip),+)?), library: notsettable!(library, LibraryId::from_raw($lid) $(, $($skip),+)?),
directory: notsettable!(directory, Path::new(concat!("/S/S", $id)).to_owned().into() $(, $($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),+)?), relative_poster_path: notsettable!(relative_poster_path, Some(concat!("S/S/Poster", $id).to_owned()) $(, $($skip),+)?),
}.insert($db).await }.insert($db).await
}; };
} }
@@ -292,8 +292,8 @@ mod tests {
assert_eq!(model.slug, concat!("S/S/E", $id).to_string()); assert_eq!(model.slug, concat!("S/S/E", $id).to_string());
assert_eq!(model.library, LibraryId::from_raw($lid)); 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.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_media_path, concat!("E/Media", $id));
assert_eq!(model.relative_poster_path, noneable!(relative_poster_path, Path::new(concat!("S/S/E/Poster", $id)).to_owned().into() $(, $($skip),+)?)); assert_eq!(model.relative_poster_path, noneable!(relative_poster_path, concat!("S/S/E/Poster", $id).to_owned() $(, $($skip),+)?));
}; };
($db:expr, $id:literal, $season:literal, $episode:literal, $lid:literal, $error:ident $(; $($skip:ident),+)?) => { ($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),+)?) let model = assert_episode!(@insert, $db, $id, $season, $episode, $lid $(; $($skip),+)?)
@@ -306,11 +306,12 @@ mod tests {
show: notsettable!(id, ShowId::from_raw($id) $(, $($skip),+)?), show: notsettable!(id, ShowId::from_raw($id) $(, $($skip),+)?),
season: notsettable!(season, $season $(, $($skip),+)?), season: notsettable!(season, $season $(, $($skip),+)?),
episode: notsettable!(episode, $episode $(, $($skip),+)?), episode: notsettable!(episode, $episode $(, $($skip),+)?),
count: notsettable!(count, 0 $(, $($skip),+)?),
slug: notsettable!(slug, concat!("S/S/E", $id).to_string() $(, $($skip),+)?), slug: notsettable!(slug, concat!("S/S/E", $id).to_string() $(, $($skip),+)?),
library: notsettable!(library, LibraryId::from_raw($lid) $(, $($skip),+)?), library: notsettable!(library, LibraryId::from_raw($lid) $(, $($skip),+)?),
directory: notsettable!(directory, Path::new(concat!("/S/S/E", $id)).to_owned().into() $(, $($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_media_path: notsettable!(relative_media_path, concat!("E/Media", $id).to_owned() $(, $($skip),+)?),
relative_poster_path: notsettable!(relative_poster_path, Some(Path::new(concat!("S/S/E/Poster", $id)).to_owned().into()) $(, $($skip),+)?), relative_poster_path: notsettable!(relative_poster_path, Some(concat!("S/S/E/Poster", $id).to_owned()) $(, $($skip),+)?),
}.insert($db).await }.insert($db).await
}; };
} }
@@ -320,9 +321,9 @@ mod tests {
make_flix_episode!(&db, 1, 1, 2); make_flix_episode!(&db, 1, 1, 2);
make_flix_episode!(&db, 2, 1, 1); make_flix_episode!(&db, 2, 1, 1);
make_flix_episode!(&db, 3, 1, 1); make_flix_episode!(&db, 3, 1, 1);
make_flix_show!(&db, 10); make_flix_show!(&db, 11);
make_flix_season!(&db, 10, 1); make_flix_season!(&db, 11, 1);
make_flix_episode!(&db, 10, 1, 1); make_flix_episode!(&db, 11, 1, 1);
assert_episode!(&db, 1, 1, 1, 1, Success); assert_episode!(&db, 1, 1, 1, 1, Success);
assert_episode!(&db, 1, 1, 2, 1, Success); assert_episode!(&db, 1, 1, 2, 1, Success);
@@ -331,10 +332,11 @@ mod tests {
assert_episode!(&db, 3, 1, 1, 1, Success; show); assert_episode!(&db, 3, 1, 1, 1, Success; show);
assert_episode!(&db, 4, 1, 1, 1, NotNullViolation; season); assert_episode!(&db, 4, 1, 1, 1, NotNullViolation; season);
assert_episode!(&db, 5, 1, 1, 1, NotNullViolation; episode); assert_episode!(&db, 5, 1, 1, 1, NotNullViolation; episode);
assert_episode!(&db, 6, 1, 1, 1, NotNullViolation; slug); assert_episode!(&db, 6, 1, 1, 1, NotNullViolation; count);
assert_episode!(&db, 7, 1, 1, 1, NotNullViolation; library); assert_episode!(&db, 7, 1, 1, 1, NotNullViolation; slug);
assert_episode!(&db, 8, 1, 1, 1, NotNullViolation; directory); assert_episode!(&db, 8, 1, 1, 1, NotNullViolation; library);
assert_episode!(&db, 9, 1, 1, 1, NotNullViolation; relative_media_path); assert_episode!(&db, 9, 1, 1, 1, NotNullViolation; directory);
assert_episode!(&db, 10, 1, 1, 1, Success; relative_poster_path); assert_episode!(&db, 10, 1, 1, 1, NotNullViolation; relative_media_path);
assert_episode!(&db, 11, 1, 1, 1, Success; relative_poster_path);
} }
} }
+32 -2
View File
@@ -25,9 +25,9 @@ pub struct Model {
/// The movie's directory /// The movie's directory
pub directory: PathBytes, pub directory: PathBytes,
/// The movie's media path /// The movie's media path
pub relative_media_path: PathBytes, pub relative_media_path: String,
/// The movie's poster path /// The movie's poster path
pub relative_poster_path: Option<PathBytes>, pub relative_poster_path: Option<String>,
} }
impl ActiveModelBehavior for ActiveModel {} impl ActiveModelBehavior for ActiveModel {}
@@ -53,6 +53,24 @@ pub enum Relation {
on_delete = "Cascade" on_delete = "Cascade"
)] )]
Library, Library,
/// The media info for this movie
#[sea_orm(
belongs_to = "super::super::info::movies::Entity",
from = "Column::Id",
to = "super::super::info::movies::Column::Id",
on_update = "Cascade",
on_delete = "Cascade"
)]
MediaInfo,
/// The watched info for this movie
#[sea_orm(
belongs_to = "super::super::watched::movies::Entity",
from = "Column::Id",
to = "super::super::watched::movies::Column::Id",
on_update = "Cascade",
on_delete = "Cascade"
)]
WatchInfo,
} }
impl Related<super::collections::Entity> for Entity { impl Related<super::collections::Entity> for Entity {
@@ -66,3 +84,15 @@ impl Related<super::libraries::Entity> for Entity {
Relation::Library.def() Relation::Library.def()
} }
} }
impl Related<super::super::info::movies::Entity> for Entity {
fn to() -> RelationDef {
Relation::MediaInfo.def()
}
}
impl Related<super::super::watched::movies::Entity> for Entity {
fn to() -> RelationDef {
Relation::WatchInfo.def()
}
}
+31 -1
View File
@@ -27,7 +27,7 @@ pub struct Model {
/// The season's directory /// The season's directory
pub directory: PathBytes, pub directory: PathBytes,
/// The season's poster path /// The season's poster path
pub relative_poster_path: Option<PathBytes>, pub relative_poster_path: Option<String>,
} }
impl ActiveModelBehavior for ActiveModel {} impl ActiveModelBehavior for ActiveModel {}
@@ -44,6 +44,24 @@ pub enum Relation {
on_delete = "Cascade" on_delete = "Cascade"
)] )]
Library, Library,
/// The media info for this show
#[sea_orm(
belongs_to = "super::super::info::seasons::Entity",
from = "(Column::Show, Column::Season)",
to = "(super::super::info::seasons::Column::Show, super::super::info::seasons::Column::Season)",
on_update = "Cascade",
on_delete = "Cascade"
)]
MediaInfo,
/// The watched info for this show
#[sea_orm(
belongs_to = "super::super::watched::seasons::Entity",
from = "(Column::Show, Column::Season)",
to = "(super::super::watched::seasons::Column::Show, super::super::watched::seasons::Column::Season)",
on_update = "Cascade",
on_delete = "Cascade"
)]
WatchInfo,
} }
impl Related<super::libraries::Entity> for Entity { impl Related<super::libraries::Entity> for Entity {
@@ -51,3 +69,15 @@ impl Related<super::libraries::Entity> for Entity {
Relation::Library.def() Relation::Library.def()
} }
} }
impl Related<super::super::info::seasons::Entity> for Entity {
fn to() -> RelationDef {
Relation::MediaInfo.def()
}
}
impl Related<super::super::watched::seasons::Entity> for Entity {
fn to() -> RelationDef {
Relation::WatchInfo.def()
}
}
+31 -1
View File
@@ -25,7 +25,7 @@ pub struct Model {
/// The show's directory /// The show's directory
pub directory: PathBytes, pub directory: PathBytes,
/// The show's poster path /// The show's poster path
pub relative_poster_path: Option<PathBytes>, pub relative_poster_path: Option<String>,
} }
impl ActiveModelBehavior for ActiveModel {} impl ActiveModelBehavior for ActiveModel {}
@@ -51,6 +51,24 @@ pub enum Relation {
on_delete = "Cascade" on_delete = "Cascade"
)] )]
Library, Library,
/// The media info for this show
#[sea_orm(
belongs_to = "super::super::info::shows::Entity",
from = "Column::Id",
to = "super::super::info::shows::Column::Id",
on_update = "Cascade",
on_delete = "Cascade"
)]
MediaInfo,
/// The watched info for this show
#[sea_orm(
belongs_to = "super::super::watched::shows::Entity",
from = "Column::Id",
to = "super::super::watched::shows::Column::Id",
on_update = "Cascade",
on_delete = "Cascade"
)]
WatchInfo,
} }
impl Related<super::collections::Entity> for Entity { impl Related<super::collections::Entity> for Entity {
@@ -64,3 +82,15 @@ impl Related<super::libraries::Entity> for Entity {
Relation::Library.def() Relation::Library.def()
} }
} }
impl Related<super::super::info::shows::Entity> for Entity {
fn to() -> RelationDef {
Relation::MediaInfo.def()
}
}
impl Related<super::super::watched::shows::Entity> for Entity {
fn to() -> RelationDef {
Relation::WatchInfo.def()
}
}
+14 -7
View File
@@ -108,7 +108,7 @@ mod tests {
slug: Set(::std::string::String::new()), slug: Set(::std::string::String::new()),
library: Set(::flix_model::id::LibraryId::from_raw($lid)), library: Set(::flix_model::id::LibraryId::from_raw($lid)),
directory: Set(::std::path::PathBuf::new().into()), directory: Set(::std::path::PathBuf::new().into()),
relative_poster_path: Set(None), relative_poster_path: Set(::core::option::Option::None),
} }
.insert($db) .insert($db)
.await .await
@@ -144,8 +144,8 @@ mod tests {
slug: Set(::std::string::String::new()), slug: Set(::std::string::String::new()),
library: Set(::flix_model::id::LibraryId::from_raw($lid)), library: Set(::flix_model::id::LibraryId::from_raw($lid)),
directory: Set(::std::path::PathBuf::new().into()), directory: Set(::std::path::PathBuf::new().into()),
relative_media_path: Set(::std::path::PathBuf::new().into()), relative_media_path: Set(::std::string::String::new()),
relative_poster_path: Set(None), relative_poster_path: Set(::core::option::Option::None),
} }
.insert($db) .insert($db)
.await .await
@@ -181,7 +181,7 @@ mod tests {
slug: Set(::std::string::String::new()), slug: Set(::std::string::String::new()),
library: Set(::flix_model::id::LibraryId::from_raw($lid)), library: Set(::flix_model::id::LibraryId::from_raw($lid)),
directory: Set(::std::path::PathBuf::new().into()), directory: Set(::std::path::PathBuf::new().into()),
relative_poster_path: Set(None), relative_poster_path: Set(::core::option::Option::None),
} }
.insert($db) .insert($db)
.await .await
@@ -216,7 +216,7 @@ mod tests {
slug: Set(::std::string::String::new()), slug: Set(::std::string::String::new()),
library: Set(::flix_model::id::LibraryId::from_raw($lid)), library: Set(::flix_model::id::LibraryId::from_raw($lid)),
directory: Set(::std::path::PathBuf::new().into()), directory: Set(::std::path::PathBuf::new().into()),
relative_poster_path: Set(None), relative_poster_path: Set(::core::option::Option::None),
} }
.insert($db) .insert($db)
.await .await
@@ -245,16 +245,23 @@ mod tests {
pub(super) use make_flix_episode; pub(super) use make_flix_episode;
macro_rules! have_episode { macro_rules! have_episode {
($db:expr, $lid:literal, $show:literal, $season:literal, $episode:literal) => { ($db:expr, $lid:literal, $show:literal, $season:literal, $episode:literal) => {
have_episode!(@make, $db, $lid, $show, $season, $episode, 0);
};
($db:expr, $lid:literal, $show:literal, $season:literal, $episode:literal, >1) => {
have_episode!(@make, $db, $lid, $show, $season, $episode, 1);
};
(@make, $db:expr, $lid:literal, $show:literal, $season:literal, $episode:literal, $count:literal) => {
$crate::entity::tests::make_flix_episode!($db, $show, $season, $episode); $crate::entity::tests::make_flix_episode!($db, $show, $season, $episode);
$crate::entity::content::episodes::ActiveModel { $crate::entity::content::episodes::ActiveModel {
show: Set(::flix_model::id::ShowId::from_raw($show)), show: Set(::flix_model::id::ShowId::from_raw($show)),
season: Set($season), season: Set($season),
episode: Set($episode), episode: Set($episode),
count: Set($count),
slug: Set(::std::string::String::new()), slug: Set(::std::string::String::new()),
library: Set(::flix_model::id::LibraryId::from_raw($lid)), library: Set(::flix_model::id::LibraryId::from_raw($lid)),
directory: Set(::std::path::PathBuf::new().into()), directory: Set(::std::path::PathBuf::new().into()),
relative_media_path: Set(::std::path::PathBuf::new().into()), relative_media_path: Set(::std::string::String::new()),
relative_poster_path: Set(None), relative_poster_path: Set(::core::option::Option::None),
} }
.insert($db) .insert($db)
.await .await
+2 -4
View File
@@ -181,12 +181,10 @@ mod tests {
have_episode!(&db, 1, 1, 1, 1); have_episode!(&db, 1, 1, 1, 1);
assert_episode!(&db, 1, 1, 1, 1, Success); assert_episode!(&db, 1, 1, 1, 1, Success);
assert_episode!(&db, 1, 1, 1, 2, Success); assert_episode!(&db, 1, 1, 1, 2, Success);
have_episode!(&db, 1, 1, 1, 2); have_episode!(&db, 1, 1, 1, 2, >1); // Covers 2 and 3
make_flix_episode!(&db, 1, 1, 3);
assert_episode!(&db, 1, 1, 2, 1, Success); assert_episode!(&db, 1, 1, 2, 1, Success);
assert_episode!(&db, 1, 1, 2, 2, Success); assert_episode!(&db, 1, 1, 2, 2, Success);
have_episode!(&db, 1, 1, 1, 3);
assert_episode!(&db, 1, 1, 3, 1, Success);
assert_episode!(&db, 1, 1, 3, 2, Success);
have_season!(&db, 1, 1, 2); have_season!(&db, 1, 1, 2);
have_episode!(&db, 1, 1, 2, 1); have_episode!(&db, 1, 1, 2, 1);
assert_episode!(&db, 1, 2, 1, 1, Success); assert_episode!(&db, 1, 2, 1, 1, Success);
@@ -4,7 +4,7 @@ use sea_orm::sea_query;
use sea_orm::sea_query::{ForeignKeyCreateStatement, Table}; use sea_orm::sea_query::{ForeignKeyCreateStatement, Table};
use sea_orm::{DbErr, Iden}; use sea_orm::{DbErr, Iden};
use sea_orm_migration::SchemaManager; use sea_orm_migration::SchemaManager;
use sea_orm_migration::schema::{binary, binary_null, integer, integer_null, string}; use sea_orm_migration::schema::{binary, integer, integer_null, string, string_null};
use crate::migration::m_000001::FlixInfoCollections; use crate::migration::m_000001::FlixInfoCollections;
use crate::migration::m_000003::FlixLibraries; use crate::migration::m_000003::FlixLibraries;
@@ -30,7 +30,7 @@ pub async fn up(manager: &SchemaManager<'_>) -> Result<(), DbErr> {
.col(string(FlixCollections::Slug)) .col(string(FlixCollections::Slug))
.col(integer(FlixCollections::Library)) .col(integer(FlixCollections::Library))
.col(binary(FlixCollections::Directory)) .col(binary(FlixCollections::Directory))
.col(binary_null(FlixCollections::RelativePosterPath)) .col(string_null(FlixCollections::RelativePosterPath))
.foreign_key( .foreign_key(
ForeignKeyCreateStatement::new() ForeignKeyCreateStatement::new()
.name("fk-flix_collections-id") .name("fk-flix_collections-id")
+5 -3
View File
@@ -2,7 +2,7 @@ use sea_orm::sea_query;
use sea_orm::sea_query::{ForeignKeyCreateStatement, Index, Table}; use sea_orm::sea_query::{ForeignKeyCreateStatement, Index, Table};
use sea_orm::{DbErr, Iden}; use sea_orm::{DbErr, Iden};
use sea_orm_migration::SchemaManager; use sea_orm_migration::SchemaManager;
use sea_orm_migration::schema::{binary, binary_null, integer, string}; use sea_orm_migration::schema::{binary, integer, string, string_null};
use crate::migration::m_000001::FlixInfoEpisodes; use crate::migration::m_000001::FlixInfoEpisodes;
use crate::migration::m_000003::FlixLibraries; use crate::migration::m_000003::FlixLibraries;
@@ -13,6 +13,7 @@ pub enum FlixEpisodes {
Show, Show,
Season, Season,
Episode, Episode,
Count,
Slug, Slug,
Library, Library,
Directory, Directory,
@@ -28,11 +29,12 @@ pub async fn up(manager: &SchemaManager<'_>) -> Result<(), DbErr> {
.col(integer(FlixEpisodes::Show)) .col(integer(FlixEpisodes::Show))
.col(integer(FlixEpisodes::Season)) .col(integer(FlixEpisodes::Season))
.col(integer(FlixEpisodes::Episode)) .col(integer(FlixEpisodes::Episode))
.col(integer(FlixEpisodes::Count))
.col(string(FlixEpisodes::Slug)) .col(string(FlixEpisodes::Slug))
.col(integer(FlixEpisodes::Library)) .col(integer(FlixEpisodes::Library))
.col(binary(FlixEpisodes::Directory)) .col(binary(FlixEpisodes::Directory))
.col(binary(FlixEpisodes::RelativeMediaPath)) .col(string(FlixEpisodes::RelativeMediaPath))
.col(binary_null(FlixEpisodes::RelativePosterPath)) .col(string_null(FlixEpisodes::RelativePosterPath))
.primary_key( .primary_key(
Index::create() Index::create()
.col(FlixEpisodes::Show) .col(FlixEpisodes::Show)
+3 -3
View File
@@ -4,7 +4,7 @@ use sea_orm::sea_query;
use sea_orm::sea_query::{ForeignKeyCreateStatement, Table}; use sea_orm::sea_query::{ForeignKeyCreateStatement, Table};
use sea_orm::{DbErr, Iden}; use sea_orm::{DbErr, Iden};
use sea_orm_migration::SchemaManager; use sea_orm_migration::SchemaManager;
use sea_orm_migration::schema::{binary, binary_null, integer, integer_null, string}; use sea_orm_migration::schema::{binary, integer, integer_null, string, string_null};
use crate::migration::m_000001::FlixInfoMovies; use crate::migration::m_000001::FlixInfoMovies;
use crate::migration::m_000003::{FlixCollections, FlixLibraries}; use crate::migration::m_000003::{FlixCollections, FlixLibraries};
@@ -31,8 +31,8 @@ pub async fn up(manager: &SchemaManager<'_>) -> Result<(), DbErr> {
.col(string(FlixMovies::Slug)) .col(string(FlixMovies::Slug))
.col(integer(FlixMovies::Library)) .col(integer(FlixMovies::Library))
.col(binary(FlixMovies::Directory)) .col(binary(FlixMovies::Directory))
.col(binary(FlixMovies::RelativeMediaPath)) .col(string(FlixMovies::RelativeMediaPath))
.col(binary_null(FlixMovies::RelativePosterPath)) .col(string_null(FlixMovies::RelativePosterPath))
.foreign_key( .foreign_key(
ForeignKeyCreateStatement::new() ForeignKeyCreateStatement::new()
.name("fk-flix_movies-id") .name("fk-flix_movies-id")
+2 -2
View File
@@ -2,7 +2,7 @@ use sea_orm::sea_query;
use sea_orm::sea_query::{ForeignKeyCreateStatement, Index, Table}; use sea_orm::sea_query::{ForeignKeyCreateStatement, Index, Table};
use sea_orm::{DbErr, Iden}; use sea_orm::{DbErr, Iden};
use sea_orm_migration::SchemaManager; use sea_orm_migration::SchemaManager;
use sea_orm_migration::schema::{binary, binary_null, integer, string}; use sea_orm_migration::schema::{binary, integer, string, string_null};
use crate::migration::m_000001::FlixInfoSeasons; use crate::migration::m_000001::FlixInfoSeasons;
use crate::migration::m_000003::FlixLibraries; use crate::migration::m_000003::FlixLibraries;
@@ -28,7 +28,7 @@ pub async fn up(manager: &SchemaManager<'_>) -> Result<(), DbErr> {
.col(string(FlixSeasons::Slug)) .col(string(FlixSeasons::Slug))
.col(integer(FlixSeasons::Library)) .col(integer(FlixSeasons::Library))
.col(binary(FlixSeasons::Directory)) .col(binary(FlixSeasons::Directory))
.col(binary_null(FlixSeasons::RelativePosterPath)) .col(string_null(FlixSeasons::RelativePosterPath))
.primary_key( .primary_key(
Index::create() Index::create()
.col(FlixSeasons::Show) .col(FlixSeasons::Show)
+2 -2
View File
@@ -4,7 +4,7 @@ use sea_orm::sea_query;
use sea_orm::sea_query::{ForeignKeyCreateStatement, Table}; use sea_orm::sea_query::{ForeignKeyCreateStatement, Table};
use sea_orm::{DbErr, Iden}; use sea_orm::{DbErr, Iden};
use sea_orm_migration::SchemaManager; use sea_orm_migration::SchemaManager;
use sea_orm_migration::schema::{binary, binary_null, integer, integer_null, string}; use sea_orm_migration::schema::{binary, integer, integer_null, string, string_null};
use crate::migration::m_000001::FlixInfoShows; use crate::migration::m_000001::FlixInfoShows;
use crate::migration::m_000003::{FlixCollections, FlixLibraries}; use crate::migration::m_000003::{FlixCollections, FlixLibraries};
@@ -30,7 +30,7 @@ pub async fn up(manager: &SchemaManager<'_>) -> Result<(), DbErr> {
.col(string(FlixShows::Slug)) .col(string(FlixShows::Slug))
.col(integer(FlixShows::Library)) .col(integer(FlixShows::Library))
.col(binary(FlixShows::Directory)) .col(binary(FlixShows::Directory))
.col(binary_null(FlixShows::RelativePosterPath)) .col(string_null(FlixShows::RelativePosterPath))
.foreign_key( .foreign_key(
ForeignKeyCreateStatement::new() ForeignKeyCreateStatement::new()
.name("fk-flix_shows-id") .name("fk-flix_shows-id")
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "flix" name = "flix"
version = "0.0.9" version = "0.0.10"
categories = [] categories = []
description = "Mechanisms for interacting with flix media" description = "Mechanisms for interacting with flix media"
+2 -2
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "flix-fs" name = "flix-fs"
version = "0.0.9" version = "0.0.10"
categories = [] categories = []
description = "Filesystem scanner for flix media" description = "Filesystem scanner for flix media"
@@ -22,7 +22,7 @@ workspace = true
flix-model = { workspace = true } flix-model = { workspace = true }
async-stream = { workspace = true } async-stream = { workspace = true }
regex = { workspace = true } regex = { workspace = true, features = ["std", "perf"] }
thiserror = { workspace = true } thiserror = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
tokio-stream = { workspace = true, features = ["fs"] } tokio-stream = { workspace = true, features = ["fs"] }
+13 -13
View File
@@ -47,17 +47,17 @@ pub enum Scanner {
Show { Show {
/// The ID of the parent collection (if any) /// The ID of the parent collection (if any)
parent: Option<CollectionId>, parent: Option<CollectionId>,
/// The ID of the show this episode belongs to /// The ID of the show
id: ShowId, id: ShowId,
/// The file name of the poster file /// The file name of the poster file
poster_file_name: Option<String>, poster_file_name: Option<String>,
}, },
/// A scanned episode /// A scanned episode
Season { Season {
/// The ID of the show this episode belongs to /// The ID of the show this season belongs to
show: ShowId, show: ShowId,
/// The season this episode belongs to /// The number of this season
number: SeasonNumber, season: SeasonNumber,
/// The file name of the poster file /// The file name of the poster file
poster_file_name: Option<String>, poster_file_name: Option<String>,
}, },
@@ -68,7 +68,7 @@ pub enum Scanner {
/// The season this episode belongs to /// The season this episode belongs to
season: SeasonNumber, season: SeasonNumber,
/// The number(s) of this episode /// The number(s) of this episode
number: EpisodeNumbers, episode: EpisodeNumbers,
/// The file name of the media file /// The file name of the media file
media_file_name: String, media_file_name: String,
/// The file name of the poster file /// The file name of the poster file
@@ -108,23 +108,23 @@ impl From<show::Scanner> for Scanner {
}, },
show::Scanner::Season { show::Scanner::Season {
show, show,
number, season,
poster_file_name, poster_file_name,
} => Self::Season { } => Self::Season {
show, show,
number, season,
poster_file_name, poster_file_name,
}, },
show::Scanner::Episode { show::Scanner::Episode {
show, show,
season, season,
number, episode,
media_file_name, media_file_name,
poster_file_name, poster_file_name,
} => Self::Episode { } => Self::Episode {
show, show,
season, season,
number, episode,
media_file_name, media_file_name,
poster_file_name, poster_file_name,
}, },
@@ -166,23 +166,23 @@ impl From<generic::Scanner> for Scanner {
}, },
generic::Scanner::Season { generic::Scanner::Season {
show, show,
number, season,
poster_file_name, poster_file_name,
} => Self::Season { } => Self::Season {
show, show,
number, season,
poster_file_name, poster_file_name,
}, },
generic::Scanner::Episode { generic::Scanner::Episode {
show, show,
season, season,
number, episode,
media_file_name, media_file_name,
poster_file_name, poster_file_name,
} => Self::Episode { } => Self::Episode {
show, show,
season, season,
number, episode,
media_file_name, media_file_name,
poster_file_name, poster_file_name,
}, },
+3 -3
View File
@@ -26,7 +26,7 @@ pub enum Scanner {
/// The season this episode belongs to /// The season this episode belongs to
season: SeasonNumber, season: SeasonNumber,
/// The number(s) of this episode /// The number(s) of this episode
number: EpisodeNumbers, episode: EpisodeNumbers,
/// The file name of the media file /// The file name of the media file
media_file_name: String, media_file_name: String,
/// The file name of the poster file /// The file name of the poster file
@@ -40,7 +40,7 @@ impl Scanner {
path: &Path, path: &Path,
show: ShowId, show: ShowId,
season: SeasonNumber, season: SeasonNumber,
number: EpisodeNumbers, episode: EpisodeNumbers,
) -> impl Stream<Item = Item> { ) -> impl Stream<Item = Item> {
stream!({ stream!({
let dirs = match fs::read_dir(path).await { let dirs = match fs::read_dir(path).await {
@@ -137,7 +137,7 @@ impl Scanner {
event: Ok(Self::Episode { event: Ok(Self::Episode {
show, show,
season, season,
number, episode,
media_file_name, media_file_name,
poster_file_name, poster_file_name,
}), }),
+17 -15
View File
@@ -51,17 +51,17 @@ pub enum Scanner {
Show { Show {
/// The ID of the parent collection (if any) /// The ID of the parent collection (if any)
parent: Option<CollectionId>, parent: Option<CollectionId>,
/// The ID of the show this episode belongs to /// The ID of the show
id: ShowId, id: ShowId,
/// The file name of the poster file /// The file name of the poster file
poster_file_name: Option<String>, poster_file_name: Option<String>,
}, },
/// A scanned episode /// A scanned episode
Season { Season {
/// The ID of the show this episode belongs to /// The ID of the show this season belongs to
show: ShowId, show: ShowId,
/// The season this episode belongs to /// The season this episode belongs to
number: SeasonNumber, season: SeasonNumber,
/// The file name of the poster file /// The file name of the poster file
poster_file_name: Option<String>, poster_file_name: Option<String>,
}, },
@@ -72,7 +72,7 @@ pub enum Scanner {
/// The season this episode belongs to /// The season this episode belongs to
season: SeasonNumber, season: SeasonNumber,
/// The number(s) of this episode /// The number(s) of this episode
number: EpisodeNumbers, episode: EpisodeNumbers,
/// The file name of the media file /// The file name of the media file
media_file_name: String, media_file_name: String,
/// The file name of the poster file /// The file name of the poster file
@@ -114,23 +114,23 @@ impl From<collection::Scanner> for Scanner {
}, },
collection::Scanner::Season { collection::Scanner::Season {
show, show,
number, season,
poster_file_name, poster_file_name,
} => Self::Season { } => Self::Season {
show, show,
number, season,
poster_file_name, poster_file_name,
}, },
collection::Scanner::Episode { collection::Scanner::Episode {
show, show,
season, season,
number, episode,
media_file_name, media_file_name,
poster_file_name, poster_file_name,
} => Self::Episode { } => Self::Episode {
show, show,
season, season,
number, episode,
media_file_name, media_file_name,
poster_file_name, poster_file_name,
}, },
@@ -170,23 +170,23 @@ impl From<show::Scanner> for Scanner {
}, },
show::Scanner::Season { show::Scanner::Season {
show, show,
number, season,
poster_file_name, poster_file_name,
} => Self::Season { } => Self::Season {
show, show,
number, season,
poster_file_name, poster_file_name,
}, },
show::Scanner::Episode { show::Scanner::Episode {
show, show,
season, season,
number, episode,
media_file_name, media_file_name,
poster_file_name, poster_file_name,
} => Self::Episode { } => Self::Episode {
show, show,
season, season,
number, episode,
media_file_name, media_file_name,
poster_file_name, poster_file_name,
}, },
@@ -211,10 +211,12 @@ impl Scanner {
} }
let media_folder_re = MEDIA_FOLDER_REGEX.get_or_init(|| { let media_folder_re = MEDIA_FOLDER_REGEX.get_or_init(|| {
Regex::new(r"^[\w ]+ \(\d+\) \[\d+\]$").unwrap_or_else(|_| panic!("regex is invalid")) Regex::new(r"^[[[:alnum:]] -]+ \([[:digit:]]+\) \[[[:digit:]]+\]$")
.unwrap_or_else(|err| panic!("regex is invalid: {err}"))
});
let season_folder_re = SEASON_FOLDER_REGEX.get_or_init(|| {
Regex::new(r"^S[[:digit:]]+$").unwrap_or_else(|err| panic!("regex is invalid: {err}"))
}); });
let season_folder_re = SEASON_FOLDER_REGEX
.get_or_init(|| Regex::new(r"^S\d+$").unwrap_or_else(|_| panic!("regex is invalid")));
stream!({ stream!({
let Some(dir_name) = path.file_name().and_then(OsStr::to_str) else { let Some(dir_name) = path.file_name().and_then(OsStr::to_str) else {
+15 -12
View File
@@ -22,10 +22,10 @@ pub type Item = crate::Item<Scanner>;
pub enum Scanner { pub enum Scanner {
/// A scanned episode /// A scanned episode
Season { Season {
/// The ID of the show this episode belongs to /// The ID of the show this season belongs to
show: ShowId, show: ShowId,
/// The season this episode belongs to /// The season this episode belongs to
number: SeasonNumber, season: SeasonNumber,
/// The file name of the poster file /// The file name of the poster file
poster_file_name: Option<String>, poster_file_name: Option<String>,
}, },
@@ -36,7 +36,7 @@ pub enum Scanner {
/// The season this episode belongs to /// The season this episode belongs to
season: SeasonNumber, season: SeasonNumber,
/// The number(s) of this episode /// The number(s) of this episode
number: EpisodeNumbers, episode: EpisodeNumbers,
/// The file name of the media file /// The file name of the media file
media_file_name: String, media_file_name: String,
/// The file name of the poster file /// The file name of the poster file
@@ -50,13 +50,13 @@ impl From<episode::Scanner> for Scanner {
episode::Scanner::Episode { episode::Scanner::Episode {
show, show,
season, season,
number, episode,
media_file_name, media_file_name,
poster_file_name, poster_file_name,
} => Self::Episode { } => Self::Episode {
show, show,
season, season,
number, episode,
media_file_name, media_file_name,
poster_file_name, poster_file_name,
}, },
@@ -69,7 +69,7 @@ impl Scanner {
pub fn scan_season( pub fn scan_season(
path: &Path, path: &Path,
show: ShowId, show: ShowId,
number: SeasonNumber, season: SeasonNumber,
) -> impl Stream<Item = Item> { ) -> impl Stream<Item = Item> {
stream!({ stream!({
let dirs = match fs::read_dir(path).await { let dirs = match fs::read_dir(path).await {
@@ -141,7 +141,7 @@ impl Scanner {
path: path.to_owned(), path: path.to_owned(),
event: Ok(Self::Season { event: Ok(Self::Season {
show, show,
number, season,
poster_file_name, poster_file_name,
}), }),
}; };
@@ -170,14 +170,14 @@ impl Scanner {
continue; continue;
}; };
let Ok(season) = s_str.parse::<SeasonNumber>() else { let Ok(season_number) = s_str.parse::<SeasonNumber>() else {
yield Item { yield Item {
path: path.to_owned(), path: path.to_owned(),
event: Err(Error::UnexpectedFolder), event: Err(Error::UnexpectedFolder),
}; };
continue; continue;
}; };
if season != number { if season_number != season {
yield Item { yield Item {
path: path.to_owned(), path: path.to_owned(),
event: Err(Error::Inconsistent), event: Err(Error::Inconsistent),
@@ -204,9 +204,12 @@ impl Scanner {
continue; continue;
}; };
for await event in for await event in episode::Scanner::scan_episode(
episode::Scanner::scan_episode(&episode_dir, show, number, episode_numbers) &episode_dir,
{ show,
season_number,
episode_numbers,
) {
yield event.map(|e| e.into()); yield event.map(|e| e.into());
} }
} }
+8 -8
View File
@@ -24,17 +24,17 @@ pub enum Scanner {
Show { Show {
/// The ID of the parent collection (if any) /// The ID of the parent collection (if any)
parent: Option<CollectionId>, parent: Option<CollectionId>,
/// The ID of the show this episode belongs to /// The ID of the show
id: ShowId, id: ShowId,
/// The file name of the poster file /// The file name of the poster file
poster_file_name: Option<String>, poster_file_name: Option<String>,
}, },
/// A scanned episode /// A scanned episode
Season { Season {
/// The ID of the show this episode belongs to /// The ID of the show this season belongs to
show: ShowId, show: ShowId,
/// The season this episode belongs to /// The season this episode belongs to
number: SeasonNumber, season: SeasonNumber,
/// The file name of the poster file /// The file name of the poster file
poster_file_name: Option<String>, poster_file_name: Option<String>,
}, },
@@ -45,7 +45,7 @@ pub enum Scanner {
/// The season this episode belongs to /// The season this episode belongs to
season: SeasonNumber, season: SeasonNumber,
/// The number(s) of this episode /// The number(s) of this episode
number: EpisodeNumbers, episode: EpisodeNumbers,
/// The file name of the media file /// The file name of the media file
media_file_name: String, media_file_name: String,
/// The file name of the poster file /// The file name of the poster file
@@ -58,23 +58,23 @@ impl From<season::Scanner> for Scanner {
match value { match value {
season::Scanner::Season { season::Scanner::Season {
show, show,
number, season,
poster_file_name, poster_file_name,
} => Self::Season { } => Self::Season {
show, show,
number, season,
poster_file_name, poster_file_name,
}, },
season::Scanner::Episode { season::Scanner::Episode {
show, show,
season, season,
number, episode,
media_file_name, media_file_name,
poster_file_name, poster_file_name,
} => Self::Episode { } => Self::Episode {
show, show,
season, season,
number, episode,
media_file_name, media_file_name,
poster_file_name, poster_file_name,
}, },
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "flix-model" name = "flix-model"
version = "0.0.9" version = "0.0.10"
categories = [] categories = []
description = "Core types for flix data" description = "Core types for flix data"
-18
View File
@@ -58,21 +58,3 @@ impl EpisodeNumbers {
&self.0 &self.0
} }
} }
// impl EpisodeNumbers {
// /// Get the primary episode number of this episode
// pub fn primary_episode_number(&self) -> Option<EpisodeNumber> {
// match self {
// EpisodeNumbers::Single { number } => Some(*number),
// EpisodeNumbers::Multiple { numbers } => numbers.first().copied(),
// }
// }
// /// Get additional episode numbers of this episode
// pub fn additional_episode_numbers(&self) -> &[EpisodeNumber] {
// match self {
// EpisodeNumbers::Single { number: _ } => &[],
// EpisodeNumbers::Multiple { numbers } => numbers.get(1..).unwrap_or(&[]),
// }
// }
// }
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "flix-tmdb" name = "flix-tmdb"
version = "0.0.9" version = "0.0.10"
categories = [] categories = []
description = "Clients and models for fetching data from TMDB" description = "Clients and models for fetching data from TMDB"