2020-06-22 21:52:53 +02:00
|
|
|
package scanner
|
|
|
|
|
|
|
|
import (
|
2020-06-23 15:13:07 +02:00
|
|
|
"fmt"
|
2020-06-22 21:52:53 +02:00
|
|
|
"io/ioutil"
|
2021-02-26 20:34:08 +01:00
|
|
|
"log"
|
2021-04-26 20:37:29 +02:00
|
|
|
"os"
|
2020-06-22 21:52:53 +02:00
|
|
|
"path"
|
|
|
|
|
2020-12-17 22:51:43 +01:00
|
|
|
"github.com/photoview/photoview/api/graphql/models"
|
2022-02-14 23:57:45 +01:00
|
|
|
"github.com/photoview/photoview/api/scanner/media_encoding"
|
|
|
|
"github.com/photoview/photoview/api/scanner/scanner_task"
|
|
|
|
"github.com/photoview/photoview/api/scanner/scanner_tasks"
|
2021-05-06 21:54:31 +02:00
|
|
|
"github.com/photoview/photoview/api/scanner/scanner_utils"
|
2020-12-17 22:51:43 +01:00
|
|
|
"github.com/photoview/photoview/api/utils"
|
2020-11-23 19:59:01 +01:00
|
|
|
"github.com/pkg/errors"
|
2020-11-23 19:39:44 +01:00
|
|
|
"gorm.io/gorm"
|
2020-06-22 21:52:53 +02:00
|
|
|
)
|
|
|
|
|
2020-12-22 01:14:43 +01:00
|
|
|
func NewRootAlbum(db *gorm.DB, rootPath string, owner *models.User) (*models.Album, error) {
|
|
|
|
|
2021-04-26 20:37:29 +02:00
|
|
|
if !ValidRootPath(rootPath) {
|
|
|
|
return nil, ErrorInvalidRootPath
|
|
|
|
}
|
|
|
|
|
|
|
|
if !path.IsAbs(rootPath) {
|
|
|
|
wd, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
rootPath = path.Join(wd, rootPath)
|
|
|
|
}
|
|
|
|
|
2020-12-22 01:14:43 +01:00
|
|
|
owners := []models.User{
|
|
|
|
*owner,
|
|
|
|
}
|
|
|
|
|
2020-12-31 00:37:11 +01:00
|
|
|
var matchedAlbums []models.Album
|
2021-01-17 12:45:23 +01:00
|
|
|
if err := db.Where("path_hash = ?", models.MD5Hash(rootPath)).Find(&matchedAlbums).Error; err != nil {
|
2020-12-22 01:14:43 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-12-31 00:37:11 +01:00
|
|
|
if len(matchedAlbums) > 0 {
|
|
|
|
album := matchedAlbums[0]
|
|
|
|
|
2021-04-26 15:07:06 +02:00
|
|
|
var matchedUserAlbumCount int64
|
|
|
|
if err := db.Table("user_albums").Where("user_id = ?", owner.ID).Where("album_id = ?", album.ID).Count(&matchedUserAlbumCount).Error; err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if matchedUserAlbumCount > 0 {
|
2021-04-26 20:37:29 +02:00
|
|
|
return nil, errors.New(fmt.Sprintf("user already owns a path containing this path: %s", rootPath))
|
2021-04-26 15:07:06 +02:00
|
|
|
}
|
|
|
|
|
2020-12-31 00:37:11 +01:00
|
|
|
if err := db.Model(&owner).Association("Albums").Append(&album); err != nil {
|
2022-02-14 23:57:45 +01:00
|
|
|
return nil, errors.Wrap(err, "add owner to already existing album")
|
2020-12-31 00:37:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return &album, nil
|
|
|
|
} else {
|
|
|
|
album := models.Album{
|
|
|
|
Title: path.Base(rootPath),
|
|
|
|
Path: rootPath,
|
|
|
|
Owners: owners,
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := db.Create(&album).Error; err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &album, nil
|
|
|
|
}
|
2020-12-22 01:14:43 +01:00
|
|
|
}
|
|
|
|
|
2021-04-26 20:37:29 +02:00
|
|
|
var ErrorInvalidRootPath = errors.New("invalid root path")
|
|
|
|
|
|
|
|
func ValidRootPath(rootPath string) bool {
|
|
|
|
_, err := os.Stat(rootPath)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Warn: invalid root path: '%s'\n%s\n", rootPath, err)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2022-02-15 17:22:41 +01:00
|
|
|
func ScanAlbum(ctx scanner_task.TaskContext) error {
|
2022-02-14 23:57:45 +01:00
|
|
|
newCtx, err := scanner_tasks.Tasks.BeforeScanAlbum(ctx)
|
|
|
|
if err != nil {
|
2022-02-15 17:22:41 +01:00
|
|
|
return errors.Wrapf(err, "before scan album (%s)", ctx.GetAlbum().Path)
|
2022-02-14 23:57:45 +01:00
|
|
|
}
|
|
|
|
ctx = newCtx
|
2020-06-23 15:13:07 +02:00
|
|
|
|
2020-06-22 23:52:41 +02:00
|
|
|
// Scan for photos
|
2022-02-14 23:57:45 +01:00
|
|
|
albumMedia, err := findMediaForAlbum(ctx)
|
2020-06-22 23:52:41 +02:00
|
|
|
if err != nil {
|
2022-03-28 18:43:00 +02:00
|
|
|
return errors.Wrapf(err, "find media for album (%s): %s", ctx.GetAlbum().Path, err)
|
2020-06-22 23:52:41 +02:00
|
|
|
}
|
|
|
|
|
2022-02-15 17:22:41 +01:00
|
|
|
changedMedia := make([]*models.Media, 0)
|
|
|
|
for i, media := range albumMedia {
|
2022-03-02 17:26:06 +01:00
|
|
|
mediaData := media_encoding.NewEncodeMediaData(media)
|
2022-02-15 17:22:41 +01:00
|
|
|
|
2023-02-03 09:14:30 +01:00
|
|
|
if err := scanMedia(ctx, media, &mediaData, i, len(albumMedia)); err != nil {
|
2024-03-30 10:04:03 +01:00
|
|
|
scanner_utils.ScannerError("Error scanning media for album (%d) file (%s): %s\n", ctx.GetAlbum().ID, media.Path, err)
|
2021-03-16 22:27:27 +01:00
|
|
|
}
|
2020-06-23 15:13:07 +02:00
|
|
|
}
|
2020-06-23 00:40:47 +02:00
|
|
|
|
2022-02-15 17:22:41 +01:00
|
|
|
if err := scanner_tasks.Tasks.AfterScanAlbum(ctx, changedMedia, albumMedia); err != nil {
|
|
|
|
return errors.Wrap(err, "after scan album")
|
2022-02-14 23:57:45 +01:00
|
|
|
}
|
|
|
|
|
2022-02-15 17:22:41 +01:00
|
|
|
return nil
|
2020-06-22 23:52:41 +02:00
|
|
|
}
|
|
|
|
|
2022-02-14 23:57:45 +01:00
|
|
|
func findMediaForAlbum(ctx scanner_task.TaskContext) ([]*models.Media, error) {
|
2020-06-22 21:52:53 +02:00
|
|
|
|
2022-02-14 23:57:45 +01:00
|
|
|
albumMedia := make([]*models.Media, 0)
|
2020-06-22 21:52:53 +02:00
|
|
|
|
2022-02-14 23:57:45 +01:00
|
|
|
dirContent, err := ioutil.ReadDir(ctx.GetAlbum().Path)
|
2020-06-22 21:52:53 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, item := range dirContent {
|
2022-02-14 23:57:45 +01:00
|
|
|
mediaPath := path.Join(ctx.GetAlbum().Path, item.Name())
|
2020-06-22 21:52:53 +02:00
|
|
|
|
2022-02-14 23:57:45 +01:00
|
|
|
isDirSymlink, err := utils.IsDirSymlink(mediaPath)
|
2021-07-15 16:16:05 +02:00
|
|
|
if err != nil {
|
2022-02-14 23:57:45 +01:00
|
|
|
log.Printf("Cannot detect whether %s is symlink to a directory. Pretending it is not", mediaPath)
|
2021-07-15 16:16:05 +02:00
|
|
|
isDirSymlink = false
|
|
|
|
}
|
|
|
|
|
2022-02-14 23:57:45 +01:00
|
|
|
if !item.IsDir() && !isDirSymlink && ctx.GetCache().IsPathMedia(mediaPath) {
|
|
|
|
skip, err := scanner_tasks.Tasks.MediaFound(ctx, item, mediaPath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if skip {
|
2021-03-10 21:51:36 +01:00
|
|
|
continue
|
2021-02-26 20:34:08 +01:00
|
|
|
}
|
|
|
|
|
2022-02-15 17:22:41 +01:00
|
|
|
err = ctx.DatabaseTransaction(func(ctx scanner_task.TaskContext) error {
|
|
|
|
media, isNewMedia, err := ScanMedia(ctx.GetDB(), mediaPath, ctx.GetAlbum().ID, ctx.GetCache())
|
2020-11-23 19:59:01 +01:00
|
|
|
if err != nil {
|
2022-02-14 23:57:45 +01:00
|
|
|
return errors.Wrapf(err, "scanning media error (%s)", mediaPath)
|
2020-11-23 19:59:01 +01:00
|
|
|
}
|
2020-06-22 21:52:53 +02:00
|
|
|
|
2022-02-14 23:57:45 +01:00
|
|
|
if err = scanner_tasks.Tasks.AfterMediaFound(ctx, media, isNewMedia); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-06-22 21:52:53 +02:00
|
|
|
|
2022-02-14 23:57:45 +01:00
|
|
|
albumMedia = append(albumMedia, media)
|
2020-11-23 19:59:01 +01:00
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
2020-06-22 21:52:53 +02:00
|
|
|
|
|
|
|
if err != nil {
|
2022-02-14 23:57:45 +01:00
|
|
|
scanner_utils.ScannerError("Error scanning media for album (%d): %s\n", ctx.GetAlbum().ID, err)
|
2020-06-22 21:52:53 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
2022-07-07 22:00:05 +02:00
|
|
|
|
2020-06-22 21:52:53 +02:00
|
|
|
}
|
|
|
|
|
2022-02-14 23:57:45 +01:00
|
|
|
return albumMedia, nil
|
|
|
|
}
|
|
|
|
|
2022-02-15 17:22:41 +01:00
|
|
|
func processMedia(ctx scanner_task.TaskContext, mediaData *media_encoding.EncodeMediaData) ([]*models.MediaURL, error) {
|
2022-02-14 23:57:45 +01:00
|
|
|
|
|
|
|
// Make sure media cache directory exists
|
2022-03-02 17:26:06 +01:00
|
|
|
mediaCachePath, err := mediaData.Media.CachePath()
|
2022-02-14 23:57:45 +01:00
|
|
|
if err != nil {
|
2022-02-15 17:22:41 +01:00
|
|
|
return []*models.MediaURL{}, errors.Wrap(err, "cache directory error")
|
|
|
|
}
|
|
|
|
|
2022-03-02 17:26:06 +01:00
|
|
|
return scanner_tasks.Tasks.ProcessMedia(ctx, mediaData, mediaCachePath)
|
2020-06-22 21:52:53 +02:00
|
|
|
}
|