Stacks: Use "Stackable" int8 instead of "Unstacked" bool #616 #667

This commit is contained in:
Michael Mayer
2020-12-19 19:15:32 +01:00
parent 4891377d35
commit 12cb89eca5
16 changed files with 126 additions and 105 deletions

View File

@@ -88,6 +88,21 @@
<td>{{ model.CameraSerial }} <td>{{ model.CameraSerial }}
</td> </td>
</tr> </tr>
<tr v-if="model.Stack < 1">
<td>
<translate>Stackable</translate>
</td>
<td>
<v-switch
@change="save"
hide-details
:true-value="0"
:false-value="-1"
v-model="model.Stack"
:label="model.Stack ? $gettext('Yes') : $gettext('No')"
></v-switch>
</td>
</tr>
<tr> <tr>
<td> <td>
<translate>Favorite</translate> <translate>Favorite</translate>
@@ -114,19 +129,6 @@
></v-switch> ></v-switch>
</td> </td>
</tr> </tr>
<tr>
<td>
<translate>Unstacked</translate>
</td>
<td>
<v-switch
@change="save"
hide-details
v-model="model.Single"
:label="model.Single ? $gettext('Yes') : $gettext('No')"
></v-switch>
</td>
</tr>
<tr> <tr>
<td> <td>
<translate>Scan</translate> <translate>Scan</translate>

View File

