2020-01-05 14:18:40 +01:00
package query
2019-12-11 07:37:39 +01:00
import (
"fmt"
"strings"
"time"
2019-12-11 16:55:18 +01:00
"github.com/photoprism/photoprism/internal/entity"
2019-12-11 07:37:39 +01:00
"github.com/photoprism/photoprism/internal/form"
2020-01-13 11:07:09 +01:00
"github.com/photoprism/photoprism/pkg/capture"
2020-07-06 07:41:33 +02:00
"github.com/photoprism/photoprism/pkg/txt"
2019-12-11 07:37:39 +01:00
)
// AlbumResult contains found albums
type AlbumResult struct {
2020-05-26 09:02:19 +02:00
ID uint ` json:"-" `
AlbumUID string ` json:"UID" `
CoverUID string ` json:"CoverUID" `
FolderUID string ` json:"FolderUID" `
AlbumSlug string ` json:"Slug" `
AlbumType string ` json:"Type" `
AlbumTitle string ` json:"Title" `
2020-07-12 16:36:39 +02:00
AlbumLocation string ` json:"Location" `
2020-05-26 09:02:19 +02:00
AlbumCategory string ` json:"Category" `
AlbumCaption string ` json:"Caption" `
AlbumDescription string ` json:"Description" `
AlbumNotes string ` json:"Notes" `
AlbumFilter string ` json:"Filter" `
AlbumOrder string ` json:"Order" `
AlbumTemplate string ` json:"Template" `
2020-12-08 19:47:27 +01:00
AlbumPath string ` json:"Path" `
2020-05-26 09:02:19 +02:00
AlbumCountry string ` json:"Country" `
AlbumYear int ` json:"Year" `
AlbumMonth int ` json:"Month" `
2020-07-06 07:41:33 +02:00
AlbumDay int ` json:"Day" `
2020-05-26 09:02:19 +02:00
AlbumFavorite bool ` json:"Favorite" `
AlbumPrivate bool ` json:"Private" `
PhotoCount int ` json:"PhotoCount" `
LinkCount int ` json:"LinkCount" `
CreatedAt time . Time ` json:"CreatedAt" `
UpdatedAt time . Time ` json:"UpdatedAt" `
DeletedAt time . Time ` json:"DeletedAt,omitempty" `
2019-12-11 07:37:39 +01:00
}
2020-06-23 07:55:33 +02:00
type AlbumResults [ ] AlbumResult
2020-05-23 20:58:58 +02:00
// AlbumByUID returns a Album based on the UID.
func AlbumByUID ( albumUID string ) ( album entity . Album , err error ) {
2020-06-22 15:16:26 +02:00
if err := Db ( ) . Where ( "album_uid = ?" , albumUID ) . First ( & album ) . Error ; err != nil {
2019-12-11 07:37:39 +01:00
return album , err
}
return album , nil
}
2020-05-30 01:41:47 +02:00
// AlbumCoverByUID returns a album preview file based on the uid.
func AlbumCoverByUID ( albumUID string ) ( file entity . File , err error ) {
a := entity . Album { }
if err := Db ( ) . Where ( "album_uid = ?" , albumUID ) . First ( & a ) . Error ; err != nil {
return file , err
2020-06-08 18:32:51 +02:00
} else if a . AlbumType != entity . AlbumDefault { // TODO: Optimize
2020-05-30 14:52:47 +02:00
f := form . PhotoSearch { Album : a . AlbumUID , Filter : a . AlbumFilter , Order : entity . SortOrderRelevance , Count : 1 , Offset : 0 , Merged : false }
2020-05-30 01:41:47 +02:00
if photos , _ , err := PhotoSearch ( f ) ; err != nil {
return file , err
} else if len ( photos ) > 0 {
for _ , photo := range photos {
2020-05-30 14:52:47 +02:00
if err := Db ( ) . Where ( "photo_uid = ? AND file_primary = 1" , photo . PhotoUID ) . First ( & file ) . Error ; err != nil {
return file , err
} else {
return file , nil
}
2020-05-30 01:41:47 +02:00
}
}
return file , fmt . Errorf ( "found no cover for moment" )
}
2020-05-30 14:52:47 +02:00
if err := Db ( ) . Where ( "files.file_primary = 1 AND files.file_missing = 0 AND files.file_type = 'jpg' AND files.deleted_at IS NULL" ) .
2020-05-23 20:58:58 +02:00
Joins ( "JOIN albums ON albums.album_uid = ?" , albumUID ) .
2020-11-21 18:05:20 +01:00
Joins ( "JOIN photos_albums pa ON pa.album_uid = albums.album_uid AND pa.photo_uid = files.photo_uid AND pa.hidden = 0" ) .
2020-04-27 17:06:36 +02:00
Joins ( "JOIN photos ON photos.id = files.photo_id AND photos.photo_private = 0 AND photos.deleted_at IS NULL" ) .
2020-04-27 14:25:04 +02:00
Order ( "photos.photo_quality DESC, photos.taken_at DESC" ) .
2019-12-11 07:37:39 +01:00
First ( & file ) . Error ; err != nil {
return file , err
}
return file , nil
}
2020-05-30 21:31:32 +02:00
// AlbumPhotos returns up to count photos from an album.
func AlbumPhotos ( a entity . Album , count int ) ( results PhotoResults , err error ) {
results , _ , err = PhotoSearch ( form . PhotoSearch {
Album : a . AlbumUID ,
Filter : a . AlbumFilter ,
Count : count ,
Offset : 0 ,
} )
return results , err
}
2020-05-25 19:10:44 +02:00
// AlbumSearch searches albums based on their name.
2020-06-23 07:55:33 +02:00
func AlbumSearch ( f form . AlbumSearch ) ( results AlbumResults , err error ) {
2019-12-11 07:37:39 +01:00
if err := f . ParseQueryString ( ) ; err != nil {
return results , err
}
2020-05-23 20:58:58 +02:00
defer log . Debug ( capture . Time ( time . Now ( ) , fmt . Sprintf ( "albums: search %s" , form . Serialize ( f , true ) ) ) )
2019-12-11 07:37:39 +01:00
2020-06-23 07:55:33 +02:00
s := UnscopedDb ( ) . Table ( "albums" ) .
Select ( "albums.*, cp.photo_count, cl.link_count" ) .
2020-06-23 09:41:59 +02:00
Joins ( "LEFT JOIN (SELECT album_uid, count(photo_uid) AS photo_count FROM photos_albums WHERE hidden = 0 GROUP BY album_uid) AS cp ON cp.album_uid = albums.album_uid" ) .
2020-06-23 07:55:33 +02:00
Joins ( "LEFT JOIN (SELECT share_uid, count(share_uid) AS link_count FROM links GROUP BY share_uid) AS cl ON cl.share_uid = albums.album_uid" ) .
2020-12-08 22:40:13 +01:00
Where ( "albums.album_type <> 'folder' OR albums.album_path IN (SELECT photos.photo_path FROM photos WHERE photos.deleted_at IS NULL)" ) .
2020-06-23 07:55:33 +02:00
Where ( "albums.deleted_at IS NULL" )
2019-12-11 07:37:39 +01:00
2020-01-30 18:19:26 +01:00
if f . ID != "" {
2020-05-29 12:21:17 +02:00
s = s . Where ( "albums.album_uid IN (?)" , strings . Split ( f . ID , "," ) )
2020-01-30 18:19:26 +01:00
2020-03-28 17:17:41 +01:00
if result := s . Scan ( & results ) ; result . Error != nil {
2020-01-30 18:19:26 +01:00
return results , result . Error
}
return results , nil
}
2019-12-11 07:37:39 +01:00
if f . Query != "" {
likeString := "%" + strings . ToLower ( f . Query ) + "%"
2020-07-12 16:36:39 +02:00
s = s . Where ( "LOWER(albums.album_title) LIKE ? OR LOWER(albums.album_location) LIKE ?" , likeString , likeString )
2019-12-11 07:37:39 +01:00
}
2020-05-29 12:21:17 +02:00
if f . Type != "" {
s = s . Where ( "albums.album_type IN (?)" , strings . Split ( f . Type , "," ) )
}
if f . Category != "" {
s = s . Where ( "albums.album_category IN (?)" , strings . Split ( f . Category , "," ) )
}
2020-07-12 16:36:39 +02:00
if f . Location != "" {
s = s . Where ( "albums.album_location IN (?)" , strings . Split ( f . Location , "," ) )
}
2020-05-14 19:03:12 +02:00
if f . Favorite {
2020-03-28 17:17:41 +01:00
s = s . Where ( "albums.album_favorite = 1" )
2019-12-11 07:37:39 +01:00
}
2020-07-06 07:41:33 +02:00
if ( f . Year > 0 && f . Year <= txt . YearMax ) || f . Year == entity . YearUnknown {
s = s . Where ( "albums.album_year = ?" , f . Year )
}
if ( f . Month >= txt . MonthMin && f . Month <= txt . MonthMax ) || f . Month == entity . MonthUnknown {
s = s . Where ( "albums.album_month = ?" , f . Month )
}
if ( f . Day >= txt . DayMin && f . Month <= txt . DayMax ) || f . Day == entity . DayUnknown {
s = s . Where ( "albums.album_day = ?" , f . Day )
}
2019-12-11 07:37:39 +01:00
switch f . Order {
case "slug" :
2020-03-28 17:17:41 +01:00
s = s . Order ( "albums.album_favorite DESC, album_slug ASC" )
2019-12-11 07:37:39 +01:00
default :
2020-07-06 07:41:33 +02:00
s = s . Order ( "albums.album_favorite DESC, albums.album_year DESC, albums.album_month DESC, albums.album_day DESC, albums.album_title, albums.created_at DESC" )
2019-12-11 07:37:39 +01:00
}
2020-07-01 09:49:03 +02:00
if f . Count > 0 && f . Count <= MaxResults {
2020-03-28 17:17:41 +01:00
s = s . Limit ( f . Count ) . Offset ( f . Offset )
2019-12-11 07:37:39 +01:00
} else {
2020-07-01 09:49:03 +02:00
s = s . Limit ( MaxResults ) . Offset ( f . Offset )
2019-12-11 07:37:39 +01:00
}
2020-03-28 17:17:41 +01:00
if result := s . Scan ( & results ) ; result . Error != nil {
2019-12-11 07:37:39 +01:00
return results , result . Error
}
return results , nil
}