From b09d32019eca23346a55f360f815a46f2e864480 Mon Sep 17 00:00:00 2001
From: viktorstrate <viktorstrate@gmail.com>
Date: Wed, 2 Mar 2022 17:26:06 +0100
Subject: [PATCH] Make it compile again

---
 api/graphql/models/media.go                   |  4 ++
 api/routes/photos.go                          |  2 +-
 api/routes/videos.go                          |  2 +-
 api/scanner/media_encoding/encode_photo.go    |  6 ++
 api/scanner/scanner_album.go                  | 42 +-------------
 api/scanner/scanner_media.go                  | 46 +++++++++++++--
 api/scanner/scanner_queue/queue.go            |  2 +
 api/scanner/scanner_tasks/exif_task.go        | 11 ++--
 .../processing_tasks/process_video_task.go    | 10 ++--
 api/scanner/scanner_tasks/scanner_tasks.go    |  1 +
 .../video_metadata_task.go}                   | 25 +++++++-
 api/utils/media_cache.go                      | 58 +++++++++++++++++++
 api/utils/utils.go                            | 20 -------
 13 files changed, 151 insertions(+), 78 deletions(-)
 rename api/scanner/{video_metadata.go => scanner_tasks/video_metadata_task.go} (72%)
 create mode 100644 api/utils/media_cache.go

diff --git a/api/graphql/models/media.go b/api/graphql/models/media.go
index 02aae2d..a3fe7b8 100644
--- a/api/graphql/models/media.go
+++ b/api/graphql/models/media.go
@@ -77,6 +77,10 @@ func (m *Media) GetHighRes() (*MediaURL, error) {
 	return nil, nil
 }
 
