Files
silo-server/internal/taskmanager/tasks/cleanup_orphaned_media_items.go
zZebrahzandGitHub 96bae2f346 fix(library): clean orphaned provisional items (#76)
* fix(library): clean orphaned provisional items

* test(metadata): use item not found sentinel in fake repo

* fix(library): preserve abs references during orphan cleanup

* fix(library): avoid dropped abs cleanup tables

* fix(library): harden provisional orphan cleanup

* fix(library): preserve home dismissal series orphans

* fix(library): Delete matched items created after the first orphan sweep
2026-06-07 21:32:40 -04:00

82 lines
2.5 KiB
Go

package tasks
import (
"context"
"encoding/json"
"fmt"
"github.com/Silo-Server/silo-server/internal/catalog"
"github.com/Silo-Server/silo-server/internal/taskmanager"
)
const orphanedMediaCleanupBatchSize = 1000
type OrphanedMediaItemCleaner interface {
Cleanup(ctx context.Context, batchSize int) (catalog.OrphanedProvisionalCleanupStats, error)
}
type CleanupOrphanedMediaItemsTask struct {
cleaner OrphanedMediaItemCleaner
}
type cleanupOrphanedMediaItemsResult struct {
Candidates int `json:"candidates"`
Deleted int `json:"deleted"`
}
func NewCleanupOrphanedMediaItemsTask(cleaner OrphanedMediaItemCleaner) *CleanupOrphanedMediaItemsTask {
return &CleanupOrphanedMediaItemsTask{cleaner: cleaner}
}
func (t *CleanupOrphanedMediaItemsTask) Key() string { return "cleanup_orphaned_media_items" }
func (t *CleanupOrphanedMediaItemsTask) Name() string {
return "Clean Orphaned Media Items"
}
func (t *CleanupOrphanedMediaItemsTask) Description() string {
return "Deletes detached provisional media items that have no files, library memberships, or durable user/sync references."
}
func (t *CleanupOrphanedMediaItemsTask) Category() taskmanager.TaskCategory {
return taskmanager.TaskCategoryLibrary
}
func (t *CleanupOrphanedMediaItemsTask) IsHidden() bool { return false }
func (t *CleanupOrphanedMediaItemsTask) DefaultTriggers() []taskmanager.TriggerConfig {
return nil
}
func (t *CleanupOrphanedMediaItemsTask) Execute(ctx context.Context, progress taskmanager.ProgressReporter) error {
if t == nil || t.cleaner == nil {
progress.Report(100, "Orphaned media cleanup is not configured")
return nil
}
progress.Report(0, "Checking for orphaned provisional media items")
stats, err := t.cleaner.Cleanup(ctx, orphanedMediaCleanupBatchSize)
setCleanupOrphanedMediaItemsResult(progress, cleanupOrphanedMediaItemsResult{
Candidates: stats.Candidates,
Deleted: stats.Deleted,
})
if err != nil {
progress.Report(100, fmt.Sprintf("Orphaned media cleanup failed after deleting %d of %d candidates", stats.Deleted, stats.Candidates))
return err
}
if stats.Candidates == 0 {
progress.Report(100, "No orphaned provisional media items found")
return nil
}
progress.Report(100, fmt.Sprintf("Deleted %d orphaned provisional media items (%d candidates)", stats.Deleted, stats.Candidates))
return nil
}
func setCleanupOrphanedMediaItemsResult(progress taskmanager.ProgressReporter, result cleanupOrphanedMediaItemsResult) {
if progress == nil {
return
}
data, err := json.Marshal(result)
if err != nil {
return
}
progress.SetResultData(data)
}