Videos: Don't skip transcoding QuickTime files, only MP4 AVC #3525
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
parent
3b7b551cca
commit
08070978cf
5 changed files with 125 additions and 32 deletions
|
@ -6,13 +6,12 @@ import (
|
|||
|
||||
const CodecUnknown = ""
|
||||
const CodecAv1 = string(video.CodecAV1)
|
||||
const CodecVP9 = string(video.CodecVP9)
|
||||
const CodecAvc1 = string(video.CodecAVC)
|
||||
const CodecJpeg = "jpeg"
|
||||
const CodecHeic = "heic"
|
||||
const CodecXMP = "xmp"
|
||||
|
||||
// CodecAvc returns true if the video format is MPEG-4 AVC.
|
||||
// CodecAvc returns true if the video codec is AVC.
|
||||
func (data Data) CodecAvc() bool {
|
||||
return data.Codec == CodecAvc1
|
||||
}
|
|
@ -6,7 +6,7 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestData_CodecAvc1(t *testing.T) {
|
||||
func TestData_CodecAvc(t *testing.T) {
|
||||
t.Run("true", func(t *testing.T) {
|
||||
data := Data{
|
||||
Codec: "avc1",
|
||||
|
@ -14,7 +14,6 @@ func TestData_CodecAvc1(t *testing.T) {
|
|||
|
||||
assert.Equal(t, true, data.CodecAvc())
|
||||
})
|
||||
|
||||
t.Run("false", func(t *testing.T) {
|
||||
data := Data{
|
||||
Codec: "heic",
|
|
@ -19,24 +19,37 @@ func ConvertWorker(jobs <-chan ConvertJob) {
|
|||
}
|
||||
|
||||
for job := range jobs {
|
||||
switch {
|
||||
case job.file == nil:
|
||||
// File and convert service must not be nil.
|
||||
if job.file == nil || job.convert == nil {
|
||||
continue
|
||||
case job.convert == nil:
|
||||
continue
|
||||
case job.file.IsAnimated():
|
||||
_, _ = job.convert.ToJson(job.file, false)
|
||||
}
|
||||
|
||||
// Create JPEG preview and AVC encoded version for videos.
|
||||
if _, err := job.convert.ToImage(job.file, job.force); err != nil {
|
||||
// f is the media file to be converted.
|
||||
f := job.file
|
||||
|
||||
switch {
|
||||
case f.IsAnimated():
|
||||
// Extract metadata.
|
||||
_, _ = job.convert.ToJson(f, false)
|
||||
|
||||
// Create cover image.
|
||||
if _, err := job.convert.ToImage(f, job.force); err != nil {
|
||||
logError(err, job)
|
||||
} else if metaData := job.file.MetaData(); metaData.CodecAvc() {
|
||||
}
|
||||
|
||||
// Check if the file has a playable format or has already been transcoded.
|
||||
if f.SkipTranscoding() {
|
||||
log.Debugf("convert: %s does not require transcoding", clean.Log(f.RelName(job.convert.conf.OriginalsPath())))
|
||||
continue
|
||||
} else if _, err := job.convert.ToAvc(job.file, job.convert.conf.FFmpegEncoder(), false, false); err != nil {
|
||||
}
|
||||
|
||||
// Transcode to MP4 AVC.
|
||||
if _, err := job.convert.ToAvc(f, job.convert.conf.FFmpegEncoder(), false, false); err != nil {
|
||||
logError(err, job)
|
||||
}
|
||||
default:
|
||||
if _, err := job.convert.ToImage(job.file, job.force); err != nil {
|
||||
// Create preview image.
|
||||
if _, err := job.convert.ToImage(f, job.force); err != nil {
|
||||
logError(err, job)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -810,6 +810,11 @@ func (m *MediaFile) IsAnimated() bool {
|
|||
return m.IsVideo() || m.IsAnimatedImage()
|
||||
}
|
||||
|
||||
// NotAnimated checks if the file is not a video or an animated image.
|
||||
func (m *MediaFile) NotAnimated() bool {
|
||||
return !m.IsAnimated()
|
||||
}
|
||||
|
||||
// IsVideo returns true if this is a video file.
|
||||
func (m *MediaFile) IsVideo() bool {
|
||||
return m.HasMediaType(media.Video)
|
||||
|
@ -845,9 +850,24 @@ func (m *MediaFile) InSidecar() bool {
|
|||
return m.Root() == entity.RootSidecar
|
||||
}
|
||||
|
||||
// IsPlayableVideo checks if the file is a video in playable format.
|
||||
func (m *MediaFile) IsPlayableVideo() bool {
|
||||
return m.IsVideo() && (m.HasFileType(fs.VideoMP4) || m.HasFileType(fs.VideoAVC))
|
||||
// NeedsTranscoding checks whether the media file is a video or an animated image and should be transcoded to a playable format.
|
||||
func (m *MediaFile) NeedsTranscoding() bool {
|
||||
if m.NotAnimated() {
|
||||
return false
|
||||
} else if m.HasFileType(fs.VideoAVC) || m.HasFileType(fs.VideoMP4) && m.MetaData().CodecAvc() {
|
||||
return false
|
||||
}
|
||||
|
||||
if m.IsAnimatedImage() {
|
||||
return fs.VideoMP4.FindFirst(m.FileName(), []string{Config().SidecarPath(), fs.HiddenPath}, Config().OriginalsPath(), false) == ""
|
||||
}
|
||||
|
||||
return fs.VideoAVC.FindFirst(m.FileName(), []string{Config().SidecarPath(), fs.HiddenPath}, Config().OriginalsPath(), false) == ""
|
||||
}
|
||||
|
||||
// SkipTranscoding checks if the media file is not animated or has already been transcoded to a playable format.
|
||||
func (m *MediaFile) SkipTranscoding() bool {
|
||||
return !m.NeedsTranscoding()
|
||||
}
|
||||
|
||||
// IsImageOther returns true if this is a PNG, GIF, BMP, TIFF, or WebP file.
|
||||
|
|
|
@ -1293,7 +1293,7 @@ func TestMediaFile_IsImageOther(t *testing.T) {
|
|||
assert.Equal(t, true, mediaFile.IsImageNative())
|
||||
assert.Equal(t, true, mediaFile.IsImageOther())
|
||||
assert.Equal(t, false, mediaFile.IsVideo())
|
||||
assert.Equal(t, false, mediaFile.IsPlayableVideo())
|
||||
assert.Equal(t, true, mediaFile.SkipTranscoding())
|
||||
})
|
||||
t.Run("yellow_rose-small.bmp", func(t *testing.T) {
|
||||
mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/yellow_rose-small.bmp")
|
||||
|
@ -1310,7 +1310,7 @@ func TestMediaFile_IsImageOther(t *testing.T) {
|
|||
assert.Equal(t, true, mediaFile.IsImageNative())
|
||||
assert.Equal(t, true, mediaFile.IsImageOther())
|
||||
assert.Equal(t, false, mediaFile.IsVideo())
|
||||
assert.Equal(t, false, mediaFile.IsPlayableVideo())
|
||||
assert.Equal(t, true, mediaFile.SkipTranscoding())
|
||||
})
|
||||
t.Run("preloader.gif", func(t *testing.T) {
|
||||
mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/preloader.gif")
|
||||
|
@ -1328,7 +1328,7 @@ func TestMediaFile_IsImageOther(t *testing.T) {
|
|||
assert.Equal(t, true, mediaFile.IsImageNative())
|
||||
assert.Equal(t, true, mediaFile.IsImageOther())
|
||||
assert.Equal(t, false, mediaFile.IsVideo())
|
||||
assert.Equal(t, false, mediaFile.IsPlayableVideo())
|
||||
assert.Equal(t, true, mediaFile.SkipTranscoding())
|
||||
})
|
||||
t.Run("norway-kjetil-moe.webp", func(t *testing.T) {
|
||||
mediaFile, err := NewMediaFile("testdata/norway-kjetil-moe.webp")
|
||||
|
@ -1347,7 +1347,7 @@ func TestMediaFile_IsImageOther(t *testing.T) {
|
|||
assert.Equal(t, true, mediaFile.IsImageNative())
|
||||
assert.Equal(t, true, mediaFile.IsImageOther())
|
||||
assert.Equal(t, false, mediaFile.IsVideo())
|
||||
assert.Equal(t, false, mediaFile.IsPlayableVideo())
|
||||
assert.Equal(t, true, mediaFile.SkipTranscoding())
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1503,6 +1503,7 @@ func TestMediaFile_IsAnimated(t *testing.T) {
|
|||
assert.Equal(t, true, f.IsImage())
|
||||
assert.Equal(t, true, f.IsAVIFS())
|
||||
assert.Equal(t, true, f.IsAnimated())
|
||||
assert.Equal(t, false, f.NotAnimated())
|
||||
assert.Equal(t, true, f.IsAnimatedImage())
|
||||
assert.Equal(t, true, f.ExifSupported())
|
||||
assert.Equal(t, false, f.IsVideo())
|
||||
|
@ -1521,6 +1522,7 @@ func TestMediaFile_IsAnimated(t *testing.T) {
|
|||
assert.Equal(t, true, f.IsImage())
|
||||
assert.Equal(t, true, f.IsWebP())
|
||||
assert.Equal(t, true, f.IsAnimated())
|
||||
assert.Equal(t, false, f.NotAnimated())
|
||||
assert.Equal(t, true, f.IsAnimatedImage())
|
||||
assert.Equal(t, false, f.ExifSupported())
|
||||
assert.Equal(t, false, f.IsVideo())
|
||||
|
@ -1539,6 +1541,7 @@ func TestMediaFile_IsAnimated(t *testing.T) {
|
|||
assert.Equal(t, true, f.IsImage())
|
||||
assert.Equal(t, false, f.IsVideo())
|
||||
assert.Equal(t, false, f.IsAnimated())
|
||||
assert.Equal(t, true, f.NotAnimated())
|
||||
assert.Equal(t, true, f.IsGIF())
|
||||
assert.Equal(t, false, f.IsAnimatedImage())
|
||||
assert.Equal(t, false, f.IsSidecar())
|
||||
|
@ -1551,6 +1554,7 @@ func TestMediaFile_IsAnimated(t *testing.T) {
|
|||
assert.Equal(t, true, f.IsImage())
|
||||
assert.Equal(t, false, f.IsVideo())
|
||||
assert.Equal(t, true, f.IsAnimated())
|
||||
assert.Equal(t, false, f.NotAnimated())
|
||||
assert.Equal(t, true, f.IsGIF())
|
||||
assert.Equal(t, true, f.IsAnimatedImage())
|
||||
assert.Equal(t, false, f.IsSidecar())
|
||||
|
@ -1563,6 +1567,7 @@ func TestMediaFile_IsAnimated(t *testing.T) {
|
|||
assert.Equal(t, false, f.IsImage())
|
||||
assert.Equal(t, true, f.IsVideo())
|
||||
assert.Equal(t, true, f.IsAnimated())
|
||||
assert.Equal(t, false, f.NotAnimated())
|
||||
assert.Equal(t, false, f.IsGIF())
|
||||
assert.Equal(t, false, f.IsAnimatedImage())
|
||||
assert.Equal(t, false, f.IsSidecar())
|
||||
|
@ -2376,28 +2381,85 @@ func TestMediaFile_IsJson(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestMediaFile_IsPlayableVideo(t *testing.T) {
|
||||
t.Run("false", func(t *testing.T) {
|
||||
conf := config.TestConfig()
|
||||
func TestMediaFile_NeedsTranscoding(t *testing.T) {
|
||||
c := config.TestConfig()
|
||||
|
||||
mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/blue-go-video.json")
|
||||
t.Run("json", func(t *testing.T) {
|
||||
f, err := NewMediaFile(c.ExamplesPath() + "/blue-go-video.json")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.False(t, mediaFile.IsPlayableVideo())
|
||||
assert.False(t, f.NeedsTranscoding())
|
||||
})
|
||||
t.Run("true", func(t *testing.T) {
|
||||
conf := config.TestConfig()
|
||||
|
||||
mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/blue-go-video.mp4")
|
||||
t.Run("mp4", func(t *testing.T) {
|
||||
f, err := NewMediaFile(c.ExamplesPath() + "/blue-go-video.mp4")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.True(t, mediaFile.IsPlayableVideo())
|
||||
assert.False(t, f.NeedsTranscoding())
|
||||
})
|
||||
t.Run("mov", func(t *testing.T) {
|
||||
f, err := NewMediaFile(c.ExamplesPath() + "/earth.mov")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.True(t, f.NeedsTranscoding())
|
||||
})
|
||||
t.Run("gif", func(t *testing.T) {
|
||||
f, err := NewMediaFile(c.ExamplesPath() + "/pythagoras.gif")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.True(t, f.NeedsTranscoding())
|
||||
})
|
||||
}
|
||||
|
||||
func TestMediaFile_SkipTranscoding(t *testing.T) {
|
||||
c := config.TestConfig()
|
||||
|
||||
t.Run("json", func(t *testing.T) {
|
||||
f, err := NewMediaFile(c.ExamplesPath() + "/blue-go-video.json")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.True(t, f.SkipTranscoding())
|
||||
})
|
||||
t.Run("mp4", func(t *testing.T) {
|
||||
f, err := NewMediaFile(c.ExamplesPath() + "/blue-go-video.mp4")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.True(t, f.SkipTranscoding())
|
||||
})
|
||||
t.Run("mov", func(t *testing.T) {
|
||||
f, err := NewMediaFile(c.ExamplesPath() + "/earth.mov")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.False(t, f.SkipTranscoding())
|
||||
})
|
||||
t.Run("gif", func(t *testing.T) {
|
||||
f, err := NewMediaFile(c.ExamplesPath() + "/pythagoras.gif")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.False(t, f.SkipTranscoding())
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue