diff --git a/mediarepo-daemon/Cargo.lock b/mediarepo-daemon/Cargo.lock index f18bf7e..ce0fcff 100644 --- a/mediarepo-daemon/Cargo.lock +++ b/mediarepo-daemon/Cargo.lock @@ -1395,7 +1395,7 @@ dependencies = [ [[package]] name = "mediarepo-daemon" -version = "1.0.0-rc.4" +version = "1.0.0" dependencies = [ "console-subscriber", "glob", diff --git a/mediarepo-daemon/Cargo.toml b/mediarepo-daemon/Cargo.toml index 7700298..97df94e 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.0-rc.4" +version = "1.0.0" edition = "2018" license = "gpl-3" repository = "https://github.com/Trivernis/mediarepo-daemon" diff --git a/mediarepo-daemon/mediarepo-logic/src/dao/tag/mappings.rs b/mediarepo-daemon/mediarepo-logic/src/dao/tag/mappings.rs index ac9f26c..2289c1f 100644 --- a/mediarepo-daemon/mediarepo-logic/src/dao/tag/mappings.rs +++ b/mediarepo-daemon/mediarepo-logic/src/dao/tag/mappings.rs @@ -1,9 +1,10 @@ use sea_orm::prelude::*; +use sea_orm::sea_query::Query; use sea_orm::ActiveValue::Set; use sea_orm::{DatabaseTransaction, TransactionTrait}; use mediarepo_core::error::RepoResult; -use mediarepo_database::entities::content_descriptor_tag; +use mediarepo_database::entities::{content_descriptor_tag, namespace, tag}; use crate::dao::tag::TagDao; @@ -41,11 +42,15 @@ impl TagDao { #[tracing::instrument(level = "debug", skip(self))] pub async fn remove_mappings(&self, cd_ids: Vec, tag_ids: Vec) -> RepoResult<()> { + let trx = self.ctx.db.begin().await?; content_descriptor_tag::Entity::delete_many() .filter(content_descriptor_tag::Column::CdId.is_in(cd_ids)) .filter(content_descriptor_tag::Column::TagId.is_in(tag_ids)) - .exec(&self.ctx.db) + .exec(&trx) .await?; + delete_orphans(&trx).await?; + + trx.commit().await?; Ok(()) } @@ -66,3 +71,34 @@ async fn get_existing_mappings( .collect(); Ok(existing_mappings) } + +/// Deletes orphaned tag entries and namespaces from the database +async fn delete_orphans(trx: &DatabaseTransaction) -> RepoResult<()> { + tag::Entity::delete_many() + .filter( + tag::Column::Id.not_in_subquery( + Query::select() + .column(content_descriptor_tag::Column::TagId) + .from(content_descriptor_tag::Entity) + .group_by_col(content_descriptor_tag::Column::TagId) + .to_owned(), + ), + ) + .exec(trx) + .await?; + + namespace::Entity::delete_many() + .filter( + namespace::Column::Id.not_in_subquery( + Query::select() + .column(tag::Column::NamespaceId) + .from(tag::Entity) + .group_by_col(tag::Column::NamespaceId) + .to_owned(), + ), + ) + .exec(trx) + .await?; + + Ok(()) +} diff --git a/mediarepo-ui/package.json b/mediarepo-ui/package.json index c32a8c9..21c8392 100644 --- a/mediarepo-ui/package.json +++ b/mediarepo-ui/package.json @@ -1,6 +1,6 @@ { "name": "mediarepo-ui", - "version": "1.0.0-rc.4", + "version": "1.0.0", "scripts": { "ng": "ng", "start": "ng serve", diff --git a/mediarepo-ui/src-tauri/Cargo.lock b/mediarepo-ui/src-tauri/Cargo.lock index 25b9158..37eaf8b 100644 --- a/mediarepo-ui/src-tauri/Cargo.lock +++ b/mediarepo-ui/src-tauri/Cargo.lock @@ -40,7 +40,7 @@ checksum = "94a45b455c14666b85fc40a019e8ab9eb75e3a124e05494f5397122bc9eb06e0" [[package]] name = "app" -version = "1.0.0-rc.4" +version = "1.0.0" dependencies = [ "mediarepo-api", "serde", diff --git a/mediarepo-ui/src-tauri/Cargo.toml b/mediarepo-ui/src-tauri/Cargo.toml index d5827af..471b3ed 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.0-rc.4" +version = "1.0.0" description = "The UI for the mediarepo media management tool" authors = ["you"] license = "" diff --git a/mediarepo-ui/src/api/api-types/files.ts b/mediarepo-ui/src/api/api-types/files.ts index f40d1bb..de453ec 100644 --- a/mediarepo-ui/src/api/api-types/files.ts +++ b/mediarepo-ui/src/api/api-types/files.ts @@ -28,9 +28,9 @@ export type PropertyQuery = PropertyQueryStatus export type PropertyQueryStatus = { Status: FileStatus }; export type PropertyQueryFileSize = { FileSize: ValueComparator }; -export type PropertyQueryImportedTime = { ImportedTime: ValueComparator }; -export type PropertyQueryChangedTime = { ChangedTime: ValueComparator }; -export type PropertyQueryCreatedTime = { CreatedTime: ValueComparator }; +export type PropertyQueryImportedTime = { ImportedTime: ValueComparator }; +export type PropertyQueryChangedTime = { ChangedTime: ValueComparator }; +export type PropertyQueryCreatedTime = { CreatedTime: ValueComparator }; export type PropertyQueryTagCount = { TagCount: ValueComparator }; export type PropertyQueryCd = { Cd: string }; export type PropertyQueryId = { Id: number }; diff --git a/mediarepo-ui/src/api/models/File.ts b/mediarepo-ui/src/api/models/File.ts index 67f3bd8..d73cfed 100644 --- a/mediarepo-ui/src/api/models/File.ts +++ b/mediarepo-ui/src/api/models/File.ts @@ -1,9 +1,14 @@ import {FileBasicData, FileStatus} from "../api-types/files"; +import {BehaviorSubject, Observable} from "rxjs"; export class File { + + private statusSubject: BehaviorSubject; + constructor( private basicData: FileBasicData, ) { + this.statusSubject = new BehaviorSubject(basicData.status); } public get rawData(): FileBasicData { @@ -18,15 +23,20 @@ export class File { return this.basicData.cd; } - public get status(): FileStatus { - return this.basicData.status; + public get status(): Observable { + return this.statusSubject.asObservable(); + } + + public get mimeType(): string { + return this.basicData.mime_type; } - public set status(value: FileStatus) { + public setStatus(value: FileStatus) { this.basicData.status = value; + this.statusSubject.next(value); } - public get mimeType(): string { - return this.basicData.mime_type; + public getStatus(): FileStatus { + return this.basicData.status; } } diff --git a/mediarepo-ui/src/api/models/FilterQueryBuilder.ts b/mediarepo-ui/src/api/models/FilterQueryBuilder.ts index 95e7827..f9f45dc 100644 --- a/mediarepo-ui/src/api/models/FilterQueryBuilder.ts +++ b/mediarepo-ui/src/api/models/FilterQueryBuilder.ts @@ -29,21 +29,21 @@ export class FilterQueryBuilder { public static importedTime(date: Date, comparator: Comparator, max_date: Date): FilterQuery { return filterQuery({ - ImportedTime: valuesToCompareEnum(date, comparator, - max_date + ImportedTime: valuesToCompareEnum(formatDate(date)!!, comparator, + formatDate(max_date) ) }); } public static changedTime(date: Date, comparator: Comparator, max_date: Date): FilterQuery { return filterQuery({ - ChangedTime: valuesToCompareEnum(date, comparator, max_date) + ChangedTime: valuesToCompareEnum(formatDate(date)!!, comparator, formatDate(max_date)) }); } public static createdTime(date: Date, comparator: Comparator, max_date: Date): FilterQuery { return filterQuery({ - CreatedTime: valuesToCompareEnum(date, comparator, max_date) + CreatedTime: valuesToCompareEnum(formatDate(date)!!, comparator, formatDate(max_date)) }); } @@ -150,6 +150,7 @@ export class FilterQueryBuilder { } break; case "ImportedTime": + console.debug(propertyName, rawComparator, compareValue); value = this.parsePropertyValue(compareValue, parseDate); if (value != undefined) { return this.importedTime(value[0], comparator, value[1]); @@ -263,7 +264,7 @@ function filterQuery(propertyQuery: PropertyQuery): FilterQuery { return { Property: propertyQuery }; } -function valuesToCompareEnum(min_value: T, comparator: Comparator, max_value?: T): ValueComparator { +function valuesToCompareEnum(min_value: T, comparator: Comparator, max_value: T | undefined): ValueComparator { switch (comparator) { case "Less": return { Less: min_value }; @@ -299,9 +300,9 @@ function parseByteSize(value: string): number | undefined { if (number) { for (const key of Object.keys(valueMappings)) { if (checkUnit(key)) { - console.log("key", key, "valueMapping", valueMappings[key]); + console.debug("key", key, "valueMapping", valueMappings[key]); number *= valueMappings[key]; - console.log("number", number); + console.debug("number", number); break; } } @@ -311,7 +312,7 @@ function parseByteSize(value: string): number | undefined { } function parseDate(value: string): Date | undefined { - const date = Date.parse(value); + const date = Date.parse(value.toUpperCase()); if (isNaN(date)) { return undefined; @@ -331,3 +332,13 @@ function parseStatus(value: string): FileStatus | undefined { return undefined; } } + +function formatDate(date?: Date): string | undefined { + if (date) { + const pad = (s: number) => s.toString().padStart(2, "0"); + return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad( + date.getMinutes())}:${pad( + date.getSeconds())}`; + } + return; +} diff --git a/mediarepo-ui/src/app/components/shared/app-base/file-action-base/file-action-base.component.ts b/mediarepo-ui/src/app/components/shared/app-base/file-action-base/file-action-base.component.ts index e912a41..a43053a 100644 --- a/mediarepo-ui/src/app/components/shared/app-base/file-action-base/file-action-base.component.ts +++ b/mediarepo-ui/src/app/components/shared/app-base/file-action-base/file-action-base.component.ts @@ -56,7 +56,7 @@ export class FileActionBaseComponent { if (changeConfirmed) { await this.errorBroker.try(async () => { const newFile = await this.fileService.updateFileStatus(files[0].id, status); - files[0].status = newFile.status; + files[0].setStatus(newFile.getStatus()); }); } } else { @@ -72,7 +72,7 @@ export class FileActionBaseComponent { files, (file) => this.errorBroker.try(async () => { const newFile = await this.fileService.updateFileStatus(file.id, status); - file.status = newFile.status; + file.setStatus(newFile.getStatus()); }) ); } diff --git a/mediarepo-ui/src/app/components/shared/file/file-card/file-card.component.html b/mediarepo-ui/src/app/components/shared/file/file-card/file-card.component.html index 1856588..fc6cf37 100644 --- a/mediarepo-ui/src/app/components/shared/file/file-card/file-card.component.html +++ b/mediarepo-ui/src/app/components/shared/file/file-card/file-card.component.html @@ -5,7 +5,6 @@ diff --git a/mediarepo-ui/src/app/components/shared/file/file-card/file-card.component.ts b/mediarepo-ui/src/app/components/shared/file/file-card/file-card.component.ts index 1936c67..7819289 100644 --- a/mediarepo-ui/src/app/components/shared/file/file-card/file-card.component.ts +++ b/mediarepo-ui/src/app/components/shared/file/file-card/file-card.component.ts @@ -13,7 +13,6 @@ import { import {File} from "../../../../../api/models/File"; import {Selectable} from "../../../../models/Selectable"; import {SchedulingService} from "../../../../services/scheduling/scheduling.service"; -import {BehaviorSubject} from "rxjs"; const LOADING_WORK_KEY = "FILE_THUMBNAIL_LOADING"; @@ -26,7 +25,6 @@ const LOADING_WORK_KEY = "FILE_THUMBNAIL_LOADING"; export class FileCardComponent implements OnInit, OnChanges, OnDestroy { @Input() public entry!: Selectable; - @Input() public fileChanged: BehaviorSubject = new BehaviorSubject(undefined); @Output() clickEvent = new EventEmitter(); @Output() dblClickEvent = new EventEmitter(); @@ -47,9 +45,6 @@ export class FileCardComponent implements OnInit, OnChanges, OnDestroy { this.cachedId = this.entry.data.id; this.loading = true; } - if (changes["fileChanged"]) { - this.fileChanged.subscribe(() => this.changeDetector.markForCheck()); - } } public ngOnDestroy(): void { @@ -62,19 +57,4 @@ export class FileCardComponent implements OnInit, OnChanges, OnDestroy { console.debug(this.entry.data.id); this.clickEvent.emit(this); } - - private setImageDelayed() { - if (this.workId) { - this.schedulingService.cancelWork(LOADING_WORK_KEY, this.workId); - } - this.loading = true; - this.workId = this.schedulingService.addWork( - LOADING_WORK_KEY, - async () => { - await this.schedulingService.delay(0); - this.loading = false; - this.changeDetector.markForCheck(); - } - ); - } } diff --git a/mediarepo-ui/src/app/components/shared/file/file-context-menu/file-context-menu.component.ts b/mediarepo-ui/src/app/components/shared/file/file-context-menu/file-context-menu.component.ts index 90a2151..1b4a3e7 100644 --- a/mediarepo-ui/src/app/components/shared/file/file-context-menu/file-context-menu.component.ts +++ b/mediarepo-ui/src/app/components/shared/file/file-context-menu/file-context-menu.component.ts @@ -60,11 +60,11 @@ export class FileContextMenuComponent extends FileActionBaseComponent implements this.actionDelete = this.actionArchive = this.actionImported = this.actionRestore = false; for (const file of this.files) { - this.actionDeletePermantently &&= file.status === "Deleted"; - this.actionDelete ||= file.status !== "Deleted"; - this.actionArchive ||= file.status !== "Archived" && file.status !== "Deleted"; - this.actionImported ||= file.status !== "Imported" && file.status !== "Deleted"; - this.actionRestore ||= file.status === "Deleted"; + this.actionDeletePermantently &&= file.getStatus() === "Deleted"; + this.actionDelete ||= file.getStatus() !== "Deleted"; + this.actionArchive ||= file.getStatus() !== "Archived" && file.getStatus() !== "Deleted"; + this.actionImported ||= file.getStatus() !== "Imported" && file.getStatus() !== "Deleted"; + this.actionRestore ||= file.getStatus() === "Deleted"; } } } diff --git a/mediarepo-ui/src/app/components/shared/file/file-multiview/file-gallery/file-gallery.component.html b/mediarepo-ui/src/app/components/shared/file/file-multiview/file-gallery/file-gallery.component.html index 281b9bc..ed3abbc 100644 --- a/mediarepo-ui/src/app/components/shared/file/file-multiview/file-gallery/file-gallery.component.html +++ b/mediarepo-ui/src/app/components/shared/file/file-multiview/file-gallery/file-gallery.component.html @@ -24,11 +24,10 @@
+ [entry]="entry">
+ (fileDeleted)="this.fileDeleted.emit($event)"> diff --git a/mediarepo-ui/src/app/components/shared/file/file-multiview/file-gallery/file-gallery.component.ts b/mediarepo-ui/src/app/components/shared/file/file-multiview/file-gallery/file-gallery.component.ts index 963006d..4e93fe8 100644 --- a/mediarepo-ui/src/app/components/shared/file/file-multiview/file-gallery/file-gallery.component.ts +++ b/mediarepo-ui/src/app/components/shared/file/file-multiview/file-gallery/file-gallery.component.ts @@ -19,7 +19,6 @@ import {SafeResourceUrl} from "@angular/platform-browser"; import {Selectable} from "../../../../../models/Selectable"; import {TabService} from "../../../../../services/tab/tab.service"; import {Key} from "w3c-keys"; -import {BehaviorSubject} from "rxjs"; @Component({ selector: "app-file-gallery", @@ -43,7 +42,6 @@ export class FileGalleryComponent implements OnChanges, OnInit, AfterViewInit, A public entries: Selectable[] = []; public selectedFile: Selectable | undefined; public fileContentUrl: SafeResourceUrl | undefined; - public fileChanged = new BehaviorSubject(undefined); public selectedIndex = 0; public imageViewHeightPercent = 80; @@ -182,10 +180,6 @@ export class FileGalleryComponent implements OnChanges, OnInit, AfterViewInit, A return item?.data.id; } - public onFileStatusChange(): void { - this.fileChanged.next(); - } - public togglePreviewStrip(): void { if (this.previewStripVisible) { this.imageViewHeightPercent = 100; diff --git a/mediarepo-ui/src/app/components/shared/file/file-multiview/file-grid/file-grid.component.html b/mediarepo-ui/src/app/components/shared/file/file-multiview/file-grid/file-grid.component.html index 19a4afd..94afd4d 100644 --- a/mediarepo-ui/src/app/components/shared/file/file-multiview/file-grid/file-grid.component.html +++ b/mediarepo-ui/src/app/components/shared/file/file-multiview/file-grid/file-grid.component.html @@ -14,7 +14,7 @@ (contextmenu)="this.selectEntryWhenNotSelected(gridEntry); fileContextMenu.onContextMenu($event, this.getSelectedFiles())" (dblClickEvent)="fileOpen.emit($event.entry.data)" *ngIf="gridEntry" - [entry]="gridEntry" [fileChanged]="this.fileChanged"> + [entry]="gridEntry">
@@ -23,8 +23,7 @@ + (fileDeleted)="this.fileDeleted.emit($event)">