2020-01-05 13:18:40 +00:00
package query
2019-12-11 06:37:39 +00:00
import (
2022-11-22 21:14:34 +00:00
"time"
2021-10-05 16:42:39 +00:00
"github.com/dustin/go-humanize/english"
2020-01-29 15:49:42 +00:00
"github.com/jinzhu/gorm"
2021-10-06 09:50:48 +00:00
2019-12-11 15:55:18 +00:00
"github.com/photoprism/photoprism/internal/entity"
2021-10-06 09:50:48 +00:00
"github.com/photoprism/photoprism/internal/mutex"
2019-12-11 06:37:39 +00:00
)
2020-03-28 14:29:17 +00:00
// PhotoByID returns a Photo based on the ID.
2020-05-08 13:41:01 +00:00
func PhotoByID ( photoID uint64 ) ( photo entity . Photo , err error ) {
if err := UnscopedDb ( ) . Where ( "id = ?" , photoID ) .
2020-05-26 17:27:29 +00:00
Preload ( "Labels" , func ( db * gorm . DB ) * gorm . DB {
return db . Order ( "photos_labels.uncertainty ASC, photos_labels.label_id DESC" )
} ) .
Preload ( "Labels.Label" ) .
2020-05-18 20:18:58 +00:00
Preload ( "Camera" ) .
Preload ( "Lens" ) .
Preload ( "Details" ) .
2020-05-27 11:40:21 +00:00
Preload ( "Place" ) .
2020-07-12 06:27:05 +00:00
Preload ( "Cell" ) .
Preload ( "Cell.Place" ) .
2020-04-16 18:57:00 +00:00
First ( & photo ) . Error ; err != nil {
2019-12-11 06:37:39 +00:00
return photo , err
}
return photo , nil
}
2020-05-23 18:58:58 +00:00
// PhotoByUID returns a Photo based on the UID.
func PhotoByUID ( photoUID string ) ( photo entity . Photo , err error ) {
if err := UnscopedDb ( ) . Where ( "photo_uid = ?" , photoUID ) .
2020-05-26 17:27:29 +00:00
Preload ( "Labels" , func ( db * gorm . DB ) * gorm . DB {
return db . Order ( "photos_labels.uncertainty ASC, photos_labels.label_id DESC" )
} ) .
Preload ( "Labels.Label" ) .
2020-05-18 20:18:58 +00:00
Preload ( "Camera" ) .
Preload ( "Lens" ) .
Preload ( "Details" ) .
2020-05-27 11:40:21 +00:00
Preload ( "Place" ) .
2020-07-12 06:27:05 +00:00
Preload ( "Cell" ) .
Preload ( "Cell.Place" ) .
2020-04-16 18:57:00 +00:00
First ( & photo ) . Error ; err != nil {
2019-12-11 06:37:39 +00:00
return photo , err
}
return photo , nil
}
2019-12-11 18:11:44 +00:00
2020-05-26 17:27:29 +00:00
// PhotoPreloadByUID returns a Photo based on the UID with all dependencies preloaded.
func PhotoPreloadByUID ( photoUID string ) ( photo entity . Photo , err error ) {
2020-05-23 18:58:58 +00:00
if err := UnscopedDb ( ) . Where ( "photo_uid = ?" , photoUID ) .
2020-01-29 15:49:42 +00:00
Preload ( "Labels" , func ( db * gorm . DB ) * gorm . DB {
2020-04-18 23:13:55 +00:00
return db . Order ( "photos_labels.uncertainty ASC, photos_labels.label_id DESC" )
2020-01-29 15:49:42 +00:00
} ) .
2020-01-29 14:28:20 +00:00
Preload ( "Labels.Label" ) .
Preload ( "Camera" ) .
Preload ( "Lens" ) .
2020-05-26 17:27:29 +00:00
Preload ( "Details" ) .
2020-05-27 11:40:21 +00:00
Preload ( "Place" ) .
2020-07-12 06:27:05 +00:00
Preload ( "Cell" ) .
Preload ( "Cell.Place" ) .
2020-01-29 14:28:20 +00:00
First ( & photo ) . Error ; err != nil {
2019-12-11 18:11:44 +00:00
return photo , err
}
2020-04-30 18:07:03 +00:00
photo . PreloadMany ( )
2019-12-11 18:11:44 +00:00
return photo , nil
}
2020-05-07 17:42:04 +00:00
2020-05-26 17:27:29 +00:00
// PhotosMissing returns photo entities without existing files.
2020-06-01 07:45:24 +00:00
func PhotosMissing ( limit int , offset int ) ( entities entity . Photos , err error ) {
2020-05-08 13:41:01 +00:00
err = Db ( ) .
2020-05-07 18:33:11 +00:00
Select ( "photos.*" ) .
2021-02-08 13:09:58 +00:00
Where ( "id NOT IN (SELECT photo_id FROM files WHERE file_missing = 0 AND file_root = '/' AND deleted_at IS NULL)" ) .
2022-04-15 07:42:07 +00:00
Where ( "photos.photo_type <> ?" , entity . MediaText ) .
2020-05-07 18:33:11 +00:00
Group ( "photos.id" ) .
Limit ( limit ) . Offset ( offset ) . Find ( & entities ) . Error
2020-05-07 17:42:04 +00:00
return entities , err
}
2020-05-08 10:01:22 +00:00
2021-11-18 01:23:25 +00:00
// PhotosMetadataUpdate returns photos selected for metadata maintenance.
2021-11-20 18:14:00 +00:00
func PhotosMetadataUpdate ( limit , offset int , delay , interval time . Duration ) ( entities entity . Photos , err error ) {
2020-05-26 17:27:29 +00:00
err = Db ( ) .
Preload ( "Labels" , func ( db * gorm . DB ) * gorm . DB {
return db . Order ( "photos_labels.uncertainty ASC, photos_labels.label_id DESC" )
} ) .
Preload ( "Labels.Label" ) .
Preload ( "Camera" ) .
Preload ( "Lens" ) .
Preload ( "Details" ) .
2020-05-27 11:40:21 +00:00
Preload ( "Place" ) .
2020-07-12 06:27:05 +00:00
Preload ( "Cell" ) .
Preload ( "Cell.Place" ) .
2021-11-20 18:14:00 +00:00
Where ( "checked_at IS NULL OR checked_at < ?" , time . Now ( ) . Add ( - 1 * interval ) ) .
2020-12-11 21:09:11 +00:00
Where ( "updated_at < ? OR (cell_id = 'zz' AND photo_lat <> 0)" , time . Now ( ) . Add ( - 1 * delay ) ) .
2020-12-09 20:44:04 +00:00
Order ( "photos.ID ASC" ) . Limit ( limit ) . Offset ( offset ) . Find ( & entities ) . Error
2020-05-26 17:27:29 +00:00
return entities , err
2020-12-09 20:49:41 +00:00
}
2021-01-24 16:46:18 +00:00
2021-02-06 15:30:30 +00:00
// OrphanPhotos finds orphan index entries that may be removed.
func OrphanPhotos ( ) ( photos entity . Photos , err error ) {
2021-01-24 16:46:18 +00:00
err = UnscopedDb ( ) .
Raw ( ` SELECT * FROM photos WHERE
deleted_at IS NOT NULL
AND photo_quality = - 1
AND id NOT IN ( SELECT photo_id FROM files WHERE files . deleted_at IS NULL ) ` ) .
Find ( & photos ) . Error
return photos , err
}
2021-01-24 19:40:40 +00:00
// FixPrimaries tries to set a primary file for photos that have none.
func FixPrimaries ( ) error {
2021-12-09 01:33:41 +00:00
mutex . Index . Lock ( )
defer mutex . Index . Unlock ( )
2021-10-06 09:50:48 +00:00
2021-10-05 16:42:39 +00:00
start := time . Now ( )
2021-10-02 12:24:44 +00:00
2021-01-24 19:40:40 +00:00
var photos entity . Photos
2021-10-06 00:59:27 +00:00
// Remove primary file flag from broken or missing files.
if err := UnscopedDb ( ) . Table ( entity . File { } . TableName ( ) ) .
2022-11-21 11:20:28 +00:00
Where ( "(file_error <> '' OR file_missing = 1) AND file_primary <> 0" ) .
2022-04-04 06:54:03 +00:00
UpdateColumn ( "file_primary" , 0 ) . Error ; err != nil {
2021-10-06 00:59:27 +00:00
return err
}
// Find photos without primary file.
2021-01-24 19:40:40 +00:00
if err := UnscopedDb ( ) .
2021-10-02 12:24:44 +00:00
Raw ( ` SELECT * FROM photos
WHERE deleted_at IS NULL
2021-02-08 06:39:29 +00:00
AND id NOT IN ( SELECT photo_id FROM files WHERE file_primary = 1 ) ` ) .
2021-01-24 19:40:40 +00:00
Find ( & photos ) . Error ; err != nil {
return err
}
2021-10-05 16:42:39 +00:00
if len ( photos ) == 0 {
log . Debugf ( "index: found no photos without primary file [%s]" , time . Since ( start ) )
return nil
}
2021-10-06 00:59:27 +00:00
// Try to find matching primary files.
2021-01-24 19:40:40 +00:00
for _ , p := range photos {
2021-10-05 16:42:39 +00:00
log . Debugf ( "index: searching primary file for %s" , p . PhotoUID )
2021-01-24 19:40:40 +00:00
if err := p . SetPrimary ( "" ) ; err != nil {
2022-04-16 11:50:35 +00:00
log . Infof ( "index: %s" , err )
2021-01-24 19:40:40 +00:00
}
}
2021-12-09 06:41:07 +00:00
log . Debugf ( "index: updated primary files [%s]" , time . Since ( start ) )
2021-10-05 16:42:39 +00:00
2021-01-24 19:40:40 +00:00
return nil
}
2021-10-06 09:50:48 +00:00
// FlagHiddenPhotos sets the quality score of photos without valid primary file to -1.
2022-11-21 11:20:28 +00:00
func FlagHiddenPhotos ( ) ( err error ) {
2021-12-09 01:33:41 +00:00
mutex . Index . Lock ( )
defer mutex . Index . Unlock ( )
2021-10-06 09:50:48 +00:00
2022-11-21 11:20:28 +00:00
// Start time for logs.
2021-10-06 09:50:48 +00:00
start := time . Now ( )
2022-11-21 11:20:28 +00:00
// IDs of hidden photos.
var hidden [ ] uint
2021-10-06 09:50:48 +00:00
2022-11-21 11:20:28 +00:00
// Find and flag hidden photos.
if err = Db ( ) . Table ( entity . Photo { } . TableName ( ) ) .
Where ( "id NOT IN (SELECT photo_id FROM files WHERE file_primary = 1 AND file_missing = 0 AND file_error = '' AND deleted_at IS NULL) AND photo_quality > -1" ) .
Pluck ( "id" , & hidden ) . Error ; err != nil {
// Find failed.
return err
} else if n := len ( hidden ) ; n == 0 {
// Nothing to do.
2021-12-09 06:41:07 +00:00
return nil
2022-11-21 11:20:28 +00:00
} else if err = Db ( ) . Table ( entity . Photo { } . TableName ( ) ) . Where ( "id IN (?)" , hidden ) . UpdateColumn ( "photo_quality" , - 1 ) . Error ; err != nil {
// Update failed.
return err
} else {
// Log result.
log . Infof ( "index: flagged %s as hidden or missing [%s]" , english . Plural ( int ( n ) , "photo" , "photos" ) , time . Since ( start ) )
2021-10-06 09:50:48 +00:00
}
2022-11-21 11:20:28 +00:00
return nil
2021-10-06 09:50:48 +00:00
}