Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
parent
9e1d9702ae
commit
50abd9b632
4 changed files with 144 additions and 28 deletions
|
@ -9,7 +9,7 @@ import (
|
|||
)
|
||||
|
||||
func TestJSON_Motion(t *testing.T) {
|
||||
t.Run("GooglePixel2_JPG", func(t *testing.T) {
|
||||
t.Run("google_pixel2.jpg.json", func(t *testing.T) {
|
||||
data, err := JSON("testdata/motion/google_pixel2.jpg.json", "")
|
||||
|
||||
if err != nil {
|
||||
|
@ -40,7 +40,7 @@ func TestJSON_Motion(t *testing.T) {
|
|||
assert.Equal(t, "Pixel 2", data.CameraModel)
|
||||
assert.Equal(t, "", data.LensModel)
|
||||
})
|
||||
t.Run("GooglePixel4a_JPG", func(t *testing.T) {
|
||||
t.Run("google_pixel4a.jpg.json", func(t *testing.T) {
|
||||
data, err := JSON("testdata/motion/google_pixel4a.jpg.json", "")
|
||||
|
||||
if err != nil {
|
||||
|
@ -71,7 +71,7 @@ func TestJSON_Motion(t *testing.T) {
|
|||
assert.Equal(t, "Pixel 4a", data.CameraModel)
|
||||
assert.Equal(t, "", data.LensModel)
|
||||
})
|
||||
t.Run("GooglePixel6_JPG", func(t *testing.T) {
|
||||
t.Run("google_pixel6.jpg.json", func(t *testing.T) {
|
||||
data, err := JSON("testdata/motion/google_pixel6.jpg.json", "")
|
||||
|
||||
if err != nil {
|
||||
|
@ -102,7 +102,7 @@ func TestJSON_Motion(t *testing.T) {
|
|||
assert.Equal(t, "Pixel 6", data.CameraModel)
|
||||
assert.Equal(t, "Pixel 6 back camera 6.81mm f/1.85", data.LensModel)
|
||||
})
|
||||
t.Run("GooglePixel7Pro_JPG", func(t *testing.T) {
|
||||
t.Run("google_pixel7pro.jpg.json", func(t *testing.T) {
|
||||
data, err := JSON("testdata/motion/google_pixel7pro.jpg.json", "")
|
||||
|
||||
if err != nil {
|
||||
|
@ -133,7 +133,7 @@ func TestJSON_Motion(t *testing.T) {
|
|||
assert.Equal(t, "Pixel 7 Pro", data.CameraModel)
|
||||
assert.Equal(t, "Pixel 7 Pro back camera 19.0mm f/3.5", data.LensModel)
|
||||
})
|
||||
t.Run("SamsungGalaxyS20_JPG", func(t *testing.T) {
|
||||
t.Run("samsung_galaxys20.jpg.json", func(t *testing.T) {
|
||||
data, err := JSON("testdata/motion/samsung_galaxys20.jpg.json", "")
|
||||
|
||||
if err != nil {
|
||||
|
@ -164,7 +164,7 @@ func TestJSON_Motion(t *testing.T) {
|
|||
assert.Equal(t, "SM-G780F", data.CameraModel)
|
||||
assert.Equal(t, "", data.LensModel)
|
||||
})
|
||||
t.Run("SamsungGalaxyS20_MP4", func(t *testing.T) {
|
||||
t.Run("samsung_galaxys20.mp4.json", func(t *testing.T) {
|
||||
data, err := JSON("testdata/motion/samsung_galaxys20.mp4.json", "")
|
||||
|
||||
if err != nil {
|
||||
|
@ -195,7 +195,7 @@ func TestJSON_Motion(t *testing.T) {
|
|||
assert.Equal(t, "", data.CameraModel)
|
||||
assert.Equal(t, "", data.LensModel)
|
||||
})
|
||||
t.Run("SamsungGalaxyS20FE_HEIF", func(t *testing.T) {
|
||||
t.Run("samsung_galaxys20fe.heif.json", func(t *testing.T) {
|
||||
data, err := JSON("testdata/motion/samsung_galaxys20fe.heif.json", "")
|
||||
|
||||
if err != nil {
|
||||
|
@ -226,7 +226,7 @@ func TestJSON_Motion(t *testing.T) {
|
|||
assert.Equal(t, "SM-G781B", data.CameraModel)
|
||||
assert.Equal(t, "", data.LensModel)
|
||||
})
|
||||
t.Run("SamsungGalaxyS21Ultra_JPG", func(t *testing.T) {
|
||||
t.Run("samsung_galaxys21ultra.jpg.json", func(t *testing.T) {
|
||||
data, err := JSON("testdata/motion/samsung_galaxys21ultra.jpg.json", "")
|
||||
|
||||
if err != nil {
|
||||
|
@ -257,7 +257,7 @@ func TestJSON_Motion(t *testing.T) {
|
|||
assert.Equal(t, "SM-G998B", data.CameraModel)
|
||||
assert.Equal(t, "", data.LensModel)
|
||||
})
|
||||
t.Run("SamsungGalaxyS21Ultra_MP4", func(t *testing.T) {
|
||||
t.Run("samsung_galaxys21ultra.mp4.json", func(t *testing.T) {
|
||||
data, err := JSON("testdata/motion/samsung_galaxys21ultra.mp4.json", "")
|
||||
|
||||
if err != nil {
|
||||
|
@ -288,4 +288,33 @@ func TestJSON_Motion(t *testing.T) {
|
|||
assert.Equal(t, "", data.CameraModel)
|
||||
assert.Equal(t, "", data.LensModel)
|
||||
})
|
||||
t.Run("samsung_galaxya71.jpg.json", func(t *testing.T) {
|
||||
data, err := JSON("testdata/motion/samsung_galaxya71.jpg.json", "")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// t.Logf("DATA: %#v", data)
|
||||
|
||||
assert.Equal(t, "motion-photo.jpg", data.FileName)
|
||||
assert.Equal(t, media.Live, data.MediaType)
|
||||
assert.Equal(t, true, data.EmbeddedThumb)
|
||||
assert.Equal(t, true, data.EmbeddedVideo)
|
||||
assert.Equal(t, CodecJpeg, data.Codec)
|
||||
assert.Equal(t, int64(0), data.Duration.Milliseconds())
|
||||
assert.Equal(t, "0s", data.Duration.String())
|
||||
assert.Equal(t, 308000000, data.TakenNs)
|
||||
assert.Equal(t, "", data.TimeZone)
|
||||
assert.Equal(t, 4624, data.Width)
|
||||
assert.Equal(t, 3468, data.Height)
|
||||
assert.Equal(t, 3468, data.ActualWidth())
|
||||
assert.Equal(t, 4624, data.ActualHeight())
|
||||
assert.Equal(t, 6, data.Orientation)
|
||||
assert.Equal(t, float32(0), data.Lat)
|
||||
assert.Equal(t, float32(0), data.Lng)
|
||||
assert.Equal(t, "Samsung", data.CameraMake)
|
||||
assert.Equal(t, "Galaxy A71", data.CameraModel)
|
||||
assert.Equal(t, "", data.LensModel)
|
||||
})
|
||||
}
|
||||
|
|
84
internal/meta/testdata/motion/samsung_galaxya71.jpg.json
vendored
Normal file
84
internal/meta/testdata/motion/samsung_galaxya71.jpg.json
vendored
Normal file
|
@ -0,0 +1,84 @@
|
|||
[{
|
||||
"SourceFile": "motion-photo.jpg",
|
||||
"ExifToolVersion": 12.40,
|
||||
"FileName": "motion-photo.jpg",
|
||||
"Directory": ".",
|
||||
"FileSize": 6094850,
|
||||
"FileModifyDate": "2023:09:23 12:48:37+02:00",
|
||||
"FileAccessDate": "2023:09:23 12:49:31+02:00",
|
||||
"FileInodeChangeDate": "2023:09:23 12:49:07+02:00",
|
||||
"FilePermissions": 100664,
|
||||
"FileType": "JPEG",
|
||||
"FileTypeExtension": "JPG",
|
||||
"MIMEType": "image/jpeg",
|
||||
"ExifByteOrder": "II",
|
||||
"Make": "Samsung",
|
||||
"Model": "Galaxy A71",
|
||||
"Orientation": 6,
|
||||
"XResolution": 72,
|
||||
"YResolution": 72,
|
||||
"ResolutionUnit": 2,
|
||||
"Software": "A715FXXU8DWB5",
|
||||
"ModifyDate": "2023:04:24 13:03:58",
|
||||
"YCbCrPositioning": 1,
|
||||
"ExposureTime": 0.001328021248,
|
||||
"FNumber": 1.8,
|
||||
"ExposureProgram": 2,
|
||||
"ISO": 32,
|
||||
"ExifVersion": "0220",
|
||||
"DateTimeOriginal": "2023:04:24 13:03:58",
|
||||
"CreateDate": "2023:04:24 13:03:58",
|
||||
"OffsetTime": "+02:00",
|
||||
"OffsetTimeOriginal": "+02:00",
|
||||
"ShutterSpeedValue": 0.999079909359437,
|
||||
"ApertureValue": 1.79626474576787,
|
||||
"BrightnessValue": 7.67,
|
||||
"ExposureCompensation": 0,
|
||||
"MaxApertureValue": 1.79626474576787,
|
||||
"MeteringMode": 2,
|
||||
"Flash": 0,
|
||||
"FocalLength": 5.23,
|
||||
"SubSecTime": 308,
|
||||
"SubSecTimeOriginal": 308,
|
||||
"SubSecTimeDigitized": 308,
|
||||
"ColorSpace": 1,
|
||||
"ExifImageWidth": 4624,
|
||||
"ExifImageHeight": 3468,
|
||||
"ExposureMode": 0,
|
||||
"WhiteBalance": 0,
|
||||
"DigitalZoomRatio": 1,
|
||||
"FocalLengthIn35mmFormat": 24,
|
||||
"SceneCaptureType": 0,
|
||||
"ImageUniqueID": "A64QLMD00YM",
|
||||
"Compression": 6,
|
||||
"ThumbnailOffset": 998,
|
||||
"ThumbnailLength": 59060,
|
||||
"XMPToolkit": "Adobe XMP Core 5.1.0-jc003",
|
||||
"MotionPhoto": 1,
|
||||
"MotionPhotoVersion": 1,
|
||||
"MotionPhotoPresentationTimestampUs": 2998371,
|
||||
"DirectoryItemMime": "image/jpeg",
|
||||
"DirectoryItemSemantic": "Primary",
|
||||
"DirectoryItemLength": 0,
|
||||
"DirectoryItemPadding": 59,
|
||||
"ImageWidth": 4624,
|
||||
"ImageHeight": 3468,
|
||||
"EncodingProcess": 0,
|
||||
"BitsPerSample": 8,
|
||||
"ColorComponents": 3,
|
||||
"YCbCrSubSampling": "2 2",
|
||||
"Aperture": 1.8,
|
||||
"ImageSize": "4624 3468",
|
||||
"Megapixels": 16.036032,
|
||||
"ScaleFactor35efl": 4.58891013384321,
|
||||
"ShutterSpeed": 0.001328021248,
|
||||
"SubSecCreateDate": "2023:04:24 13:03:58.308",
|
||||
"SubSecDateTimeOriginal": "2023:04:24 13:03:58.308+02:00",
|
||||
"SubSecModifyDate": "2023:04:24 13:03:58.308+02:00",
|
||||
"ThumbnailImage": "(Binary data 59060 bytes, use -b option to extract)",
|
||||
"CircleOfConfusion": "0.00654758096204051",
|
||||
"FOV": 73.7398575770812,
|
||||
"FocalLength35efl": 24,
|
||||
"HyperfocalDistance": 2.32086562100636,
|
||||
"LightValue": 12.8963560579259
|
||||
}]
|
|
@ -359,6 +359,8 @@ func (ind *Index) UserMediaFile(m *MediaFile, o IndexOptions, originalName, phot
|
|||
// Reset file perceptive diff and chroma percent.
|
||||
file.FileDiff = -1
|
||||
file.FileChroma = -1
|
||||
file.FileVideo = m.IsVideo()
|
||||
file.MediaType = m.Media().String()
|
||||
|
||||
// Handle file types.
|
||||
switch {
|
||||
|
@ -411,6 +413,16 @@ func (ind *Index) UserMediaFile(m *MediaFile, o IndexOptions, originalName, phot
|
|||
file.SetDuration(info.Duration)
|
||||
file.SetFPS(info.FPS)
|
||||
file.SetFrames(info.Frames)
|
||||
|
||||
// Change file and photo type to "live" if the file has a video embedded.
|
||||
file.FileVideo = true
|
||||
file.MediaType = entity.MediaLive
|
||||
if photo.TypeSrc == entity.SrcAuto {
|
||||
photo.PhotoType = entity.MediaLive
|
||||
}
|
||||
} else if photo.TypeSrc == entity.SrcAuto && photo.PhotoType == entity.MediaLive {
|
||||
// Image does not include a compatible video.
|
||||
photo.PhotoType = entity.MediaImage
|
||||
}
|
||||
|
||||
if file.OriginalName == "" && filepath.Base(file.FileName) != data.FileName {
|
||||
|
@ -425,11 +437,6 @@ func (ind *Index) UserMediaFile(m *MediaFile, o IndexOptions, originalName, phot
|
|||
|
||||
file.InstanceID = data.InstanceID
|
||||
}
|
||||
|
||||
// Change photo type to "live" if the file has a video embedded.
|
||||
if photo.TypeSrc == entity.SrcAuto && data.MediaType == media.Live {
|
||||
photo.PhotoType = entity.MediaLive
|
||||
}
|
||||
}
|
||||
|
||||
// Change the photo type to animated if it is an animated PNG.
|
||||
|
@ -514,12 +521,20 @@ func (ind *Index) UserMediaFile(m *MediaFile, o IndexOptions, originalName, phot
|
|||
file.SetDuration(data.Duration)
|
||||
file.SetFPS(data.FPS)
|
||||
file.SetFrames(data.Frames)
|
||||
file.FileVideo = false
|
||||
} else if info := m.VideoInfo(); info.Compatible {
|
||||
file.SetDuration(info.Duration)
|
||||
file.SetFPS(info.FPS)
|
||||
file.SetFrames(info.Frames)
|
||||
|
||||
// Change file and photo type to "live" if the file has a video embedded.
|
||||
file.FileVideo = true
|
||||
file.MediaType = entity.MediaLive
|
||||
if photo.TypeSrc == entity.SrcAuto {
|
||||
photo.PhotoType = entity.MediaLive
|
||||
}
|
||||
} else if photo.TypeSrc == entity.SrcAuto && photo.PhotoType == entity.MediaLive {
|
||||
// HEIC does not include a compatible video.
|
||||
photo.PhotoType = entity.MediaImage
|
||||
}
|
||||
|
||||
// Set photo resolution based on the largest media file.
|
||||
|
@ -785,19 +800,11 @@ func (ind *Index) UserMediaFile(m *MediaFile, o IndexOptions, originalName, phot
|
|||
|
||||
// Update file properties.
|
||||
file.FileSidecar = m.IsSidecar()
|
||||
file.FileVideo = m.IsVideo() || m.MetaData().EmbeddedVideo
|
||||
file.FileType = m.FileType().String()
|
||||
file.FileMime = m.MimeType()
|
||||
file.SetOrientation(m.Orientation(), entity.SrcMeta)
|
||||
file.ModTime = modTime.UTC().Truncate(time.Second).Unix()
|
||||
|
||||
// Update file media type.
|
||||
if mediaType := m.MetaData().MediaType; mediaType != media.Unknown {
|
||||
file.MediaType = mediaType.String()
|
||||
} else {
|
||||
file.MediaType = m.Media().String()
|
||||
}
|
||||
|
||||
// Detect ICC color profile for JPEGs if still unknown at this point.
|
||||
if file.FileColorProfile == "" && fs.ImageJPEG.Equal(file.FileType) {
|
||||
file.SetColorProfile(m.ColorProfile())
|
||||
|
|
|
@ -887,10 +887,6 @@ func (m *MediaFile) IsImageNative() bool {
|
|||
|
||||
// IsLive checks if the file is a live photo.
|
||||
func (m *MediaFile) IsLive() bool {
|
||||
if m.MetaData().MediaType == media.Live {
|
||||
return true
|
||||
}
|
||||
|
||||
if m.IsHEIC() {
|
||||
return fs.VideoMOV.FindFirst(m.FileName(), []string{}, Config().OriginalsPath(), false) != ""
|
||||
}
|
||||
|
@ -899,7 +895,7 @@ func (m *MediaFile) IsLive() bool {
|
|||
return fs.ImageHEIC.FindFirst(m.FileName(), []string{}, Config().OriginalsPath(), false) != ""
|
||||
}
|
||||
|
||||
return false
|
||||
return m.MetaData().MediaType == media.Live && m.VideoInfo().Compatible
|
||||
}
|
||||
|
||||
// ExifSupported returns true if parsing exif metadata is supported for the media file type.
|
||||
|
|
Loading…
Reference in a new issue