2020-01-07 17:36:49 +01:00
package meta
import (
2020-05-14 11:57:26 +02:00
"math"
2020-01-07 17:36:49 +01:00
"time"
2020-07-23 15:34:20 +02:00
2023-09-22 23:59:56 +02:00
"github.com/photoprism/photoprism/pkg/media"
2020-07-23 15:34:20 +02:00
"github.com/photoprism/photoprism/pkg/rnd"
2021-11-27 18:41:10 +01:00
"github.com/photoprism/photoprism/pkg/s2"
2020-01-07 17:36:49 +01:00
)
2022-01-05 16:37:19 +01:00
const (
ImageTypeHDR = 3 // see https://exiftool.org/TagNames/Apple.html
)
2022-03-28 15:57:29 +02:00
// Data represents image metadata.
2020-01-07 17:36:49 +01:00
type Data struct {
2023-10-10 13:38:03 +02:00
FileName string ` meta:"FileName" `
MimeType string ` meta:"MIMEType" report:"-" `
2023-12-19 16:55:08 +01:00
DocumentID string ` meta:"ContentIdentifier,MediaGroupUUID,BurstUUID,OriginalDocumentID,DocumentID,ImageUniqueID,DigitalImageGUID" ` // see https://exiftool.org/forum/index.php?topic=14874.0
2023-10-10 13:38:03 +02:00
InstanceID string ` meta:"InstanceID,DocumentID" `
2023-10-21 17:32:42 +02:00
CreatedAt time . Time ` meta:"SubSecCreateDate,CreationDate,CreateDate,MediaCreateDate,ContentCreateDate,TrackCreateDate" `
2023-10-10 13:38:03 +02:00
TakenAt time . Time ` meta:"SubSecDateTimeOriginal,SubSecDateTimeCreated,DateTimeOriginal,CreationDate,DateTimeCreated,DateTime,DateTimeDigitized" xmp:"DateCreated" `
TakenAtLocal time . Time ` meta:"SubSecDateTimeOriginal,SubSecDateTimeCreated,DateTimeOriginal,CreationDate,DateTimeCreated,DateTime,DateTimeDigitized" `
TakenGps time . Time ` meta:"GPSDateTime,GPSDateStamp" `
TakenNs int ` meta:"-" `
TimeZone string ` meta:"-" `
2023-10-21 00:11:11 +02:00
TimeOffset string ` meta:"OffsetTime,OffsetTimeOriginal,OffsetTimeDigitized" `
2023-10-10 13:38:03 +02:00
MediaType media . Type ` meta:"-" `
HasThumbEmbedded bool ` meta:"ThumbnailImage,PhotoshopThumbnail" report:"-" `
HasVideoEmbedded bool ` meta:"EmbeddedVideoFile,MotionPhoto,MotionPhotoVideo,MicroVideo" report:"-" `
Duration time . Duration ` meta:"Duration,MediaDuration,TrackDuration,PreviewDuration" `
FPS float64 ` meta:"VideoFrameRate,VideoAvgFrameRate" `
Frames int ` meta:"FrameCount,AnimationFrames" `
Codec string ` meta:"CompressorID,VideoCodecID,CodecID,OtherFormat,FileType" `
Title string ` meta:"Title,Headline" xmp:"dc:title" dc:"title,title.Alt" `
Description string ` meta:"Description,ImageDescription,Caption,Caption-Abstract" xmp:"Description,Description.Alt" `
Subject string ` meta:"Subject,PersonInImage,ObjectName,HierarchicalSubject,CatalogSets" xmp:"Subject" `
Keywords Keywords ` meta:"Keywords" `
Favorite bool ` meta:"Favorite" `
Notes string ` meta:"Comment,UserComment" `
Artist string ` meta:"Artist,Creator,By-line,OwnerName,Owner" xmp:"Creator" `
Copyright string ` meta:"Rights,Copyright,CopyrightNotice,WebStatement" xmp:"Rights,Rights.Alt" `
License string ` meta:"UsageTerms,License" `
Projection string ` meta:"ProjectionType" `
ColorProfile string ` meta:"ICCProfileName,ProfileDescription" `
CameraMake string ` meta:"CameraMake,Make" xmp:"Make" `
CameraModel string ` meta:"CameraModel,Model,CameraID,UniqueCameraModel" xmp:"CameraModel,Model" `
CameraOwner string ` meta:"OwnerName" `
CameraSerial string ` meta:"SerialNumber" `
LensMake string ` meta:"LensMake" `
LensModel string ` meta:"LensModel,Lens,LensID" xmp:"LensModel,Lens" `
Software string ` meta:"Software,CreatorTool,HistorySoftwareAgent,ProcessingSoftware" `
Flash bool ` meta:"FlashFired" `
FocalLength int ` meta:"FocalLength,FocalLengthIn35mmFormat" `
FocalDistance float64 ` meta:"HyperfocalDistance" `
Exposure string ` meta:"ExposureTime,ShutterSpeedValue,ShutterSpeed,TargetExposureTime" `
Aperture float32 ` meta:"ApertureValue,Aperture" `
FNumber float32 ` meta:"FNumber" `
Iso int ` meta:"ISO" `
ImageType int ` meta:"HDRImageType" `
GPSPosition string ` meta:"GPSPosition" `
GPSLatitude string ` meta:"GPSLatitude" `
GPSLongitude string ` meta:"GPSLongitude" `
Lat float32 ` meta:"-" `
Lng float32 ` meta:"-" `
Altitude float64 ` meta:"GlobalAltitude,GPSAltitude" `
Width int ` meta:"ImageWidth,PixelXDimension,ExifImageWidth,SourceImageWidth" `
Height int ` meta:"ImageHeight,ImageLength,PixelYDimension,ExifImageHeight,SourceImageHeight" `
Orientation int ` meta:"-" `
Rotation int ` meta:"Rotation" `
Views int ` meta:"-" `
Albums [ ] string ` meta:"-" `
Warning string ` meta:"Warning" report:"-" `
Error error ` meta:"-" `
json map [ string ] string
exif map [ string ] string
2020-01-07 17:36:49 +01:00
}
2020-05-14 11:57:26 +02:00
2023-09-22 23:59:56 +02:00
// NewData returns a new Data struct with default values.
func NewData ( ) Data {
2022-04-09 19:56:38 +02:00
return Data { }
2020-07-11 16:46:29 +02:00
}
2020-05-14 11:57:26 +02:00
// AspectRatio returns the aspect ratio based on width and height.
func ( data Data ) AspectRatio ( ) float32 {
2022-04-14 10:49:56 +02:00
w := float64 ( data . ActualWidth ( ) )
h := float64 ( data . ActualHeight ( ) )
2020-05-14 11:57:26 +02:00
2022-04-14 10:49:56 +02:00
if w <= 0 || h <= 0 {
2020-05-14 11:57:26 +02:00
return 0
}
2022-04-14 10:49:56 +02:00
return float32 ( math . Round ( ( w / h ) * 100 ) / 100 )
2020-05-14 11:57:26 +02:00
}
2022-01-05 11:40:44 +01:00
// Portrait returns true if it is a portrait picture or video based on width and height.
2020-05-14 11:57:26 +02:00
func ( data Data ) Portrait ( ) bool {
2020-08-28 09:27:25 +02:00
return data . ActualWidth ( ) < data . ActualHeight ( )
2020-05-14 11:57:26 +02:00
}
2022-01-05 16:37:19 +01:00
// IsHDR tests if it is a high dynamic range file.
func ( data Data ) IsHDR ( ) bool {
return data . ImageType == ImageTypeHDR
}
2020-05-14 11:57:26 +02:00
// Megapixels returns the resolution in megapixels.
func ( data Data ) Megapixels ( ) int {
return int ( math . Round ( float64 ( data . Width * data . Height ) / 1000000 ) )
}
2020-05-27 13:40:21 +02:00
// HasDocumentID returns true if a DocumentID exists.
func ( data Data ) HasDocumentID ( ) bool {
2022-09-28 09:01:17 +02:00
return rnd . IsUUID ( data . DocumentID )
2020-05-27 13:40:21 +02:00
}
// HasInstanceID returns true if an InstanceID exists.
func ( data Data ) HasInstanceID ( ) bool {
2022-09-28 09:01:17 +02:00
return rnd . IsUUID ( data . InstanceID )
2020-05-27 13:40:21 +02:00
}
2021-02-05 09:45:28 +01:00
// HasTimeAndPlace if data contains a time and GPS position.
2020-05-27 13:40:21 +02:00
func ( data Data ) HasTimeAndPlace ( ) bool {
return ! data . TakenAt . IsZero ( ) && data . Lat != 0 && data . Lng != 0
}
2020-06-04 14:56:27 +02:00
// ActualWidth is the width after rotating the media file if needed.
func ( data Data ) ActualWidth ( ) int {
if data . Orientation > 4 {
return data . Height
}
return data . Width
}
// ActualHeight is the height after rotating the media file if needed.
func ( data Data ) ActualHeight ( ) int {
if data . Orientation > 4 {
return data . Width
}
return data . Height
}
2020-12-04 19:51:51 +01:00
// CellID returns the S2 cell ID.
func ( data Data ) CellID ( ) string {
return s2 . PrefixedToken ( float64 ( data . Lat ) , float64 ( data . Lng ) )
}