+func (m *Media) CachePath() (string, error) {
+	return utils.CachePathForMedia(m.AlbumID, m.ID)
+}
+
 type MediaType string
 
 const (
diff --git a/api/routes/photos.go b/api/routes/photos.go
index 74bea71..b14a9a7 100644
--- a/api/routes/photos.go
+++ b/api/routes/photos.go
@@ -46,7 +46,7 @@ func RegisterPhotoRoutes(db *gorm.DB, router *mux.Router) {
 
 		if _, err := os.Stat(cachedPath); os.IsNotExist((err)) {
 			err := db.Transaction(func(tx *gorm.DB) error {
-				if _, err = scanner.ProcessMedia(tx, media); err != nil {
+				if err = scanner.ProcessSingleMedia(tx, media); err != nil {
 					log.Printf("ERROR: processing image not found in cache (%s): %s\n", cachedPath, err)
 					return err
 				}
diff --git a/api/routes/videos.go b/api/routes/videos.go
index 4f90bf6..dd1d502 100644
--- a/api/routes/videos.go
+++ b/api/routes/videos.go
@@ -52,7 +52,7 @@ func RegisterVideoRoutes(db *gorm.DB, router *mux.Router) {
 		if _, err := os.Stat(cachedPath); err != nil {
 			if os.IsNotExist(err) {
 				err := db.Transaction(func(tx *gorm.DB) error {
-					if _, err := scanner.ProcessMedia(tx, media); err != nil {
+					if err := scanner.ProcessSingleMedia(tx, media); err != nil {
 						log.Printf("ERROR: processing video not found in cache: %s\n", err)
 						return err
 					}
diff --git a/api/scanner/media_encoding/encode_photo.go b/api/scanner/media_encoding/encode_photo.go
index 257b3e2..fedc47c 100644
--- a/api/scanner/media_encoding/encode_photo.go
+++ b/api/scanner/media_encoding/encode_photo.go
@@ -61,6 +61,12 @@ type EncodeMediaData struct {
 	_videoMetadata  *ffprobe.ProbeData
 }
 
+func NewEncodeMediaData(media *models.Media) EncodeMediaData {
+	return EncodeMediaData{
+		Media: media,
+	}
+}
+
 // ContentType reads the image to determine its content type
 func (img *EncodeMediaData) ContentType() (*media_type.MediaType, error) {
 	if img._contentType != nil {
diff --git a/api/scanner/scanner_album.go b/api/scanner/scanner_album.go
index 6ee87b0..97e126a 100644
--- a/api/scanner/scanner_album.go
+++ b/api/scanner/scanner_album.go
@@ -6,7 +6,6 @@ import (
 	"log"
 	"os"
 	"path"
-	"strconv"
 
 	"github.com/photoview/photoview/api/graphql/models"
 	"github.com/photoview/photoview/api/scanner/media_encoding"
@@ -104,9 +103,7 @@ func ScanAlbum(ctx scanner_task.TaskContext) error {
 	for i, media := range albumMedia {
 		updatedURLs := []*models.MediaURL{}
 
-		mediaData := media_encoding.EncodeMediaData{
-			Media: media,
-		}
+		mediaData := media_encoding.NewEncodeMediaData(media)
 
 		// define new ctx for scope of for-loop
 		ctx, err := scanner_tasks.Tasks.BeforeProcessMedia(ctx, &mediaData)
@@ -200,44 +197,11 @@ func findMediaForAlbum(ctx scanner_task.TaskContext) ([]*models.Media, error) {
 
 func processMedia(ctx scanner_task.TaskContext, mediaData *media_encoding.EncodeMediaData) ([]*models.MediaURL, error) {
 
-	_, err := mediaData.ContentType()
-	if err != nil {
-		return []*models.MediaURL{}, errors.Wrapf(err, "get content-type of media (%s)", mediaData.Media.Path)
-	}
-
 	// Make sure media cache directory exists
-	mediaCachePath, err := makeMediaCacheDir(mediaData.Media)
+	mediaCachePath, err := mediaData.Media.CachePath()
 	if err != nil {
 		return []*models.MediaURL{}, errors.Wrap(err, "cache directory error")
 	}
 
-	return scanner_tasks.Tasks.ProcessMedia(ctx, mediaData, *mediaCachePath)
-}
-
-func makeMediaCacheDir(media *models.Media) (*string, error) {
-
-	// Make root cache dir if not exists
-	if _, err := os.Stat(utils.MediaCachePath()); os.IsNotExist(err) {
-		if err := os.Mkdir(utils.MediaCachePath(), os.ModePerm); err != nil {
-			return nil, errors.Wrap(err, "could not make root image cache directory")
-		}
-	}
-
-	// Make album cache dir if not exists
-	albumCachePath := path.Join(utils.MediaCachePath(), strconv.Itoa(int(media.AlbumID)))
-	if _, err := os.Stat(albumCachePath); os.IsNotExist(err) {
-		if err := os.Mkdir(albumCachePath, os.ModePerm); err != nil {
-			return nil, errors.Wrap(err, "could not make album image cache directory")
-		}
-	}
-
-	// Make photo cache dir if not exists
-	photoCachePath := path.Join(albumCachePath, strconv.Itoa(int(media.ID)))
-	if _, err := os.Stat(photoCachePath); os.IsNotExist(err) {
-		if err := os.Mkdir(photoCachePath, os.ModePerm); err != nil {
-			return nil, errors.Wrap(err, "could not make photo image cache directory")
-		}
-	}
-
-	return &photoCachePath, nil
+	return scanner_tasks.Tasks.ProcessMedia(ctx, mediaData, mediaCachePath)
 }
diff --git a/api/scanner/scanner_media.go b/api/scanner/scanner_media.go
index e513ecd..eaefb02 100644
--- a/api/scanner/scanner_media.go
+++ b/api/scanner/scanner_media.go
@@ -1,12 +1,16 @@
 package scanner
 
 import (
+	"context"
 	"log"
 	"os"
 	"path"
 
 	"github.com/photoview/photoview/api/graphql/models"
+	"github.com/photoview/photoview/api/scanner/media_encoding"
 	"github.com/photoview/photoview/api/scanner/scanner_cache"
+	"github.com/photoview/photoview/api/scanner/scanner_task"
+	"github.com/photoview/photoview/api/scanner/scanner_tasks"
 	"github.com/pkg/errors"
 	"gorm.io/gorm"
 )
@@ -62,11 +66,41 @@ func ScanMedia(tx *gorm.DB, mediaPath string, albumId int, cache *scanner_cache.
 		return nil, false, errors.Wrap(err, "could not insert media into database")
 	}
 
-	if media.Type == models.MediaTypeVideo {
-		if err = ScanVideoMetadata(tx, &media); err != nil {
-			log.Printf("WARN: ScanVideoMetadata for %s failed: %s\n", mediaName, err)
-		}
-	}
-
 	return &media, true, nil
 }
+
+// ProcessSingleMedia processes a single media, might be used to reprocess media with corrupted cache
+// Function waits for processing to finish before returning.
+func ProcessSingleMedia(db *gorm.DB, media *models.Media) error {
+	album_cache := scanner_cache.MakeAlbumCache()
+
+	var album models.Album
+	if err := db.Model(media).Association("Album").Find(&album); err != nil {
+		return err
+	}
+
+	media_data := media_encoding.NewEncodeMediaData(media)
+
+	task_context := scanner_task.NewTaskContext(context.Background(), db, &album, album_cache)
+	new_ctx, err := scanner_tasks.Tasks.BeforeProcessMedia(task_context, &media_data)
+	if err != nil {
+		return err
+	}
+
+	mediaCachePath, err := media.CachePath()
+	if err != nil {
+		return err
+	}
+
+	updated_urls, err := scanner_tasks.Tasks.ProcessMedia(new_ctx, &media_data, mediaCachePath)
+	if err != nil {
+		return err
+	}
+
+	err = scanner_tasks.Tasks.AfterProcessMedia(new_ctx, &media_data, updated_urls, 0, 1)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
diff --git a/api/scanner/scanner_queue/queue.go b/api/scanner/scanner_queue/queue.go
index 0c02593..7dea950 100644
--- a/api/scanner/scanner_queue/queue.go
+++ b/api/scanner/scanner_queue/queue.go
@@ -217,6 +217,8 @@ func AddAllToQueue() error {
 	return nil
 }
 
+// AddUserToQueue finds all root albums owned by the given user and adds them to the scanner queue.
+// Function does not block.
 func AddUserToQueue(user *models.User) error {
 	album_cache := scanner_cache.MakeAlbumCache()
 	albums, album_errors := scanner.FindAlbumsForUser(global_scanner_queue.db, user, album_cache)
diff --git a/api/scanner/scanner_tasks/exif_task.go b/api/scanner/scanner_tasks/exif_task.go
index 0900824..5fd5894 100644
--- a/api/scanner/scanner_tasks/exif_task.go
+++ b/api/scanner/scanner_tasks/exif_task.go
@@ -5,7 +5,6 @@ import (
 
 	"github.com/photoview/photoview/api/graphql/models"
 	"github.com/photoview/photoview/api/scanner/exif"
-	"github.com/photoview/photoview/api/scanner/media_encoding"
 	"github.com/photoview/photoview/api/scanner/scanner_task"
 )
 
@@ -13,11 +12,15 @@ type ExifTask struct {
 	scanner_task.ScannerTaskBase
 }
 
-func (t ExifTask) AfterProcessMedia(ctx scanner_task.TaskContext, mediaData *media_encoding.EncodeMediaData, updatedURLs []*models.MediaURL, mediaIndex int, mediaTotal int) error {
+func (t ExifTask) AfterMediaFound(ctx scanner_task.TaskContext, media *models.Media, newMedia bool) error {
 
-	_, err := exif.SaveEXIF(ctx.GetDB(), mediaData.Media)
+	if !newMedia {
+		return nil
+	}
+
+	_, err := exif.SaveEXIF(ctx.GetDB(), media)
 	if err != nil {
-		log.Printf("WARN: SaveEXIF for %s failed: %s\n", mediaData.Media.Title, err)
+		log.Printf("WARN: SaveEXIF for %s failed: %s\n", media.Title, err)
 	}
 
 	return nil
diff --git a/api/scanner/scanner_tasks/processing_tasks/process_video_task.go b/api/scanner/scanner_tasks/processing_tasks/process_video_task.go
index 8f00d07..b29f482 100644
--- a/api/scanner/scanner_tasks/processing_tasks/process_video_task.go
+++ b/api/scanner/scanner_tasks/processing_tasks/process_video_task.go
@@ -59,7 +59,7 @@ func (t ProcessVideoTask) ProcessMedia(ctx scanner_task.TaskContext, mediaData *
 		origVideoPath := video.Path
 		videoMediaName := generateUniqueMediaName(video.Path)
 
-		webMetadata, err := readVideoStreamMetadata(origVideoPath)
+		webMetadata, err := ReadVideoStreamMetadata(origVideoPath)
 		if err != nil {
 			return []*models.MediaURL{}, errors.Wrapf(err, "failed to read metadata for original video (%s)", video.Title)
 		}
@@ -99,7 +99,7 @@ func (t ProcessVideoTask) ProcessMedia(ctx scanner_task.TaskContext, mediaData *
 			return []*models.MediaURL{}, errors.Wrapf(err, "could not encode mp4 video (%s)", video.Path)
 		}
 
-		webMetadata, err := readVideoStreamMetadata(webVideoPath)
+		webMetadata, err := ReadVideoStreamMetadata(webVideoPath)
 		if err != nil {
 			return []*models.MediaURL{}, errors.Wrapf(err, "failed to read metadata for encoded web-video (%s)", video.Title)
 		}
@@ -205,7 +205,7 @@ func (t ProcessVideoTask) ProcessMedia(ctx scanner_task.TaskContext, mediaData *
 	return updatedURLs, nil
 }
 
-func readVideoMetadata(videoPath string) (*ffprobe.ProbeData, error) {
+func ReadVideoMetadata(videoPath string) (*ffprobe.ProbeData, error) {
 	ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
 	defer cancelFn()
 
@@ -217,8 +217,8 @@ func readVideoMetadata(videoPath string) (*ffprobe.ProbeData, error) {
 	return data, nil
 }
 
-func readVideoStreamMetadata(videoPath string) (*ffprobe.Stream, error) {
-	data, err := readVideoMetadata(videoPath)
+func ReadVideoStreamMetadata(videoPath string) (*ffprobe.Stream, error) {
+	data, err := ReadVideoMetadata(videoPath)
 	if err != nil {
 		return nil, errors.Wrap(err, "read video stream metadata")
 	}
diff --git a/api/scanner/scanner_tasks/scanner_tasks.go b/api/scanner/scanner_tasks/scanner_tasks.go
index 353176c..5290670 100644
--- a/api/scanner/scanner_tasks/scanner_tasks.go
+++ b/api/scanner/scanner_tasks/scanner_tasks.go
@@ -19,6 +19,7 @@ var allTasks []scanner_task.ScannerTask = []scanner_task.ScannerTask{
 	processing_tasks.ProcessVideoTask{},
 	FaceDetectionTask{},
 	ExifTask{},
+	VideoMetadataTask{},
 	cleanup_tasks.MediaCleanupTask{},
 }
 
diff --git a/api/scanner/video_metadata.go b/api/scanner/scanner_tasks/video_metadata_task.go
similarity index 72%
rename from api/scanner/video_metadata.go
rename to api/scanner/scanner_tasks/video_metadata_task.go
index f79e0c5..e67d6eb 100644
--- a/api/scanner/video_metadata.go
+++ b/api/scanner/scanner_tasks/video_metadata_task.go
@@ -1,18 +1,39 @@
-package scanner
+package scanner_tasks
 
 import (
 	"fmt"
+	"log"
 	"strconv"
 	"strings"
 
 	"github.com/photoview/photoview/api/graphql/models"
+	"github.com/photoview/photoview/api/scanner/scanner_task"
+	"github.com/photoview/photoview/api/scanner/scanner_tasks/processing_tasks"
 	"github.com/pkg/errors"
 	"gorm.io/gorm"
 )
 
+type VideoMetadataTask struct {
+	scanner_task.ScannerTaskBase
+}
+
+func (t VideoMetadataTask) AfterMediaFound(ctx scanner_task.TaskContext, media *models.Media, newMedia bool) error {
+
+	if !newMedia || media.Type != models.MediaTypeVideo {
+		return nil
+	}
+
+	err := ScanVideoMetadata(ctx.GetDB(), media)
+	if err != nil {
+		log.Printf("WARN: ScanVideoMetadata for %s failed: %s\n", media.Title, err)
+	}
+
+	return nil
+}
+
 func ScanVideoMetadata(tx *gorm.DB, video *models.Media) error {
 
-	data, err := readVideoMetadata(video.Path)
+	data, err := processing_tasks.ReadVideoMetadata(video.Path)
 	if err != nil {
 		return errors.Wrapf(err, "scan video metadata failed (%s)", video.Title)
 	}
diff --git a/api/utils/media_cache.go b/api/utils/media_cache.go
new file mode 100644
index 0000000..42a38d4
--- /dev/null
+++ b/api/utils/media_cache.go
@@ -0,0 +1,58 @@
+package utils
+
+import (
+	"os"
+	"path"
+	"strconv"
+
+	"github.com/pkg/errors"
+)
+
+// CachePathForMedia is a low-level implementation for Media.CachePath()
+func CachePathForMedia(albumID int, mediaID int) (string, error) {
+
+	// Make root cache dir if not exists
+	if _, err := os.Stat(MediaCachePath()); os.IsNotExist(err) {
+		if err := os.Mkdir(MediaCachePath(), os.ModePerm); err != nil {
+			return "", errors.Wrap(err, "could not make root image cache directory")
+		}
+	}
+
+	// Make album cache dir if not exists
+	albumCachePath := path.Join(MediaCachePath(), strconv.Itoa(int(albumID)))
+	if _, err := os.Stat(albumCachePath); os.IsNotExist(err) {
+		if err := os.Mkdir(albumCachePath, os.ModePerm); err != nil {
+			return "", errors.Wrap(err, "could not make album image cache directory")
+		}
+	}
+
+	// Make photo cache dir if not exists
+	photoCachePath := path.Join(albumCachePath, strconv.Itoa(int(mediaID)))
+	if _, err := os.Stat(photoCachePath); os.IsNotExist(err) {
+		if err := os.Mkdir(photoCachePath, os.ModePerm); err != nil {
+			return "", errors.Wrap(err, "could not make photo image cache directory")
+		}
+	}
+
+	return photoCachePath, nil
+}
+
+var test_cache_path string = ""
+
+func ConfigureTestCache(tmp_dir string) {
+	test_cache_path = tmp_dir
+}
+
+// MediaCachePath returns the path for where the media cache is located on the file system
+func MediaCachePath() string {
+	if test_cache_path != "" {
+		return test_cache_path
+	}
+
+	photoCache := EnvMediaCachePath.GetValue()
+	if photoCache == "" {
+		photoCache = "./media_cache"
+	}
+
+	return photoCache
+}
diff --git a/api/utils/utils.go b/api/utils/utils.go
index a69fc0c..c6620d7 100644
--- a/api/utils/utils.go
+++ b/api/utils/utils.go
@@ -47,26 +47,6 @@ func HandleError(message string, err error) PhotoviewError {
 	}
 }
 
-var test_cache_path string = ""
-
-func ConfigureTestCache(tmp_dir string) {
-	test_cache_path = tmp_dir
-}
-
-// MediaCachePath returns the path for where the media cache is located on the file system
-func MediaCachePath() string {
-	if test_cache_path != "" {
-		return test_cache_path
-	}
-
-	photoCache := EnvMediaCachePath.GetValue()
-	if photoCache == "" {
-		photoCache = "./media_cache"
-	}
-
-	return photoCache
-}
-
 var test_face_recognition_models_path string = ""
 
 func ConfigureTestFaceRecognitionModelsPath(path string) {
