diff --git a/mediarepo-api/Cargo.toml b/mediarepo-api/Cargo.toml index 5a8a80e..522a42f 100644 --- a/mediarepo-api/Cargo.toml +++ b/mediarepo-api/Cargo.toml @@ -1,52 +1,52 @@ [package] name = "mediarepo-api" -version = "0.28.0" +version = "0.28.1" edition = "2018" license = "gpl-3" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tracing = "^0.1.29" -thiserror = "^1.0.30" -async-trait = {version = "^0.1.52", optional=true} -parking_lot = {version="^0.11.2", optional=true} -serde_json = {version="^1.0.73", optional=true} -directories = {version="^4.0.1", optional=true} -mime_guess = {version = "^2.0.3", optional=true} -serde_piecewise_default = "^0.2.0" -futures = {version = "^0.3.19", optional=true} -url = {version = "^2.2.2", optional=true } -pathsearch = {version="^0.2.0", optional=true} +tracing = "0.1.30" +thiserror = "1.0.30" +async-trait = { version = "0.1.52", optional = true } +parking_lot = { version = "0.12.0", optional = true } +serde_json = { version = "1.0.78", optional = true } +directories = { version = "4.0.1", optional = true } +mime_guess = { version = "2.0.3", optional = true } +serde_piecewise_default = "0.2.0" +futures = { version = "0.3.19", optional = true } +url = { version = "2.2.2", optional = true } +pathsearch = { version = "0.2.0", optional = true } [dependencies.bromine] -version = "^0.17.1" +version = "0.17.1" optional = true features = ["serialize_bincode"] [dependencies.serde] -version = "^1.0.132" +version = "1.0.136" features = ["serde_derive"] [dependencies.chrono] -version = "^0.4.19" +version = "0.4.19" features = ["serde"] [dependencies.tauri] -version = "^1.0.0-beta.8" +version = "1.0.0-beta.8" optional=true default-features = false features = [] [dependencies.tokio] -version = "^1.15.0" +version = "1.16.1" optional = true features = ["sync", "fs", "net", "io-util", "io-std", "time", "rt", "process"] [dependencies.toml] -version = "^0.5.8" +version = "0.5.8" optional = true [features] tauri-plugin = ["client-api","tauri", "parking_lot", "serde_json", "tokio", "toml", "directories", "mime_guess", "futures", "url"] -client-api = ["bromine", "async-trait", "tokio", "pathsearch"] \ No newline at end of file +client-api = ["bromine", "async-trait", "tokio", "pathsearch"] diff --git a/mediarepo-api/src/client_api/mod.rs b/mediarepo-api/src/client_api/mod.rs index c3fbf2f..3dfbcab 100644 --- a/mediarepo-api/src/client_api/mod.rs +++ b/mediarepo-api/src/client_api/mod.rs @@ -4,6 +4,7 @@ pub mod job; pub mod protocol; pub mod repo; pub mod tag; +pub mod preset; use crate::client_api::error::{ApiError, ApiResult}; use crate::client_api::file::FileApi; @@ -15,6 +16,7 @@ use async_trait::async_trait; use bromine::ipc::stream_emitter::EmitMetadata; use bromine::prelude::*; use tokio::time::Duration; +use crate::client_api::preset::PresetApi; #[async_trait] pub trait IPCApi { @@ -48,6 +50,7 @@ pub struct ApiClient { pub tag: TagApi, pub repo: RepoApi, pub job: JobApi, + pub preset: PresetApi, } impl Clone for ApiClient { @@ -58,6 +61,7 @@ impl Clone for ApiClient { tag: self.tag.clone(), repo: self.repo.clone(), job: self.job.clone(), + preset: self.preset.clone(), } } } @@ -70,6 +74,7 @@ impl ApiClient { tag: TagApi::new(ctx.clone()), repo: RepoApi::new(ctx.clone()), job: JobApi::new(ctx.clone()), + preset: PresetApi::new(ctx.clone()), ctx, } } diff --git a/mediarepo-api/src/client_api/preset.rs b/mediarepo-api/src/client_api/preset.rs new file mode 100644 index 0000000..31beaa6 --- /dev/null +++ b/mediarepo-api/src/client_api/preset.rs @@ -0,0 +1,54 @@ +use std::time::Duration; +use bromine::prelude::*; +use crate::client_api::error::ApiResult; +use crate::types::filtering::{SortingPreset, SortKey}; +use super::IPCApi; + +#[derive(Clone)] +pub struct PresetApi { + ctx: PooledContext, +} + +impl IPCApi for PresetApi { + fn namespace() -> &'static str { + "presets" + } + + fn ctx(&self) -> PoolGuard { + self.ctx.acquire() + } +} + +impl PresetApi { + pub fn new(ctx: PooledContext) -> Self { + Self { ctx } + } + + /// Returns all sorting presets of the repository + #[tracing::instrument(level = "debug", skip(self))] + pub async fn all_sorting_presets(&self) -> ApiResult> { + self.emit_and_get( + "all_sorting_presets", + (), + Some(Duration::from_secs(1)) + ) + .await + } + + /// Adds a new sorting preset with the given keys + #[tracing::instrument(level = "debug", skip(self))] + pub async fn add_sorting_preset(&self, keys: Vec) -> ApiResult { + self.emit_and_get( + "add_sorting_preset", + keys, + Some(Duration::from_secs(1)) + ) + .await + } + + /// Deletes a given sorting preset by id + #[tracing::instrument(level = "debug", skip(self))] + pub async fn delete_sorting_preset(&self, id: i32) -> ApiResult<()> { + self.emit_and_get("delete_sorting_preset", id, Some(Duration::from_secs(1))).await + } +} diff --git a/mediarepo-api/src/tauri_plugin/commands/mod.rs b/mediarepo-api/src/tauri_plugin/commands/mod.rs index bd6a259..c06b7c8 100644 --- a/mediarepo-api/src/tauri_plugin/commands/mod.rs +++ b/mediarepo-api/src/tauri_plugin/commands/mod.rs @@ -5,6 +5,7 @@ pub use file::*; pub use job::*; pub use repo::*; pub use tag::*; +pub use preset::*; use crate::tauri_plugin::state::{ApiState, AppState, BufferState}; @@ -13,6 +14,7 @@ pub mod file; pub mod job; pub mod repo; pub mod tag; +pub mod preset; pub type ApiAccess<'a> = State<'a, ApiState>; pub type AppAccess<'a> = State<'a, AppState>; diff --git a/mediarepo-api/src/tauri_plugin/commands/preset.rs b/mediarepo-api/src/tauri_plugin/commands/preset.rs new file mode 100644 index 0000000..8bdb206 --- /dev/null +++ b/mediarepo-api/src/tauri_plugin/commands/preset.rs @@ -0,0 +1,27 @@ +use crate::tauri_plugin::commands::ApiAccess; +use crate::tauri_plugin::error::PluginResult; +use crate::types::filtering::{SortingPreset, SortKey}; + +#[tauri::command] +pub async fn all_sorting_presets(api_state: ApiAccess<'_>) -> PluginResult> { + let api = api_state.api().await?; + let presets = api.preset.all_sorting_presets().await?; + + Ok(presets) +} + +#[tauri::command] +pub async fn add_sorting_preset(api_state: ApiAccess<'_>, sort_keys: Vec) -> PluginResult { + let api = api_state.api().await?; + let preset = api.preset.add_sorting_preset(sort_keys).await?; + + Ok(preset) +} + +#[tauri::command] +pub async fn delete_sorting_preset(api_state: ApiAccess<'_>, id: i32) -> PluginResult<()> { + let api = api_state.api().await?; + api.preset.delete_sorting_preset(id).await?; + + Ok(()) +} \ No newline at end of file diff --git a/mediarepo-api/src/tauri_plugin/mod.rs b/mediarepo-api/src/tauri_plugin/mod.rs index 37e11c6..7a570a7 100644 --- a/mediarepo-api/src/tauri_plugin/mod.rs +++ b/mediarepo-api/src/tauri_plugin/mod.rs @@ -70,7 +70,10 @@ impl MediarepoPlugin { run_job, update_file_status, delete_file, - get_file_tag_map + get_file_tag_map, + all_sorting_presets, + add_sorting_preset, + delete_sorting_preset ]), } } diff --git a/mediarepo-api/src/types/filtering.rs b/mediarepo-api/src/types/filtering.rs index 916db73..3ffca1e 100644 --- a/mediarepo-api/src/types/filtering.rs +++ b/mediarepo-api/src/types/filtering.rs @@ -71,3 +71,9 @@ pub enum SortDirection { } impl Eq for SortDirection {} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct SortingPreset { + pub id: i32, + pub keys: Vec, +} \ No newline at end of file diff --git a/mediarepo-daemon/Cargo.lock b/mediarepo-daemon/Cargo.lock index c095862..b4dee04 100644 --- a/mediarepo-daemon/Cargo.lock +++ b/mediarepo-daemon/Cargo.lock @@ -216,23 +216,23 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "blake2b_simd" -version = "0.5.11" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" +checksum = "72936ee4afc7f8f736d1c38383b56480b5497b4617b4a77bdbf1d2ababc76127" dependencies = [ "arrayref", - "arrayvec 0.5.2", + "arrayvec 0.7.2", "constant_time_eq", ] [[package]] name = "blake2s_simd" -version = "0.5.11" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e461a7034e85b211a4acb57ee2e6730b32912b06c08cc242243c39fc21ae6a2" +checksum = "db539cc2b5f6003621f1cd9ef92d7ded8ea5232c7de0f9faa2de251cd98730d4" dependencies = [ "arrayref", - "arrayvec 0.5.2", + "arrayvec 0.7.2", "constant_time_eq", ] @@ -255,15 +255,17 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "block-padding", "generic-array", ] [[package]] -name = "block-padding" -version = "0.2.1" +name = "block-buffer" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" +checksum = "03588e54c62ae6d763e2a80090d50353b785795361b4ff5b3bf0a5097fc31c0b" +dependencies = [ + "generic-array", +] [[package]] name = "bromine" @@ -407,9 +409,9 @@ dependencies = [ [[package]] name = "console-api" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f67643a7d716307ad10b3e3aef02826382acbe349a3e7605ac57556148bc87" +checksum = "cc347c19eb5b940f396ac155822caee6662f850d97306890ac3773ed76c90c5a" dependencies = [ "prost", "prost-types", @@ -462,6 +464,15 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + [[package]] name = "cpufeatures" version = "0.2.1" @@ -502,7 +513,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils 0.8.6", + "crossbeam-utils 0.8.7", ] [[package]] @@ -513,17 +524,17 @@ checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" dependencies = [ "cfg-if 1.0.0", "crossbeam-epoch", - "crossbeam-utils 0.8.6", + "crossbeam-utils 0.8.7", ] [[package]] name = "crossbeam-epoch" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97242a70df9b89a65d0b6df3c4bf5b9ce03c5b7309019777fbde37e7537f8762" +checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils 0.8.6", + "crossbeam-utils 0.8.7", "lazy_static", "memoffset", "scopeguard", @@ -531,12 +542,12 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b979d76c9fcb84dffc80a73f7290da0f83e4c95773494674cb44b76d13a7a110" +checksum = "4dd435b205a4842da59efd07628f921c096bc1cc0a156835b4fa0bcb9a19bcce" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils 0.8.6", + "crossbeam-utils 0.8.7", ] [[package]] @@ -552,14 +563,23 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcae03edb34f947e64acdb1c33ec169824e20657e9ecb61cef6c8c74dcb8120" +checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6" dependencies = [ "cfg-if 1.0.0", "lazy_static", ] +[[package]] +name = "crypto-common" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d6b536309245c849479fba3da410962a43ed8e51c26b729208ec0ac2798d0" +dependencies = [ + "generic-array", +] + [[package]] name = "data-encoding" version = "2.3.2" @@ -613,6 +633,17 @@ dependencies = [ "generic-array", ] +[[package]] +name = "digest" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b697d66081d42af4fba142d56918a3cb21dc8eb63372c6b85d14f44fb9c5979b" +dependencies = [ + "block-buffer 0.10.1", + "crypto-common", + "generic-array", +] + [[package]] name = "dotenv" version = "0.15.0" @@ -1329,7 +1360,7 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] name = "mediarepo-api" -version = "0.28.0" +version = "0.28.1" dependencies = [ "bromine", "chrono", @@ -1513,26 +1544,26 @@ dependencies = [ [[package]] name = "multihash" -version = "0.15.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49e540106213dc639fe2b632a7d9e3525169ef081378a7c2da4457b84fec44c0" +checksum = "7392bffd88bc0c4f8297e36a777ab9f80b7127409c4a1acb8fee99c9f27addcd" dependencies = [ "blake2b_simd", "blake2s_simd", "blake3", - "digest", - "generic-array", + "core2", + "digest 0.10.1", "multihash-derive", - "sha2", + "sha2 0.10.1", "sha3", "unsigned-varint", ] [[package]] name = "multihash-derive" -version = "0.7.2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "424f6e86263cd5294cbd7f1e95746b95aca0e0d66bff31e5a40d6baa87b4aa99" +checksum = "fc076939022111618a5026d3be019fd8b366e76314538ff9a1b59ffbcbf98bcd" dependencies = [ "proc-macro-crate", "proc-macro-error", @@ -2048,7 +2079,7 @@ checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" dependencies = [ "crossbeam-channel", "crossbeam-deque", - "crossbeam-utils 0.8.6", + "crossbeam-utils 0.8.7", "lazy_static", "num_cpus", ] @@ -2251,9 +2282,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fed7948b6c68acbb6e20c334f55ad635dc0f75506963de4464289fbd3b051ac" +checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" dependencies = [ "bitflags", "core-foundation", @@ -2264,9 +2295,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a57321bf8bc2362081b2599912d2961fe899c0efadf1b4b2f8d48b3e253bb96c" +checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" dependencies = [ "core-foundation-sys", "libc", @@ -2350,23 +2381,32 @@ version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ - "block-buffer", + "block-buffer 0.9.0", "cfg-if 1.0.0", "cpufeatures", - "digest", + "digest 0.9.0", "opaque-debug", ] +[[package]] +name = "sha2" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99c3bd8169c58782adad9290a9af5939994036b76187f7b4f0e6de91dbbfc0ec" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.1", +] + [[package]] name = "sha3" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +checksum = "31f935e31cf406e8c0e96c2815a5516181b7004ae8c5f296293221e9b1e356bd" dependencies = [ - "block-buffer", - "digest", + "digest 0.10.1", "keccak", - "opaque-debug", ] [[package]] @@ -2460,7 +2500,7 @@ dependencies = [ "crc", "crossbeam-channel", "crossbeam-queue", - "crossbeam-utils 0.8.6", + "crossbeam-utils 0.8.7", "either", "flume", "futures-channel", @@ -2483,7 +2523,7 @@ dependencies = [ "rust_decimal", "serde 1.0.136", "serde_json", - "sha2", + "sha2 0.9.9", "smallvec", "sqlformat", "sqlx-rt", @@ -2507,7 +2547,7 @@ dependencies = [ "proc-macro2 1.0.36", "quote 1.0.15", "serde_json", - "sha2", + "sha2 0.9.9", "sqlx-core", "sqlx-rt", "syn 1.0.86", @@ -2921,9 +2961,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tracing" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" +checksum = "2d8d93354fe2a8e50d5953f5ae2e47a3fc2ef03292e7ea46e3cc38f549525fb9" dependencies = [ "cfg-if 1.0.0", "log", @@ -2945,9 +2985,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" +checksum = "8276d9a4a3a558d7b7ad5303ad50b53d58264641b82914b7ada36bd762e7a716" dependencies = [ "proc-macro2 1.0.36", "quote 1.0.15", @@ -2956,11 +2996,12 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" +checksum = "03cfcb51380632a72d3111cb8d3447a8d908e577d31beeac006f836383d29a23" dependencies = [ "lazy_static", + "valuable", ] [[package]] @@ -2998,9 +3039,9 @@ dependencies = [ [[package]] name = "tracing-serde" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" dependencies = [ "serde 1.0.136", "tracing-core", @@ -3008,9 +3049,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5312f325fe3588e277415f5a6cca1f4ccad0f248c4cd5a4bd33032d7286abc22" +checksum = "74786ce43333fcf51efe947aed9718fbe46d5c7328ec3f1029e818083966d9aa" dependencies = [ "ansi_term", "lazy_static", @@ -3127,6 +3168,12 @@ dependencies = [ "serde 1.0.136", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/mediarepo-daemon/Cargo.toml b/mediarepo-daemon/Cargo.toml index f97dc99..b4fce69 100644 --- a/mediarepo-daemon/Cargo.toml +++ b/mediarepo-daemon/Cargo.toml @@ -16,17 +16,17 @@ name = "mediarepo-daemon" path = "src/main.rs" [dependencies] -tracing = "^0.1.29" -toml = "^0.5.8" -structopt ="^0.3.26" -glob = "^0.3.0" -tracing-flame = "^0.2.0" -tracing-appender = "^0.2.0" -tracing-log = "^0.1.2" -rolling-file = "^0.1.0" -num-integer = "^0.1.44" -console-subscriber = "^0.1.1" -log = "^0.4.14" +tracing = "0.1.30" +toml = "0.5.8" +structopt = "0.3.26" +glob = "0.3.0" +tracing-flame = "0.2.0" +tracing-appender = "0.2.0" +tracing-log = "0.1.2" +rolling-file = "0.1.0" +num-integer = "0.1.44" +console-subscriber = "0.1.1" +log = "0.4.14" [dependencies.mediarepo-core] path = "./mediarepo-core" @@ -42,9 +42,9 @@ version = "1.16.1" features = ["macros", "rt-multi-thread", "io-std", "io-util"] [dependencies.tracing-subscriber] -version="0.3.7" +version= "0.3.8" features = ["env-filter", "ansi", "json"] [features] default = ["ffmpeg"] -ffmpeg = ["mediarepo-core/ffmpeg", "mediarepo-logic/ffmpeg"] \ No newline at end of file +ffmpeg = ["mediarepo-core/ffmpeg", "mediarepo-logic/ffmpeg"] diff --git a/mediarepo-daemon/mediarepo-core/Cargo.toml b/mediarepo-daemon/mediarepo-core/Cargo.toml index 8960310..75034c6 100644 --- a/mediarepo-daemon/mediarepo-core/Cargo.toml +++ b/mediarepo-daemon/mediarepo-core/Cargo.toml @@ -7,39 +7,39 @@ workspace = ".." # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -thiserror = "^1.0.30" -multihash = "^0.15.0" -multibase = "^0.9.1" -base64 = "^0.13.0" -toml = "^0.5.8" -serde = "^1.0.136" -typemap_rev = "^0.1.5" -futures = "^0.3.19" -itertools = "^0.10.3" -glob = "^0.3.0" -tracing = "^0.1.29" -data-encoding = "^2.3.2" -tokio-graceful-shutdown = "^0.4.3" +thiserror = "1.0.30" +multihash = "0.16.1" +multibase = "0.9.1" +base64 = "0.13.0" +toml = "0.5.8" +serde = "1.0.136" +typemap_rev = "0.1.5" +futures = "0.3.19" +itertools = "0.10.3" +glob = "0.3.0" +tracing = "0.1.30" +data-encoding = "2.3.2" +tokio-graceful-shutdown = "0.4.3" [dependencies.thumbnailer] -version = "^0.3" +version = "0.3.0" default-features = false [dependencies.sea-orm] -version = "^0.5.0" +version = "0.5.0" default-features = false [dependencies.sqlx] -version = "^0.5.10" +version = "0.5.10" default-features = false features = ["migrate"] [dependencies.tokio] -version = "^1.16.1" +version = "1.16.1" features = ["fs", "io-util", "io-std"] [dependencies.config] -version = "^0.11.0" +version = "0.11.0" features = ["toml"] [dependencies.mediarepo-api] @@ -48,4 +48,4 @@ features = ["bromine"] [features] default = [] -ffmpeg = ["thumbnailer/ffmpeg"] \ No newline at end of file +ffmpeg = ["thumbnailer/ffmpeg"] diff --git a/mediarepo-daemon/mediarepo-database/Cargo.toml b/mediarepo-daemon/mediarepo-database/Cargo.toml index 4a2e670..1e79ee4 100644 --- a/mediarepo-daemon/mediarepo-database/Cargo.toml +++ b/mediarepo-daemon/mediarepo-database/Cargo.toml @@ -7,17 +7,17 @@ workspace = ".." # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -chrono = "^0.4.19" -tracing = "^0.1.29" +chrono = "0.4.19" +tracing = "0.1.30" [dependencies.mediarepo-core] path = "../mediarepo-core" [dependencies.sqlx] -version = "^0.5.10" +version = "0.5.10" features = ["migrate"] [dependencies.sea-orm] -version = "^0.5.0" +version = "0.5.0" features = ["sqlx-sqlite", "runtime-tokio-native-tls", "macros", "debug-print"] -default-features = false \ No newline at end of file +default-features = false diff --git a/mediarepo-daemon/mediarepo-database/migrations/20220205092929_add-sorting-preset-tables.sql b/mediarepo-daemon/mediarepo-database/migrations/20220205092929_add-sorting-preset-tables.sql new file mode 100644 index 0000000..0f0faa4 --- /dev/null +++ b/mediarepo-daemon/mediarepo-database/migrations/20220205092929_add-sorting-preset-tables.sql @@ -0,0 +1,20 @@ +-- Add migration script here +CREATE TABLE sorting_presets ( + id INTEGER PRIMARY KEY AUTOINCREMENT +); + +CREATE TABLE sort_keys ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + key_type INTEGER NOT NULL DEFAULT 0, + ascending INTEGER NOT NULL CHECK (ascending IN (0, 1)), + value VARCHAR(128) +); + +CREATE TABLE sorting_preset_keys ( + preset_id INTEGER REFERENCES sorting_presets (id) ON DELETE CASCADE, + key_id INTEGER REFERENCES sort_keys (id), + key_index INTEGER NOT NULL DEFAULT 0, + PRIMARY KEY (preset_id, key_id) +); + +CREATE INDEX sorting_preset_index ON sorting_preset_keys (preset_id); \ No newline at end of file diff --git a/mediarepo-daemon/mediarepo-database/migrations/20220205165133_fix-uniqueness-of-sorting-preset-keys.sql b/mediarepo-daemon/mediarepo-database/migrations/20220205165133_fix-uniqueness-of-sorting-preset-keys.sql new file mode 100644 index 0000000..e2b3c71 --- /dev/null +++ b/mediarepo-daemon/mediarepo-database/migrations/20220205165133_fix-uniqueness-of-sorting-preset-keys.sql @@ -0,0 +1,17 @@ +PRAGMA foreign_keys= off; +ALTER TABLE sorting_preset_keys + RENAME TO _sorting_preset_keys_old; + +CREATE TABLE sorting_preset_keys +( + preset_id INTEGER REFERENCES sorting_presets (id) ON DELETE CASCADE, + key_id INTEGER REFERENCES sort_keys (id), + key_index INTEGER NOT NULL DEFAULT 0, + PRIMARY KEY (preset_id, key_id, key_index) +); + +INSERT INTO sorting_preset_keys SELECT * FROM _sorting_preset_keys_old; + +DROP TABLE _sorting_preset_keys_old; + +PRAGMA foreign_keys= on; diff --git a/mediarepo-daemon/mediarepo-database/src/entities/mod.rs b/mediarepo-daemon/mediarepo-database/src/entities/mod.rs index 4b8acde..3eee5ae 100644 --- a/mediarepo-daemon/mediarepo-database/src/entities/mod.rs +++ b/mediarepo-daemon/mediarepo-database/src/entities/mod.rs @@ -4,5 +4,8 @@ pub mod content_descriptor_tag; pub mod file; pub mod file_metadata; pub mod namespace; +pub mod sort_key; +pub mod sorting_preset; +pub mod sorting_preset_key; pub mod source; pub mod tag; diff --git a/mediarepo-daemon/mediarepo-database/src/entities/sort_key.rs b/mediarepo-daemon/mediarepo-database/src/entities/sort_key.rs new file mode 100644 index 0000000..4ebdd1f --- /dev/null +++ b/mediarepo-daemon/mediarepo-database/src/entities/sort_key.rs @@ -0,0 +1,26 @@ +use sea_orm::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel)] +#[sea_orm(table_name = "sort_keys")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub key_type: i32, + pub ascending: bool, + pub value: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl Related for Entity { + fn to() -> RelationDef { + super::sorting_preset_key::Relation::SortingPreset.def() + } + + fn via() -> Option { + Some(super::sorting_preset_key::Relation::SortingKey.def().rev()) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/mediarepo-daemon/mediarepo-database/src/entities/sorting_preset.rs b/mediarepo-daemon/mediarepo-database/src/entities/sorting_preset.rs new file mode 100644 index 0000000..5bae84b --- /dev/null +++ b/mediarepo-daemon/mediarepo-database/src/entities/sorting_preset.rs @@ -0,0 +1,27 @@ +use sea_orm::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel)] +#[sea_orm(table_name = "sorting_presets")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl Related for Entity { + fn to() -> RelationDef { + super::sorting_preset_key::Relation::SortingKey.def() + } + + fn via() -> Option { + Some( + super::sorting_preset_key::Relation::SortingPreset + .def() + .rev(), + ) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/mediarepo-daemon/mediarepo-database/src/entities/sorting_preset_key.rs b/mediarepo-daemon/mediarepo-database/src/entities/sorting_preset_key.rs new file mode 100644 index 0000000..6d882db --- /dev/null +++ b/mediarepo-daemon/mediarepo-database/src/entities/sorting_preset_key.rs @@ -0,0 +1,45 @@ +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel)] +#[sea_orm(table_name = "sorting_preset_keys")] +pub struct Model { + #[sea_orm(primary_key)] + preset_id: i32, + + #[sea_orm(primary_key)] + key_id: i32, + + #[sea_orm(primary_key)] + key_index: i32, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::sorting_preset::Entity" + from = "Column::PresetId", + to = "super::sorting_preset::Column::Id" + )] + SortingPreset, + + #[sea_orm( + belongs_to = "super::sort_key::Entity", + from = "Column::KeyId", + to = "super::sort_key::Column::Id" + )] + SortingKey, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::SortingPreset.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::SortingKey.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/mediarepo-daemon/mediarepo-logic/Cargo.toml b/mediarepo-daemon/mediarepo-logic/Cargo.toml index ca50233..08a02e6 100644 --- a/mediarepo-daemon/mediarepo-logic/Cargo.toml +++ b/mediarepo-daemon/mediarepo-logic/Cargo.toml @@ -7,13 +7,13 @@ workspace = ".." # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -chrono = "^0.4.19" -typemap_rev = "^0.1.5" -serde = "^1.0.136" -mime_guess = "^2.0.3" -mime = "^0.3.16" -tracing = "^0.1.29" -async-trait = "^0.1.52" +chrono = "0.4.19" +typemap_rev = "0.1.5" +serde = "1.0.136" +mime_guess = "2.0.3" +mime = "0.3.16" +tracing = "0.1.30" +async-trait = "0.1.52" [dependencies.mediarepo-core] path = "../mediarepo-core" @@ -22,14 +22,14 @@ path = "../mediarepo-core" path = "../mediarepo-database" [dependencies.sea-orm] -version = "^0.5.0-rc.1" +version = "0.5.0" features = ["runtime-tokio-native-tls", "macros"] default-features = false [dependencies.tokio] -version = "^1.15.0" +version = "1.16.1" features = ["fs", "io-std", "io-util"] [features] -ffmpeg = ["mediarepo-core/ffmpeg"] \ No newline at end of file +ffmpeg = ["mediarepo-core/ffmpeg"] diff --git a/mediarepo-daemon/mediarepo-logic/src/dao/file/mod.rs b/mediarepo-daemon/mediarepo-logic/src/dao/file/mod.rs index 210cf50..93f012c 100644 --- a/mediarepo-daemon/mediarepo-logic/src/dao/file/mod.rs +++ b/mediarepo-daemon/mediarepo-logic/src/dao/file/mod.rs @@ -1,10 +1,10 @@ use sea_orm::prelude::*; use tokio::io::AsyncReadExt; +use crate::dao_provider; use mediarepo_core::error::RepoResult; use mediarepo_database::entities::{content_descriptor, file, file_metadata}; -use crate::dao::{DaoContext, DaoProvider}; use crate::dto::{FileDto, FileMetadataDto, ThumbnailDto}; pub mod add; @@ -12,21 +12,9 @@ pub mod delete; pub mod find; pub mod update; -pub struct FileDao { - ctx: DaoContext, -} - -impl DaoProvider for FileDao { - fn dao_ctx(&self) -> DaoContext { - self.ctx.clone() - } -} +dao_provider!(FileDao); impl FileDao { - pub fn new(ctx: DaoContext) -> Self { - Self { ctx } - } - #[tracing::instrument(level = "debug", skip(self))] pub async fn all(&self) -> RepoResult> { let files = file::Entity::find() diff --git a/mediarepo-daemon/mediarepo-logic/src/dao/job/mod.rs b/mediarepo-daemon/mediarepo-logic/src/dao/job/mod.rs index c47b28a..16bace3 100644 --- a/mediarepo-daemon/mediarepo-logic/src/dao/job/mod.rs +++ b/mediarepo-daemon/mediarepo-logic/src/dao/job/mod.rs @@ -1,20 +1,6 @@ +use crate::dao_provider; + pub mod migrate_content_descriptors; pub mod sqlite_operations; -use crate::dao::{DaoContext, DaoProvider}; - -pub struct JobDao { - ctx: DaoContext, -} - -impl DaoProvider for JobDao { - fn dao_ctx(&self) -> DaoContext { - self.ctx.clone() - } -} - -impl JobDao { - pub fn new(ctx: DaoContext) -> JobDao { - Self { ctx } - } -} +dao_provider!(JobDao); diff --git a/mediarepo-daemon/mediarepo-logic/src/dao/mod.rs b/mediarepo-daemon/mediarepo-logic/src/dao/mod.rs index f3cf620..84c5964 100644 --- a/mediarepo-daemon/mediarepo-logic/src/dao/mod.rs +++ b/mediarepo-daemon/mediarepo-logic/src/dao/mod.rs @@ -5,13 +5,38 @@ use mediarepo_core::fs::thumbnail_store::ThumbnailStore; use crate::dao::file::FileDao; use crate::dao::job::JobDao; +use crate::dao::sorting_preset::SortingPresetDao; use crate::dao::tag::TagDao; pub mod file; pub mod job; pub mod repo; +pub mod sorting_preset; pub mod tag; +#[macro_export] +macro_rules! dao_provider { + ($name:ident) => { + use crate::dao::{DaoContext, DaoProvider}; + + pub struct $name { + ctx: DaoContext, + } + + impl DaoProvider for $name { + fn dao_ctx(&self) -> DaoContext { + self.ctx.clone() + } + } + + impl $name { + pub fn new(ctx: DaoContext) -> Self { + Self { ctx } + } + } + }; +} + #[derive(Clone)] pub struct DaoContext { pub db: DatabaseConnection, @@ -33,6 +58,10 @@ pub trait DaoProvider { fn job(&self) -> JobDao { JobDao::new(self.dao_ctx()) } + + fn sorting_preset(&self) -> SortingPresetDao { + SortingPresetDao::new(self.dao_ctx()) + } } fn opt_to_active_val>(opt: Option) -> ActiveValue { diff --git a/mediarepo-daemon/mediarepo-logic/src/dao/sorting_preset/add.rs b/mediarepo-daemon/mediarepo-logic/src/dao/sorting_preset/add.rs new file mode 100644 index 0000000..81c8aeb --- /dev/null +++ b/mediarepo-daemon/mediarepo-logic/src/dao/sorting_preset/add.rs @@ -0,0 +1,172 @@ +use crate::dao::sorting_preset::SortingPresetDao; +use crate::dto::{AddSortKeyDto, AddSortingPresetDto, SortKeyDto, SortingPresetDto}; +use mediarepo_core::error::RepoResult; +use mediarepo_database::entities::{sort_key, sorting_preset, sorting_preset_key}; +use sea_orm::prelude::*; +use sea_orm::ActiveValue::Set; +use sea_orm::{ + Condition, ConnectionTrait, DatabaseTransaction, DbBackend, FromQueryResult, JoinType, + QuerySelect, Statement, +}; + +#[allow(unused_imports)] +use sea_orm::TryGetableMany; // otherwise intellijrust hates on me + +impl SortingPresetDao { + #[tracing::instrument(level = "debug", skip(self))] + pub async fn add(&self, preset: AddSortingPresetDto) -> RepoResult { + let trx = self.ctx.db.begin().await?; + let keys = add_keys(&trx, preset.keys).await?; + let key_ids = keys + .iter() + .enumerate() + .map(|(idx, key)| (idx, key.id())) + .collect::>(); + let condition = key_ids + .iter() + .cloned() + .map(create_mapping_condition) + .fold(Condition::any(), |acc, cond| acc.add(cond)); + let existing_preset: Option = sorting_preset::Entity::find() + .join( + JoinType::InnerJoin, + sorting_preset_key::Relation::SortingPreset.def().rev(), + ) + .filter(condition) + .one(&trx) + .await?; + if let Some(model) = existing_preset { + trx.commit().await?; + return Ok(SortingPresetDto::new(model, keys)); + } + + // sea_orm currently doesn't support all-default-value inserts. + // TODOD: Replace after the change for default inserts has been merged + let preset_model = sorting_preset::Model::find_by_statement(Statement::from_string( + DbBackend::Sqlite, + "INSERT INTO sorting_presets DEFAULT VALUES RETURNING *;".to_string(), + )) + .one(&trx) + .await? + .expect("failed to insert new sorting preset"); + + let mapping_models = key_ids + .into_iter() + .map(|(idx, key)| sorting_preset_key::ActiveModel { + preset_id: Set(preset_model.id), + key_id: Set(key), + key_index: Set(idx as i32), + }) + .collect::>(); + + if !mapping_models.is_empty() { + sorting_preset_key::Entity::insert_many(mapping_models) + .exec(&trx) + .await?; + } + trx.commit().await?; + + Ok(SortingPresetDto::new(preset_model, keys)) + } +} + +async fn add_keys( + trx: &DatabaseTransaction, + keys: Vec, +) -> RepoResult> { + let mut key_dtos = find_sort_keys(trx, &keys).await?; + let mut insert_keys = keys.clone(); + + key_dtos.iter().for_each(|key| { + insert_keys.retain(|k| { + k.ascending != key.ascending() + || k.key_type != key.key_type().unwrap() + || !compare_opts_eq(key.value(), k.value.as_ref()) + }) + }); + + if !insert_keys.is_empty() { + let active_models: Vec = insert_keys + .iter() + .cloned() + .map(|key| sort_key::ActiveModel { + key_type: Set(key.key_type.to_number()), + ascending: Set(key.ascending), + value: Set(key.value), + ..Default::default() + }) + .collect(); + sort_key::Entity::insert_many(active_models) + .exec(trx) + .await?; + let mut new_keys = find_sort_keys(trx, &insert_keys).await?; + key_dtos.append(&mut new_keys); + } + + let keys_original_order = keys + .into_iter() + .filter_map(|k| { + key_dtos + .iter() + .find(|key| { + k.ascending == key.ascending() + && k.key_type == key.key_type().unwrap() + && compare_opts_eq(key.value(), k.value.as_ref()) + }) + .cloned() + }) + .collect::>(); + + Ok(keys_original_order) +} + +async fn find_sort_keys( + trx: &DatabaseTransaction, + keys: &Vec, +) -> RepoResult> { + if keys.is_empty() { + return Ok(vec![]); + } + let condition = keys + .iter() + .cloned() + .map(create_sort_key_condition) + .fold(Condition::any(), |acc, cond| acc.add(cond)); + let keys = sort_key::Entity::find() + .filter(condition) + .all(trx) + .await? + .into_iter() + .map(SortKeyDto::new) + .collect(); + + Ok(keys) +} + +fn create_sort_key_condition(key: AddSortKeyDto) -> Condition { + let mut condition = Condition::all() + .add(sort_key::Column::KeyType.eq(key.key_type.to_number())) + .add(sort_key::Column::Ascending.eq(key.ascending)); + + if let Some(value) = key.value { + condition = condition.add(sort_key::Column::Value.eq(value)) + } else { + condition = condition.add(sort_key::Column::Value.is_null()) + } + + condition +} + +fn create_mapping_condition(entry: (usize, i32)) -> Condition { + Condition::all() + .add(sorting_preset_key::Column::KeyId.eq(entry.1)) + .add(sorting_preset_key::Column::KeyIndex.eq(entry.0 as i32)) +} + +fn compare_opts_eq(opt1: Option, opt2: Option) -> bool { + if let (Some(opt1), Some(opt2)) = (&opt1, &opt2) { + opt1 == opt2 + } else { + opt1.is_none() && opt2.is_none() + } +} diff --git a/mediarepo-daemon/mediarepo-logic/src/dao/sorting_preset/mod.rs b/mediarepo-daemon/mediarepo-logic/src/dao/sorting_preset/mod.rs new file mode 100644 index 0000000..06fbf1c --- /dev/null +++ b/mediarepo-daemon/mediarepo-logic/src/dao/sorting_preset/mod.rs @@ -0,0 +1,42 @@ +pub mod add; + +use crate::dao_provider; +use crate::dto::{SortKeyDto, SortingPresetDto}; +use mediarepo_core::error::RepoResult; +use mediarepo_database::entities::{sort_key, sorting_preset, sorting_preset_key}; +use sea_orm::prelude::*; +use sea_orm::QueryOrder; + +dao_provider!(SortingPresetDao); + +impl SortingPresetDao { + #[tracing::instrument(level = "debug", skip(self))] + pub async fn all(&self) -> RepoResult> { + let presets = sorting_preset::Entity::find() + .find_with_related(sort_key::Entity) + .order_by_asc(sorting_preset_key::Column::KeyIndex) + .all(&self.ctx.db) + .await? + .into_iter() + .map(map_sorting_preset_dto) + .collect(); + + Ok(presets) + } + + #[tracing::instrument(level = "debug", skip(self))] + pub async fn delete(&self, id: i32) -> RepoResult<()> { + sorting_preset::Entity::delete_many() + .filter(sorting_preset::Column::Id.eq(id)) + .exec(&self.ctx.db) + .await?; + + Ok(()) + } +} + +fn map_sorting_preset_dto( + entry: (sorting_preset::Model, Vec), +) -> SortingPresetDto { + SortingPresetDto::new(entry.0, entry.1.into_iter().map(SortKeyDto::new).collect()) +} diff --git a/mediarepo-daemon/mediarepo-logic/src/dao/tag/mod.rs b/mediarepo-daemon/mediarepo-logic/src/dao/tag/mod.rs index 7f38e23..c931f10 100644 --- a/mediarepo-daemon/mediarepo-logic/src/dao/tag/mod.rs +++ b/mediarepo-daemon/mediarepo-logic/src/dao/tag/mod.rs @@ -10,7 +10,7 @@ use mediarepo_core::utils::parse_namespace_and_tag; use mediarepo_database::entities::{content_descriptor, content_descriptor_tag, namespace, tag}; use crate::dao::tag::by_name::TagByNameQuery; -use crate::dao::{DaoContext, DaoProvider}; +use crate::dao_provider; use crate::dto::{NamespaceDto, TagDto}; pub mod add; @@ -19,21 +19,9 @@ pub mod by_name; pub mod cdids_with_namespaced_tags; pub mod mappings; -pub struct TagDao { - ctx: DaoContext, -} - -impl DaoProvider for TagDao { - fn dao_ctx(&self) -> DaoContext { - self.ctx.clone() - } -} +dao_provider!(TagDao); impl TagDao { - pub fn new(ctx: DaoContext) -> Self { - Self { ctx } - } - #[tracing::instrument(level = "debug", skip(self))] pub async fn all(&self) -> RepoResult> { let tags = tag::Entity::find() diff --git a/mediarepo-daemon/mediarepo-logic/src/dto/mod.rs b/mediarepo-daemon/mediarepo-logic/src/dto/mod.rs index 0d7a962..2444214 100644 --- a/mediarepo-daemon/mediarepo-logic/src/dto/mod.rs +++ b/mediarepo-daemon/mediarepo-logic/src/dto/mod.rs @@ -1,12 +1,13 @@ pub use file::*; pub use file_metadata::*; pub use namespace::*; +pub use sorting_preset::*; pub use tag::*; pub use thumbnail::*; mod file; mod file_metadata; -mod tag; mod namespace; +mod sorting_preset; +mod tag; mod thumbnail; - diff --git a/mediarepo-daemon/mediarepo-logic/src/dto/sorting_preset.rs b/mediarepo-daemon/mediarepo-logic/src/dto/sorting_preset.rs new file mode 100644 index 0000000..f83eef4 --- /dev/null +++ b/mediarepo-daemon/mediarepo-logic/src/dto/sorting_preset.rs @@ -0,0 +1,101 @@ +use crate::dto::KeyType::{ + FileChangeTime, FileCreatedTime, FileImportedTime, FileName, FileSize, FileType, Namespace, + NumTags, +}; +use mediarepo_database::entities::sort_key; +use mediarepo_database::entities::sorting_preset; + +#[derive(Clone, Debug)] +pub struct SortingPresetDto { + model: sorting_preset::Model, + keys: Vec, +} + +impl SortingPresetDto { + pub fn new(model: sorting_preset::Model, keys: Vec) -> Self { + Self { model, keys } + } + + pub fn id(&self) -> i32 { + self.model.id + } + + pub fn keys(&self) -> &Vec { + &self.keys + } + + pub fn into_keys(self) -> Vec { + self.keys + } +} + +#[derive(Clone, Debug)] +pub struct SortKeyDto { + model: sort_key::Model, +} + +impl SortKeyDto { + pub fn new(model: sort_key::Model) -> Self { + Self { model } + } + + pub fn id(&self) -> i32 { + self.model.id + } + + pub fn key_type(&self) -> Option { + KeyType::from_number(self.model.key_type) + } + + pub fn ascending(&self) -> bool { + self.model.ascending + } + + pub fn value(&self) -> Option<&String> { + self.model.value.as_ref() + } +} + +#[derive(Clone, Copy, Debug, PartialOrd, PartialEq)] +pub enum KeyType { + Namespace = 0, + FileName = 1, + FileSize = 2, + FileImportedTime = 3, + FileCreatedTime = 4, + FileChangeTime = 5, + FileType = 6, + NumTags = 7, +} + +impl KeyType { + pub fn from_number(number: i32) -> Option { + match number { + 0 => Some(Namespace), + 1 => Some(FileName), + 2 => Some(FileSize), + 3 => Some(FileImportedTime), + 4 => Some(FileCreatedTime), + 5 => Some(FileChangeTime), + 6 => Some(FileType), + 7 => Some(NumTags), + _ => None, + } + } + + pub fn to_number(&self) -> i32 { + self.clone() as i32 + } +} + +#[derive(Clone, Debug)] +pub struct AddSortingPresetDto { + pub keys: Vec, +} + +#[derive(Clone, Debug)] +pub struct AddSortKeyDto { + pub key_type: KeyType, + pub ascending: bool, + pub value: Option, +} diff --git a/mediarepo-daemon/mediarepo-socket/Cargo.toml b/mediarepo-daemon/mediarepo-socket/Cargo.toml index 2c56382..9f38030 100644 --- a/mediarepo-daemon/mediarepo-socket/Cargo.toml +++ b/mediarepo-daemon/mediarepo-socket/Cargo.toml @@ -7,11 +7,11 @@ workspace = ".." # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -serde = "^1.0.136" -tracing = "^0.1.29" -compare = "^0.1.0" -port_check = "^0.1.5" -rayon = "^1.5.1" +serde = "1.0.136" +tracing = "0.1.30" +compare = "0.1.0" +port_check = "0.1.5" +rayon = "1.5.1" [dependencies.mediarepo-core] path = "../mediarepo-core" @@ -23,13 +23,13 @@ path = "../mediarepo-database" path = "../mediarepo-logic" [dependencies.tokio] -version = "^1.16.1" +version = "1.16.1" features = ["net"] [dependencies.chrono] -version = "^0.4.19" +version = "0.4.19" features = ["serde"] [dependencies.tracing-futures] -version = "^0.2.5" -features = ["tokio-executor"] \ No newline at end of file +version = "0.2.5" +features = ["tokio-executor"] diff --git a/mediarepo-daemon/mediarepo-socket/src/from_model.rs b/mediarepo-daemon/mediarepo-socket/src/from_model.rs index eeffd64..bf101e9 100644 --- a/mediarepo-daemon/mediarepo-socket/src/from_model.rs +++ b/mediarepo-daemon/mediarepo-socket/src/from_model.rs @@ -1,9 +1,13 @@ use mediarepo_core::mediarepo_api::types::files::{ FileBasicDataResponse, FileMetadataResponse, FileStatus, ThumbnailMetadataResponse, }; +use mediarepo_core::mediarepo_api::types::filtering::{ + SortDirection, SortKey, SortNamespace, SortingPreset, +}; use mediarepo_core::mediarepo_api::types::tags::{NamespaceResponse, TagResponse}; use mediarepo_logic::dto::{ - FileDto, FileMetadataDto, FileStatus as FileStatusModel, NamespaceDto, TagDto, ThumbnailDto, + FileDto, FileMetadataDto, FileStatus as FileStatusModel, KeyType, NamespaceDto, SortKeyDto, + SortingPresetDto, TagDto, ThumbnailDto, }; pub trait FromModel { @@ -73,3 +77,42 @@ impl FromModel for NamespaceResponse { } } } + +impl FromModel for SortingPreset { + fn from_model(model: SortingPresetDto) -> Self { + SortingPreset { + id: model.id(), + keys: model + .into_keys() + .into_iter() + .filter_map(map_sort_dto_to_key) + .collect(), + } + } +} + +fn map_sort_dto_to_key(dto: SortKeyDto) -> Option { + let direction = map_direction(dto.ascending()); + + match dto.key_type()? { + KeyType::Namespace => Some(SortKey::Namespace(SortNamespace { + name: dto.value()?.to_owned(), + direction, + })), + KeyType::FileName => Some(SortKey::FileName(direction)), + KeyType::FileSize => Some(SortKey::FileSize(direction)), + KeyType::FileImportedTime => Some(SortKey::FileImportedTime(direction)), + KeyType::FileCreatedTime => Some(SortKey::FileCreatedTime(direction)), + KeyType::FileChangeTime => Some(SortKey::FileChangeTime(direction)), + KeyType::FileType => Some(SortKey::FileType(direction)), + KeyType::NumTags => Some(SortKey::NumTags(direction)), + } +} + +fn map_direction(ascending: bool) -> SortDirection { + if ascending { + SortDirection::Ascending + } else { + SortDirection::Descending + } +} diff --git a/mediarepo-daemon/mediarepo-socket/src/namespaces/files/mod.rs b/mediarepo-daemon/mediarepo-socket/src/namespaces/files/mod.rs index 644a6f0..339b6bd 100644 --- a/mediarepo-daemon/mediarepo-socket/src/namespaces/files/mod.rs +++ b/mediarepo-daemon/mediarepo-socket/src/namespaces/files/mod.rs @@ -291,10 +291,8 @@ impl FilesNamespace { let found_thumbnail = thumbnails.into_iter().find(|thumb| { let Dimensions { height, width } = thumb.size(); - *height >= min_size.0 - && *height <= max_size.0 - && *width >= min_size.1 - && *width <= max_size.1 + (*height <= max_size.0 && *width <= max_size.1) + && (*width >= min_size.1 || *height >= min_size.0) }); let thumbnail = if let Some(thumbnail) = found_thumbnail { diff --git a/mediarepo-daemon/mediarepo-socket/src/namespaces/mod.rs b/mediarepo-daemon/mediarepo-socket/src/namespaces/mod.rs index 3817972..164ad5f 100644 --- a/mediarepo-daemon/mediarepo-socket/src/namespaces/mod.rs +++ b/mediarepo-daemon/mediarepo-socket/src/namespaces/mod.rs @@ -3,6 +3,7 @@ use mediarepo_core::bromine::{namespace, namespace::Namespace, IPCBuilder}; pub mod files; pub mod jobs; +pub mod presets; pub mod repo; pub mod tags; @@ -12,4 +13,5 @@ pub fn build_namespaces(builder: IPCBuilder) .add_namespace(namespace!(tags::TagsNamespace)) .add_namespace(namespace!(repo::RepoNamespace)) .add_namespace(namespace!(jobs::JobsNamespace)) + .add_namespace(namespace!(presets::PresetsNamespace)) } diff --git a/mediarepo-daemon/mediarepo-socket/src/namespaces/presets.rs b/mediarepo-daemon/mediarepo-socket/src/namespaces/presets.rs new file mode 100644 index 0000000..fbcd50c --- /dev/null +++ b/mediarepo-daemon/mediarepo-socket/src/namespaces/presets.rs @@ -0,0 +1,118 @@ +use crate::from_model::FromModel; +use crate::utils::get_repo_from_context; +use mediarepo_core::bromine::prelude::*; +use mediarepo_core::mediarepo_api::types::filtering::{SortDirection, SortKey, SortingPreset}; +use mediarepo_logic::dao::DaoProvider; +use mediarepo_logic::dto::{AddSortKeyDto, AddSortingPresetDto, KeyType}; + +pub struct PresetsNamespace; + +impl NamespaceProvider for PresetsNamespace { + fn name() -> &'static str { + "presets" + } + + fn register(handler: &mut EventHandler) { + events!(handler, + "all_sorting_presets" => Self::all_sorting_presets, + "add_sorting_preset" => Self::add_sorting_preset, + "delete_sorting_preset" => Self::delete_sorting_preset + ); + } +} + +impl PresetsNamespace { + #[tracing::instrument(skip_all)] + pub async fn all_sorting_presets(ctx: &Context, _: Event) -> IPCResult<()> { + let repo = get_repo_from_context(ctx).await; + let sorting_presets: Vec = repo + .sorting_preset() + .all() + .await? + .into_iter() + .map(SortingPreset::from_model) + .collect(); + ctx.emit_to(Self::name(), "all_sorting_presets", sorting_presets) + .await?; + + Ok(()) + } + + #[tracing::instrument(skip_all)] + pub async fn add_sorting_preset(ctx: &Context, event: Event) -> IPCResult<()> { + let keys = event + .payload::>()? + .into_iter() + .map(sort_key_to_add_dto) + .collect(); + let repo = get_repo_from_context(ctx).await; + let preset = repo + .sorting_preset() + .add(AddSortingPresetDto { keys }) + .await?; + ctx.emit_to( + Self::name(), + "add_sorting_preset", + SortingPreset::from_model(preset), + ) + .await?; + + Ok(()) + } + + #[tracing::instrument(skip_all)] + pub async fn delete_sorting_preset(ctx: &Context, event: Event) -> IPCResult<()> { + let id = event.payload::()?; + let repo = get_repo_from_context(ctx).await; + repo.sorting_preset().delete(id).await?; + ctx.emit_to(Self::name(), "delete_sorting_preset", ()) + .await?; + + Ok(()) + } +} + +fn sort_key_to_add_dto(key: SortKey) -> AddSortKeyDto { + match key { + SortKey::Namespace(namespace) => AddSortKeyDto { + ascending: namespace.direction == SortDirection::Ascending, + key_type: KeyType::Namespace, + value: Some(namespace.name), + }, + SortKey::FileName(dir) => AddSortKeyDto { + ascending: dir == SortDirection::Ascending, + key_type: KeyType::FileName, + value: None, + }, + SortKey::FileSize(dir) => AddSortKeyDto { + ascending: dir == SortDirection::Ascending, + key_type: KeyType::FileSize, + value: None, + }, + SortKey::FileImportedTime(dir) => AddSortKeyDto { + ascending: dir == SortDirection::Ascending, + key_type: KeyType::FileImportedTime, + value: None, + }, + SortKey::FileCreatedTime(dir) => AddSortKeyDto { + ascending: dir == SortDirection::Ascending, + key_type: KeyType::FileCreatedTime, + value: None, + }, + SortKey::FileChangeTime(dir) => AddSortKeyDto { + ascending: dir == SortDirection::Ascending, + key_type: KeyType::FileChangeTime, + value: None, + }, + SortKey::FileType(dir) => AddSortKeyDto { + ascending: dir == SortDirection::Ascending, + key_type: KeyType::FileType, + value: None, + }, + SortKey::NumTags(dir) => AddSortKeyDto { + ascending: dir == SortDirection::Ascending, + key_type: KeyType::NumTags, + value: None, + }, + } +} diff --git a/mediarepo-ui/src-tauri/Cargo.lock b/mediarepo-ui/src-tauri/Cargo.lock index 9b93484..b5081b6 100644 --- a/mediarepo-ui/src-tauri/Cargo.lock +++ b/mediarepo-ui/src-tauri/Cargo.lock @@ -161,9 +161,9 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] name = "block-buffer" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1d36a02058e76b040de25a4464ba1c80935655595b661505c8b39b664828b95" +checksum = "03588e54c62ae6d763e2a80090d50353b785795361b4ff5b3bf0a5097fc31c0b" dependencies = [ "generic-array", ] @@ -506,9 +506,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97242a70df9b89a65d0b6df3c4bf5b9ce03c5b7309019777fbde37e7537f8762" +checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9" dependencies = [ "cfg-if 1.0.0", "crossbeam-utils", @@ -519,9 +519,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcae03edb34f947e64acdb1c33ec169824e20657e9ecb61cef6c8c74dcb8120" +checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6" dependencies = [ "cfg-if 1.0.0", "lazy_static", @@ -799,9 +799,9 @@ dependencies = [ [[package]] name = "futf" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c9c1ce3fa9336301af935ab852c437817d14cd33690446569392e65170aac3b" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" dependencies = [ "mac", "new_debug_unreachable", @@ -1416,9 +1416,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.116" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "565dbd88872dbe4cc8a46e527f26483c1d1f7afa6b884a3bd6cd893d4f98da74" +checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c" [[package]] name = "lock_api" @@ -1499,7 +1499,7 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] name = "mediarepo-api" -version = "0.28.0" +version = "0.28.1" dependencies = [ "async-trait", "bromine", @@ -1507,7 +1507,7 @@ dependencies = [ "directories", "futures", "mime_guess", - "parking_lot", + "parking_lot 0.12.0", "pathsearch", "serde", "serde_json", @@ -1865,7 +1865,17 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", - "parking_lot_core", + "parking_lot_core 0.8.5", +] + +[[package]] +name = "parking_lot" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.0", ] [[package]] @@ -1882,6 +1892,19 @@ dependencies = [ "winapi", ] +[[package]] +name = "parking_lot_core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2f4f894f3865f6c0e02810fc597300f34dc2510f66400da262d8ae10e75767d" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + [[package]] name = "pathdiff" version = "0.2.1" @@ -2377,7 +2400,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.4", + "semver 1.0.5", ] [[package]] @@ -2425,9 +2448,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "security-framework" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fed7948b6c68acbb6e20c334f55ad635dc0f75506963de4464289fbd3b051ac" +checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" dependencies = [ "bitflags", "core-foundation 0.9.2", @@ -2438,9 +2461,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a57321bf8bc2362081b2599912d2961fe899c0efadf1b4b2f8d48b3e253bb96c" +checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" dependencies = [ "core-foundation-sys 0.8.3", "libc", @@ -2477,9 +2500,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" +checksum = "0486718e92ec9a68fbed73bb5ef687d71103b142595b406835649bebd33f72c7" [[package]] name = "semver-parser" @@ -2654,14 +2677,14 @@ dependencies = [ [[package]] name = "string_cache" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "923f0f39b6267d37d23ce71ae7235602134b250ace715dd2c90421998ddac0c6" +checksum = "33994d0838dc2d152d17a62adf608a869b5e846b65b389af7f3dbc1de45c5b26" dependencies = [ "lazy_static", "new_debug_unreachable", - "parking_lot", - "phf_shared 0.8.0", + "parking_lot 0.11.2", + "phf_shared 0.10.0", "precomputed-hash", "serde", ] @@ -2811,7 +2834,7 @@ dependencies = [ "ndk-glue", "ndk-sys", "objc", - "parking_lot", + "parking_lot 0.11.2", "raw-window-handle 0.3.4", "scopeguard", "serde", @@ -2857,7 +2880,7 @@ dependencies = [ "rand 0.8.4", "raw-window-handle 0.3.4", "rfd", - "semver 1.0.4", + "semver 1.0.5", "serde", "serde_json", "serde_repr", @@ -3088,9 +3111,9 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" +checksum = "2d8d93354fe2a8e50d5953f5ae2e47a3fc2ef03292e7ea46e3cc38f549525fb9" dependencies = [ "cfg-if 1.0.0", "pin-project-lite", @@ -3100,9 +3123,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" +checksum = "8276d9a4a3a558d7b7ad5303ad50b53d58264641b82914b7ada36bd762e7a716" dependencies = [ "proc-macro2 1.0.36", "quote 1.0.15", @@ -3111,11 +3134,12 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" +checksum = "03cfcb51380632a72d3111cb8d3447a8d908e577d31beeac006f836383d29a23" dependencies = [ "lazy_static", + "valuable", ] [[package]] @@ -3131,9 +3155,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5312f325fe3588e277415f5a6cca1f4ccad0f248c4cd5a4bd33032d7286abc22" +checksum = "74786ce43333fcf51efe947aed9718fbe46d5c7328ec3f1029e818083966d9aa" dependencies = [ "ansi_term", "lazy_static", @@ -3235,6 +3259,12 @@ dependencies = [ "getrandom 0.2.4", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "vcpkg" version = "0.2.15" @@ -3476,6 +3506,49 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ceb069ac8b2117d36924190469735767f0990833935ab430155e71a44bafe148" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d027175d00b01e0cbeb97d6ab6ebe03b12330a35786cbaca5252b1c4bf5d9b" + +[[package]] +name = "windows_i686_gnu" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8793f59f7b8e8b01eda1a652b2697d87b93097198ae85f823b969ca5b89bba58" + +[[package]] +name = "windows_i686_msvc" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8602f6c418b67024be2996c512f5f995de3ba417f4c75af68401ab8756796ae4" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d615f419543e0bd7d2b3323af0d86ff19cbc4f816e6453f36a2c2ce889c354" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d95421d9ed3672c280884da53201a5c46b7b2765ca6faf34b0d71cf34a3561" + [[package]] name = "winres" version = "0.1.12" diff --git a/mediarepo-ui/src-tauri/Cargo.toml b/mediarepo-ui/src-tauri/Cargo.toml index 28ad054..ee837a0 100644 --- a/mediarepo-ui/src-tauri/Cargo.toml +++ b/mediarepo-ui/src-tauri/Cargo.toml @@ -10,20 +10,20 @@ edition = "2018" build = "src/build.rs" [build-dependencies] -tauri-build = { version = "^1.0.0-beta.4" } +tauri-build = "1.0.0-beta.4" [dependencies] -serde_json = "^1.0" -serde = { version = "^1.0", features = ["derive"] } -thiserror = "^1.0.30" -typemap_rev = "^0.1.5" +serde_json = "1.0.78" +serde = { version = "1.0.136", features = ["derive"] } +thiserror = "1.0.30" +typemap_rev = "0.1.5" [dependencies.tauri] -version = "^1.0.0-beta.8" +version = "1.0.0-beta.8" features = ["dialog-all", "path-all", "shell-all"] [dependencies.tracing-subscriber] -version = "^0.3.0" +version = "0.3.8" features = ["env-filter"] [dependencies.mediarepo-api] diff --git a/mediarepo-ui/src/api/Api.ts b/mediarepo-ui/src/api/Api.ts index 292cd8f..27e1d58 100644 --- a/mediarepo-ui/src/api/Api.ts +++ b/mediarepo-ui/src/api/Api.ts @@ -4,12 +4,14 @@ import {ApiFunction} from "./api-types/functions"; import { AddLocalFileREquest, AddRepositoryRequest, + AddSortingPresetRequest, ChangeFileTagsRequest, CheckDaemonRunningRequest, CheckLocalRepositoryExistsRequest, CreateTagsRequest, DeleteFileRequest, DeleteRepositoryRequest, + DeleteSortingPresetRequest, DeleteThumbnailsRequest, FindFilesRequest, GetFileMetadataRequest, @@ -31,6 +33,7 @@ import { import {RepositoryData, RepositoryMetadata, SizeMetadata} from "./api-types/repo"; import {CdTagMappings, NamespaceData, TagData} from "./api-types/tags"; import {ShortCache} from "./ShortCache"; +import {SortingPresetData} from "./api-types/presets"; export class MediarepoApi { @@ -184,6 +187,18 @@ export class MediarepoApi { return this.invokePlugin(ApiFunction.RunJob, request); } + public static async getAllSortingPresets(): Promise { + return ShortCache.cached("sorting-presets", () => this.invokePlugin(ApiFunction.GetAllSortingPresets), 1000); + } + + public static async addSortingPreset(request: AddSortingPresetRequest): Promise { + return this.invokePlugin(ApiFunction.AddSortingPreset, request); + } + + public static async deleteSortingPreset(request: DeleteSortingPresetRequest): Promise { + return this.invokePlugin(ApiFunction.DeleteSortingPreset, request); + } + private static async invokePlugin(fn: ApiFunction, args?: any): Promise { return invoke(`plugin:mediarepo|${fn}`, args); } diff --git a/mediarepo-ui/src/api/api-types/files.ts b/mediarepo-ui/src/api/api-types/files.ts index 004917e..cf6387d 100644 --- a/mediarepo-ui/src/api/api-types/files.ts +++ b/mediarepo-ui/src/api/api-types/files.ts @@ -41,7 +41,7 @@ export type ValueComparator = | { Greater: T } | { Between: T[] } -export type SortKey = { Namespace: SortNamespace } +export type SortKeyData = { Namespace: SortNamespace } | { FileName: SortDirection } | { FileSize: SortDirection } | { FileImportedTime: SortDirection } diff --git a/mediarepo-ui/src/api/api-types/functions.ts b/mediarepo-ui/src/api/api-types/functions.ts index 5f61ed0..44f8041 100644 --- a/mediarepo-ui/src/api/api-types/functions.ts +++ b/mediarepo-ui/src/api/api-types/functions.ts @@ -40,4 +40,8 @@ export enum ApiFunction { SetFrontendState = "set_frontend_state", // jobs RunJob = "run_job", + // presets + GetAllSortingPresets = "all_sorting_presets", + AddSortingPreset = "add_sorting_preset", + DeleteSortingPreset = "delete_sorting_preset", } diff --git a/mediarepo-ui/src/api/api-types/presets.ts b/mediarepo-ui/src/api/api-types/presets.ts new file mode 100644 index 0000000..01eb00f --- /dev/null +++ b/mediarepo-ui/src/api/api-types/presets.ts @@ -0,0 +1,6 @@ +import {SortKeyData} from "./files"; + +export type SortingPresetData = { + id: number, + keys: SortKeyData[], +} diff --git a/mediarepo-ui/src/api/api-types/requests.ts b/mediarepo-ui/src/api/api-types/requests.ts index c6c0d24..f0bec62 100644 --- a/mediarepo-ui/src/api/api-types/requests.ts +++ b/mediarepo-ui/src/api/api-types/requests.ts @@ -1,4 +1,4 @@ -import {FileOsMetadata, FileStatus, FilterExpression, SortKey} from "./files"; +import {FileOsMetadata, FileStatus, FilterExpression, SortKeyData} from "./files"; import {RepositoryData, SizeType} from "./repo"; import {JobType} from "./job"; @@ -40,7 +40,7 @@ export type GetSizeRequest = { export type FindFilesRequest = { filters: FilterExpression[], - sortBy: SortKey[] + sortBy: SortKeyData[] }; export type UpdateFileNameRequest = { @@ -107,4 +107,12 @@ export type SetFrontendStateRequest = { export type RunJobRequest = { jobType: JobType, -} +}; + +export type AddSortingPresetRequest = { + sortKeys: SortKeyData[] +}; + +export type DeleteSortingPresetRequest = { + id: number +}; diff --git a/mediarepo-ui/src/api/models/File.ts b/mediarepo-ui/src/api/models/File.ts index 84c5a8f..67f3bd8 100644 --- a/mediarepo-ui/src/api/models/File.ts +++ b/mediarepo-ui/src/api/models/File.ts @@ -22,11 +22,11 @@ export class File { return this.basicData.status; } - public get mimeType(): string { - return this.basicData.mime_type; - } - public set status(value: FileStatus) { this.basicData.status = value; } + + public get mimeType(): string { + return this.basicData.mime_type; + } } diff --git a/mediarepo-ui/src/api/models/Repository.ts b/mediarepo-ui/src/api/models/Repository.ts index 9eb5ed6..cbc8efa 100644 --- a/mediarepo-ui/src/api/models/Repository.ts +++ b/mediarepo-ui/src/api/models/Repository.ts @@ -22,7 +22,7 @@ export class Repository { return this.repoData.local; } - public update(data: {name?: string, address?: string, path?: string, local?: boolean}) { + public update(data: { name?: string, address?: string, path?: string, local?: boolean }) { this.repoData = Object.assign(this.repoData, data); } } diff --git a/mediarepo-ui/src/api/models/SortKey.ts b/mediarepo-ui/src/api/models/SortKey.ts new file mode 100644 index 0000000..4bd651c --- /dev/null +++ b/mediarepo-ui/src/api/models/SortKey.ts @@ -0,0 +1,108 @@ +import {SortDirection, SortKeyData} from "../api-types/files"; + +export type SortType = + "Namespace" + | "FileName" + | "FileSize" + | "FileImportedTime" + | "FileCreatedTime" + | "FileChangeTime" + | "FileType" + | "NumTags"; + +export class SortKey { + + constructor(private data: SortKeyData) { + this.data = data; + } + + public get sortType(): SortType { + return Reflect.ownKeys(this.data)[0] as SortType; + } + + public set sortType(value: SortType) { + if (value == "Namespace") { + this.data = { + Namespace: { + direction: this.sortDirection, + name: "" + } + }; + } else { + this.data = { + [value]: this.sortDirection + } as SortKeyData; + } + } + + public get sortDirection(): SortDirection { + if ("Namespace" in this.data) { + return this.data.Namespace.direction; + } else { + // @ts-ignore + return this.data[this.sortType]; + } + } + + public set sortDirection(value: SortDirection) { + const sortType = this.sortType; + if ("Namespace" in this.data) { + this.data.Namespace.direction = value; + } else { + // @ts-ignore + this.data[this.sortType] = value; + } + } + + public get namespaceName(): string | undefined { + if ("Namespace" in this.data) { + return this.data.Namespace.name; + } + return undefined; + } + + public set namespaceName(value: string | undefined) { + if (value && "Namespace" in this.data) { + this.data.Namespace.name = value; + } + } + + public get rawData(): SortKeyData { + return this.data; + } + + public static fromValues( + sortType: SortType, + sortDirection: SortDirection, + namespaceName: string | undefined + ) { + let data; + + if (sortType === "Namespace") { + data = { + Namespace: { + name: namespaceName!, + direction: sortDirection + } + }; + } else { + data = { + [sortType]: sortDirection + } as SortKeyData; + } + + return new SortKey(data); + } + + public toString(): string { + if (this.sortType == "Namespace") { + return `${this.sortType} '${this.namespaceName}' ${this.getDirectionString()}`; + } else { + return `${this.sortType} ${this.getDirectionString()}`; + } + } + + private getDirectionString(): string { + return this.sortDirection === "Ascending" ? "â–²" : "â–¼"; + } +} diff --git a/mediarepo-ui/src/api/models/SortingPreset.ts b/mediarepo-ui/src/api/models/SortingPreset.ts new file mode 100644 index 0000000..1b0dc9b --- /dev/null +++ b/mediarepo-ui/src/api/models/SortingPreset.ts @@ -0,0 +1,48 @@ +import {SortKey} from "./SortKey"; +import {SortingPresetData} from "../api-types/presets"; +import {mapNew} from "./adaptors"; + +export class SortingPreset { + private keys: SortKey[]; + + constructor(presetData: SortingPresetData) { + this._id = presetData.id; + this.keys = presetData.keys.map(mapNew(SortKey)); + } + + private _id: number; + + public get id(): number { + return this._id; + } + + public set id(value: number) { + this._id = value; + } + + public get sortKeys(): SortKey[] { + return this.keys; + } + + public get rawData(): SortingPresetData { + return { + id: this._id, + keys: this.keys.map(k => k.rawData), + }; + } + + public static fromValues(id: number, keys: SortKey[]) { + let preset = new SortingPreset({ id, keys: [] }); + preset.keys = keys; + return preset; + } + + public setData(data: SortingPresetData) { + this._id = data.id; + this.keys = data.keys.map(mapNew(SortKey)); + } + + public toString(): string { + return this.sortKeys.join(", "); + } +} diff --git a/mediarepo-ui/src/app/components/core/core.component.scss b/mediarepo-ui/src/app/components/core/core.component.scss index e4f50ca..a805f59 100644 --- a/mediarepo-ui/src/app/components/core/core.component.scss +++ b/mediarepo-ui/src/app/components/core/core.component.scss @@ -36,6 +36,7 @@ mat-tab-group { right: 0; top: 0; height: 100%; + ng-icon { font-size: 1.5em; margin-top: calc(-50%); diff --git a/mediarepo-ui/src/app/components/core/core.module.ts b/mediarepo-ui/src/app/components/core/core.module.ts index d3ffbc9..dbd097f 100644 --- a/mediarepo-ui/src/app/components/core/core.module.ts +++ b/mediarepo-ui/src/app/components/core/core.module.ts @@ -1,17 +1,11 @@ import {NgModule} from "@angular/core"; import {CommonModule} from "@angular/common"; import {CoreComponent} from "./core.component"; -import { - RepositoriesTabComponent -} from "./repositories-tab/repositories-tab.component"; +import {RepositoriesTabComponent} from "./repositories-tab/repositories-tab.component"; import {FilesTabComponent} from "./files-tab/files-tab.component"; -import { - FilesTabSidebarComponent -} from "./files-tab/files-tab-sidebar/files-tab-sidebar.component"; +import {FilesTabSidebarComponent} from "./files-tab/files-tab-sidebar/files-tab-sidebar.component"; import {ImportTabComponent} from "./import-tab/import-tab.component"; -import { - ImportTabSidebarComponent -} from "./import-tab/import-tab-sidebar/import-tab-sidebar.component"; +import {ImportTabSidebarComponent} from "./import-tab/import-tab-sidebar/import-tab-sidebar.component"; import {MatButtonModule} from "@angular/material/button"; import {MatSidenavModule} from "@angular/material/sidenav"; import {MatProgressBarModule} from "@angular/material/progress-bar"; @@ -30,9 +24,7 @@ import {SidebarModule} from "../shared/sidebar/sidebar.module"; import {FileModule} from "../shared/file/file.module"; import {AppCommonModule} from "../shared/app-common/app-common.module"; import {ReactiveFormsModule} from "@angular/forms"; -import { - RepositoryCardComponent -} from "./repositories-tab/repository-card/repository-card.component"; +import {RepositoryCardComponent} from "./repositories-tab/repository-card/repository-card.component"; import {MatCardModule} from "@angular/material/card"; import {MatListModule} from "@angular/material/list"; import {MatDialogModule} from "@angular/material/dialog"; @@ -42,9 +34,7 @@ import {TagModule} from "../shared/tag/tag.module"; import { DownloadDaemonDialogComponent } from "./repositories-tab/download-daemon-dialog/download-daemon-dialog.component"; -import { - RepositoryModule -} from "../shared/repository/repository/repository.module"; +import {RepositoryModule} from "../shared/repository/repository/repository.module"; import {MatToolbarModule} from "@angular/material/toolbar"; import { RepositoryDetailsViewComponent @@ -76,7 +66,7 @@ import { MatProgressBarModule, MatCheckboxModule, ScrollingModule, - NgIconsModule.withIcons({...materialIcons}), + NgIconsModule.withIcons({ ...materialIcons }), FlexModule, MatButtonModule, MatMenuModule, diff --git a/mediarepo-ui/src/app/components/core/files-tab/files-tab-sidebar/files-tab-sidebar.component.html b/mediarepo-ui/src/app/components/core/files-tab/files-tab-sidebar/files-tab-sidebar.component.html index 6c677c9..9a29759 100644 --- a/mediarepo-ui/src/app/components/core/files-tab/files-tab-sidebar/files-tab-sidebar.component.html +++ b/mediarepo-ui/src/app/components/core/files-tab/files-tab-sidebar/files-tab-sidebar.component.html @@ -1,9 +1,12 @@