diff --git a/pkg/manager/task_clean.go b/pkg/manager/task_clean.go index 198ba1260..9e76acae5 100644 --- a/pkg/manager/task_clean.go +++ b/pkg/manager/task_clean.go @@ -5,7 +5,9 @@ import ( "github.com/stashapp/stash/pkg/database" "github.com/stashapp/stash/pkg/logger" "github.com/stashapp/stash/pkg/models" + "github.com/stashapp/stash/pkg/utils" "os" + "path/filepath" "strconv" "sync" ) @@ -21,16 +23,121 @@ func (t *CleanTask) Start(wg *sync.WaitGroup) { logger.Debugf("Found: %s", t.Scene.Path) } else { logger.Debugf("Deleting missing file: %s", t.Scene.Path) - t.deleteScene(strconv.Itoa(t.Scene.ID)) + t.deleteScene(t.Scene.ID) } } -func (t *CleanTask) deleteScene(id string) { +func (t *CleanTask) deleteScene(sceneID int) { ctx := context.TODO() qb := models.NewSceneQueryBuilder() + jqb := models.NewJoinsQueryBuilder() tx := database.DB.MustBeginTx(ctx, nil) + strSceneID := strconv.Itoa(sceneID) defer tx.Commit() - qb.Destroy(strconv.Itoa(t.Scene.ID), tx) + + //check and make sure it still exists. scene is also used to delete generated files + scene, err := qb.Find(sceneID) + if err != nil { + _ = tx.Rollback() + } + + if err := jqb.DestroyScenesTags(sceneID, tx); err != nil { + _ = tx.Rollback() + } + + if err := jqb.DestroyPerformersScenes(sceneID, tx); err != nil { + _ = tx.Rollback() + } + + if err := jqb.DestroyScenesMarkers(sceneID, tx); err != nil { + _ = tx.Rollback() + } + + if err := jqb.DestroyScenesGalleries(sceneID, tx); err != nil { + _ = tx.Rollback() + } + + if err := qb.Destroy(strSceneID, tx); err != nil { + _ = tx.Rollback() + } + + t.deleteGeneratedSceneFiles(scene) +} + + +func (t *CleanTask) deleteGeneratedSceneFiles(scene *models.Scene) { + markersFolder := filepath.Join(instance.Paths.Generated.Markers, scene.Checksum) + + exists, _ := utils.FileExists(markersFolder) + if exists { + err := os.RemoveAll(markersFolder) + if err != nil { + logger.Warnf("Could not delete file %s: %s", scene.Path, err.Error()) + } + } + + thumbPath := instance.Paths.Scene.GetThumbnailScreenshotPath(scene.Checksum) + exists, _ = utils.FileExists(thumbPath) + if exists { + err := os.Remove(thumbPath) + if err != nil { + logger.Warnf("Could not delete file %s: %s", thumbPath, err.Error()) + } + } + + normalPath := instance.Paths.Scene.GetScreenshotPath(scene.Checksum) + exists, _ = utils.FileExists(normalPath) + if exists { + err := os.Remove(normalPath) + if err != nil { + logger.Warnf("Could not delete file %s: %s", normalPath, err.Error()) + } + } + + streamPreviewPath := instance.Paths.Scene.GetStreamPreviewPath(scene.Checksum) + exists, _ = utils.FileExists(streamPreviewPath) + if exists { + err := os.Remove(streamPreviewPath) + if err != nil { + logger.Warnf("Could not delete file %s: %s", streamPreviewPath, err.Error()) + } + } + + streamPreviewImagePath := instance.Paths.Scene.GetStreamPreviewImagePath(scene.Checksum) + exists, _ = utils.FileExists(streamPreviewImagePath) + if exists { + err := os.Remove(streamPreviewImagePath) + if err != nil { + logger.Warnf("Could not delete file %s: %s", streamPreviewImagePath, err.Error()) + } + } + + transcodePath := instance.Paths.Scene.GetTranscodePath(scene.Checksum) + exists, _ = utils.FileExists(transcodePath) + if exists { + err := os.Remove(transcodePath) + if err != nil { + logger.Warnf("Could not delete file %s: %s", transcodePath, err.Error()) + } + } + + spritePath := instance.Paths.Scene.GetSpriteImageFilePath(scene.Checksum) + exists, _ = utils.FileExists(spritePath) + if exists { + err := os.Remove(spritePath) + if err != nil { + logger.Warnf("Could not delete file %s: %s", spritePath, err.Error()) + } + } + + vttPath := instance.Paths.Scene.GetSpriteVttFilePath(scene.Checksum) + exists, _ = utils.FileExists(vttPath) + if exists { + err := os.Remove(vttPath) + if err != nil { + logger.Warnf("Could not delete file %s: %s", vttPath, err.Error()) + } + } } func (t *CleanTask) fileExists(filename string) bool { diff --git a/pkg/models/querybuilder_joins.go b/pkg/models/querybuilder_joins.go index efcf435f2..5e534294b 100644 --- a/pkg/models/querybuilder_joins.go +++ b/pkg/models/querybuilder_joins.go @@ -33,6 +33,14 @@ func (qb *JoinsQueryBuilder) UpdatePerformersScenes(sceneID int, updatedJoins [] return qb.CreatePerformersScenes(updatedJoins, tx) } +func (qb *JoinsQueryBuilder) DestroyPerformersScenes(sceneID int, tx *sqlx.Tx) error { + ensureTx(tx) + + // Delete the existing joins + _, err := tx.Exec("DELETE FROM performers_scenes WHERE scene_id = ?", sceneID) + return err +} + func (qb *JoinsQueryBuilder) CreateScenesTags(newJoins []ScenesTags, tx *sqlx.Tx) error { ensureTx(tx) for _, join := range newJoins { @@ -47,6 +55,16 @@ func (qb *JoinsQueryBuilder) CreateScenesTags(newJoins []ScenesTags, tx *sqlx.Tx return nil } +func (qb *JoinsQueryBuilder) DestroyScenesTags(sceneID int, tx *sqlx.Tx) error { + ensureTx(tx) + + // Delete the existing joins + _, err := tx.Exec("DELETE FROM scenes_tags WHERE scene_id = ?", sceneID) + + return err +} + + func (qb *JoinsQueryBuilder) UpdateScenesTags(sceneID int, updatedJoins []ScenesTags, tx *sqlx.Tx) error { ensureTx(tx) @@ -82,3 +100,32 @@ func (qb *JoinsQueryBuilder) UpdateSceneMarkersTags(sceneMarkerID int, updatedJo } return qb.CreateSceneMarkersTags(updatedJoins, tx) } + +func (qb *JoinsQueryBuilder) DestroySceneMarkersTags(sceneMarkerID int, updatedJoins []SceneMarkersTags, tx *sqlx.Tx) error { + ensureTx(tx) + + // Delete the existing joins + _, err := tx.Exec("DELETE FROM scene_markers_tags WHERE scene_marker_id = ?", sceneMarkerID) + return err +} + +func (qb *JoinsQueryBuilder) DestroyScenesGalleries(sceneID int, tx *sqlx.Tx) error { + ensureTx(tx) + + // Unset the existing scene id from galleries + _, err := tx.Exec("UPDATE galleries SET scene_id = null WHERE scene_id = ?", sceneID) + + return err +} + +func (qb *JoinsQueryBuilder) DestroyScenesMarkers(sceneID int, tx *sqlx.Tx) error { + ensureTx(tx) + + // Delete the scene marker tags + _, err := tx.Exec("DELETE t FROM scene_markers_tags t join scene_markers m on t.scene_marker_id = m.id WHERE m.scene_id = ?", sceneID) + + // Delete the existing joins + _, err = tx.Exec("DELETE FROM scene_markers WHERE scene_id = ?", sceneID) + + return err +} diff --git a/pkg/models/querybuilder_scene.go b/pkg/models/querybuilder_scene.go index 544546e72..13cc37c87 100644 --- a/pkg/models/querybuilder_scene.go +++ b/pkg/models/querybuilder_scene.go @@ -7,7 +7,6 @@ import ( "github.com/jmoiron/sqlx" "github.com/stashapp/stash/pkg/database" - "github.com/stashapp/stash/pkg/logger" ) const scenesForPerformerQuery = ` @@ -75,6 +74,10 @@ func (qb *SceneQueryBuilder) Update(updatedScene ScenePartial, tx *sqlx.Tx) (*Sc return qb.find(updatedScene.ID, tx) } +func (qb *SceneQueryBuilder) Destroy(id string, tx *sqlx.Tx) error { + return executeDeleteQuery("scenes", id, tx) +} + func (qb *SceneQueryBuilder) Find(id int) (*Scene, error) { return qb.find(id, nil) } @@ -294,18 +297,3 @@ func (qb *SceneQueryBuilder) queryScenes(query string, args []interface{}, tx *s return scenes, nil } -func (qb *SceneQueryBuilder) Destroy(id string, tx *sqlx.Tx) error { - - _, err := tx.Exec("DELETE FROM performers_scenes WHERE scene_id = ?", id) - if err != nil { - logger.Debugf("error deleting performers_scenes for scene_id: %s", err) - return err - } - _, err = tx.Exec("DELETE FROM scenes_tags WHERE scene_id = ?", id) - if err != nil { - logger.Debugf("error deleting scenes_tags for scene_id: %s", err) - return err - } - - return executeDeleteQuery("scenes", id, tx) -} diff --git a/ui/v2/src/components/Settings/SettingsTasksPanel/SettingsTasksPanel.tsx b/ui/v2/src/components/Settings/SettingsTasksPanel/SettingsTasksPanel.tsx index 449fd42ad..b117c435a 100644 --- a/ui/v2/src/components/Settings/SettingsTasksPanel/SettingsTasksPanel.tsx +++ b/ui/v2/src/components/Settings/SettingsTasksPanel/SettingsTasksPanel.tsx @@ -79,11 +79,7 @@ export const SettingsTasksPanel: FunctionComponent = (props: IProps) => labelFor="clean" inline={true} > -