82d61d1f93
Animated GIFs are transcoded to AVC because it is much smaller and thus also suitable for long/large animations. In addition, this commit adds support for more metadata fields such as frame rate, number of frames, file capture timestamp (unix milliseconds), media type, and software version. Support for SVG files can later be implemented in a similar way.
128 lines
3.2 KiB
Go
128 lines
3.2 KiB
Go
package entity
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/photoprism/photoprism/pkg/txt"
|
|
)
|
|
|
|
// TrustedTime tests if the photo has a known date and time from a trusted source.
|
|
func (m *Photo) TrustedTime() bool {
|
|
if SrcPriority[m.TakenSrc] <= SrcPriority[SrcEstimate] {
|
|
return false
|
|
} else if m.TakenAt.IsZero() || m.TakenAtLocal.IsZero() {
|
|
return false
|
|
} else if m.TimeZone == "" {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// 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 || taken.Year() > txt.YearMax {
|
|
return
|
|
}
|
|
|
|
if SrcPriority[source] < SrcPriority[m.TakenSrc] && !m.TakenAt.IsZero() {
|
|
return
|
|
}
|
|
|
|
// Remove time zone if time was extracted from file name.
|
|
if source == SrcName {
|
|
zone = ""
|
|
}
|
|
|
|
// Round times to avoid jitter.
|
|
taken = taken.Truncate(time.Second).UTC()
|
|
|
|
// Default local time to taken if zero or invalid.
|
|
if local.IsZero() || local.Year() < 1000 {
|
|
local = taken
|
|
} else {
|
|
local = local.Truncate(time.Second)
|
|
}
|
|
|
|
// Don't update older date.
|
|
if SrcPriority[source] <= SrcPriority[SrcAuto] && !m.TakenAt.IsZero() && taken.After(m.TakenAt) {
|
|
return
|
|
}
|
|
|
|
// Set UTC time and date source.
|
|
m.TakenAt = taken
|
|
m.TakenAtLocal = local
|
|
m.TakenSrc = source
|
|
|
|
if zone == time.UTC.String() && m.TimeZone != "" {
|
|
// Location exists, set local time from UTC.
|
|
m.TakenAtLocal = m.GetTakenAtLocal()
|
|
} else if zone != "" {
|
|
// Apply new time zone.
|
|
m.TimeZone = zone
|
|
m.TakenAt = m.GetTakenAt()
|
|
} else if m.TimeZoneUTC() {
|
|
// Local is UTC.
|
|
m.TimeZone = zone
|
|
m.TakenAtLocal = taken
|
|
} else if m.TimeZone != "" {
|
|
// Apply existing time zone.
|
|
m.TakenAt = m.GetTakenAt()
|
|
}
|
|
|
|
m.UpdateDateFields()
|
|
}
|
|
|
|
// TimeZoneUTC tests if the current time zone is UTC.
|
|
func (m *Photo) TimeZoneUTC() bool {
|
|
return strings.EqualFold(m.TimeZone, time.UTC.String())
|
|
}
|
|
|
|
// UpdateTimeZone updates the time zone.
|
|
func (m *Photo) UpdateTimeZone(zone string) {
|
|
if zone == "" || zone == time.UTC.String() || zone == m.TimeZone {
|
|
return
|
|
}
|
|
|
|
if SrcPriority[m.TakenSrc] >= SrcPriority[SrcManual] && m.TimeZone != "" {
|
|
return
|
|
}
|
|
|
|
if m.TimeZoneUTC() {
|
|
m.TimeZone = zone
|
|
m.TakenAtLocal = m.GetTakenAtLocal()
|
|
} else {
|
|
m.TimeZone = zone
|
|
m.TakenAt = m.GetTakenAt()
|
|
}
|
|
}
|
|
|
|
// UpdateDateFields updates internal date fields.
|
|
func (m *Photo) UpdateDateFields() {
|
|
if m.TakenAt.IsZero() || m.TakenAt.Year() < 1000 {
|
|
return
|
|
}
|
|
|
|
if m.TakenAtLocal.IsZero() || m.TakenAtLocal.Year() < 1000 {
|
|
m.TakenAtLocal = m.TakenAt
|
|
}
|
|
|
|
// Set date to unknown if file system date is about the same as indexing time.
|
|
if m.TakenSrc == SrcAuto && m.TakenAt.After(m.CreatedAt.Add(-24*time.Hour)) {
|
|
m.PhotoYear = UnknownYear
|
|
m.PhotoMonth = UnknownMonth
|
|
m.PhotoDay = UnknownDay
|
|
} else if m.TakenSrc != SrcManual {
|
|
m.PhotoYear = m.TakenAtLocal.Year()
|
|
m.PhotoMonth = int(m.TakenAtLocal.Month())
|
|
m.PhotoDay = m.TakenAtLocal.Day()
|
|
}
|
|
|
|
// Update photo_taken_at column in related files.
|
|
Log("photo", "update date fields",
|
|
UnscopedDb().Model(File{}).
|
|
Where("photo_id = ? AND photo_taken_at <> ?", m.ID, m.TakenAtLocal).
|
|
Updates(File{PhotoTakenAt: m.TakenAtLocal}).Error,
|
|
)
|
|
}
|