From a2d0509caddfc02511d0151a1b92d9b0baa4906c Mon Sep 17 00:00:00 2001
From: viktorstrate <viktorstrate@gmail.com>
Date: Wed, 12 Feb 2020 18:10:52 +0100
Subject: [PATCH] Update scanner to delete removed files

---
 .gitignore                                |  3 ++
 api/database/migrations/0001_user.up.sql  |  2 +-
 api/database/migrations/0002_photo.up.sql | 10 ++---
 api/scanner/scanner.go                    | 45 +++++++++++++++++++++++
 4 files changed, 54 insertions(+), 6 deletions(-)

diff --git a/.gitignore b/.gitignore
index fd8974c..2fa10b3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,3 +30,6 @@ yarn.lock
 npm-debug.log*
 yarn-debug.log*
 yarn-error.log*
+
+# vscode
+__debug_bin
diff --git a/api/database/migrations/0001_user.up.sql b/api/database/migrations/0001_user.up.sql
index 93ef1b5..ccb1d12 100644
--- a/api/database/migrations/0001_user.up.sql
+++ b/api/database/migrations/0001_user.up.sql
@@ -15,5 +15,5 @@ CREATE TABLE IF NOT EXISTS access_token (
 	expire timestamp NOT NULL,
 
 	PRIMARY KEY (token_id),
-  FOREIGN KEY (user_id) REFERENCES user(user_id)
+  FOREIGN KEY (user_id) REFERENCES user(user_id) ON DELETE CASCADE
 );
diff --git a/api/database/migrations/0002_photo.up.sql b/api/database/migrations/0002_photo.up.sql
index f389329..15c8dcb 100644
--- a/api/database/migrations/0002_photo.up.sql
+++ b/api/database/migrations/0002_photo.up.sql
@@ -22,8 +22,8 @@ CREATE TABLE IF NOT EXISTS album (
   path varchar(512) NOT NULL UNIQUE,
 
   PRIMARY KEY (album_id),
-  FOREIGN KEY (parent_album) REFERENCES album(album_id),
-  FOREIGN KEY (owner_id) REFERENCES user(user_id)
+  FOREIGN KEY (parent_album) REFERENCES album(album_id) ON DELETE CASCADE,
+  FOREIGN KEY (owner_id) REFERENCES user(user_id) ON DELETE CASCADE
 );
 
 CREATE TABLE IF NOT EXISTS photo (
@@ -34,8 +34,8 @@ CREATE TABLE IF NOT EXISTS photo (
   exif_id int,
 
   PRIMARY KEY (photo_id),
-  FOREIGN KEY (album_id) REFERENCES album(album_id),
-  FOREIGN KEY (exif_id) REFERENCES photo_exif(exif_id)
+  FOREIGN KEY (album_id) REFERENCES album(album_id) ON DELETE CASCADE,
+  FOREIGN KEY (exif_id) REFERENCES photo_exif(exif_id) ON DELETE CASCADE
 );
 
 CREATE TABLE IF NOT EXISTS photo_url (
@@ -48,5 +48,5 @@ CREATE TABLE IF NOT EXISTS photo_url (
   content_type varchar(64) NOT NULL,
 
   PRIMARY KEY (url_id),
-  FOREIGN KEY (photo_id) REFERENCES photo(photo_id)
+  FOREIGN KEY (photo_id) REFERENCES photo(photo_id) ON DELETE CASCADE
 );
\ No newline at end of file
diff --git a/api/scanner/scanner.go b/api/scanner/scanner.go
index 1020c0b..79297cf 100644
--- a/api/scanner/scanner.go
+++ b/api/scanner/scanner.go
@@ -8,6 +8,8 @@ import (
 	"log"
 	"os"
 	"path"
+	"strconv"
+	"strings"
 
 	"github.com/h2non/filetype"
 	"github.com/viktorstrate/photoview/api/graphql/models"
@@ -45,7 +47,9 @@ func ScanUser(database *sql.DB, userId int) error {
 }
 
 func scan(database *sql.DB, user *models.User) {
+	// Start scanning
 	scanner_cache := make(scanner_cache)
+	album_paths_scanned := make([]interface{}, 0)
 
 	type scanInfo struct {
 		path     string
@@ -65,6 +69,8 @@ func scan(database *sql.DB, user *models.User) {
 		albumPath := albumInfo.path
 		albumParentId := albumInfo.parentId
 
+		album_paths_scanned = append(album_paths_scanned, albumPath)
+
 		// Read path
 		dirContent, err := ioutil.ReadDir(albumPath)
 		if err != nil {
@@ -143,6 +149,8 @@ func scan(database *sql.DB, user *models.User) {
 		}
 	}
 
+	cleanupCache(database, album_paths_scanned)
+
 	log.Println("Done scanning")
 }
 
@@ -226,3 +234,40 @@ func isPathImage(path string, cache *scanner_cache) bool {
 	log.Printf("Unsupported image %s of type %s\n", path, imgType.MIME.Value)
 	return false
 }
+
+func cleanupCache(database *sql.DB, scanned_albums []interface{}) {
+	if len(scanned_albums) == 0 {
+		return
+	}
+
+	albums_questions := strings.Repeat("?,", len(scanned_albums))[:len(scanned_albums)*2-1]
+	rows, err := database.Query("SELECT album_id FROM album WHERE path NOT IN ("+albums_questions+")", scanned_albums...)
+	if err != nil {
+		log.Printf("ERROR: Could not get albums from database: %s\n", err)
+		return
+	}
+
+	deleted_albums := 0
+	deleted_ids := make([]interface{}, 0)
+	for rows.Next() {
+		var album_id int
+		rows.Scan(&album_id)
+		deleted_ids = append(deleted_ids, album_id)
+		cache_path := path.Join("./image-cache", strconv.Itoa(album_id))
+		err := os.RemoveAll(cache_path)
+		if err != nil {
+			log.Printf("ERROR: Could not delete unused cache folder: %s\n%s\n", cache_path, err)
+		} else {
+			deleted_albums++
+		}
+	}
+
+	if len(deleted_ids) > 0 {
+		albums_questions = strings.Repeat("?,", len(deleted_ids))[:len(deleted_ids)*2-1]
+		if _, err := database.Exec("DELETE FROM album WHERE album_id IN ("+albums_questions+")", deleted_ids...); err != nil {
+			log.Printf("ERROR: Could not delete old albums from database:\n%s\n", err)
+		}
+	}
+
+	log.Printf("Deleted %d unused albums from cache", deleted_albums)
+}
