mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-12 00:34:13 +01:00
Panoramas: Increase projection type string limit to 32 characters #1508
This commit is contained in:
@@ -1,7 +1,69 @@
|
|||||||
package entity
|
package entity
|
||||||
|
|
||||||
|
// Panorama Projection Types
|
||||||
|
// TODO: Move to separate package.
|
||||||
|
|
||||||
|
const (
|
||||||
|
ProjDefault = ""
|
||||||
|
ProjEquirectangular = "equirectangular"
|
||||||
|
ProjCubestrip = "cubestrip"
|
||||||
|
ProjCylindrical = "cylindrical"
|
||||||
|
ProjTransverseCylindrical = "transverse-cylindrical"
|
||||||
|
ProjPseudocylindricalCompromise = "pseudocylindrical-compromise"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Content Types
|
||||||
|
|
||||||
|
const (
|
||||||
|
TypeDefault = ""
|
||||||
|
TypeImage = "image"
|
||||||
|
TypeLive = "live"
|
||||||
|
TypeVideo = "video"
|
||||||
|
TypeRaw = "raw"
|
||||||
|
TypeText = "text"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Root Directories Types
|
||||||
|
|
||||||
|
const (
|
||||||
|
RootUnknown = ""
|
||||||
|
RootOriginals = "/"
|
||||||
|
RootExamples = "examples"
|
||||||
|
RootSidecar = "sidecar"
|
||||||
|
RootImport = "import"
|
||||||
|
RootPath = "/"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Unknown Values
|
||||||
|
|
||||||
|
const (
|
||||||
|
UnknownYear = -1
|
||||||
|
UnknownMonth = -1
|
||||||
|
UnknownDay = -1
|
||||||
|
UnknownName = "Unknown"
|
||||||
|
UnknownTitle = UnknownName
|
||||||
|
UnknownID = "zz"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Event Types
|
||||||
|
|
||||||
|
const (
|
||||||
|
Updated = "updated"
|
||||||
|
Created = "created"
|
||||||
|
Deleted = "deleted"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Photo Stacks
|
||||||
|
|
||||||
|
const (
|
||||||
|
IsStacked int8 = 1
|
||||||
|
IsStackable int8 = 0
|
||||||
|
IsUnstacked int8 = -1
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sort Orders
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Sort orders:
|
|
||||||
SortOrderAdded = "added"
|
SortOrderAdded = "added"
|
||||||
SortOrderNewest = "newest"
|
SortOrderNewest = "newest"
|
||||||
SortOrderOldest = "oldest"
|
SortOrderOldest = "oldest"
|
||||||
@@ -9,44 +71,4 @@ const (
|
|||||||
SortOrderSimilar = "similar"
|
SortOrderSimilar = "similar"
|
||||||
SortOrderRelevance = "relevance"
|
SortOrderRelevance = "relevance"
|
||||||
SortOrderEdited = "edited"
|
SortOrderEdited = "edited"
|
||||||
|
|
||||||
// Unknown values:
|
|
||||||
UnknownYear = -1
|
|
||||||
UnknownMonth = -1
|
|
||||||
UnknownDay = -1
|
|
||||||
UnknownName = "Unknown"
|
|
||||||
UnknownTitle = UnknownName
|
|
||||||
UnknownID = "zz"
|
|
||||||
|
|
||||||
// Content types:
|
|
||||||
TypeDefault = ""
|
|
||||||
TypeImage = "image"
|
|
||||||
TypeLive = "live"
|
|
||||||
TypeVideo = "video"
|
|
||||||
TypeRaw = "raw"
|
|
||||||
TypeText = "text"
|
|
||||||
|
|
||||||
// Root directories:
|
|
||||||
RootUnknown = ""
|
|
||||||
RootOriginals = "/"
|
|
||||||
RootExamples = "examples"
|
|
||||||
RootSidecar = "sidecar"
|
|
||||||
RootImport = "import"
|
|
||||||
RootPath = "/"
|
|
||||||
|
|
||||||
// Panorama projections:
|
|
||||||
ProjectionDefault = ""
|
|
||||||
ProjectionEquirectangular = "equirectangular"
|
|
||||||
ProjectionCubestrip = "cubestrip"
|
|
||||||
ProjectionCylindrical = "cylindrical"
|
|
||||||
|
|
||||||
// Event names:
|
|
||||||
Updated = "updated"
|
|
||||||
Created = "created"
|
|
||||||
Deleted = "deleted"
|
|
||||||
|
|
||||||
// Photo stacks:
|
|
||||||
IsStacked int8 = 1
|
|
||||||
IsStackable int8 = 0
|
|
||||||
IsUnstacked int8 = -1
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ type File struct {
|
|||||||
FileWidth int `json:"Width" yaml:"Width,omitempty"`
|
FileWidth int `json:"Width" yaml:"Width,omitempty"`
|
||||||
FileHeight int `json:"Height" yaml:"Height,omitempty"`
|
FileHeight int `json:"Height" yaml:"Height,omitempty"`
|
||||||
FileOrientation int `json:"Orientation" yaml:"Orientation,omitempty"`
|
FileOrientation int `json:"Orientation" yaml:"Orientation,omitempty"`
|
||||||
FileProjection string `gorm:"type:VARBINARY(16);" json:"Projection,omitempty" yaml:"Projection,omitempty"`
|
FileProjection string `gorm:"type:VARBINARY(32);" json:"Projection,omitempty" yaml:"Projection,omitempty"`
|
||||||
FileAspectRatio float32 `gorm:"type:FLOAT;" json:"AspectRatio" yaml:"AspectRatio,omitempty"`
|
FileAspectRatio float32 `gorm:"type:FLOAT;" json:"AspectRatio" yaml:"AspectRatio,omitempty"`
|
||||||
FileMainColor string `gorm:"type:VARBINARY(16);index;" json:"MainColor" yaml:"MainColor,omitempty"`
|
FileMainColor string `gorm:"type:VARBINARY(16);index;" json:"MainColor" yaml:"MainColor,omitempty"`
|
||||||
FileColors string `gorm:"type:VARBINARY(9);" json:"Colors" yaml:"Colors,omitempty"`
|
FileColors string `gorm:"type:VARBINARY(9);" json:"Colors" yaml:"Colors,omitempty"`
|
||||||
@@ -394,7 +394,17 @@ func (m *File) Panorama() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.FileProjection != ProjectionDefault || (m.FileWidth/m.FileHeight) >= 2
|
return m.Projection() != ProjDefault || (m.FileWidth/m.FileHeight) >= 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// Projection returns the panorama projection type string.
|
||||||
|
func (m *File) Projection() string {
|
||||||
|
return SanitizeTypeString(m.FileProjection)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetProjection sets the panorama projection type string.
|
||||||
|
func (m *File) SetProjection(projType string) {
|
||||||
|
m.FileProjection = SanitizeTypeString(projType)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddFaces adds face markers to the file.
|
// AddFaces adds face markers to the file.
|
||||||
|
|||||||
@@ -289,7 +289,11 @@ func TestFile_Panorama(t *testing.T) {
|
|||||||
assert.False(t, file.Panorama())
|
assert.False(t, file.Panorama())
|
||||||
})
|
})
|
||||||
t.Run("equirectangular", func(t *testing.T) {
|
t.Run("equirectangular", func(t *testing.T) {
|
||||||
file := &File{Photo: nil, FileType: "jpg", FileSidecar: false, FileWidth: 1500, FileHeight: 1000, FileProjection: ProjectionEquirectangular}
|
file := &File{Photo: nil, FileType: "jpg", FileSidecar: false, FileWidth: 1500, FileHeight: 1000, FileProjection: ProjEquirectangular}
|
||||||
|
assert.True(t, file.Panorama())
|
||||||
|
})
|
||||||
|
t.Run("transverse-cylindrical", func(t *testing.T) {
|
||||||
|
file := &File{Photo: nil, FileType: "jpg", FileSidecar: false, FileWidth: 1500, FileHeight: 1000, FileProjection: ProjTransverseCylindrical}
|
||||||
assert.True(t, file.Panorama())
|
assert.True(t, file.Panorama())
|
||||||
})
|
})
|
||||||
t.Run("sidecar", func(t *testing.T) {
|
t.Run("sidecar", func(t *testing.T) {
|
||||||
@@ -298,6 +302,39 @@ func TestFile_Panorama(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFile_SetProjection(t *testing.T) {
|
||||||
|
t.Run(ProjDefault, func(t *testing.T) {
|
||||||
|
m := &File{}
|
||||||
|
m.SetProjection(ProjDefault)
|
||||||
|
assert.Equal(t, ProjDefault, m.FileProjection)
|
||||||
|
})
|
||||||
|
t.Run(ProjCubestrip, func(t *testing.T) {
|
||||||
|
m := &File{}
|
||||||
|
m.SetProjection(ProjCubestrip)
|
||||||
|
assert.Equal(t, ProjCubestrip, m.FileProjection)
|
||||||
|
})
|
||||||
|
t.Run(ProjCylindrical, func(t *testing.T) {
|
||||||
|
m := &File{}
|
||||||
|
m.SetProjection(ProjCylindrical)
|
||||||
|
assert.Equal(t, ProjCylindrical, m.FileProjection)
|
||||||
|
})
|
||||||
|
t.Run(ProjTransverseCylindrical, func(t *testing.T) {
|
||||||
|
m := &File{}
|
||||||
|
m.SetProjection(ProjTransverseCylindrical)
|
||||||
|
assert.Equal(t, ProjTransverseCylindrical, m.FileProjection)
|
||||||
|
})
|
||||||
|
t.Run(ProjPseudocylindricalCompromise, func(t *testing.T) {
|
||||||
|
m := &File{}
|
||||||
|
m.SetProjection(ProjPseudocylindricalCompromise)
|
||||||
|
assert.Equal(t, ProjPseudocylindricalCompromise, m.FileProjection)
|
||||||
|
})
|
||||||
|
t.Run("Sanitize", func(t *testing.T) {
|
||||||
|
m := &File{}
|
||||||
|
m.SetProjection(" 幸福 Hanzi are logograms developed for the writing of Chinese! ")
|
||||||
|
assert.Equal(t, "hanzi are logograms developed for the writing of chinese", m.FileProjection)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestFile_Delete(t *testing.T) {
|
func TestFile_Delete(t *testing.T) {
|
||||||
t.Run("permanently", func(t *testing.T) {
|
t.Run("permanently", func(t *testing.T) {
|
||||||
file := &File{FileType: "jpg", FileSize: 500, FileName: "ToBePermanentlyDeleted", FileRoot: "", PhotoID: 5678}
|
file := &File{FileType: "jpg", FileSize: 500, FileName: "ToBePermanentlyDeleted", FileRoot: "", PhotoID: 5678}
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
package entity
|
package entity
|
||||||
|
|
||||||
import "reflect"
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
TrimTypeString = 32
|
||||||
|
)
|
||||||
|
|
||||||
// Values is a shortcut for map[string]interface{}
|
// Values is a shortcut for map[string]interface{}
|
||||||
type Values map[string]interface{}
|
type Values map[string]interface{}
|
||||||
@@ -34,3 +41,33 @@ func GetValues(m interface{}, omit ...string) (result Values) {
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToASCII removes all non-ascii characters from a string and returns it.
|
||||||
|
func ToASCII(s string) string {
|
||||||
|
result := make([]rune, 0, len(s))
|
||||||
|
|
||||||
|
for _, r := range s {
|
||||||
|
if r <= 127 {
|
||||||
|
result = append(result, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim shortens a string to the given number of characters, and removes all leading and trailing white space.
|
||||||
|
func Trim(s string, maxLen int) string {
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
l := len(s)
|
||||||
|
|
||||||
|
if l <= maxLen {
|
||||||
|
return s
|
||||||
|
} else {
|
||||||
|
return s[:l-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SanitizeTypeString converts a type string to lowercase, omits invalid runes, and shortens it if needed.
|
||||||
|
func SanitizeTypeString(s string) string {
|
||||||
|
return Trim(ToASCII(strings.ToLower(s)), TrimTypeString)
|
||||||
|
}
|
||||||
|
|||||||
22
internal/entity/values_test.go
Normal file
22
internal/entity/values_test.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package entity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestToASCII(t *testing.T) {
|
||||||
|
result := ToASCII("幸福 = Happiness.")
|
||||||
|
assert.Equal(t, " = Happiness.", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTrim(t *testing.T) {
|
||||||
|
result := Trim(" 幸福 Hanzi are logograms developed for the writing of Chinese! ", 16)
|
||||||
|
assert.Equal(t, "幸福 Hanzi are logograms developed for the writing of Chinese", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSanitizeTypeString(t *testing.T) {
|
||||||
|
result := SanitizeTypeString(" 幸福 Hanzi are logograms developed for the writing of Chinese! ")
|
||||||
|
assert.Equal(t, "hanzi are logograms developed for the writing of chinese", result)
|
||||||
|
}
|
||||||
@@ -321,7 +321,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
|
|||||||
|
|
||||||
if metaData := m.MetaData(); metaData.Error == nil {
|
if metaData := m.MetaData(); metaData.Error == nil {
|
||||||
file.FileCodec = metaData.Codec
|
file.FileCodec = metaData.Codec
|
||||||
file.FileProjection = metaData.Projection
|
file.SetProjection(metaData.Projection)
|
||||||
|
|
||||||
if metaData.HasInstanceID() {
|
if metaData.HasInstanceID() {
|
||||||
log.Infof("index: %s has instance_id %s", logName, txt.Quote(metaData.InstanceID))
|
log.Infof("index: %s has instance_id %s", logName, txt.Quote(metaData.InstanceID))
|
||||||
@@ -379,7 +379,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
|
|||||||
file.FileHeight = m.Height()
|
file.FileHeight = m.Height()
|
||||||
file.FileAspectRatio = m.AspectRatio()
|
file.FileAspectRatio = m.AspectRatio()
|
||||||
file.FilePortrait = m.Portrait()
|
file.FilePortrait = m.Portrait()
|
||||||
file.FileProjection = metaData.Projection
|
file.SetProjection(metaData.Projection)
|
||||||
|
|
||||||
if res := m.Megapixels(); res > photo.PhotoResolution {
|
if res := m.Megapixels(); res > photo.PhotoResolution {
|
||||||
photo.PhotoResolution = res
|
photo.PhotoResolution = res
|
||||||
@@ -429,7 +429,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
|
|||||||
file.FileAspectRatio = m.AspectRatio()
|
file.FileAspectRatio = m.AspectRatio()
|
||||||
file.FilePortrait = m.Portrait()
|
file.FilePortrait = m.Portrait()
|
||||||
file.FileDuration = metaData.Duration
|
file.FileDuration = metaData.Duration
|
||||||
file.FileProjection = metaData.Projection
|
file.SetProjection(metaData.Projection)
|
||||||
|
|
||||||
if res := m.Megapixels(); res > photo.PhotoResolution {
|
if res := m.Megapixels(); res > photo.PhotoResolution {
|
||||||
photo.PhotoResolution = res
|
photo.PhotoResolution = res
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import "strings"
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
ClipDefault = 160
|
ClipDefault = 160
|
||||||
ClipSlug = 80
|
|
||||||
ClipKeyword = 40
|
ClipKeyword = 40
|
||||||
|
ClipSlug = 80
|
||||||
ClipVarchar = 255
|
ClipVarchar = 255
|
||||||
ClipQuery = 1000
|
ClipQuery = 1000
|
||||||
ClipDescription = 16000
|
ClipDescription = 16000
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Clip shortens a string to the given number of runes, and removes all leading and trailing white space.
|
||||||
func Clip(s string, size int) string {
|
func Clip(s string, size int) string {
|
||||||
s = strings.TrimSpace(s)
|
s = strings.TrimSpace(s)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user