Replace database, mostly album related

This commit is contained in:
viktorstrate
2020-11-23 19:59:01 +01:00
parent 27b2f95a7b
commit 3c23f82330
11 changed files with 75 additions and 147 deletions

View File

@@ -71,7 +71,13 @@ func SetupDatabase() (*gorm.DB, error) {
func MigrateDatabase(db *gorm.DB) error {
db.AutoMigrate(&models.User{}, &models.AccessToken{})
db.AutoMigrate(
&models.User{},
&models.AccessToken{},
&models.SiteInfo{},
&models.Album{},
&models.MediaEXIF{},
)
return nil
}

View File

@@ -1,48 +1,19 @@
package models
import (
"database/sql"
"gorm.io/gorm"
)
type Album struct {
AlbumID int
gorm.Model
Title string
ParentAlbum *int
OwnerID int
Owner User
Path string
PathHash string
}
func (a *Album) ID() int {
return a.AlbumID
}
func (a *Album) FilePath() string {
return a.Path
}
func NewAlbumFromRow(row *sql.Row) (*Album, error) {
album := Album{}
if err := row.Scan(&album.AlbumID, &album.Title, &album.ParentAlbum, &album.OwnerID, &album.Path, &album.PathHash); err != nil {
return nil, err
}
return &album, nil
}
func NewAlbumsFromRows(rows *sql.Rows) ([]*Album, error) {
albums := make([]*Album, 0)
for rows.Next() {
var album Album
if err := rows.Scan(&album.AlbumID, &album.Title, &album.ParentAlbum, &album.OwnerID, &album.Path, &album.PathHash); err != nil {
return nil, err
}
albums = append(albums, &album)
}
rows.Close()
return albums, nil
}

View File

@@ -52,13 +52,13 @@ type SearchResult struct {
}
// General information about the site
type SiteInfo struct {
InitialSetup bool `json:"initialSetup"`
// How often automatic scans should be initiated in seconds
PeriodicScanInterval int `json:"periodicScanInterval"`
// How many max concurrent scanner jobs that should run at once
ConcurrentWorkers int `json:"concurrentWorkers"`
}
// type SiteInfo struct {
// InitialSetup bool `json:"initialSetup"`
// // How often automatic scans should be initiated in seconds
// PeriodicScanInterval int `json:"periodicScanInterval"`
// // How many max concurrent scanner jobs that should run at once
// ConcurrentWorkers int `json:"concurrentWorkers"`
// }
type MediaType string

View File

@@ -1,12 +1,13 @@
package models
import (
"database/sql"
"time"
"gorm.io/gorm"
)
type MediaEXIF struct {
ExifID int
gorm.Model
Camera *string
Maker *string
Lens *string
@@ -21,21 +22,3 @@ type MediaEXIF struct {
GPSLatitude *float64
GPSLonitude *float64
}
func (exif *MediaEXIF) Media() *Media {
panic("not implemented")
}
func (exif *MediaEXIF) ID() int {
return exif.ExifID
}
func NewMediaExifFromRow(row *sql.Row) (*MediaEXIF, error) {
exif := MediaEXIF{}
if err := row.Scan(&exif.ExifID, &exif.Camera, &exif.Maker, &exif.Lens, &exif.DateShot, &exif.Exposure, &exif.Aperture, &exif.Iso, &exif.FocalLength, &exif.Flash, &exif.Orientation, &exif.ExposureProgram, &exif.GPSLatitude, &exif.GPSLonitude); err != nil {
return nil, err
}
return &exif, nil
}

View File

@@ -5,55 +5,26 @@ import (
"gorm.io/gorm"
)
// func initializeSiteInfoRow(db *gorm.DB) (*SiteInfo, error) {
// _, err := db.Exec("INSERT INTO site_info (initial_setup, periodic_scan_interval, concurrent_workers) VALUES (true, 0, 3)")
// if err != nil {
// return nil, errors.Wrap(err, "initialize site_info row")
// }
// siteInfo := &SiteInfo{}
// row := db.QueryRow("SELECT * FROM site_info")
// if err := row.Scan(&siteInfo.InitialSetup, &siteInfo.PeriodicScanInterval, &siteInfo.ConcurrentWorkers); err != nil {
// return nil, errors.Wrap(err, "get site_info row after initialization")
// }
// return siteInfo, nil
// }
type SiteInfo struct {
gorm.Model
InitialSetup bool
PeriodicScanInterval int
ConcurrentWorkers int
}
// GetSiteInfo gets the site info row from the database, and creates it if it does not exist
func GetSiteInfo(db *gorm.DB) (*SiteInfo, error) {
var siteInfo SiteInfo
result := db.FirstOrCreate(&siteInfo, SiteInfo{
err := db.FirstOrCreate(&siteInfo, SiteInfo{
InitialSetup: true,
PeriodicScanInterval: 0,
ConcurrentWorkers: 3,
})
if result.Error != nil {
return nil, errors.Wrap(result.Error, "get site info from database")
}).Error
if err != nil {
return nil, errors.Wrap(err, "get site info from database")
}
// rows, err := db.Query("SELECT * FROM site_info")
// defer rows.Close()
// if err != nil {
// return nil, err
// }
// siteInfo := &SiteInfo{}
// if !rows.Next() {
// // Entry does not exist
// siteInfo, err = initializeSiteInfoRow(db)
// if err != nil {
// return nil, err
// }
// } else {
// if err := rows.Scan(&siteInfo.InitialSetup, &siteInfo.PeriodicScanInterval, &siteInfo.ConcurrentWorkers); err != nil {
// return nil, err
// }
// }
return &siteInfo, nil
}

View File

@@ -12,7 +12,7 @@ import (
"gorm.io/gorm"
)
func CleanupMedia(db *gorm.DB, albumId int, albumMedia []*models.Media) []error {
func CleanupMedia(db *gorm.DB, albumId uint, albumMedia []*models.Media) []error {
albumMediaIds := make([]interface{}, len(albumMedia))
for i, photo := range albumMedia {
albumMediaIds[i] = photo.MediaID

View File

@@ -5,6 +5,7 @@ import (
"sync"
"time"
"github.com/viktorstrate/photoview/api/graphql/models"
"gorm.io/gorm"
)
@@ -18,14 +19,13 @@ type periodicScanner struct {
var mainPeriodicScanner *periodicScanner = nil
func getPeriodicScanInterval(db *gorm.DB) (time.Duration, error) {
row := db.QueryRow("SELECT periodic_scan_interval FROM site_info")
var intervalSeconds int
if err := row.Scan(&intervalSeconds); err != nil {
var siteInfo models.SiteInfo
if err := db.First(&siteInfo).Error; err != nil {
return 0, err
}
return time.Duration(intervalSeconds) * time.Second, nil
return time.Duration(siteInfo.PeriodicScanInterval) * time.Second, nil
}
func InitializePeriodicScanner(db *gorm.DB) error {

View File

@@ -11,6 +11,7 @@ import (
"github.com/pkg/errors"
"github.com/viktorstrate/photoview/api/graphql/models"
"github.com/viktorstrate/photoview/api/utils"
"gorm.io/gorm"
// Image decoders
_ "image/gif"
@@ -42,7 +43,7 @@ func makePhotoURLChecker(tx *sql.Tx, mediaID int) (func(purpose models.MediaPurp
}, nil
}
func ProcessMedia(tx *sql.Tx, media *models.Media) (bool, error) {
func ProcessMedia(tx *gorm.DB, media *models.Media) (bool, error) {
imageData := EncodeMediaData{
media: media,
}
@@ -65,7 +66,7 @@ func ProcessMedia(tx *sql.Tx, media *models.Media) (bool, error) {
}
}
func processPhoto(tx *sql.Tx, imageData *EncodeMediaData, photoCachePath *string) (bool, error) {
func processPhoto(tx *gorm.DB, imageData *EncodeMediaData, photoCachePath *string) (bool, error) {
photo := imageData.media

View File

@@ -196,7 +196,7 @@ func (queue *ScannerQueue) jobOnQueue(job *ScannerJob) (bool, error) {
scannerJobs := append(queue.in_progress, queue.up_next...)
for _, scannerJob := range scannerJobs {
if scannerJob.album.AlbumID == job.album.AlbumID {
if scannerJob.album.ID == job.album.ID {
return true, nil
}
}

View File

@@ -6,6 +6,7 @@ import (
"path"
"time"
"github.com/pkg/errors"
"github.com/viktorstrate/photoview/api/graphql/models"
"github.com/viktorstrate/photoview/api/graphql/notification"
"github.com/viktorstrate/photoview/api/utils"
@@ -37,37 +38,35 @@ func scanAlbum(album *models.Album, cache *AlbumScannerCache, db *gorm.DB) {
album_has_changes := false
for count, photo := range albumPhotos {
tx, err := db.Begin()
if err != nil {
ScannerError("Failed to begin database transaction: %s", err)
}
// tx, err := db.Begin()
processing_was_needed, err := ProcessMedia(tx, photo)
if err != nil {
tx.Rollback()
ScannerError("Failed to process photo (%s): %s", photo.Path, err)
continue
}
transactionResult := 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)
}
if processing_was_needed {
album_has_changes = true
progress := float64(count) / float64(len(albumPhotos)) * 100.0
notification.BroadcastNotification(&models.Notification{
Key: album_notify_key,
Type: models.NotificationTypeProgress,
Header: fmt.Sprintf("Processing media for album '%s'", album.Title),
Content: fmt.Sprintf("Processed media at %s", photo.Path),
Progress: &progress,
})
}
if processing_was_needed {
album_has_changes = true
progress := float64(count) / float64(len(albumPhotos)) * 100.0
notification.BroadcastNotification(&models.Notification{
Key: album_notify_key,
Type: models.NotificationTypeProgress,
Header: fmt.Sprintf("Processing media for album '%s'", album.Title),
Content: fmt.Sprintf("Processed media at %s", photo.Path),
Progress: &progress,
})
}
err = tx.Commit()
if err != nil {
ScannerError("Failed to commit database transaction: %s", err)
return nil
})
if transactionResult.Error != nil {
ScannerError("Failed to begin database transaction: %s", transactionResult.Error)
}
}
cleanup_errors := CleanupMedia(db, album.AlbumID, albumPhotos)
cleanup_errors := CleanupMedia(db, album.ID, albumPhotos)
for _, err := range cleanup_errors {
ScannerError("Failed to delete old media: %s", err)
}
@@ -98,24 +97,20 @@ func findMediaForAlbum(album *models.Album, cache *AlbumScannerCache, db *gorm.D
photoPath := path.Join(album.Path, item.Name())
if !item.IsDir() && isPathMedia(photoPath, cache) {
tx, err := db.Begin()
if err != nil {
ScannerError("Could not begin database transaction for image %s: %s\n", photoPath, err)
continue
}
photo, isNewPhoto, err := ScanMedia(tx, photoPath, album.AlbumID, cache)
if err != nil {
ScannerError("Scanning media error (%s): %s", photoPath, err)
tx.Rollback()
continue
}
db.Transaction(func(tx *gorm.DB) error {
photo, isNewPhoto, err := ScanMedia(tx, photoPath, album.ID, cache)
if err != nil {
return errors.Wrapf(err, "Scanning media error (%s)", photoPath)
}
onScanPhoto(photo, isNewPhoto)
onScanPhoto(photo, isNewPhoto)
albumPhotos = append(albumPhotos, photo)
albumPhotos = append(albumPhotos, photo)
return nil
})
tx.Commit()
}
}

View File

@@ -11,6 +11,7 @@ import (
"github.com/pkg/errors"
"github.com/viktorstrate/photoview/api/graphql/models"
"gorm.io/gorm"
)
func scanForSideCarFile(path string) *string {
@@ -47,7 +48,7 @@ func hashSideCarFile(path *string) *string {
return &hash
}
func ScanMedia(tx *sql.Tx, mediaPath string, albumId int, cache *AlbumScannerCache) (*models.Media, bool, error) {
func ScanMedia(tx *gorm.DB, mediaPath string, albumId uint, cache *AlbumScannerCache) (*models.Media, bool, error) {
mediaName := path.Base(mediaPath)
// Check if image already exists