photoprism/internal/photoprism/mediafile_related.go
Michael Mayer b44b8d52c1 WebP: Add support for indexing and playing animations #3197 #668
Signed-off-by: Michael Mayer <michael@photoprism.app>
2023-02-22 20:58:21 +01:00

131 lines
3.6 KiB
Go

package photoprism
import (
"fmt"
"path/filepath"
"regexp"
"sort"
"strings"
"github.com/photoprism/photoprism/pkg/clean"
"github.com/photoprism/photoprism/pkg/fs"
)
// RelatedFiles returns files which are related to this file.
func (m *MediaFile) RelatedFiles(stripSequence bool) (result RelatedFiles, err error) {
// File path and name without any extensions.
prefix := m.AbsPrefix(stripSequence)
// Storage folder path prefixes.
sidecarPrefix := Config().SidecarPath() + "/"
originalsPrefix := Config().OriginalsPath() + "/"
// Ignore RAW images?
skipRaw := Config().DisableRaw()
// Ignore JPEG XL files?
skipJpegXL := Config().DisableJpegXL()
// Ignore vector graphics?
skipVectors := Config().DisableVectors()
// Replace sidecar with originals path in search prefix.
if len(sidecarPrefix) > 1 && sidecarPrefix != originalsPrefix && strings.HasPrefix(prefix, sidecarPrefix) {
prefix = strings.Replace(prefix, sidecarPrefix, originalsPrefix, 1)
log.Debugf("media: replaced sidecar with originals path in related file matching pattern")
}
// Quote path for glob.
if stripSequence {
// Strip common name sequences like "copy 2" and escape meta characters.
prefix = regexp.QuoteMeta(prefix)
} else {
// Use strict file name matching and escape meta characters.
prefix = regexp.QuoteMeta(prefix + ".")
}
// Find related files.
matches, err := filepath.Glob(prefix + "*")
if err != nil {
return result, err
}
if name := m.EditedName(); name != "" {
matches = append(matches, name)
}
isHEIC := false
for _, fileName := range matches {
f, fileErr := NewMediaFile(fileName)
if fileErr != nil || f.Empty() {
continue
}
// Ignore file format?
switch {
case skipRaw && f.IsRaw():
log.Debugf("media: skipped related raw image %s", clean.Log(f.RootRelName()))
continue
case skipJpegXL && f.IsJpegXL():
log.Debugf("media: skipped related JPEG XL file %s", clean.Log(f.RootRelName()))
continue
case skipVectors && f.IsVector():
log.Debugf("media: skipped related vector graphic %s", clean.Log(f.RootRelName()))
continue
}
// Set main file.
if result.Main == nil && f.IsPreviewImage() {
result.Main = f
} else if f.IsRaw() {
result.Main = f
} else if f.IsVector() {
result.Main = f
} else if f.IsHEIC() {
isHEIC = true
result.Main = f
} else if f.IsHEIF() {
result.Main = f
} else if f.IsImage() && !f.IsPreviewImage() {
result.Main = f
} else if f.IsVideo() && !isHEIC {
result.Main = f
} else if result.Main != nil && f.IsPreviewImage() {
if result.Main.IsPreviewImage() && len(result.Main.FileName()) > len(f.FileName()) {
result.Main = f
}
}
result.Files = append(result.Files, f)
}
if len(result.Files) == 0 || result.Main == nil {
t := m.MimeType()
if t == "" {
t = "unknown type"
}
return result, fmt.Errorf("%s is unsupported (%s)", clean.Log(m.BaseName()), t)
}
// Add hidden preview image if needed.
if !result.HasPreview() {
if jpegName := fs.ImageJPEG.FindFirst(result.Main.FileName(), []string{Config().SidecarPath(), fs.HiddenPath}, Config().OriginalsPath(), stripSequence); jpegName != "" {
if resultFile, _ := NewMediaFile(jpegName); resultFile.Ok() {
result.Files = append(result.Files, resultFile)
}
} else if pngName := fs.ImagePNG.FindFirst(result.Main.FileName(), []string{Config().SidecarPath(), fs.HiddenPath}, Config().OriginalsPath(), stripSequence); pngName != "" {
if resultFile, _ := NewMediaFile(pngName); resultFile.Ok() {
result.Files = append(result.Files, resultFile)
}
}
}
sort.Sort(result.Files)
return result, nil
}