1
Fork 0

Replace database, work on scanning

This commit is contained in:
viktorstrate 2020-11-30 21:29:49 +01:00
parent 364521958b
commit 98f13d76e6
No known key found for this signature in database
GPG Key ID: 3F855605109C1E8A
9 changed files with 69 additions and 71 deletions

View File

@ -10,6 +10,7 @@ import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
// SetupDatabase connects to the database using environment variables
@ -32,39 +33,17 @@ func SetupDatabase() (*gorm.DB, error) {
log.Printf("Connecting to database: %s", address)
db, err := gorm.Open(mysql.Open(address.String()), &gorm.Config{})
config := gorm.Config{}
// Enable database debug logging
config.Logger = logger.Default.LogMode(logger.Info)
db, err := gorm.Open(mysql.Open(address.String()), &config)
if err != nil {
return nil, errors.Wrap(err, "could not connect to database")
}
// var db *sql.DB
// db, err = sql.Open("mysql", address.String())
// if err != nil {
// return nil, errors.New("Could not connect to database, exiting")
// }
// tryCount := 0
// for {
// ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
// defer cancel()
// if err := db.PingContext(ctx); err != nil {
// if tryCount < 4 {
// tryCount++
// log.Printf("WARN: Could not ping database: %s, Will retry after 1 second", err)
// time.Sleep(time.Second)
// continue
// } else {
// return nil, errors.Wrap(err, "Could not ping database, exiting")
// }
// }
// break
// }
// db.SetMaxOpenConns(80)
// TODO: Add connection retries
return db, nil
}

View File

@ -1,11 +1,14 @@
package models
import (
"crypto/md5"
"encoding/hex"
"path"
"strings"
"time"
"github.com/viktorstrate/photoview/api/utils"
"gorm.io/gorm"
)
type Media struct {
@ -32,6 +35,20 @@ func (Media) TableName() string {
return "media"
}
func (m *Media) BeforeSave(tx *gorm.DB) error {
// Update hashes
hash := md5.Sum([]byte(m.Path))
m.PathHash = hex.EncodeToString(hash[:])
if m.SideCarPath != nil {
hash = md5.Sum([]byte(*m.SideCarPath))
encodedHash := hex.EncodeToString(hash[:])
m.SideCarHash = &encodedHash
}
return nil
}
type MediaPurpose string
const (

View File

@ -67,7 +67,7 @@ func (r *albumResolver) Media(ctx context.Context, album *models.Album, filter *
query := r.Database.
Joins("Album").
Where("Album.id = ?", album.ID).
Where("media.id IN (?)", r.Database.Model(&models.MediaURL{})).Select("media_id").Where("media_url.media_id = media.id")
Where("media.id IN (?)", r.Database.Model(&models.MediaURL{}).Select("media_urls.media_id").Where("media_urls.media_id = media.id"))
if onlyFavorites != nil && *onlyFavorites == true {
query = query.Where("media.favorite = 1")

View File

@ -149,7 +149,7 @@ func (r *mediaResolver) Downloads(ctx context.Context, media *models.Media) ([]*
func (r *mediaResolver) HighRes(ctx context.Context, media *models.Media) (*models.MediaURL, error) {
var url models.MediaURL
err := r.Database.
Where("media_url = ?", media.ID).
Where("media_id = ?", media.ID).
Where("purpose = ? OR (purpose = ? AND content_type IN ?)", models.PhotoHighRes, models.MediaOriginal, scanner.WebMimetypes).
First(&url).Error
@ -167,7 +167,7 @@ func (r *mediaResolver) HighRes(ctx context.Context, media *models.Media) (*mode
func (r *mediaResolver) Thumbnail(ctx context.Context, media *models.Media) (*models.MediaURL, error) {
var url models.MediaURL
err := r.Database.
Where("media_url = ?", media.ID).
Where("media_id = ?", media.ID).
Where("purpose = ? OR purpose = ?", models.PhotoThumbnail, models.VideoThumbnail).
First(&url).Error
@ -182,7 +182,7 @@ func (r *mediaResolver) VideoWeb(ctx context.Context, media *models.Media) (*mod
var url models.MediaURL
err := r.Database.
Where("media_url = ?", media.ID).
Where("media_id = ?", media.ID).
Where("purpose = ?", models.VideoWeb).
First(&url).Error

View File

@ -20,14 +20,14 @@ func CleanupMedia(db *gorm.DB, albumId int, albumMedia []*models.Media) []error
// Will get from database
var mediaList []models.Media
db.Where("album_id = ?", albumId)
query := db.Where("album_id = ?", albumId)
// Select media from database that was not found on hard disk
if len(albumMedia) > 0 {
db.Not(albumMediaIds)
query.Where("NOT id IN ?", albumMediaIds)
}
if err := db.Find(&mediaList).Error; err != nil {
if err := query.Find(&mediaList).Error; err != nil {
return []error{errors.Wrap(err, "get media files to be deleted from database")}
}
@ -45,8 +45,10 @@ func CleanupMedia(db *gorm.DB, albumId int, albumMedia []*models.Media) []error
}
if err := db.Where("id IN ?", mediaIDs).Delete(models.Media{}).Error; err != nil {
deleteErrors = append(deleteErrors, errors.Wrap(err, "delete old media from database"))
if len(mediaIDs) > 0 {
if err := db.Where("id IN ?", mediaIDs).Delete(models.Media{}).Error; err != nil {
deleteErrors = append(deleteErrors, errors.Wrap(err, "delete old media from database"))
}
}
return deleteErrors

View File

@ -22,19 +22,22 @@ import (
)
// Higher order function used to check if MediaURL for a given MediaPurpose exists
func makePhotoURLChecker(tx *gorm.DB, mediaID int) (func(purpose models.MediaPurpose) (*models.MediaURL, error), error) {
func makePhotoURLChecker(tx *gorm.DB, mediaID int) func(purpose models.MediaPurpose) (*models.MediaURL, error) {
return func(purpose models.MediaPurpose) (*models.MediaURL, error) {
var mediaURL models.MediaURL
var mediaURL []*models.MediaURL
if err := tx.Where("purpose = ?", purpose).First(&mediaURL, mediaID).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, err
result := tx.Where("purpose = ?", purpose).Find(&mediaURL, mediaID)
if result.Error != nil {
return nil, result.Error
}
return &mediaURL, nil
}, nil
if result.RowsAffected > 0 {
return mediaURL[0], nil
}
return nil, nil
}
}
func ProcessMedia(tx *gorm.DB, media *models.Media) (bool, error) {
@ -68,25 +71,22 @@ func processPhoto(tx *gorm.DB, imageData *EncodeMediaData, photoCachePath *strin
didProcess := false
photoUrlFromDB, err := makePhotoURLChecker(tx, photo.ID)
if err != nil {
return false, err
}
photoURLFromDB := makePhotoURLChecker(tx, photo.ID)
// original photo url
origURL, err := photoUrlFromDB(models.MediaOriginal)
origURL, err := photoURLFromDB(models.MediaOriginal)
if err != nil {
return false, err
}
// Thumbnail
thumbURL, err := photoUrlFromDB(models.PhotoThumbnail)
thumbURL, err := photoURLFromDB(models.PhotoThumbnail)
if err != nil {
return false, errors.Wrap(err, "error processing photo thumbnail")
}
// Highres
highResURL, err := photoUrlFromDB(models.PhotoHighRes)
highResURL, err := photoURLFromDB(models.PhotoHighRes)
if err != nil {
return false, errors.Wrap(err, "error processing photo highres")
}

View File

@ -22,17 +22,14 @@ func processVideo(tx *gorm.DB, mediaData *EncodeMediaData, videoCachePath *strin
log.Printf("Processing video: %s", video.Path)
mediaUrlFromDB, err := makePhotoURLChecker(tx, video.ID)
if err != nil {
return false, err
}
mediaURLFromDB := makePhotoURLChecker(tx, video.ID)
videoWebURL, err := mediaUrlFromDB(models.VideoWeb)
videoWebURL, err := mediaURLFromDB(models.VideoWeb)
if err != nil {
return false, errors.Wrap(err, "error processing video web-format")
}
videoThumbnailURL, err := mediaUrlFromDB(models.VideoThumbnail)
videoThumbnailURL, err := mediaURLFromDB(models.VideoThumbnail)
if err != nil {
return false, errors.Wrap(err, "error processing video thumbnail")
}

View File

@ -40,7 +40,7 @@ func scanAlbum(album *models.Album, cache *AlbumScannerCache, db *gorm.DB) {
for count, photo := range albumPhotos {
// tx, err := db.Begin()
transactionResult := db.Transaction(func(tx *gorm.DB) error {
transactionError := db.Transaction(func(tx *gorm.DB) error {
processing_was_needed, err := ProcessMedia(tx, photo)
if err != nil {
return errors.Wrapf(err, "failed to process photo (%s)", photo.Path)
@ -61,8 +61,8 @@ func scanAlbum(album *models.Album, cache *AlbumScannerCache, db *gorm.DB) {
return nil
})
if transactionResult.Error != nil {
ScannerError("Failed to begin database transaction: %s", transactionResult.Error)
if transactionError != nil {
ScannerError("Failed to begin database transaction: %s", transactionError)
}
}

View File

@ -52,14 +52,17 @@ func ScanMedia(tx *gorm.DB, mediaPath string, albumId int, cache *AlbumScannerCa
// Check if media already exists
{
var media models.Media
if err := tx.Where("path_hash = MD5(?)", mediaPath).First(&media).Error; errors.Is(err, gorm.ErrRecordNotFound) {
if err == nil {
log.Printf("Media already scanned: %s\n", mediaPath)
return &media, false, nil
} else {
return nil, false, errors.Wrap(err, "scan media fetch from database")
}
var media []*models.Media
result := tx.Where("path_hash = MD5(?)", mediaPath).Find(&media)
if result.Error != nil {
return nil, false, errors.Wrap(result.Error, "scan media fetch from database")
}
if result.RowsAffected > 0 {
log.Printf("Media already scanned: %s\n", mediaPath)
return media[0], false, nil
}
}