Videos: Extract still image after 3 seconds if duration > 3100ms #1241
This way, still images of live photos remain unchanged, while other videos might get better preview images, especially if the first few frames are only black or white. Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
parent
fdd758ace5
commit
709683ef59
|
@ -173,7 +173,11 @@ func (c *Convert) JpegConvertCommands(f *MediaFile, jpegName string, xmpName str
|
|||
|
||||
// Video thumbnails can be created with FFmpeg.
|
||||
if f.IsVideo() && c.conf.FFmpegEnabled() {
|
||||
result = append(result, exec.Command(c.conf.FFmpegBin(), "-y", "-i", f.FileName(), "-ss", "00:00:00.001", "-vframes", "1", jpegName))
|
||||
if f.Duration() > LivePhotoDurationLimit {
|
||||
result = append(result, exec.Command(c.conf.FFmpegBin(), "-y", "-i", f.FileName(), "-ss", "00:00:03.000", "-vframes", "1", jpegName))
|
||||
} else {
|
||||
result = append(result, exec.Command(c.conf.FFmpegBin(), "-y", "-i", f.FileName(), "-ss", "00:00:00.001", "-vframes", "1", jpegName))
|
||||
}
|
||||
}
|
||||
|
||||
// RAW files may be concerted with Darktable and Rawtherapee.
|
||||
|
|
|
@ -566,7 +566,7 @@ func (ind *Index) UserMediaFile(m *MediaFile, o IndexOptions, originalName, phot
|
|||
|
||||
if photo.TypeSrc == entity.SrcAuto {
|
||||
// Update photo type only if not manually modified.
|
||||
if file.FileDuration == 0 || file.FileDuration > time.Millisecond*3100 {
|
||||
if file.FileDuration == 0 || file.FileDuration > LivePhotoDurationLimit {
|
||||
photo.PhotoType = entity.MediaVideo
|
||||
} else {
|
||||
photo.PhotoType = entity.MediaLive
|
||||
|
|
6
internal/photoprism/livephoto.go
Normal file
6
internal/photoprism/livephoto.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
package photoprism
|
||||
|
||||
import "time"
|
||||
|
||||
// LivePhotoDurationLimit is the maximum duration of a live photo.
|
||||
var LivePhotoDurationLimit = time.Millisecond * 3100
|
|
@ -763,6 +763,15 @@ func (m *MediaFile) IsVideo() bool {
|
|||
return strings.HasPrefix(m.MimeType(), "video/") || m.Media() == media.Video
|
||||
}
|
||||
|
||||
// Duration returns the duration if the file is a video.
|
||||
func (m *MediaFile) Duration() time.Duration {
|
||||
if !m.IsVideo() {
|
||||
return 0
|
||||
}
|
||||
|
||||
return m.MetaData().Duration
|
||||
}
|
||||
|
||||
// IsAnimatedGif returns true if it is an animated GIF.
|
||||
func (m *MediaFile) IsAnimatedGif() bool {
|
||||
return m.IsGif() && m.MetaData().Frames > 1
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
|
@ -2428,55 +2429,63 @@ func TestMediaFile_RemoveSidecarFiles(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestMediaFile_ColorProfile(t *testing.T) {
|
||||
c := config.TestConfig()
|
||||
|
||||
t.Run("iphone_7.json", func(t *testing.T) {
|
||||
mediaFile, err := NewMediaFile(c.ExamplesPath() + "/iphone_7.json")
|
||||
mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/iphone_7.json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, "", mediaFile.ColorProfile())
|
||||
})
|
||||
t.Run("iphone_7.xmp", func(t *testing.T) {
|
||||
mediaFile, err := NewMediaFile(c.ExamplesPath() + "/iphone_7.xmp")
|
||||
mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/iphone_7.xmp")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, "", mediaFile.ColorProfile())
|
||||
})
|
||||
t.Run("iphone_7.heic", func(t *testing.T) {
|
||||
mediaFile, err := NewMediaFile(c.ExamplesPath() + "/iphone_7.heic")
|
||||
mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/iphone_7.heic")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, "", mediaFile.ColorProfile())
|
||||
})
|
||||
t.Run("canon_eos_6d.dng", func(t *testing.T) {
|
||||
mediaFile, err := NewMediaFile(c.ExamplesPath() + "/canon_eos_6d.dng")
|
||||
mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/canon_eos_6d.dng")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, "", mediaFile.ColorProfile())
|
||||
})
|
||||
t.Run("elephants.jpg", func(t *testing.T) {
|
||||
mediaFile, err := NewMediaFile(c.ExamplesPath() + "/elephants.jpg")
|
||||
mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/elephants.jpg")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, "Adobe RGB (1998)", mediaFile.ColorProfile())
|
||||
})
|
||||
t.Run("/beach_wood.jpg", func(t *testing.T) {
|
||||
mediaFile, err := NewMediaFile(c.ExamplesPath() + "/beach_wood.jpg")
|
||||
mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/beach_wood.jpg")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, "", mediaFile.ColorProfile())
|
||||
})
|
||||
t.Run("/peacock_blue.jpg", func(t *testing.T) {
|
||||
mediaFile, err := NewMediaFile(c.ExamplesPath() + "/peacock_blue.jpg")
|
||||
mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/peacock_blue.jpg")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, "sRGB IEC61966-2.1", mediaFile.ColorProfile())
|
||||
})
|
||||
}
|
||||
|
||||
func TestMediaFile_Duration(t *testing.T) {
|
||||
t.Run("earth.mov", func(t *testing.T) {
|
||||
if f, err := NewMediaFile(filepath.Join(conf.ExamplesPath(), "blue-go-video.mp4")); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
assert.Equal(t, time.Duration(2000000000), f.Duration())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue