diff --git a/graphql/documents/mutations/metadata.graphql b/graphql/documents/mutations/metadata.graphql index e7346bca5..84902a87a 100644 --- a/graphql/documents/mutations/metadata.graphql +++ b/graphql/documents/mutations/metadata.graphql @@ -26,8 +26,8 @@ mutation MetadataAutoTag($input: AutoTagMetadataInput!) { metadataAutoTag(input: $input) } -mutation MetadataClean { - metadataClean +mutation MetadataClean($input: CleanMetadataInput!) { + metadataClean(input: $input) } mutation MigrateHashNaming { diff --git a/graphql/schema/schema.graphql b/graphql/schema/schema.graphql index 5fc6dd48b..b320a10f8 100644 --- a/graphql/schema/schema.graphql +++ b/graphql/schema/schema.graphql @@ -213,7 +213,7 @@ type Mutation { """Start auto-tagging. Returns the job ID""" metadataAutoTag(input: AutoTagMetadataInput!): String! """Clean metadata. Returns the job ID""" - metadataClean: String! + metadataClean(input: CleanMetadataInput!): String! """Migrate generated files for the current hash naming""" migrateHashNaming: String! diff --git a/graphql/schema/types/metadata.graphql b/graphql/schema/types/metadata.graphql index cd88bfad8..89156d49b 100644 --- a/graphql/schema/types/metadata.graphql +++ b/graphql/schema/types/metadata.graphql @@ -44,6 +44,11 @@ input ScanMetadataInput { scanGenerateSprites: Boolean! } +input CleanMetadataInput { + """Do a dry run. Don't delete any files""" + dryRun: Boolean! +} + input AutoTagMetadataInput { """IDs of performers to tag files with, or "*" for all""" performers: [String!] @@ -95,4 +100,4 @@ input ImportObjectsInput { input BackupDatabaseInput { download: Boolean -} \ No newline at end of file +} diff --git a/pkg/api/resolver_mutation_metadata.go b/pkg/api/resolver_mutation_metadata.go index c0b9d2057..7f0649cd9 100644 --- a/pkg/api/resolver_mutation_metadata.go +++ b/pkg/api/resolver_mutation_metadata.go @@ -70,8 +70,8 @@ func (r *mutationResolver) MetadataAutoTag(ctx context.Context, input models.Aut return "todo", nil } -func (r *mutationResolver) MetadataClean(ctx context.Context) (string, error) { - manager.GetInstance().Clean() +func (r *mutationResolver) MetadataClean(ctx context.Context, input models.CleanMetadataInput) (string, error) { + manager.GetInstance().Clean(input) return "todo", nil } diff --git a/pkg/manager/manager_tasks.go b/pkg/manager/manager_tasks.go index f5332d1b7..03fe4c499 100644 --- a/pkg/manager/manager_tasks.go +++ b/pkg/manager/manager_tasks.go @@ -783,7 +783,7 @@ func (s *singleton) autoTagTags(tagIds []string) { } } -func (s *singleton) Clean() { +func (s *singleton) Clean(input models.CleanMetadataInput) { if s.Status.Status != Idle { return } @@ -803,8 +803,13 @@ func (s *singleton) Clean() { gqb := r.Gallery() logger.Infof("Starting cleaning of tracked files") + if input.DryRun { + logger.Infof("Running in Dry Mode") + } var err error + scenes, err = qb.All() + if err != nil { return errors.New("failed to fetch list of scenes for cleaning") } @@ -853,7 +858,7 @@ func (s *singleton) Clean() { Scene: scene, fileNamingAlgorithm: fileNamingAlgo, } - go task.Start(&wg) + go task.Start(&wg, input.DryRun) wg.Wait() } @@ -875,7 +880,7 @@ func (s *singleton) Clean() { TxnManager: s.TxnManager, Image: img, } - go task.Start(&wg) + go task.Start(&wg, input.DryRun) wg.Wait() } @@ -897,7 +902,7 @@ func (s *singleton) Clean() { TxnManager: s.TxnManager, Gallery: gallery, } - go task.Start(&wg) + go task.Start(&wg, input.DryRun) wg.Wait() } diff --git a/pkg/manager/task_clean.go b/pkg/manager/task_clean.go index 672745e6e..d4b4ada56 100644 --- a/pkg/manager/task_clean.go +++ b/pkg/manager/task_clean.go @@ -21,18 +21,18 @@ type CleanTask struct { fileNamingAlgorithm models.HashAlgorithm } -func (t *CleanTask) Start(wg *sync.WaitGroup) { +func (t *CleanTask) Start(wg *sync.WaitGroup, dryRun bool) { defer wg.Done() - if t.Scene != nil && t.shouldCleanScene(t.Scene) { + if t.Scene != nil && t.shouldCleanScene(t.Scene) && !dryRun { t.deleteScene(t.Scene.ID) } - if t.Gallery != nil && t.shouldCleanGallery(t.Gallery) { + if t.Gallery != nil && t.shouldCleanGallery(t.Gallery) && !dryRun { t.deleteGallery(t.Gallery.ID) } - if t.Image != nil && t.shouldCleanImage(t.Image) { + if t.Image != nil && t.shouldCleanImage(t.Image) && !dryRun { t.deleteImage(t.Image.ID) } } diff --git a/ui/v2.5/src/components/Changelog/versions/v050.md b/ui/v2.5/src/components/Changelog/versions/v050.md index 96116bc2f..0efc790f2 100644 --- a/ui/v2.5/src/components/Changelog/versions/v050.md +++ b/ui/v2.5/src/components/Changelog/versions/v050.md @@ -7,6 +7,7 @@ * Allow configuration of visible navbar items. ### 🎨 Improvements +* Add dry-run option for Clean task. * Refresh UI when changing custom CSS options. * Add batch deleting of performers, tags, studios, and movies. * Reset cache after scan/clean to ensure scenes are updated. diff --git a/ui/v2.5/src/components/Settings/SettingsTasksPanel/SettingsTasksPanel.tsx b/ui/v2.5/src/components/Settings/SettingsTasksPanel/SettingsTasksPanel.tsx index 435c274c2..ef2d2cf3e 100644 --- a/ui/v2.5/src/components/Settings/SettingsTasksPanel/SettingsTasksPanel.tsx +++ b/ui/v2.5/src/components/Settings/SettingsTasksPanel/SettingsTasksPanel.tsx @@ -41,6 +41,7 @@ export const SettingsTasksPanel: React.FC = () => { const [scanGenerateSprites, setScanGenerateSprites] = useState( false ); + const [cleanDryRun, setCleanDryRun] = useState(false); const [ scanGenerateImagePreviews, setScanGenerateImagePreviews, @@ -132,12 +133,31 @@ export const SettingsTasksPanel: React.FC = () => { function onClean() { setIsCleanAlertOpen(false); - mutateMetadataClean().then(() => { + mutateMetadataClean({ + dryRun: cleanDryRun, + }).then(() => { jobStatus.refetch(); }); } function renderCleanAlert() { + let msg; + if (cleanDryRun) { + msg = ( +

+ Dry Mode selected. No actual deleting will take place, only logging. +

+ ); + } else { + msg = ( +

+ Are you sure you want to Clean? This will delete database information + and generated content for all scenes and galleries that are no longer + found in the filesystem. +

+ ); + } + return ( { accept={{ text: "Clean", variant: "danger", onClick: onClean }} cancel={{ onClick: () => setIsCleanAlertOpen(false) }} > -

- Are you sure you want to Clean? This will delete database information - and generated content for all scenes and galleries that are no longer - found in the filesystem. -

+ {msg}
); } @@ -442,6 +458,17 @@ export const SettingsTasksPanel: React.FC = () => {
Generated Content
+ +
+
Maintenance
+ + setCleanDryRun(!cleanDryRun)} + /> +