Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
commit
65f47a5db4
@ -84,19 +84,29 @@
|
||||
</h3>
|
||||
<div class="caption">
|
||||
<button @click.exact="editPhoto(index)">
|
||||
<v-icon size="14">date_range</v-icon>
|
||||
<v-icon size="14" title="Taken" v-if="photo.TakenSrc">date_range</v-icon>
|
||||
<v-icon size="14" title="Imported" v-else>save</v-icon>
|
||||
{{ photo.getDateString() }}
|
||||
</button>
|
||||
<br/>
|
||||
<button @click.exact="editPhoto(index)">
|
||||
<button @click.exact="editPhoto(index)" title="Camera">
|
||||
<v-icon size="14">photo_camera</v-icon>
|
||||
{{ photo.getCamera() }}
|
||||
</button>
|
||||
<br/>
|
||||
<button @click.exact="openLocation(index)" v-if="showLocation && photo.LocationID">
|
||||
<v-icon size="14">location_on</v-icon>
|
||||
{{ photo.getLocation() }}
|
||||
</button>
|
||||
<template v-if="showLocation && photo.LocationID">
|
||||
<br/>
|
||||
<button @click.exact="openLocation(index)" title="Location">
|
||||
<v-icon size="14">location_on</v-icon>
|
||||
{{ photo.getLocation() }}
|
||||
</button>
|
||||
</template>
|
||||
<template v-if="debug">
|
||||
<br/>
|
||||
<button @click.exact="openUUID(index)" title="Unique ID">
|
||||
<v-icon size="14">fingerprint</v-icon>
|
||||
{{ photo.PhotoUUID }}
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</v-card-title>
|
||||
@ -121,6 +131,7 @@
|
||||
return {
|
||||
showLocation: this.$config.settings().features.places,
|
||||
hidePrivate: this.$config.settings().library.private,
|
||||
debug: this.$config.get('debug'),
|
||||
mouseDown: {
|
||||
index: -1,
|
||||
timeStamp: -1,
|
||||
@ -161,7 +172,10 @@
|
||||
},
|
||||
selectRange(index) {
|
||||
this.$clipboard.addRange(index, this.photos);
|
||||
}
|
||||
},
|
||||
openUUID(index) {
|
||||
window.open("/api/v1/" + this.photos[index].getEntityResource(), '_blank');
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@ -33,7 +33,7 @@ func GetPhoto(router *gin.RouterGroup, conf *config.Config) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, p)
|
||||
c.IndentedJSON(http.StatusOK, p)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -18,13 +18,14 @@ import (
|
||||
// Photo represents a photo, all its properties, and link to all its images and sidecar files.
|
||||
type Photo struct {
|
||||
ID uint `gorm:"primary_key"`
|
||||
TakenAt time.Time `gorm:"type:datetime;index:idx_photos_taken_uuid;" json:"TakenAt"`
|
||||
TakenSrc string `gorm:"type:varbinary(8);" json:"TakenSrc"`
|
||||
PhotoUUID string `gorm:"type:varbinary(36);unique_index;index:idx_photos_taken_uuid;"`
|
||||
PhotoPath string `gorm:"type:varbinary(768);index;"`
|
||||
PhotoName string `gorm:"type:varbinary(255);"`
|
||||
TakenAt time.Time `gorm:"type:datetime;index:idx_photos_taken_uuid;" json:"TakenAt"`
|
||||
TakenAtLocal time.Time `gorm:"type:datetime;"`
|
||||
TakenSrc string `gorm:"type:varbinary(8);" json:"TakenSrc"`
|
||||
PhotoTitle string `gorm:"type:varchar(255);" json:"PhotoTitle"`
|
||||
TitleSrc string `gorm:"type:varbinary(8);" json:"TitleSrc"`
|
||||
PhotoPath string `gorm:"type:varbinary(768);index;"`
|
||||
PhotoName string `gorm:"type:varbinary(255);"`
|
||||
PhotoQuality int `gorm:"type:SMALLINT" json:"PhotoQuality"`
|
||||
PhotoResolution int `gorm:"type:SMALLINT" json:"PhotoResolution"`
|
||||
PhotoFavorite bool `json:"PhotoFavorite"`
|
||||
@ -44,11 +45,10 @@ type Photo struct {
|
||||
PlaceID string `gorm:"type:varbinary(16);index;default:'zz'" json:"PlaceID"`
|
||||
LocationID string `gorm:"type:varbinary(16);index;" json:"LocationID"`
|
||||
LocationSrc string `gorm:"type:varbinary(8);" json:"LocationSrc"`
|
||||
TimeZone string `gorm:"type:varbinary(64);" json:"TimeZone"`
|
||||
PhotoCountry string `gorm:"type:varbinary(2);index:idx_photos_country_year_month;default:'zz'" json:"PhotoCountry"`
|
||||
PhotoYear int `gorm:"index:idx_photos_country_year_month;"`
|
||||
PhotoMonth int `gorm:"index:idx_photos_country_year_month;"`
|
||||
TimeZone string `gorm:"type:varbinary(64);" json:"TimeZone"`
|
||||
TakenAtLocal time.Time `gorm:"type:datetime;"`
|
||||
Description Description `json:"Description"`
|
||||
DescriptionSrc string `gorm:"type:varbinary(8);" json:"DescriptionSrc"`
|
||||
Camera *Camera `json:"Camera"`
|
||||
@ -402,7 +402,7 @@ func (m *Photo) AddLabels(labels classify.Labels, db *gorm.DB) {
|
||||
db.Set("gorm:auto_preload", true).Model(m).Related(&m.Labels)
|
||||
}
|
||||
|
||||
// SetTitle sets the photo title and clips it to 300 characters.
|
||||
// SetTitle changes the photo title and clips it to 300 characters.
|
||||
func (m *Photo) SetTitle(title, source string) {
|
||||
newTitle := txt.Clip(title, txt.ClipDefault)
|
||||
|
||||
@ -410,6 +410,68 @@ func (m *Photo) SetTitle(title, source string) {
|
||||
return
|
||||
}
|
||||
|
||||
if m.TitleSrc != SrcAuto && m.TitleSrc != source && source != SrcManual && m.HasTitle() {
|
||||
return
|
||||
}
|
||||
|
||||
m.PhotoTitle = newTitle
|
||||
m.TitleSrc = source
|
||||
}
|
||||
|
||||
// SetDescription changes the photo description if not empty and from the same source.
|
||||
func (m *Photo) SetDescription(desc, source string) {
|
||||
newDesc := txt.Clip(desc, txt.ClipDescription)
|
||||
|
||||
if newDesc == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if m.DescriptionSrc != SrcAuto && m.DescriptionSrc != source && source != SrcManual && m.Description.PhotoDescription != "" {
|
||||
return
|
||||
}
|
||||
|
||||
m.Description.PhotoDescription = newDesc
|
||||
m.DescriptionSrc = source
|
||||
}
|
||||
|
||||
// SetTakenAt changes the photo date if not empty and from the same source.
|
||||
func (m *Photo) SetTakenAt(taken, local time.Time, zone, source string) {
|
||||
if taken.IsZero() || taken.Year() < 1000 {
|
||||
return
|
||||
}
|
||||
|
||||
if m.TakenSrc != SrcAuto && m.TakenSrc != source && source != SrcManual {
|
||||
return
|
||||
}
|
||||
|
||||
m.TakenAt = taken.Round(time.Second).UTC()
|
||||
m.TakenSrc = source
|
||||
|
||||
if local.IsZero() || local.Year() < 1000 {
|
||||
m.TakenAtLocal = m.TakenAt
|
||||
} else {
|
||||
m.TakenAtLocal = local.Round(time.Second)
|
||||
}
|
||||
|
||||
if zone != "" {
|
||||
m.TimeZone = zone
|
||||
} else {
|
||||
m.TimeZone = time.UTC.String()
|
||||
}
|
||||
}
|
||||
|
||||
// SetCoordinates changes the photo lat, lng and altitude if not empty and from the same source.
|
||||
func (m *Photo) SetCoordinates(lat, lng float32, altitude int, source string) {
|
||||
if lat == 0 && lng == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if m.LocationSrc != SrcAuto && m.LocationSrc != source && source != SrcManual {
|
||||
return
|
||||
}
|
||||
|
||||
m.PhotoLat = lat
|
||||
m.PhotoLng = lng
|
||||
m.PhotoAltitude = altitude
|
||||
m.LocationSrc = source
|
||||
}
|
||||
|
@ -153,28 +153,10 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
|
||||
if fileChanged || o.UpdateExif {
|
||||
// Read UpdateExif data
|
||||
if metaData, err := m.MetaData(); err == nil {
|
||||
if photo.LocationSrc == entity.SrcAuto || photo.LocationSrc == entity.SrcExif {
|
||||
photo.PhotoLat = metaData.Lat
|
||||
photo.PhotoLng = metaData.Lng
|
||||
photo.PhotoAltitude = metaData.Altitude
|
||||
photo.LocationSrc = entity.SrcExif
|
||||
}
|
||||
|
||||
if photo.TakenSrc == entity.SrcAuto || photo.TakenSrc == entity.SrcExif {
|
||||
photo.TakenAt = metaData.TakenAt
|
||||
photo.TakenAtLocal = metaData.TakenAtLocal
|
||||
photo.TimeZone = metaData.TimeZone
|
||||
photo.TakenSrc = entity.SrcExif
|
||||
}
|
||||
|
||||
if metaData.Title != "" && (photo.NoTitle() || photo.TitleSrc == entity.SrcExif) {
|
||||
photo.SetTitle(metaData.Title, entity.SrcExif)
|
||||
}
|
||||
|
||||
if metaData.Description != "" && (photo.Description.NoDescription() || photo.DescriptionSrc == entity.SrcExif) {
|
||||
photo.Description.PhotoDescription = metaData.Description
|
||||
photo.DescriptionSrc = entity.SrcExif
|
||||
}
|
||||
photo.SetTitle(metaData.Title, entity.SrcExif)
|
||||
photo.SetDescription(metaData.Description, entity.SrcExif)
|
||||
photo.SetTakenAt(metaData.TakenAt, metaData.TakenAtLocal, metaData.TimeZone, entity.SrcExif)
|
||||
photo.SetCoordinates(metaData.Lat, metaData.Lng, metaData.Altitude, entity.SrcExif)
|
||||
|
||||
if photo.Description.NoNotes() {
|
||||
photo.Description.PhotoNotes = metaData.Comment
|
||||
@ -219,8 +201,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
|
||||
}
|
||||
|
||||
if photo.TakenAt.IsZero() || photo.TakenAtLocal.IsZero() {
|
||||
photo.TakenAt = m.DateCreated()
|
||||
photo.TakenAtLocal = photo.TakenAt
|
||||
photo.SetTakenAt(m.DateCreated(), m.DateCreated(), time.UTC.String(), entity.SrcAuto)
|
||||
}
|
||||
|
||||
if fileChanged || o.UpdateKeywords || o.UpdateLocation || o.UpdateTitle || photo.NoTitle() {
|
||||
@ -238,24 +219,19 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
|
||||
} else if m.IsXMP() {
|
||||
// TODO: Proof-of-concept for indexing XMP sidecar files
|
||||
if data, err := meta.XMP(m.FileName()); err == nil {
|
||||
if data.Title != "" && photo.TitleSrc == entity.SrcAuto {
|
||||
photo.SetTitle(data.Title, entity.SrcXmp)
|
||||
}
|
||||
photo.SetTitle(data.Title, entity.SrcXmp)
|
||||
photo.SetDescription(data.Description, entity.SrcXmp)
|
||||
|
||||
if photo.Description.NoCopyright() && data.Copyright != "" {
|
||||
photo.Description.PhotoCopyright = data.Copyright
|
||||
if photo.Description.NoNotes() && data.Comment != "" {
|
||||
photo.Description.PhotoNotes = data.Comment
|
||||
}
|
||||
|
||||
if photo.Description.NoArtist() && data.Artist != "" {
|
||||
photo.Description.PhotoArtist = data.Artist
|
||||
}
|
||||
|
||||
if photo.Description.NoDescription() && data.Description != "" {
|
||||
photo.Description.PhotoDescription = data.Description
|
||||
}
|
||||
|
||||
if photo.Description.NoNotes() && data.Comment != "" {
|
||||
photo.Description.PhotoNotes = data.Comment
|
||||
if photo.Description.NoCopyright() && data.Copyright != "" {
|
||||
photo.Description.PhotoCopyright = data.Copyright
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ type PhotoResult struct {
|
||||
DeletedAt time.Time
|
||||
TakenAt time.Time
|
||||
TakenAtLocal time.Time
|
||||
TakenSrc string
|
||||
TimeZone string
|
||||
PhotoUUID string
|
||||
PhotoPath string
|
||||
|
@ -3,9 +3,10 @@ package txt
|
||||
import "strings"
|
||||
|
||||
const (
|
||||
ClipDefault = 160
|
||||
ClipSlug = 80
|
||||
ClipKeyword = 40
|
||||
ClipDefault = 160
|
||||
ClipSlug = 80
|
||||
ClipKeyword = 40
|
||||
ClipDescription = 16000
|
||||
)
|
||||
|
||||
func Clip(s string, size int) string {
|
||||
|
Loading…
x
Reference in New Issue
Block a user