@@ -57,6 +57,7 @@ export class Photo extends RestModel {
DocumentID: "", DocumentID: "",
Type: TypeImage, Type: TypeImage,
TypeSrc: "", TypeSrc: "",
Stack: 0,
Favorite: false, Favorite: false,
Private: false, Private: false,
Scan: false, Scan: false,

View File

@@ -50,7 +50,7 @@ func StartIndexing(router *gin.RouterGroup) {
Rescan: f.Rescan, Rescan: f.Rescan,
Convert: conf.Settings().Index.Convert && conf.SidecarWritable(), Convert: conf.Settings().Index.Convert && conf.SidecarWritable(),
Path: filepath.Clean(f.Path), Path: filepath.Clean(f.Path),
Single: false, Stack: true,
} }
if len(indOpt.Path) > 1 { if len(indOpt.Path) > 1 {

View File

@@ -114,7 +114,7 @@ func PhotoUnstack(router *gin.RouterGroup) {
files = related.Files files = related.Files
} }
newPhoto := entity.NewPhoto(true) newPhoto := entity.NewPhoto(false)
newPhoto.PhotoPath = unstackFile.RootRelPath() newPhoto.PhotoPath = unstackFile.RootRelPath()
newPhoto.PhotoName = unstackFile.BasePrefix(false) newPhoto.PhotoName = unstackFile.BasePrefix(false)
@@ -175,7 +175,7 @@ func PhotoUnstack(router *gin.RouterGroup) {
} }
// Re-index existing photo stack. // Re-index existing photo stack.
if res := ind.FileName(photoprism.FileName(stackPrimary.FileRoot, stackPrimary.FileName), photoprism.IndexOptionsAll()); res.Failed() { if res := ind.FileName(photoprism.FileName(stackPrimary.FileRoot, stackPrimary.FileName), photoprism.IndexOptionsSingle()); res.Failed() {
log.Errorf("photo: %s (unstack %s)", res.Err, txt.Quote(baseName)) log.Errorf("photo: %s (unstack %s)", res.Err, txt.Quote(baseName))
AbortSaveFailed(c) AbortSaveFailed(c)
return return

View File

@@ -63,7 +63,7 @@ func indexAction(ctx *cli.Context) error {
Path: subPath, Path: subPath,
Rescan: ctx.Bool("all"), Rescan: ctx.Bool("all"),
Convert: conf.Settings().Index.Convert && conf.SidecarWritable(), Convert: conf.Settings().Index.Convert && conf.SidecarWritable(),
Single: false, Stack: true,
} }
indexed := ind.Start(indOpt) indexed := ind.Start(indOpt)

View File

@@ -1,7 +1,7 @@
package entity package entity
const ( const (
// Sort orders. // Sort orders:
SortOrderAdded = "added" SortOrderAdded = "added"
SortOrderNewest = "newest" SortOrderNewest = "newest"
SortOrderOldest = "oldest" SortOrderOldest = "oldest"
@@ -10,13 +10,13 @@ const (
SortOrderRelevance = "relevance" SortOrderRelevance = "relevance"
SortOrderEdited = "edited" SortOrderEdited = "edited"
// Unknown values. // Unknown values:
YearUnknown = -1 YearUnknown = -1
MonthUnknown = -1 MonthUnknown = -1
DayUnknown = -1 DayUnknown = -1
TitleUnknown = "Unknown" TitleUnknown = "Unknown"
// Content types. // Content types:
TypeDefault = "" TypeDefault = ""
TypeImage = "image" TypeImage = "image"
TypeLive = "live" TypeLive = "live"
@@ -24,7 +24,7 @@ const (
TypeRaw = "raw" TypeRaw = "raw"
TypeText = "text" TypeText = "text"
// Root directories. // Root directories:
RootUnknown = "" RootUnknown = ""
RootOriginals = "/" RootOriginals = "/"
RootExamples = "examples" RootExamples = "examples"
@@ -32,14 +32,19 @@ const (
RootImport = "import" RootImport = "import"
RootPath = "/" RootPath = "/"
// Panorama projections. // Panorama projections:
ProjectionDefault = "" ProjectionDefault = ""
ProjectionEquirectangular = "equirectangular" ProjectionEquirectangular = "equirectangular"
ProjectionCubestrip = "cubestrip" ProjectionCubestrip = "cubestrip"
ProjectionCylindrical = "cylindrical" ProjectionCylindrical = "cylindrical"
// Event names. // Event names:
Updated = "updated" Updated = "updated"
Created = "created" Created = "created"
Deleted = "deleted" Deleted = "deleted"
// Photo stacks:
IsStacked int8 = 1
IsStackable int8 = 0
IsUnstacked int8 = -1
) )

View File

@@ -97,7 +97,7 @@ func TestDetails_NoCopyright(t *testing.T) {
func TestNewDetails(t *testing.T) { func TestNewDetails(t *testing.T) {
t.Run("add to photo", func(t *testing.T) { t.Run("add to photo", func(t *testing.T) {
p := NewPhoto(false) p := NewPhoto(true)
assert.Equal(t, TitleUnknown, p.PhotoTitle) assert.Equal(t, TitleUnknown, p.PhotoTitle)

View File

@@ -57,8 +57,8 @@ type Photo struct {
PhotoPath string `gorm:"type:VARBINARY(500);index:idx_photos_path_name;" json:"Path" yaml:"-"` PhotoPath string `gorm:"type:VARBINARY(500);index:idx_photos_path_name;" json:"Path" yaml:"-"`
PhotoName string `gorm:"type:VARBINARY(255);index:idx_photos_path_name;" json:"Name" yaml:"-"` PhotoName string `gorm:"type:VARBINARY(255);index:idx_photos_path_name;" json:"Name" yaml:"-"`
OriginalName string `gorm:"type:VARBINARY(755);" json:"OriginalName" yaml:"OriginalName,omitempty"` OriginalName string `gorm:"type:VARBINARY(755);" json:"OriginalName" yaml:"OriginalName,omitempty"`
PhotoStack int8 `json:"Stack" yaml:"Stack"`
PhotoFavorite bool `json:"Favorite" yaml:"Favorite,omitempty"` PhotoFavorite bool `json:"Favorite" yaml:"Favorite,omitempty"`
PhotoSingle bool `json:"Single" yaml:"Single,omitempty"`
PhotoPrivate bool `json:"Private" yaml:"Private,omitempty"` PhotoPrivate bool `json:"Private" yaml:"Private,omitempty"`
PhotoScan bool `json:"Scan" yaml:"Scan,omitempty"` PhotoScan bool `json:"Scan" yaml:"Scan,omitempty"`
PhotoPanorama bool `json:"Panorama" yaml:"Panorama,omitempty"` PhotoPanorama bool `json:"Panorama" yaml:"Panorama,omitempty"`
@@ -102,11 +102,10 @@ type Photo struct {
} }
// NewPhoto creates a photo entity. // NewPhoto creates a photo entity.
func NewPhoto(single bool) Photo { func NewPhoto(stackable bool) Photo {
return Photo{ m := Photo{
PhotoTitle: TitleUnknown, PhotoTitle: TitleUnknown,
PhotoType: TypeImage, PhotoType: TypeImage,
PhotoSingle: single,
PhotoCountry: UnknownCountry.ID, PhotoCountry: UnknownCountry.ID,
CameraID: UnknownCamera.ID, CameraID: UnknownCamera.ID,
LensID: UnknownLens.ID, LensID: UnknownLens.ID,
@@ -117,6 +116,14 @@ func NewPhoto(single bool) Photo {
Cell: &UnknownLocation, Cell: &UnknownLocation,
Place: &UnknownPlace, Place: &UnknownPlace,
} }
if stackable {
m.PhotoStack = IsStackable
} else {
m.PhotoStack = IsUnstacked
}
return m
} }
// SavePhotoForm saves a model in the database using form data. // SavePhotoForm saves a model in the database using form data.

View File

@@ -18,15 +18,15 @@ func (m *Photo) ResolvePrimary() error {
// Identical returns identical photos that can be merged. // Identical returns identical photos that can be merged.
func (m *Photo) Identical(includeMeta, includeUuid bool) (identical Photos, err error) { func (m *Photo) Identical(includeMeta, includeUuid bool) (identical Photos, err error) {
if m.PhotoSingle || m.PhotoName == "" { if m.PhotoStack == IsUnstacked || m.PhotoName == "" {
return identical, nil return identical, nil
} }
switch { switch {
case includeMeta && includeUuid && m.HasLocation() && m.HasLatLng() && m.TakenSrc == SrcMeta && rnd.IsUUID(m.UUID): case includeMeta && includeUuid && m.HasLocation() && m.HasLatLng() && m.TakenSrc == SrcMeta && rnd.IsUUID(m.UUID):
if err := Db(). if err := Db().
Where("(taken_at = ? AND taken_src = 'meta' AND photo_single = 0 AND cell_id = ? AND camera_serial = ? AND camera_id = ?) "+ Where("(taken_at = ? AND taken_src = 'meta' AND photo_stack > -1 AND cell_id = ? AND camera_serial = ? AND camera_id = ?) "+
"OR (uuid = ? AND photo_single = 0)"+ "OR (uuid = ? AND photo_stack > -1)"+
"OR (photo_path = ? AND photo_name = ?)", "OR (photo_path = ? AND photo_name = ?)",
m.TakenAt, m.CellID, m.CameraSerial, m.CameraID, m.UUID, m.PhotoPath, m.PhotoName). m.TakenAt, m.CellID, m.CameraSerial, m.CameraID, m.UUID, m.PhotoPath, m.PhotoName).
Order("id ASC").Find(&identical).Error; err != nil { Order("id ASC").Find(&identical).Error; err != nil {
@@ -34,7 +34,7 @@ func (m *Photo) Identical(includeMeta, includeUuid bool) (identical Photos, err
} }
case includeMeta && m.HasLocation() && m.HasLatLng() && m.TakenSrc == SrcMeta: case includeMeta && m.HasLocation() && m.HasLatLng() && m.TakenSrc == SrcMeta:
if err := Db(). if err := Db().
Where("(taken_at = ? AND taken_src = 'meta' AND photo_single = 0 AND cell_id = ? AND camera_serial = ? AND camera_id = ?) "+ Where("(taken_at = ? AND taken_src = 'meta' AND photo_stack > -1 AND cell_id = ? AND camera_serial = ? AND camera_id = ?) "+
"OR (photo_path = ? AND photo_name = ?)", "OR (photo_path = ? AND photo_name = ?)",
m.TakenAt, m.CellID, m.CameraSerial, m.CameraID, m.PhotoPath, m.PhotoName). m.TakenAt, m.CellID, m.CameraSerial, m.CameraID, m.PhotoPath, m.PhotoName).
Order("id ASC").Find(&identical).Error; err != nil { Order("id ASC").Find(&identical).Error; err != nil {
@@ -42,7 +42,7 @@ func (m *Photo) Identical(includeMeta, includeUuid bool) (identical Photos, err
} }
case includeUuid && rnd.IsUUID(m.UUID): case includeUuid && rnd.IsUUID(m.UUID):
if err := Db(). if err := Db().
Where("(uuid = ? AND photo_single = 0) OR (photo_path = ? AND photo_name = ?)", Where("(uuid = ? AND photo_stack > -1) OR (photo_path = ? AND photo_name = ?)",
m.UUID, m.PhotoPath, m.PhotoName). m.UUID, m.PhotoPath, m.PhotoName).
Order("id ASC").Find(&identical).Error; err != nil { Order("id ASC").Find(&identical).Error; err != nil {
return identical, err return identical, err

View File

@@ -33,9 +33,9 @@ type Photo struct {
PhotoDescription string `json:"Description"` PhotoDescription string `json:"Description"`
DescriptionSrc string `json:"DescriptionSrc"` DescriptionSrc string `json:"DescriptionSrc"`
Details Details `json:"Details"` Details Details `json:"Details"`
PhotoStack int8 `json:"Stack"`
PhotoFavorite bool `json:"Favorite"` PhotoFavorite bool `json:"Favorite"`
PhotoPrivate bool `json:"Private"` PhotoPrivate bool `json:"Private"`
PhotoSingle bool `json:"Single"`
PhotoScan bool `json:"Scan"` PhotoScan bool `json:"Scan"`
PhotoPanorama bool `json:"Panorama"` PhotoPanorama bool `json:"Panorama"`
PhotoAltitude int `json:"Altitude"` PhotoAltitude int `json:"Altitude"`

View File

@@ -6,60 +6,61 @@ import (
// PhotoSearch represents search form fields for "/api/v1/photos". // PhotoSearch represents search form fields for "/api/v1/photos".
type PhotoSearch struct { type PhotoSearch struct {
Query string `form:"q"` Query string `form:"q"`
Filter string `form:"filter"` Filter string `form:"filter"`
ID string `form:"id"` ID string `form:"id"`
Type string `form:"type"` Type string `form:"type"`
Path string `form:"path"` Path string `form:"path"`
Folder string `form:"folder"` // Alias for Path Folder string `form:"folder"` // Alias for Path
Name string `form:"name"` Name string `form:"name"`
Filename string `form:"filename"` Filename string `form:"filename"`
Original string `form:"original"` Original string `form:"original"`
Title string `form:"title"` Title string `form:"title"`
Hash string `form:"hash"` Hash string `form:"hash"`
Primary bool `form:"primary"` Primary bool `form:"primary"`
Single bool `form:"single"` Stack bool `form:"stack"`
Video bool `form:"video"` Unstacked bool `form:"unstacked"`
Photo bool `form:"photo"` Stackable bool `form:"stackable"`
Scan bool `form:"scan"` Video bool `form:"video"`
Panorama bool `form:"panorama"` Photo bool `form:"photo"`
Error bool `form:"error"` Scan bool `form:"scan"`
Hidden bool `form:"hidden"` Panorama bool `form:"panorama"`
Archived bool `form:"archived"` Error bool `form:"error"`
Public bool `form:"public"` Hidden bool `form:"hidden"`
Private bool `form:"private"` Archived bool `form:"archived"`
Favorite bool `form:"favorite"` Public bool `form:"public"`
Unsorted bool `form:"unsorted"` Private bool `form:"private"`
Stack bool `form:"stack"` Favorite bool `form:"favorite"`
Lat float32 `form:"lat"` Unsorted bool `form:"unsorted"`
Lng float32 `form:"lng"` Lat float32 `form:"lat"`
Dist uint `form:"dist"` Lng float32 `form:"lng"`
Fmin float32 `form:"fmin"` Dist uint `form:"dist"`
Fmax float32 `form:"fmax"` Fmin float32 `form:"fmin"`
Chroma uint8 `form:"chroma"` Fmax float32 `form:"fmax"`
Diff uint32 `form:"diff"` Chroma uint8 `form:"chroma"`
Mono bool `form:"mono"` Diff uint32 `form:"diff"`
Portrait bool `form:"portrait"` Mono bool `form:"mono"`
Geo bool `form:"geo"` Portrait bool `form:"portrait"`
Album string `form:"album"` Geo bool `form:"geo"`
Label string `form:"label"` Album string `form:"album"`
Category string `form:"category"` // Moments Label string `form:"label"`
Country string `form:"country"` // Moments Category string `form:"category"` // Moments
State string `form:"state"` // Moments Country string `form:"country"` // Moments
Year int `form:"year"` // Moments State string `form:"state"` // Moments
Month int `form:"month"` // Moments Year int `form:"year"` // Moments
Day int `form:"day"` // Moments Month int `form:"month"` // Moments
Color string `form:"color"` Day int `form:"day"` // Moments
Quality int `form:"quality"` Color string `form:"color"`
Review bool `form:"review"` Quality int `form:"quality"`
Camera int `form:"camera"` Review bool `form:"review"`
Lens int `form:"lens"` Camera int `form:"camera"`
Before time.Time `form:"before" time_format:"2006-01-02"` Lens int `form:"lens"`
After time.Time `form:"after" time_format:"2006-01-02"` Before time.Time `form:"before" time_format:"2006-01-02"`
Count int `form:"count" binding:"required" serialize:"-"` After time.Time `form:"after" time_format:"2006-01-02"`
Offset int `form:"offset" serialize:"-"` Count int `form:"count" binding:"required" serialize:"-"`
Order string `form:"order" serialize:"-"` Offset int `form:"offset" serialize:"-"`
Merged bool `form:"merged" serialize:"-"` Order string `form:"order" serialize:"-"`
Merged bool `form:"merged" serialize:"-"`
} }
func (f *PhotoSearch) GetQuery() string { func (f *PhotoSearch) GetQuery() string {

View File

@@ -19,7 +19,7 @@ func TestNewPhoto(t *testing.T) {
PhotoFavorite: false, PhotoFavorite: false,
PhotoPrivate: false, PhotoPrivate: false,
PhotoType: "image", PhotoType: "image",
PhotoSingle: false, PhotoStack: int8(1),
PhotoLat: 9.9999, PhotoLat: 9.9999,
PhotoLng: 8.8888, PhotoLng: 8.8888,
PhotoAltitude: 2, PhotoAltitude: 2,
@@ -50,7 +50,7 @@ func TestNewPhoto(t *testing.T) {
assert.Equal(t, false, r.PhotoFavorite) assert.Equal(t, false, r.PhotoFavorite)
assert.Equal(t, false, r.PhotoPrivate) assert.Equal(t, false, r.PhotoPrivate)
assert.Equal(t, "image", r.PhotoType) assert.Equal(t, "image", r.PhotoType)
assert.Equal(t, false, r.PhotoSingle) assert.Equal(t, int8(1), r.PhotoStack)
assert.Equal(t, float32(9.9999), r.PhotoLat) assert.Equal(t, float32(9.9999), r.PhotoLat)
assert.Equal(t, float32(8.8888), r.PhotoLng) assert.Equal(t, float32(8.8888), r.PhotoLng)
assert.Equal(t, 2, r.PhotoAltitude) assert.Equal(t, 2, r.PhotoAltitude)

View File

@@ -89,10 +89,10 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
file, primaryFile := entity.File{}, entity.File{} file, primaryFile := entity.File{}, entity.File{}
photo := entity.NewPhoto(o.Single) photo := entity.NewPhoto(o.Stack)
metaData := meta.NewData() metaData := meta.NewData()
labels := classify.Labels{} labels := classify.Labels{}
stripSequence := Config().Settings().StackSequences() && !o.Single stripSequence := Config().Settings().StackSequences() && o.Stack
fileRoot, fileBase, filePath, fileName := m.PathNameInfo(stripSequence) fileRoot, fileBase, filePath, fileName := m.PathNameInfo(stripSequence)
fullBase := m.BasePrefix(false) fullBase := m.BasePrefix(false)
@@ -173,14 +173,14 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
// Look for existing photo if file wasn't indexed yet... // Look for existing photo if file wasn't indexed yet...
if !fileExists { if !fileExists {
if photoQuery = entity.UnscopedDb().First(&photo, "photo_path = ? AND photo_name = ?", filePath, fullBase); photoQuery.Error == nil || fileBase == fullBase || o.Single { if photoQuery = entity.UnscopedDb().First(&photo, "photo_path = ? AND photo_name = ?", filePath, fullBase); photoQuery.Error == nil || fileBase == fullBase || !o.Stack {
// Skip next query. // Skip next query.
} else if photoQuery = entity.UnscopedDb().First(&photo, "photo_path = ? AND photo_name = ? AND photo_single = 0", filePath, fileBase); photoQuery.Error == nil { } else if photoQuery = entity.UnscopedDb().First(&photo, "photo_path = ? AND photo_name = ? AND photo_stack > -1", filePath, fileBase); photoQuery.Error == nil {
fileStacked = true fileStacked = true
} }
// Stack file based on matching location and time metadata? // Stack file based on matching location and time metadata?
if !o.Single && photoQuery.Error != nil && Config().Settings().StackMeta() && m.MetaData().HasTimeAndPlace() { if o.Stack && photoQuery.Error != nil && Config().Settings().StackMeta() && m.MetaData().HasTimeAndPlace() {
metaData = m.MetaData() metaData = m.MetaData()
photoQuery = entity.UnscopedDb().First(&photo, "photo_lat = ? AND photo_lng = ? AND taken_at = ? AND taken_src = 'meta' AND camera_serial = ?", metaData.Lat, metaData.Lng, metaData.TakenAt, metaData.CameraSerial) photoQuery = entity.UnscopedDb().First(&photo, "photo_lat = ? AND photo_lng = ? AND taken_at = ? AND taken_src = 'meta' AND camera_serial = ?", metaData.Lat, metaData.Lng, metaData.TakenAt, metaData.CameraSerial)
@@ -190,7 +190,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
} }
// Stack file based on the same unique ID? // Stack file based on the same unique ID?
if !o.Single && photoQuery.Error != nil && Config().Settings().StackUUID() && m.MetaData().HasDocumentID() { if o.Stack && photoQuery.Error != nil && Config().Settings().StackUUID() && m.MetaData().HasDocumentID() {
photoQuery = entity.UnscopedDb().First(&photo, "uuid <> '' AND uuid = ?", m.MetaData().DocumentID) photoQuery = entity.UnscopedDb().First(&photo, "uuid <> '' AND uuid = ?", m.MetaData().DocumentID)
if photoQuery.Error == nil { if photoQuery.Error == nil {
@@ -229,7 +229,10 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
// Try to recover photo metadata from backup if not exists. // Try to recover photo metadata from backup if not exists.
if !photoExists { if !photoExists {
photo.PhotoQuality = -1 photo.PhotoQuality = -1
photo.PhotoSingle = o.Single
if o.Stack {
photo.PhotoStack = entity.IsStackable
}
if yamlName := fs.FormatYaml.FindFirst(m.FileName(), []string{Config().SidecarPath(), fs.HiddenPath}, Config().OriginalsPath(), stripSequence); yamlName != "" { if yamlName := fs.FormatYaml.FindFirst(m.FileName(), []string{Config().SidecarPath(), fs.HiddenPath}, Config().OriginalsPath(), stripSequence); yamlName != "" {
if err := photo.LoadFromYaml(yamlName); err != nil { if err := photo.LoadFromYaml(yamlName); err != nil {
@@ -250,7 +253,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
photo.PhotoPath = filePath photo.PhotoPath = filePath
if o.Single || photo.PhotoSingle || !stripSequence { if !o.Stack || !stripSequence || photo.PhotoStack == entity.IsUnstacked {
photo.PhotoName = fullBase photo.PhotoName = fullBase
} else { } else {
photo.PhotoName = fileBase photo.PhotoName = fileBase
@@ -823,7 +826,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
log.Errorf("index: %s in %s (set download id)", err, logName) log.Errorf("index: %s in %s (set download id)", err, logName)
} }
if o.Single || photo.PhotoSingle { if !o.Stack || photo.PhotoStack == entity.IsUnstacked {
// Do nothing. // Do nothing.
} else if original, merged, err := photo.Merge(Config().Settings().StackMeta(), Config().Settings().StackUUID()); err != nil { } else if original, merged, err := photo.Merge(Config().Settings().StackMeta(), Config().Settings().StackUUID()); err != nil {
log.Errorf("index: %s in %s (merge)", err.Error(), logName) log.Errorf("index: %s in %s (merge)", err.Error(), logName)

View File

@@ -4,7 +4,7 @@ type IndexOptions struct {
Path string Path string
Rescan bool Rescan bool
Convert bool Convert bool
Single bool Stack bool
} }
func (o *IndexOptions) SkipUnchanged() bool { func (o *IndexOptions) SkipUnchanged() bool {
@@ -17,7 +17,7 @@ func IndexOptionsAll() IndexOptions {
Path: "/", Path: "/",
Rescan: true, Rescan: true,
Convert: true, Convert: true,
Single: false, Stack: true,
} }
return result return result
@@ -29,7 +29,7 @@ func IndexOptionsSingle() IndexOptions {
Path: "/", Path: "/",
Rescan: true, Rescan: true,
Convert: true, Convert: true,
Single: true, Stack: false,
} }
return result return result

View File

@@ -31,8 +31,8 @@ type PhotoResult struct {
PhotoMonth int `json:"Month"` PhotoMonth int `json:"Month"`
PhotoDay int `json:"Day"` PhotoDay int `json:"Day"`
PhotoCountry string `json:"Country"` PhotoCountry string `json:"Country"`
PhotoStack int8 `json:"Stack"`
PhotoFavorite bool `json:"Favorite"` PhotoFavorite bool `json:"Favorite"`
PhotoSingle bool `json:"Single"`
PhotoPrivate bool `json:"Private"` PhotoPrivate bool `json:"Private"`
PhotoIso int `json:"Iso"` PhotoIso int `json:"Iso"`
PhotoFocalLength int `json:"FocalLength"` PhotoFocalLength int `json:"FocalLength"`

View File

@@ -201,8 +201,10 @@ func PhotoSearch(f form.PhotoSearch) (results PhotoResults, count int, err error
s = s.Where("photos.photo_panorama = 1") s = s.Where("photos.photo_panorama = 1")
} }
if f.Single { if f.Stackable {
s = s.Where("photos.photo_single = 1") s = s.Where("photos.photo_stack > -1")
} else if f.Unstacked {
s = s.Where("photos.photo_stack = -1")
} }
if f.Country != "" { if f.Country != "" {