diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 84413b0..246111b 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -36,7 +36,15 @@ jobs: - name: Check daemon working-directory: mediarepo-daemon - run: cargo check --no-default-features + run: cargo check + + - name: Lint api + working-directory: mediarepo-api + run: cargo clippy -- -D warnings + + - name: Lint daemon + working-directory: mediarepo-daemon + run: cargo clippy -- -D warnings - name: Install UI dependencies working-directory: mediarepo-ui @@ -47,4 +55,4 @@ jobs: - name: Lint ui frontend working-directory: mediarepo-ui - run: yarn lint \ No newline at end of file + run: yarn lint diff --git a/mediarepo-api/Cargo.toml b/mediarepo-api/Cargo.toml index 7d6ed34..9cc15f6 100644 --- a/mediarepo-api/Cargo.toml +++ b/mediarepo-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mediarepo-api" -version = "0.32.0" +version = "0.32.1" edition = "2018" license = "gpl-3" diff --git a/mediarepo-api/src/client_api/job.rs b/mediarepo-api/src/client_api/job.rs index cfbe8d1..918b18b 100644 --- a/mediarepo-api/src/client_api/job.rs +++ b/mediarepo-api/src/client_api/job.rs @@ -34,4 +34,10 @@ impl JobApi { Ok(()) } + + /// Checks if a particular job is already running + #[tracing::instrument(level = "debug", skip(self))] + pub async fn is_job_running(&self, job_type: JobType) -> ApiResult { + self.emit_and_get("is_job_running", job_type, None).await + } } diff --git a/mediarepo-api/src/tauri_plugin/commands/job.rs b/mediarepo-api/src/tauri_plugin/commands/job.rs index 313e613..d4fa84a 100644 --- a/mediarepo-api/src/tauri_plugin/commands/job.rs +++ b/mediarepo-api/src/tauri_plugin/commands/job.rs @@ -9,3 +9,11 @@ pub async fn run_job(api_state: ApiAccess<'_>, job_type: JobType, sync: bool) -> Ok(()) } + +#[tauri::command] +pub async fn is_job_running(api_state: ApiAccess<'_>, job_type: JobType) -> PluginResult { + let api = api_state.api().await?; + let running = api.job.is_job_running(job_type).await?; + + Ok(running) +} diff --git a/mediarepo-api/src/tauri_plugin/mod.rs b/mediarepo-api/src/tauri_plugin/mod.rs index 85e581a..e32264a 100644 --- a/mediarepo-api/src/tauri_plugin/mod.rs +++ b/mediarepo-api/src/tauri_plugin/mod.rs @@ -75,7 +75,8 @@ impl MediarepoPlugin { get_file_tag_map, all_sorting_presets, add_sorting_preset, - delete_sorting_preset + delete_sorting_preset, + is_job_running ]), } } diff --git a/mediarepo-daemon/Cargo.lock b/mediarepo-daemon/Cargo.lock index df9038b..da9af5c 100644 --- a/mediarepo-daemon/Cargo.lock +++ b/mediarepo-daemon/Cargo.lock @@ -8,6 +8,15 @@ version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -40,6 +49,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + [[package]] name = "aliasable" version = "0.1.3" @@ -185,6 +203,21 @@ dependencies = [ "mime", ] +[[package]] +name = "backtrace" +version = "0.3.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61" +dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "bae" version = "0.1.7" @@ -1015,6 +1048,12 @@ dependencies = [ "weezl", ] +[[package]] +name = "gimli" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" + [[package]] name = "glob" version = "0.3.0" @@ -1150,6 +1189,21 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +[[package]] +name = "human-panic" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39f357a500abcbd7c5f967c1d45c8838585b36743823b9d43488f24850534e36" +dependencies = [ + "backtrace", + "os_type", + "serde", + "serde_derive", + "termcolor", + "toml", + "uuid", +] + [[package]] name = "humantime" version = "2.1.0" @@ -1408,7 +1462,7 @@ checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" [[package]] name = "mediarepo-api" -version = "0.32.0" +version = "0.32.1" dependencies = [ "bromine", "chrono", @@ -1451,6 +1505,7 @@ version = "1.0.3" dependencies = [ "console-subscriber", "glob", + "human-panic", "log", "mediarepo-core", "mediarepo-logic", @@ -1785,6 +1840,15 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.28.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40bec70ba014595f99f7aa110b84331ffe1ee9aece7fe6f387cc7e3ecda4d456" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.10.0" @@ -1900,6 +1964,15 @@ dependencies = [ "hashbrown 0.12.0", ] +[[package]] +name = "os_type" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3df761f6470298359f84fcfb60d86db02acc22c251c37265c07a3d1057d2389" +dependencies = [ + "regex", +] + [[package]] name = "ouroboros" version = "0.15.0" @@ -2293,6 +2366,8 @@ version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" dependencies = [ + "aho-corasick", + "memchr", "regex-syntax", ] @@ -2361,6 +2436,12 @@ dependencies = [ "serde", ] +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + [[package]] name = "rustc_version" version = "0.2.3" @@ -2958,6 +3039,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + [[package]] name = "textwrap" version = "0.11.0" @@ -3689,6 +3779,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/mediarepo-daemon/Cargo.toml b/mediarepo-daemon/Cargo.toml index 0308f74..04fe127 100644 --- a/mediarepo-daemon/Cargo.toml +++ b/mediarepo-daemon/Cargo.toml @@ -4,7 +4,7 @@ default-members = ["mediarepo-core", "mediarepo-database", "mediarepo-logic", "m [package] name = "mediarepo-daemon" -version = "1.0.3" +version = "1.0.4" edition = "2018" license = "gpl-3" repository = "https://github.com/Trivernis/mediarepo-daemon" @@ -30,6 +30,7 @@ log = "0.4.16" opentelemetry = { version = "0.17.0", features = ["rt-tokio"] } opentelemetry-jaeger = { version = "0.16.0", features = ["rt-tokio"] } tracing-opentelemetry = "0.17.2" +human-panic = "1.0.3" [dependencies.mediarepo-core] path = "./mediarepo-core" diff --git a/mediarepo-daemon/mediarepo-core/src/fs/thumbnail_store.rs b/mediarepo-daemon/mediarepo-core/src/fs/thumbnail_store.rs index 0ac2a70..c1299f4 100644 --- a/mediarepo-daemon/mediarepo-core/src/fs/thumbnail_store.rs +++ b/mediarepo-daemon/mediarepo-core/src/fs/thumbnail_store.rs @@ -71,7 +71,7 @@ impl ThumbnailStore { let name = file_name.to_string_lossy(); let (height, width) = name - .split_once("-") + .split_once('-') .and_then(|(height, width)| { Some((height.parse::().ok()?, width.parse::().ok()?)) }) diff --git a/mediarepo-daemon/mediarepo-core/src/settings/logging.rs b/mediarepo-daemon/mediarepo-core/src/settings/logging.rs index 513b7a2..1ab3682 100644 --- a/mediarepo-daemon/mediarepo-core/src/settings/logging.rs +++ b/mediarepo-daemon/mediarepo-core/src/settings/logging.rs @@ -34,6 +34,7 @@ pub enum LogLevel { Trace, } +#[allow(clippy::from_over_into)] impl Into> for LogLevel { fn into(self) -> Option { match self { diff --git a/mediarepo-daemon/mediarepo-core/src/settings/mod.rs b/mediarepo-daemon/mediarepo-core/src/settings/mod.rs index 3ce242b..26362d5 100644 --- a/mediarepo-daemon/mediarepo-core/src/settings/mod.rs +++ b/mediarepo-daemon/mediarepo-core/src/settings/mod.rs @@ -1,5 +1,5 @@ use std::fs; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use config::{Config, FileFormat}; use serde::{Deserialize, Serialize}; @@ -24,7 +24,7 @@ pub struct Settings { } impl Settings { - pub fn read(root: &PathBuf) -> RepoResult { + pub fn read(root: &Path) -> RepoResult { let settings = Config::builder() .add_source(config::File::from_str( &*Settings::default().to_toml_string()?, @@ -44,7 +44,7 @@ impl Settings { settings_main.server.tcp.enabled = true; settings_main.server.tcp.port = PortSetting::Range(settings_v1.port_range); settings_main.server.tcp.listen_address = settings_v1.listen_address; - settings_main.paths.thumbnail_directory = settings_v1.thumbnail_store.into(); + settings_main.paths.thumbnail_directory = settings_v1.thumbnail_store; settings_main.paths.database_directory = PathBuf::from(settings_v1.database_path) .parent() .map(|p| p.to_string_lossy().to_string()) @@ -69,7 +69,7 @@ impl Settings { Ok(string) } - pub fn save(&self, root: &PathBuf) -> RepoResult<()> { + pub fn save(&self, root: &Path) -> RepoResult<()> { let string = toml::to_string_pretty(&self)?; fs::write(root.join("repo.toml"), string.into_bytes())?; diff --git a/mediarepo-daemon/mediarepo-core/src/settings/paths.rs b/mediarepo-daemon/mediarepo-core/src/settings/paths.rs index 162e096..830ecfc 100644 --- a/mediarepo-daemon/mediarepo-core/src/settings/paths.rs +++ b/mediarepo-daemon/mediarepo-core/src/settings/paths.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use serde::{Deserialize, Serialize}; @@ -21,27 +21,27 @@ impl Default for PathSettings { impl PathSettings { #[inline] - pub fn database_dir(&self, root: &PathBuf) -> PathBuf { + pub fn database_dir(&self, root: &Path) -> PathBuf { root.join(&self.database_directory) } #[inline] - pub fn files_dir(&self, root: &PathBuf) -> PathBuf { + pub fn files_dir(&self, root: &Path) -> PathBuf { root.join(&self.files_directory) } #[inline] - pub fn thumbs_dir(&self, root: &PathBuf) -> PathBuf { + pub fn thumbs_dir(&self, root: &Path) -> PathBuf { root.join(&self.thumbnail_directory) } #[inline] - pub fn db_file_path(&self, root: &PathBuf) -> PathBuf { + pub fn db_file_path(&self, root: &Path) -> PathBuf { self.database_dir(root).join("repo.db") } #[inline] - pub fn frontend_state_file_path(&self, root: &PathBuf) -> PathBuf { + pub fn frontend_state_file_path(&self, root: &Path) -> PathBuf { self.database_dir(root).join("frontend-state.json") } } diff --git a/mediarepo-daemon/mediarepo-core/src/tracing_layer_list.rs b/mediarepo-daemon/mediarepo-core/src/tracing_layer_list.rs index bdf13f8..53c1f00 100644 --- a/mediarepo-daemon/mediarepo-core/src/tracing_layer_list.rs +++ b/mediarepo-daemon/mediarepo-core/src/tracing_layer_list.rs @@ -7,9 +7,15 @@ use tracing_subscriber::Layer; pub struct DynLayerList(Vec + Send + Sync + 'static>>); +impl Default for DynLayerList { + fn default() -> Self { + Self(Vec::new()) + } +} + impl DynLayerList { pub fn new() -> Self { - Self(Vec::new()) + Self::default() } pub fn iter(&self) -> Iter<'_, Box + Send + Sync>> { diff --git a/mediarepo-daemon/mediarepo-core/src/traits.rs b/mediarepo-daemon/mediarepo-core/src/traits.rs deleted file mode 100644 index eab11ee..0000000 --- a/mediarepo-daemon/mediarepo-core/src/traits.rs +++ /dev/null @@ -1,7 +0,0 @@ -use async_trait::async_trait; - -#[async_trait] -pub trait AsyncTryFrom { - type Error; - fn async_try_from(other: T) -> Result; -} diff --git a/mediarepo-daemon/mediarepo-core/src/utils.rs b/mediarepo-daemon/mediarepo-core/src/utils.rs index c65a638..12d6bd9 100644 --- a/mediarepo-daemon/mediarepo-core/src/utils.rs +++ b/mediarepo-daemon/mediarepo-core/src/utils.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use futures::future; use tokio::fs::{self, OpenOptions}; @@ -16,7 +16,7 @@ pub fn parse_namespace_and_tag(norm_tag: String) -> (Option, String) { } /// Parses all tags from a file -pub async fn parse_tags_file(path: PathBuf) -> RepoResult, String)>> { +pub async fn parse_tags_file(path: &Path) -> RepoResult, String)>> { let file = OpenOptions::new().read(true).open(path).await?; let mut lines = BufReader::new(file).lines(); let mut tags = Vec::new(); @@ -47,7 +47,7 @@ pub async fn get_folder_size(path: PathBuf) -> RepoResult { } } } - let futures = all_files.into_iter().map(|f| read_file_size(f)); + let futures = all_files.into_iter().map(read_file_size); let results = future::join_all(futures).await; let size = results.into_iter().filter_map(|r| r.ok()).sum(); diff --git a/mediarepo-daemon/mediarepo-database/src/queries/analysis.rs b/mediarepo-daemon/mediarepo-database/src/queries/analysis.rs index 9c169b6..897ea0c 100644 --- a/mediarepo-daemon/mediarepo-database/src/queries/analysis.rs +++ b/mediarepo-daemon/mediarepo-database/src/queries/analysis.rs @@ -30,7 +30,7 @@ pub async fn get_all_counts(db: &DatabaseConnection) -> RepoResult { )) .one(db) .await? - .ok_or(RepoError::from("could not retrieve metadata from database"))?; + .ok_or_else(|| RepoError::from("could not retrieve metadata from database"))?; Ok(counts) } diff --git a/mediarepo-daemon/mediarepo-database/src/queries/tags.rs b/mediarepo-daemon/mediarepo-database/src/queries/tags.rs index af136fa..5683254 100644 --- a/mediarepo-daemon/mediarepo-database/src/queries/tags.rs +++ b/mediarepo-daemon/mediarepo-database/src/queries/tags.rs @@ -55,7 +55,7 @@ fn vec_to_query_list(input: Vec) -> String { let mut entries = input .into_iter() .fold(String::new(), |acc, val| format!("{}{},", acc, val)); - if entries.len() > 0 { + if !entries.is_empty() { entries.remove(entries.len() - 1); } diff --git a/mediarepo-daemon/mediarepo-logic/src/dao/file/mod.rs b/mediarepo-daemon/mediarepo-logic/src/dao/file/mod.rs index 93f012c..5e388df 100644 --- a/mediarepo-daemon/mediarepo-logic/src/dao/file/mod.rs +++ b/mediarepo-daemon/mediarepo-logic/src/dao/file/mod.rs @@ -93,7 +93,7 @@ impl FileDao { .all(&self.ctx.db) .await? .into_iter() - .map(|m| FileMetadataDto::new(m)) + .map(FileMetadataDto::new) .collect(); Ok(metadata) diff --git a/mediarepo-daemon/mediarepo-logic/src/dao/file/update.rs b/mediarepo-daemon/mediarepo-logic/src/dao/file/update.rs index 184bf1b..801efdc 100644 --- a/mediarepo-daemon/mediarepo-logic/src/dao/file/update.rs +++ b/mediarepo-daemon/mediarepo-logic/src/dao/file/update.rs @@ -22,8 +22,8 @@ impl FileDao { let trx = self.ctx.db.begin().await?; let model = file::ActiveModel { id: Set(update_dto.id), - cd_id: update_dto.cd_id.map(|v| Set(v)).unwrap_or(NotSet), - mime_type: update_dto.mime_type.map(|v| Set(v)).unwrap_or(NotSet), + cd_id: update_dto.cd_id.map(Set).unwrap_or(NotSet), + mime_type: update_dto.mime_type.map(Set).unwrap_or(NotSet), status: update_dto.status.map(|v| Set(v as i32)).unwrap_or(NotSet), }; let file_model = model.update(&trx).await?; @@ -62,8 +62,8 @@ impl FileDao { sizes: I, ) -> RepoResult> { let bytes = self.get_bytes(file.cd()).await?; - let mime_type = mime::Mime::from_str(file.mime_type()) - .unwrap_or_else(|_| mime::APPLICATION_OCTET_STREAM); + let mime_type = + mime::Mime::from_str(file.mime_type()).unwrap_or(mime::APPLICATION_OCTET_STREAM); let thumbnails = thumbnailer::create_thumbnails(Cursor::new(bytes), mime_type.clone(), sizes)?; let mut dtos = Vec::new(); diff --git a/mediarepo-daemon/mediarepo-logic/src/dao/job/state.rs b/mediarepo-daemon/mediarepo-logic/src/dao/job/state.rs index a8aab72..444dda3 100644 --- a/mediarepo-daemon/mediarepo-logic/src/dao/job/state.rs +++ b/mediarepo-daemon/mediarepo-logic/src/dao/job/state.rs @@ -40,7 +40,7 @@ impl JobDao { } } -fn build_state_filters(states: &Vec) -> Condition { +fn build_state_filters(states: &[UpsertJobStateDto]) -> Condition { states .iter() .map(|s| Condition::all().add(job_state::Column::JobType.eq(s.job_type))) diff --git a/mediarepo-daemon/mediarepo-logic/src/dao/sorting_preset/add.rs b/mediarepo-daemon/mediarepo-logic/src/dao/sorting_preset/add.rs index 39a4b96..184031a 100644 --- a/mediarepo-daemon/mediarepo-logic/src/dao/sorting_preset/add.rs +++ b/mediarepo-daemon/mediarepo-logic/src/dao/sorting_preset/add.rs @@ -122,7 +122,7 @@ async fn add_keys( async fn find_sort_keys( trx: &DatabaseTransaction, - keys: &Vec, + keys: &[AddSortKeyDto], ) -> RepoResult> { if keys.is_empty() { return Ok(vec![]); diff --git a/mediarepo-daemon/mediarepo-logic/src/dao/tag/all_for_cds_map.rs b/mediarepo-daemon/mediarepo-logic/src/dao/tag/all_for_cds_map.rs index 1c00084..d6d7761 100644 --- a/mediarepo-daemon/mediarepo-logic/src/dao/tag/all_for_cds_map.rs +++ b/mediarepo-daemon/mediarepo-logic/src/dao/tag/all_for_cds_map.rs @@ -77,14 +77,12 @@ fn create_cd_tag_map( )>, tag_id_map: HashMap, ) -> HashMap, Vec> { - let cd_tag_map = tag_cd_entries + tag_cd_entries .into_iter() .filter_map(|(t, cd)| Some((cd?, tag_id_map.get(&t.tag_id)?.clone()))) .sorted_by_key(|(cd, _)| cd.id) .group_by(|(cd, _)| cd.descriptor.to_owned()) .into_iter() .map(|(key, group)| (key, group.map(|(_, t)| t).collect::>())) - .collect(); - - cd_tag_map + .collect::, Vec>>() } diff --git a/mediarepo-daemon/mediarepo-logic/src/dao/tag/by_name.rs b/mediarepo-daemon/mediarepo-logic/src/dao/tag/by_name.rs index 1ae99bc..fd40693 100644 --- a/mediarepo-daemon/mediarepo-logic/src/dao/tag/by_name.rs +++ b/mediarepo-daemon/mediarepo-logic/src/dao/tag/by_name.rs @@ -45,11 +45,12 @@ fn name_query_to_condition(query: TagByNameQuery) -> Option { let TagByNameQuery { namespace, name } = query; let mut condition = Condition::all(); + #[allow(clippy::question_mark)] if !name.ends_with('*') { condition = condition.add(tag::Column::Name.eq(name)) } else if name.len() > 1 { condition = - condition.add(tag::Column::Name.like(&*format!("{}%", name.trim_end_matches("*")))) + condition.add(tag::Column::Name.like(&*format!("{}%", name.trim_end_matches('*')))) } else if namespace.is_none() { return None; } diff --git a/mediarepo-daemon/mediarepo-logic/src/dao/tag/mappings.rs b/mediarepo-daemon/mediarepo-logic/src/dao/tag/mappings.rs index 2289c1f..6ddea3f 100644 --- a/mediarepo-daemon/mediarepo-logic/src/dao/tag/mappings.rs +++ b/mediarepo-daemon/mediarepo-logic/src/dao/tag/mappings.rs @@ -58,12 +58,12 @@ impl TagDao { async fn get_existing_mappings( trx: &DatabaseTransaction, - cd_ids: &Vec, - tag_ids: &Vec, + cd_ids: &[i64], + tag_ids: &[i64], ) -> RepoResult> { let existing_mappings: Vec<(i64, i64)> = content_descriptor_tag::Entity::find() - .filter(content_descriptor_tag::Column::CdId.is_in(cd_ids.clone())) - .filter(content_descriptor_tag::Column::TagId.is_in(tag_ids.clone())) + .filter(content_descriptor_tag::Column::CdId.is_in(cd_ids.to_vec())) + .filter(content_descriptor_tag::Column::TagId.is_in(tag_ids.to_vec())) .all(trx) .await? .into_iter() diff --git a/mediarepo-daemon/mediarepo-logic/src/dto/file.rs b/mediarepo-daemon/mediarepo-logic/src/dto/file.rs index ced7cca..bcbb702 100644 --- a/mediarepo-daemon/mediarepo-logic/src/dto/file.rs +++ b/mediarepo-daemon/mediarepo-logic/src/dto/file.rs @@ -75,7 +75,7 @@ pub struct AddFileDto { pub name: Option, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct UpdateFileDto { pub id: i64, pub cd_id: Option, @@ -83,17 +83,6 @@ pub struct UpdateFileDto { pub status: Option, } -impl Default for UpdateFileDto { - fn default() -> Self { - Self { - id: 0, - cd_id: None, - mime_type: None, - status: None, - } - } -} - #[derive(Copy, Clone, Debug)] pub enum FileStatus { Imported = 10, diff --git a/mediarepo-daemon/mediarepo-logic/src/dto/sorting_preset.rs b/mediarepo-daemon/mediarepo-logic/src/dto/sorting_preset.rs index f83eef4..f2aeb2b 100644 --- a/mediarepo-daemon/mediarepo-logic/src/dto/sorting_preset.rs +++ b/mediarepo-daemon/mediarepo-logic/src/dto/sorting_preset.rs @@ -84,7 +84,7 @@ impl KeyType { } pub fn to_number(&self) -> i32 { - self.clone() as i32 + *self as i32 } } diff --git a/mediarepo-daemon/mediarepo-socket/src/lib.rs b/mediarepo-daemon/mediarepo-socket/src/lib.rs index efd3863..49427f6 100644 --- a/mediarepo-daemon/mediarepo-socket/src/lib.rs +++ b/mediarepo-daemon/mediarepo-socket/src/lib.rs @@ -30,8 +30,9 @@ pub fn start_tcp_server( return Err(RepoError::PortUnavailable); } } - PortSetting::Range((l, r)) => port_check::free_local_port_in_range(*l, *r) - .ok_or_else(|| RepoError::PortUnavailable)?, + PortSetting::Range((l, r)) => { + port_check::free_local_port_in_range(*l, *r).ok_or(RepoError::PortUnavailable)? + } }; let ip = settings.server.tcp.listen_address.to_owned(); let address = SocketAddr::new(ip, port); diff --git a/mediarepo-daemon/mediarepo-socket/src/namespaces/files/mod.rs b/mediarepo-daemon/mediarepo-socket/src/namespaces/files/mod.rs index 2c02703..257a422 100644 --- a/mediarepo-daemon/mediarepo-socket/src/namespaces/files/mod.rs +++ b/mediarepo-daemon/mediarepo-socket/src/namespaces/files/mod.rs @@ -151,7 +151,7 @@ impl FilesNamespace { content: bytes, mime_type: metadata .mime_type - .unwrap_or(String::from("application/octet-stream")), + .unwrap_or_else(|| String::from("application/octet-stream")), creation_time: metadata.creation_time, change_time: metadata.change_time, name: Some(metadata.name), diff --git a/mediarepo-daemon/mediarepo-socket/src/namespaces/files/searching.rs b/mediarepo-daemon/mediarepo-socket/src/namespaces/files/searching.rs index fb78e4d..a3aab2a 100644 --- a/mediarepo-daemon/mediarepo-socket/src/namespaces/files/searching.rs +++ b/mediarepo-daemon/mediarepo-socket/src/namespaces/files/searching.rs @@ -69,10 +69,10 @@ fn build_filters_from_expressions( } } }; - if filters.len() > 0 { - Some(filters) - } else { + if filters.is_empty() { None + } else { + Some(filters) } }) .collect() @@ -92,7 +92,7 @@ fn map_tag_query_to_filter( query: TagQuery, tag_id_map: &HashMap, ) -> Option { - if query.tag.ends_with("*") { + if query.tag.ends_with('*') { map_wildcard_tag_to_filter(query, tag_id_map) } else { map_tag_to_filter(query, tag_id_map) @@ -103,7 +103,7 @@ fn map_wildcard_tag_to_filter( query: TagQuery, tag_id_map: &HashMap, ) -> Option { - let filter_tag = query.tag.trim_end_matches("*"); + let filter_tag = query.tag.trim_end_matches('*'); let relevant_ids = tag_id_map .iter() .filter_map(|(name, id)| { @@ -115,15 +115,15 @@ fn map_wildcard_tag_to_filter( }) .collect::>(); - if relevant_ids.len() > 0 { + if relevant_ids.is_empty() { + None + } else { let comparator = if query.negate { IsNot(relevant_ids) } else { Is(relevant_ids) }; Some(FilterProperty::TagWildcardIds(comparator)) - } else { - None } } diff --git a/mediarepo-daemon/mediarepo-socket/src/namespaces/files/sorting.rs b/mediarepo-daemon/mediarepo-socket/src/namespaces/files/sorting.rs index f0526c7..745ffed 100644 --- a/mediarepo-daemon/mediarepo-socket/src/namespaces/files/sorting.rs +++ b/mediarepo-daemon/mediarepo-socket/src/namespaces/files/sorting.rs @@ -71,7 +71,7 @@ async fn build_sort_context( mime_type: file.mime_type().to_owned(), namespaces: cid_nsp .remove(&file.cd_id()) - .unwrap_or(HashMap::with_capacity(0)), + .unwrap_or_else(|| HashMap::with_capacity(0)), tag_count: cid_tag_counts.remove(&file.cd_id()).unwrap_or(0), import_time: metadata.import_time().to_owned(), create_time: metadata.import_time().to_owned(), @@ -176,11 +176,8 @@ fn adjust_for_dir(ordering: Ordering, direction: &SortDirection) -> Ordering { } } -fn compare_tag_lists(list_a: &Vec, list_b: &Vec) -> Ordering { - let first_diff = list_a - .into_iter() - .zip(list_b.into_iter()) - .find(|(a, b)| *a != *b); +fn compare_tag_lists(list_a: &[String], list_b: &[String]) -> Ordering { + let first_diff = list_a.iter().zip(list_b.iter()).find(|(a, b)| *a != *b); if let Some(diff) = first_diff { if let (Some(num_a), Some(num_b)) = (diff.0.parse::().ok(), diff.1.parse::().ok()) { diff --git a/mediarepo-daemon/mediarepo-socket/src/namespaces/jobs.rs b/mediarepo-daemon/mediarepo-socket/src/namespaces/jobs.rs index d86b3f1..d96ef9e 100644 --- a/mediarepo-daemon/mediarepo-socket/src/namespaces/jobs.rs +++ b/mediarepo-daemon/mediarepo-socket/src/namespaces/jobs.rs @@ -20,8 +20,9 @@ impl NamespaceProvider for JobsNamespace { fn register(handler: &mut EventHandler) { events!(handler, - "run_job" => Self::run_job - ) + "run_job" => Self::run_job, + "is_job_running" => Self::is_job_running + ); } } @@ -59,6 +60,26 @@ impl JobsNamespace { Ok(Response::empty()) } + + #[tracing::instrument(skip_all)] + pub async fn is_job_running(ctx: &Context, event: Event) -> IPCResult { + let job_type = event.payload::()?; + let dispatcher = get_job_dispatcher_from_context(ctx).await; + + let running = match job_type { + JobType::MigrateContentDescriptors => { + is_job_running::(&dispatcher).await + } + JobType::CalculateSizes => is_job_running::(&dispatcher).await, + JobType::GenerateThumbnails => { + is_job_running::(&dispatcher).await + } + JobType::CheckIntegrity => is_job_running::(&dispatcher).await, + JobType::Vacuum => is_job_running::(&dispatcher).await, + }; + + Response::payload(ctx, running) + } } async fn dispatch_job( @@ -107,3 +128,12 @@ async fn calculate_all_sizes(ctx: &Context) -> RepoResult<()> { Ok(()) } + +async fn is_job_running(dispatcher: &JobDispatcher) -> bool { + if let Some(handle) = dispatcher.get_handle::().await { + let state = handle.state().await; + state == JobState::Running + } else { + false + } +} diff --git a/mediarepo-daemon/mediarepo-socket/src/namespaces/repo.rs b/mediarepo-daemon/mediarepo-socket/src/namespaces/repo.rs index 05d605f..8dc6fee 100644 --- a/mediarepo-daemon/mediarepo-socket/src/namespaces/repo.rs +++ b/mediarepo-daemon/mediarepo-socket/src/namespaces/repo.rs @@ -95,7 +95,7 @@ async fn get_frontend_state_path(ctx: &Context) -> IPCResult { let data = ctx.data.read().await; let settings = data.get::().unwrap(); let repo_path = data.get::().unwrap(); - let state_path = settings.paths.frontend_state_file_path(&repo_path); + let state_path = settings.paths.frontend_state_file_path(repo_path); Ok(state_path) } diff --git a/mediarepo-daemon/mediarepo-socket/src/utils.rs b/mediarepo-daemon/mediarepo-socket/src/utils.rs index 2c3fc57..1427384 100644 --- a/mediarepo-daemon/mediarepo-socket/src/utils.rs +++ b/mediarepo-daemon/mediarepo-socket/src/utils.rs @@ -33,11 +33,7 @@ pub async fn file_by_identifier(identifier: FileIdentifier, repo: &Repo) -> Repo pub async fn cd_by_identifier(identifier: FileIdentifier, repo: &Repo) -> RepoResult> { match identifier { FileIdentifier::ID(id) => { - let file = repo - .file() - .by_id(id) - .await? - .ok_or_else(|| "Thumbnail not found")?; + let file = repo.file().by_id(id).await?.ok_or("Thumbnail not found")?; Ok(file.cd().to_owned()) } FileIdentifier::CD(cd) => decode_content_descriptor(cd), diff --git a/mediarepo-daemon/mediarepo-worker/src/jobs/calculate_sizes.rs b/mediarepo-daemon/mediarepo-worker/src/jobs/calculate_sizes.rs index d9ce393..b1e2821 100644 --- a/mediarepo-daemon/mediarepo-worker/src/jobs/calculate_sizes.rs +++ b/mediarepo-daemon/mediarepo-worker/src/jobs/calculate_sizes.rs @@ -6,7 +6,7 @@ use mediarepo_core::mediarepo_api::types::repo::SizeType; use mediarepo_core::settings::Settings; use mediarepo_core::utils::get_folder_size; use mediarepo_logic::dao::repo::Repo; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::sync::Arc; use tokio::fs; use tokio::sync::broadcast::{self, Sender}; @@ -72,11 +72,11 @@ impl Job for CalculateSizesJob { async fn calculate_size( size_type: &SizeType, repo: &Repo, - repo_path: &PathBuf, + repo_path: &Path, settings: &Settings, ) -> RepoResult { let size = match &size_type { - SizeType::Total => get_folder_size(repo_path.clone()).await?, + SizeType::Total => get_folder_size(repo_path.to_path_buf()).await?, SizeType::FileFolder => repo.get_main_store_size().await?, SizeType::ThumbFolder => repo.get_thumb_store_size().await?, SizeType::DatabaseFile => { diff --git a/mediarepo-daemon/mediarepo-worker/src/lib.rs b/mediarepo-daemon/mediarepo-worker/src/lib.rs index 10e7483..d85750b 100644 --- a/mediarepo-daemon/mediarepo-worker/src/lib.rs +++ b/mediarepo-daemon/mediarepo-worker/src/lib.rs @@ -1,5 +1,5 @@ use crate::job_dispatcher::JobDispatcher; -use crate::jobs::{CheckIntegrityJob, MigrateCDsJob, VacuumJob}; +use crate::jobs::{CheckIntegrityJob, MigrateCDsJob}; use mediarepo_core::error::RepoError; use mediarepo_core::tokio_graceful_shutdown::Toplevel; use mediarepo_logic::dao::repo::Repo; @@ -19,9 +19,6 @@ pub async fn start(top_level: Toplevel, repo: Repo) -> (Toplevel, JobDispatcher) let dispatcher = JobDispatcher::new(subsystem, repo); tx.send(dispatcher.clone()) .map_err(|_| RepoError::from("failed to send dispatcher"))?; - dispatcher - .dispatch_periodically(VacuumJob::default(), Duration::from_secs(60 * 30)) - .await; dispatcher .dispatch_periodically( CheckIntegrityJob::default(), diff --git a/mediarepo-daemon/src/logging.rs b/mediarepo-daemon/src/logging.rs index 74e97f5..45e0eca 100644 --- a/mediarepo-daemon/src/logging.rs +++ b/mediarepo-daemon/src/logging.rs @@ -1,5 +1,5 @@ use std::fs; -use std::path::PathBuf; +use std::path::Path; use console_subscriber::ConsoleLayer; use opentelemetry::sdk::Resource; @@ -24,7 +24,7 @@ use mediarepo_core::tracing_layer_list::DynLayerList; #[allow(dyn_drop)] pub type DropGuard = Box; -pub fn init_tracing(repo_path: &PathBuf, log_cfg: &LoggingSettings) -> Vec { +pub fn init_tracing(repo_path: &Path, log_cfg: &LoggingSettings) -> Vec { LogTracer::init().expect("failed to subscribe to log entries"); let log_path = repo_path.join("logs"); let mut guards = Vec::new(); @@ -97,11 +97,11 @@ fn add_telemetry_layer(log_cfg: &LoggingSettings, layer_list: &mut DynLayerList< fn add_app_log_layer( log_cfg: &LoggingSettings, - log_path: &PathBuf, + log_path: &Path, guards: &mut Vec, layer_list: &mut DynLayerList, ) { - let (app_log_writer, guard) = get_application_log_writer(&log_path); + let (app_log_writer, guard) = get_application_log_writer(log_path); guards.push(Box::new(guard) as DropGuard); let app_log_layer = fmt::layer() @@ -115,11 +115,11 @@ fn add_app_log_layer( fn add_bromine_layer( log_cfg: &LoggingSettings, - log_path: &PathBuf, + log_path: &Path, guards: &mut Vec, layer_list: &mut DynLayerList, ) { - let (bromine_writer, guard) = get_bromine_log_writer(&log_path); + let (bromine_writer, guard) = get_bromine_log_writer(log_path); guards.push(Box::new(guard) as DropGuard); let bromine_layer = fmt::layer() @@ -133,11 +133,11 @@ fn add_bromine_layer( fn add_sql_layer( log_cfg: &LoggingSettings, - log_path: &PathBuf, + log_path: &Path, guards: &mut Vec, layer_list: &mut DynLayerList, ) { - let (sql_writer, guard) = get_sql_log_writer(&log_path); + let (sql_writer, guard) = get_sql_log_writer(log_path); guards.push(Box::new(guard) as DropGuard); let sql_layer = fmt::layer() @@ -161,18 +161,18 @@ fn add_stdout_layer(guards: &mut Vec, layer_list: &mut DynLayerList() - .unwrap_or( + .unwrap_or_else(|_| { filter::Targets::new() .with_default(Level::INFO) - .with_target("sqlx", Level::WARN), - ), + .with_target("sqlx", Level::WARN) + }), ); layer_list.add(stdout_layer); } -fn get_sql_log_writer(log_path: &PathBuf) -> (NonBlocking, WorkerGuard) { +fn get_sql_log_writer(log_path: &Path) -> (NonBlocking, WorkerGuard) { tracing_appender::non_blocking( rolling_file::BasicRollingFileAppender::new( log_path.join("sql.log"), @@ -183,7 +183,7 @@ fn get_sql_log_writer(log_path: &PathBuf) -> (NonBlocking, WorkerGuard) { ) } -fn get_bromine_log_writer(log_path: &PathBuf) -> (NonBlocking, WorkerGuard) { +fn get_bromine_log_writer(log_path: &Path) -> (NonBlocking, WorkerGuard) { tracing_appender::non_blocking( rolling_file::BasicRollingFileAppender::new( log_path.join("bromine.log"), @@ -194,7 +194,7 @@ fn get_bromine_log_writer(log_path: &PathBuf) -> (NonBlocking, WorkerGuard) { ) } -fn get_application_log_writer(log_path: &PathBuf) -> (NonBlocking, WorkerGuard) { +fn get_application_log_writer(log_path: &Path) -> (NonBlocking, WorkerGuard) { tracing_appender::non_blocking( rolling_file::BasicRollingFileAppender::new( log_path.join("repo.log"), diff --git a/mediarepo-daemon/src/main.rs b/mediarepo-daemon/src/main.rs index e628c99..433f6d5 100644 --- a/mediarepo-daemon/src/main.rs +++ b/mediarepo-daemon/src/main.rs @@ -1,6 +1,6 @@ use std::env; use std::iter::FromIterator; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::sync::Arc; use std::time::Duration; @@ -55,6 +55,7 @@ enum SubCommand { #[tokio::main] async fn main() -> RepoResult<()> { + human_panic::setup_panic!(); let mut opt: Opt = Opt::from_args(); opt.repo = env::current_dir().unwrap().join(opt.repo); @@ -242,7 +243,7 @@ async fn init(opt: Opt, force: bool) -> RepoResult<()> { Ok(()) } -async fn clean_old_connection_files(root: &PathBuf) -> RepoResult<()> { +async fn clean_old_connection_files(root: &Path) -> RepoResult<()> { let paths = ["repo.tcp", "repo.sock"]; for path in paths { diff --git a/mediarepo-daemon/src/utils.rs b/mediarepo-daemon/src/utils.rs index 130947a..80716f2 100644 --- a/mediarepo-daemon/src/utils.rs +++ b/mediarepo-daemon/src/utils.rs @@ -1,14 +1,14 @@ -use std::path::PathBuf; +use std::path::Path; use tokio::fs; use mediarepo_core::error::RepoResult; -use mediarepo_core::settings::{PathSettings, Settings}; use mediarepo_core::settings::v1::SettingsV1; +use mediarepo_core::settings::{PathSettings, Settings}; use mediarepo_logic::dao::repo::Repo; /// Loads the settings from a toml path -pub fn load_settings(root_path: &PathBuf) -> RepoResult { +pub fn load_settings(root_path: &Path) -> RepoResult { let contents = std::fs::read_to_string(root_path.join("repo.toml"))?; if let Ok(settings_v1) = SettingsV1::from_toml_string(&contents) { @@ -21,7 +21,7 @@ pub fn load_settings(root_path: &PathBuf) -> RepoResult { } } -pub async fn get_repo(root_path: &PathBuf, path_settings: &PathSettings) -> RepoResult { +pub async fn get_repo(root_path: &Path, path_settings: &PathSettings) -> RepoResult { Repo::connect( format!( "sqlite://{}", @@ -33,7 +33,7 @@ pub async fn get_repo(root_path: &PathBuf, path_settings: &PathSettings) -> Repo .await } -pub async fn create_paths_for_repo(root: &PathBuf, settings: &PathSettings) -> RepoResult<()> { +pub async fn create_paths_for_repo(root: &Path, settings: &PathSettings) -> RepoResult<()> { if !root.exists() { fs::create_dir_all(&root).await?; } diff --git a/mediarepo-ui/package.json b/mediarepo-ui/package.json index 317fb44..307d22c 100644 --- a/mediarepo-ui/package.json +++ b/mediarepo-ui/package.json @@ -1,6 +1,6 @@ { "name": "mediarepo-ui", - "version": "1.0.3", + "version": "1.0.4", "scripts": { "ng": "ng", "start": "ng serve", diff --git a/mediarepo-ui/src-tauri/Cargo.lock b/mediarepo-ui/src-tauri/Cargo.lock index d56a359..fac9fab 100644 --- a/mediarepo-ui/src-tauri/Cargo.lock +++ b/mediarepo-ui/src-tauri/Cargo.lock @@ -64,7 +64,7 @@ checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" [[package]] name = "app" -version = "1.0.2" +version = "1.0.4" dependencies = [ "mediarepo-api", "serde", @@ -1591,7 +1591,7 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] name = "mediarepo-api" -version = "0.32.0" +version = "0.32.1" dependencies = [ "async-trait", "bromine", diff --git a/mediarepo-ui/src-tauri/Cargo.toml b/mediarepo-ui/src-tauri/Cargo.toml index eba60b6..803b3b2 100644 --- a/mediarepo-ui/src-tauri/Cargo.toml +++ b/mediarepo-ui/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "app" -version = "1.0.3" +version = "1.0.4" description = "The UI for the mediarepo media management tool" authors = ["you"] license = "" diff --git a/mediarepo-ui/src-tauri/tauri.conf.json b/mediarepo-ui/src-tauri/tauri.conf.json index e67e3b1..50ea0ae 100644 --- a/mediarepo-ui/src-tauri/tauri.conf.json +++ b/mediarepo-ui/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "package": { "productName": "mediarepo-ui", - "version": "1.0.3" + "version": "1.0.4" }, "build": { "distDir": "../dist/mediarepo-ui", diff --git a/mediarepo-ui/src/api/Api.ts b/mediarepo-ui/src/api/Api.ts index 27e1d58..7c14413 100644 --- a/mediarepo-ui/src/api/Api.ts +++ b/mediarepo-ui/src/api/Api.ts @@ -19,6 +19,7 @@ import { GetSizeRequest, GetTagsForFilesRequest, InitRepositoryRequest, + IsJobRunningRequest, ReadFileRequest, RemoveRepositoryRequest, ResolvePathsToFilesRequest, @@ -187,6 +188,10 @@ export class MediarepoApi { return this.invokePlugin(ApiFunction.RunJob, request); } + public static async isJobRunning(request: IsJobRunningRequest): Promise { + return this.invokePlugin(ApiFunction.IsJobRunning, request); + } + public static async getAllSortingPresets(): Promise { return ShortCache.cached("sorting-presets", () => this.invokePlugin(ApiFunction.GetAllSortingPresets), 1000); } diff --git a/mediarepo-ui/src/api/api-types/functions.ts b/mediarepo-ui/src/api/api-types/functions.ts index 44f8041..201a8aa 100644 --- a/mediarepo-ui/src/api/api-types/functions.ts +++ b/mediarepo-ui/src/api/api-types/functions.ts @@ -40,6 +40,7 @@ export enum ApiFunction { SetFrontendState = "set_frontend_state", // jobs RunJob = "run_job", + IsJobRunning = "is_job_running", // presets GetAllSortingPresets = "all_sorting_presets", AddSortingPreset = "add_sorting_preset", diff --git a/mediarepo-ui/src/api/api-types/requests.ts b/mediarepo-ui/src/api/api-types/requests.ts index 2a813b6..d87acdf 100644 --- a/mediarepo-ui/src/api/api-types/requests.ts +++ b/mediarepo-ui/src/api/api-types/requests.ts @@ -117,3 +117,7 @@ export type AddSortingPresetRequest = { export type DeleteSortingPresetRequest = { id: number }; + +export type IsJobRunningRequest = { + jobType: JobType, +} diff --git a/mediarepo-ui/src/app/components/core/core.module.ts b/mediarepo-ui/src/app/components/core/core.module.ts index 217d4e5..19658f5 100644 --- a/mediarepo-ui/src/app/components/core/core.module.ts +++ b/mediarepo-ui/src/app/components/core/core.module.ts @@ -17,7 +17,7 @@ import {MatSelectModule} from "@angular/material/select"; import {MatCheckboxModule} from "@angular/material/checkbox"; import {MatDividerModule} from "@angular/material/divider"; import {NgIconsModule} from "@ng-icons/core"; -import {MatPlus} from "@ng-icons/material-icons/baseline"; +import {MatMoreVert, MatPlus} from "@ng-icons/material-icons/baseline"; import {MatMenuModule} from "@angular/material/menu"; import {InputModule} from "../shared/input/input.module"; import {SidebarModule} from "../shared/sidebar/sidebar.module"; @@ -34,7 +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.module"; import {MatToolbarModule} from "@angular/material/toolbar"; import { RepositoryDetailsViewComponent @@ -72,7 +72,7 @@ import {AboutDialogComponent} from "./repositories-tab/repository-overview/about MatProgressBarModule, MatCheckboxModule, ScrollingModule, - NgIconsModule.withIcons({ MatPlus }), + NgIconsModule.withIcons({ MatPlus, MatMoreVert }), FlexModule, MatButtonModule, MatMenuModule, diff --git a/mediarepo-ui/src/app/components/core/repositories-tab/repository-card/repository-card.component.ts b/mediarepo-ui/src/app/components/core/repositories-tab/repository-card/repository-card.component.ts index 9b4f23f..a524781 100644 --- a/mediarepo-ui/src/app/components/core/repositories-tab/repository-card/repository-card.component.ts +++ b/mediarepo-ui/src/app/components/core/repositories-tab/repository-card/repository-card.component.ts @@ -6,7 +6,7 @@ import {ConfirmDialogComponent} from "../../../shared/app-common/confirm-dialog/ import {BusyIndicatorComponent} from "../../../shared/app-common/busy-indicator/busy-indicator.component"; import { EditRepositoryDialogComponent -} from "../../../shared/repository/repository/edit-repository-dialog/edit-repository-dialog.component"; +} from "../../../shared/repository/edit-repository-dialog/edit-repository-dialog.component"; @Component({ selector: "app-repository-card", diff --git a/mediarepo-ui/src/app/components/core/repositories-tab/repository-details-view/repository-details-view.component.html b/mediarepo-ui/src/app/components/core/repositories-tab/repository-details-view/repository-details-view.component.html index 66b350e..847c65c 100644 --- a/mediarepo-ui/src/app/components/core/repositories-tab/repository-details-view/repository-details-view.component.html +++ b/mediarepo-ui/src/app/components/core/repositories-tab/repository-details-view/repository-details-view.component.html @@ -47,13 +47,16 @@ {{this.databaseFileSize | async}} +
+ +
-
- +
+
diff --git a/mediarepo-ui/src/app/components/core/repositories-tab/repository-details-view/repository-details-view.component.scss b/mediarepo-ui/src/app/components/core/repositories-tab/repository-details-view/repository-details-view.component.scss index 573a3be..a57e7ce 100644 --- a/mediarepo-ui/src/app/components/core/repositories-tab/repository-details-view/repository-details-view.component.scss +++ b/mediarepo-ui/src/app/components/core/repositories-tab/repository-details-view/repository-details-view.component.scss @@ -31,14 +31,17 @@ .stats-container { margin-left: auto; margin-right: auto; + display: block; + width: 50%; } -} -.repository-charts { - margin-top: 4em; - margin-right: 2em; - height: 100%; - display: block; + .repository-charts { + margin-top: 4em; + margin-right: 2em; + height: calc(100% - 4em);; + width: 50%; + display: block; + } } .details-content { @@ -54,3 +57,7 @@ max-width: 500px; margin: auto; } + +.repo-maintenance { + padding: 2em; +} diff --git a/mediarepo-ui/src/app/components/core/repositories-tab/repository-overview/repository-overview.component.ts b/mediarepo-ui/src/app/components/core/repositories-tab/repository-overview/repository-overview.component.ts index 966a479..ca75501 100644 --- a/mediarepo-ui/src/app/components/core/repositories-tab/repository-overview/repository-overview.component.ts +++ b/mediarepo-ui/src/app/components/core/repositories-tab/repository-overview/repository-overview.component.ts @@ -7,7 +7,7 @@ import {StateService} from "../../../../services/state/state.service"; import {MatDialog, MatDialogRef} from "@angular/material/dialog"; import { AddRepositoryDialogComponent -} from "../../../shared/repository/repository/add-repository-dialog/add-repository-dialog.component"; +} from "../../../shared/repository/add-repository-dialog/add-repository-dialog.component"; import {BehaviorSubject} from "rxjs"; import {BusyDialogComponent} from "../../../shared/app-common/busy-dialog/busy-dialog.component"; import {DownloadDaemonDialogComponent} from "../download-daemon-dialog/download-daemon-dialog.component"; @@ -116,15 +116,10 @@ export class RepositoryOverviewComponent implements OnInit, AfterViewInit { private async runRepositoryStartupTasks(dialogContext: BusyDialogContext): Promise { dialogContext.message.next("Checking integrity..."); await this.jobService.runJob("CheckIntegrity"); - dialogContext.message.next("Running a vacuum on the database..."); - await this.jobService.runJob("Vacuum"); - dialogContext.message.next( - "Migrating content descriptors to new format..."); - await this.jobService.runJob("MigrateContentDescriptors"); - dialogContext.message.next("Calculating repository sizes..."); - await this.jobService.runJob("CalculateSizes", false); dialogContext.message.next("Generating missing thumbnails..."); await this.jobService.runJob("GenerateThumbnails"); + dialogContext.message.next("Calculating repository sizes..."); + await this.jobService.runJob("CalculateSizes", false); dialogContext.message.next("Finished repository startup"); } diff --git a/mediarepo-ui/src/app/components/shared/repository/repository/add-repository-dialog/add-repository-dialog.component.html b/mediarepo-ui/src/app/components/shared/repository/add-repository-dialog/add-repository-dialog.component.html similarity index 100% rename from mediarepo-ui/src/app/components/shared/repository/repository/add-repository-dialog/add-repository-dialog.component.html rename to mediarepo-ui/src/app/components/shared/repository/add-repository-dialog/add-repository-dialog.component.html diff --git a/mediarepo-ui/src/app/components/shared/repository/repository/add-repository-dialog/add-repository-dialog.component.scss b/mediarepo-ui/src/app/components/shared/repository/add-repository-dialog/add-repository-dialog.component.scss similarity index 100% rename from mediarepo-ui/src/app/components/shared/repository/repository/add-repository-dialog/add-repository-dialog.component.scss rename to mediarepo-ui/src/app/components/shared/repository/add-repository-dialog/add-repository-dialog.component.scss diff --git a/mediarepo-ui/src/app/components/shared/repository/repository/add-repository-dialog/add-repository-dialog.component.spec.ts b/mediarepo-ui/src/app/components/shared/repository/add-repository-dialog/add-repository-dialog.component.spec.ts similarity index 100% rename from mediarepo-ui/src/app/components/shared/repository/repository/add-repository-dialog/add-repository-dialog.component.spec.ts rename to mediarepo-ui/src/app/components/shared/repository/add-repository-dialog/add-repository-dialog.component.spec.ts diff --git a/mediarepo-ui/src/app/components/shared/repository/repository/add-repository-dialog/add-repository-dialog.component.ts b/mediarepo-ui/src/app/components/shared/repository/add-repository-dialog/add-repository-dialog.component.ts similarity index 91% rename from mediarepo-ui/src/app/components/shared/repository/repository/add-repository-dialog/add-repository-dialog.component.ts rename to mediarepo-ui/src/app/components/shared/repository/add-repository-dialog/add-repository-dialog.component.ts index d89cf87..2918db1 100644 --- a/mediarepo-ui/src/app/components/shared/repository/repository/add-repository-dialog/add-repository-dialog.component.ts +++ b/mediarepo-ui/src/app/components/shared/repository/add-repository-dialog/add-repository-dialog.component.ts @@ -1,8 +1,8 @@ import {Component, Inject, ViewChild} from "@angular/core"; import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; import {RepositoryFormComponent} from "../repository-form/repository-form.component"; -import {RepositoryService} from "../../../../../services/repository/repository.service"; -import {LoggingService} from "../../../../../services/logging/logging.service"; +import {RepositoryService} from "../../../../services/repository/repository.service"; +import {LoggingService} from "../../../../services/logging/logging.service"; @Component({ selector: "app-add-repository-dialog", diff --git a/mediarepo-ui/src/app/components/shared/repository/repository/edit-repository-dialog/edit-repository-dialog.component.html b/mediarepo-ui/src/app/components/shared/repository/edit-repository-dialog/edit-repository-dialog.component.html similarity index 100% rename from mediarepo-ui/src/app/components/shared/repository/repository/edit-repository-dialog/edit-repository-dialog.component.html rename to mediarepo-ui/src/app/components/shared/repository/edit-repository-dialog/edit-repository-dialog.component.html diff --git a/mediarepo-ui/src/app/components/shared/repository/repository/edit-repository-dialog/edit-repository-dialog.component.scss b/mediarepo-ui/src/app/components/shared/repository/edit-repository-dialog/edit-repository-dialog.component.scss similarity index 100% rename from mediarepo-ui/src/app/components/shared/repository/repository/edit-repository-dialog/edit-repository-dialog.component.scss rename to mediarepo-ui/src/app/components/shared/repository/edit-repository-dialog/edit-repository-dialog.component.scss diff --git a/mediarepo-ui/src/app/components/shared/repository/repository/edit-repository-dialog/edit-repository-dialog.component.spec.ts b/mediarepo-ui/src/app/components/shared/repository/edit-repository-dialog/edit-repository-dialog.component.spec.ts similarity index 100% rename from mediarepo-ui/src/app/components/shared/repository/repository/edit-repository-dialog/edit-repository-dialog.component.spec.ts rename to mediarepo-ui/src/app/components/shared/repository/edit-repository-dialog/edit-repository-dialog.component.spec.ts diff --git a/mediarepo-ui/src/app/components/shared/repository/repository/edit-repository-dialog/edit-repository-dialog.component.ts b/mediarepo-ui/src/app/components/shared/repository/edit-repository-dialog/edit-repository-dialog.component.ts similarity index 91% rename from mediarepo-ui/src/app/components/shared/repository/repository/edit-repository-dialog/edit-repository-dialog.component.ts rename to mediarepo-ui/src/app/components/shared/repository/edit-repository-dialog/edit-repository-dialog.component.ts index a53b338..33a8e9c 100644 --- a/mediarepo-ui/src/app/components/shared/repository/repository/edit-repository-dialog/edit-repository-dialog.component.ts +++ b/mediarepo-ui/src/app/components/shared/repository/edit-repository-dialog/edit-repository-dialog.component.ts @@ -1,9 +1,9 @@ import {Component, Inject, ViewChild} from "@angular/core"; import {RepositoryFormComponent} from "../repository-form/repository-form.component"; -import {RepositoryService} from "../../../../../services/repository/repository.service"; -import {LoggingService} from "../../../../../services/logging/logging.service"; +import {RepositoryService} from "../../../../services/repository/repository.service"; +import {LoggingService} from "../../../../services/logging/logging.service"; import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; -import {Repository} from "../../../../../../api/models/Repository"; +import {Repository} from "../../../../../api/models/Repository"; @Component({ selector: "app-edit-repository-dialog", diff --git a/mediarepo-ui/src/app/components/shared/repository/repository/repository-form/repository-form.component.html b/mediarepo-ui/src/app/components/shared/repository/repository-form/repository-form.component.html similarity index 100% rename from mediarepo-ui/src/app/components/shared/repository/repository/repository-form/repository-form.component.html rename to mediarepo-ui/src/app/components/shared/repository/repository-form/repository-form.component.html diff --git a/mediarepo-ui/src/app/components/shared/repository/repository/repository-form/repository-form.component.scss b/mediarepo-ui/src/app/components/shared/repository/repository-form/repository-form.component.scss similarity index 100% rename from mediarepo-ui/src/app/components/shared/repository/repository/repository-form/repository-form.component.scss rename to mediarepo-ui/src/app/components/shared/repository/repository-form/repository-form.component.scss diff --git a/mediarepo-ui/src/app/components/shared/repository/repository/repository-form/repository-form.component.spec.ts b/mediarepo-ui/src/app/components/shared/repository/repository-form/repository-form.component.spec.ts similarity index 100% rename from mediarepo-ui/src/app/components/shared/repository/repository/repository-form/repository-form.component.spec.ts rename to mediarepo-ui/src/app/components/shared/repository/repository-form/repository-form.component.spec.ts diff --git a/mediarepo-ui/src/app/components/shared/repository/repository/repository-form/repository-form.component.ts b/mediarepo-ui/src/app/components/shared/repository/repository-form/repository-form.component.ts similarity index 96% rename from mediarepo-ui/src/app/components/shared/repository/repository/repository-form/repository-form.component.ts rename to mediarepo-ui/src/app/components/shared/repository/repository-form/repository-form.component.ts index 863f304..ae57e95 100644 --- a/mediarepo-ui/src/app/components/shared/repository/repository/repository-form/repository-form.component.ts +++ b/mediarepo-ui/src/app/components/shared/repository/repository-form/repository-form.component.ts @@ -1,7 +1,7 @@ import {Component, Input, OnInit, Output} from "@angular/core"; import {AbstractControl, FormControl, FormGroup, ValidationErrors, Validators} from "@angular/forms"; -import {Repository} from "../../../../../../api/models/Repository"; -import {RepositoryService} from "../../../../../services/repository/repository.service"; +import {Repository} from "../../../../../api/models/Repository"; +import {RepositoryService} from "../../../../services/repository/repository.service"; import {dialog} from "@tauri-apps/api"; import {MatDialog} from "@angular/material/dialog"; diff --git a/mediarepo-ui/src/app/components/shared/repository/repository-maintenance/repository-maintenance.component.html b/mediarepo-ui/src/app/components/shared/repository/repository-maintenance/repository-maintenance.component.html new file mode 100644 index 0000000..b316398 --- /dev/null +++ b/mediarepo-ui/src/app/components/shared/repository/repository-maintenance/repository-maintenance.component.html @@ -0,0 +1,29 @@ + + + +

Maintenance

+
+ +
+ + + + +
+
+
diff --git a/mediarepo-ui/src/app/components/shared/repository/repository-maintenance/repository-maintenance.component.scss b/mediarepo-ui/src/app/components/shared/repository/repository-maintenance/repository-maintenance.component.scss new file mode 100644 index 0000000..70dc9dc --- /dev/null +++ b/mediarepo-ui/src/app/components/shared/repository/repository-maintenance/repository-maintenance.component.scss @@ -0,0 +1,18 @@ +mat-card-header { + display: block; +} + +mat-card-title h1 { + margin: auto; + text-align: center; +} + +.button-list { + display: block; + width: 100%; + + button { + display: block; + margin: 2em auto; + } +} diff --git a/mediarepo-ui/src/app/components/shared/repository/repository-maintenance/repository-maintenance.component.spec.ts b/mediarepo-ui/src/app/components/shared/repository/repository-maintenance/repository-maintenance.component.spec.ts new file mode 100644 index 0000000..f67bd5c --- /dev/null +++ b/mediarepo-ui/src/app/components/shared/repository/repository-maintenance/repository-maintenance.component.spec.ts @@ -0,0 +1,25 @@ +import {ComponentFixture, TestBed} from "@angular/core/testing"; + +import {RepositoryMaintenanceComponent} from "./repository-maintenance.component"; + +describe("RepositoryMaintenanceComponent", () => { + let component: RepositoryMaintenanceComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [RepositoryMaintenanceComponent] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(RepositoryMaintenanceComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/mediarepo-ui/src/app/components/shared/repository/repository-maintenance/repository-maintenance.component.ts b/mediarepo-ui/src/app/components/shared/repository/repository-maintenance/repository-maintenance.component.ts new file mode 100644 index 0000000..177655e --- /dev/null +++ b/mediarepo-ui/src/app/components/shared/repository/repository-maintenance/repository-maintenance.component.ts @@ -0,0 +1,85 @@ +import {ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit} from "@angular/core"; +import {JobService} from "../../../../services/job/job.service"; +import {JobType} from "../../../../../api/api-types/job"; +import {MatDialog} from "@angular/material/dialog"; +import {BusyDialogComponent, BusyDialogData} from "../../app-common/busy-dialog/busy-dialog.component"; +import {LoggingService} from "../../../../services/logging/logging.service"; +import {BehaviorSubject} from "rxjs"; + + +@Component({ + selector: "app-repository-maintenance", + templateUrl: "./repository-maintenance.component.html", + styleUrls: ["./repository-maintenance.component.scss"], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class RepositoryMaintenanceComponent implements OnInit, OnDestroy { + public jobState: { [Property in JobType]?: boolean } = { + CalculateSizes: false, + GenerateThumbnails: false, + }; + private jobStatusInterval: any; + + constructor( + private changeDetector: ChangeDetectorRef, + private jobService: JobService, + private dialog: MatDialog, + private logger: LoggingService + ) { + } + + public ngOnDestroy(): void { + clearInterval(this.jobStatusInterval); + } + + public async ngOnInit() { + await this.updateJobStatus(); + this.jobStatusInterval = setInterval(() => this.updateJobStatus(), 10000); + } + + public async runJob(jobType: JobType, runAsync: boolean) { + if (runAsync) { + this.jobState[jobType] = true; + this.jobService.runJob(jobType).then(() => this.delay(1000)).catch(this.logger.error).finally(() => { + this.jobState[jobType] = false; + this.changeDetector.markForCheck(); + }); + this.changeDetector.markForCheck(); + } else { + const dialog = this.dialog.open(BusyDialogComponent, { + disableClose: true, + minWidth: "30%", + minHeight: "30%", + data: { + title: "Synchronous Job", + message: new BehaviorSubject(`Running Job ${jobType}`), + allowCancel: false, + } + }); + try { + this.changeDetector.markForCheck(); + await this.jobService.runJob(jobType); + } catch (err: any) { + this.logger.error(err); + } finally { + dialog.close(); + this.changeDetector.markForCheck(); + } + } + } + + private async delay(ms: number) { + return new Promise((res, _) => setTimeout( + res, + ms + )); + } + + private async updateJobStatus() { + const indexedTypes: JobType[] = ["CalculateSizes", "GenerateThumbnails"]; + for (const jobType of indexedTypes) { + this.jobState[jobType] = await this.jobService.isJobRunning(jobType); + } + this.changeDetector.markForCheck(); + } +} diff --git a/mediarepo-ui/src/app/components/shared/repository/repository/repository.module.ts b/mediarepo-ui/src/app/components/shared/repository/repository.module.ts similarity index 74% rename from mediarepo-ui/src/app/components/shared/repository/repository/repository.module.ts rename to mediarepo-ui/src/app/components/shared/repository/repository.module.ts index 4038696..3922140 100644 --- a/mediarepo-ui/src/app/components/shared/repository/repository/repository.module.ts +++ b/mediarepo-ui/src/app/components/shared/repository/repository.module.ts @@ -11,17 +11,22 @@ import {MatInputModule} from "@angular/material/input"; import {ReactiveFormsModule} from "@angular/forms"; import {NgIconsModule} from "@ng-icons/core"; import {MatFolder} from "@ng-icons/material-icons/baseline"; +import {RepositoryMaintenanceComponent} from "./repository-maintenance/repository-maintenance.component"; +import {MatCardModule} from "@angular/material/card"; +import {MatProgressBarModule} from "@angular/material/progress-bar"; @NgModule({ declarations: [ AddRepositoryDialogComponent, EditRepositoryDialogComponent, - RepositoryFormComponent + RepositoryFormComponent, + RepositoryMaintenanceComponent ], exports: [ AddRepositoryDialogComponent, EditRepositoryDialogComponent, + RepositoryMaintenanceComponent, ], imports: [ CommonModule, @@ -31,7 +36,9 @@ import {MatFolder} from "@ng-icons/material-icons/baseline"; MatSelectModule, MatInputModule, ReactiveFormsModule, - NgIconsModule.withIcons({ MatFolder }) + NgIconsModule.withIcons({ MatFolder }), + MatCardModule, + MatProgressBarModule ] }) export class RepositoryModule { diff --git a/mediarepo-ui/src/app/services/job/job.service.ts b/mediarepo-ui/src/app/services/job/job.service.ts index 44057c3..d90c6e9 100644 --- a/mediarepo-ui/src/app/services/job/job.service.ts +++ b/mediarepo-ui/src/app/services/job/job.service.ts @@ -13,4 +13,8 @@ export class JobService { public async runJob(jobType: JobType, sync: boolean = true): Promise { return MediarepoApi.runJob({ jobType, sync }); } + + public async isJobRunning(jobType: JobType): Promise { + return MediarepoApi.isJobRunning({ jobType }); + } }