mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-12 00:34:13 +01:00
UX: Skip RAW files by default when clicking Download All #2234
Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
@@ -509,6 +509,12 @@ export class Photo extends RestModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
downloadAll() {
|
downloadAll() {
|
||||||
|
const settings = config.settings();
|
||||||
|
|
||||||
|
if (!settings || !settings.features || !settings.download || !settings.features.download) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const token = config.downloadToken();
|
const token = config.downloadToken();
|
||||||
|
|
||||||
if (!this.Files) {
|
if (!this.Files) {
|
||||||
@@ -524,9 +530,20 @@ export class Photo extends RestModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.Files.forEach((file) => {
|
this.Files.forEach((file) => {
|
||||||
if (!file || !file.Hash || file.Sidecar) {
|
if (!file || !file.Hash) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip sidecar files.
|
||||||
|
if (file.Sidecar) {
|
||||||
// Don't download broken files and sidecars.
|
// Don't download broken files and sidecars.
|
||||||
if (config.debug) console.log("download: skipped file", file);
|
if (config.debug) console.log("download: skipped sidecar", file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip RAW images.
|
||||||
|
if (!settings.download.raw && file.Type === TypeRaw) {
|
||||||
|
if (config.debug) console.log("download: skipped raw", file);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -54,7 +54,7 @@ require (
|
|||||||
github.com/ulule/deepcopier v0.0.0-20200430083143-45decc6639b6
|
github.com/ulule/deepcopier v0.0.0-20200430083143-45decc6639b6
|
||||||
github.com/urfave/cli v1.22.5
|
github.com/urfave/cli v1.22.5
|
||||||
go4.org v0.0.0-20201209231011-d4a079459e60 // indirect
|
go4.org v0.0.0-20201209231011-d4a079459e60 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29
|
golang.org/x/crypto v0.0.0-20220408190544-5352b0902921
|
||||||
golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3
|
golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3
|
||||||
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f // indirect
|
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f // indirect
|
||||||
gonum.org/v1/gonum v0.11.0
|
gonum.org/v1/gonum v0.11.0
|
||||||
|
|||||||
4
go.sum
4
go.sum
@@ -325,8 +325,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
|||||||
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 h1:tkVvjkPTB7pnW3jnid7kNyAMPVWllTNOf/qKDze4p9o=
|
golang.org/x/crypto v0.0.0-20220408190544-5352b0902921 h1:iU7T1X1J6yxDr0rda54sWGkHgOp5XJrqm79gcNlC2VM=
|
||||||
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220408190544-5352b0902921/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
|||||||
@@ -32,10 +32,12 @@ func DownloadName(c *gin.Context) entity.DownloadName {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDownload returns the raw file data.
|
||||||
|
//
|
||||||
// GET /api/v1/dl/:hash
|
// GET /api/v1/dl/:hash
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// hash: string The file hash as returned by the search API
|
// hash: string The file hash as returned by the files/photos endpoint
|
||||||
func GetDownload(router *gin.RouterGroup) {
|
func GetDownload(router *gin.RouterGroup) {
|
||||||
router.GET("/dl/:hash", func(c *gin.Context) {
|
router.GET("/dl/:hash", func(c *gin.Context) {
|
||||||
if InvalidDownloadToken(c) {
|
if InvalidDownloadToken(c) {
|
||||||
@@ -105,6 +105,11 @@ func CreateZip(router *gin.RouterGroup) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !conf.Settings().Download.Raw && fs.FormatRaw.Is(file.FileType) {
|
||||||
|
log.Debugf("download: skipped raw %s", sanitize.Log(file.FileName))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
fileName := photoprism.FileName(file.FileRoot, file.FileName)
|
fileName := photoprism.FileName(file.FileRoot, file.FileName)
|
||||||
alias := file.DownloadName(dlName, 0)
|
alias := file.DownloadName(dlName, 0)
|
||||||
key := strings.ToLower(alias)
|
key := strings.ToLower(alias)
|
||||||
@@ -88,6 +88,7 @@ type ShareSettings struct {
|
|||||||
// DownloadSettings represents content download settings.
|
// DownloadSettings represents content download settings.
|
||||||
type DownloadSettings struct {
|
type DownloadSettings struct {
|
||||||
Name entity.DownloadName `json:"name" yaml:"Name"`
|
Name entity.DownloadName `json:"name" yaml:"Name"`
|
||||||
|
Raw bool `json:"raw" yaml:"Raw"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Settings represents user settings for Web UI, indexing, and import.
|
// Settings represents user settings for Web UI, indexing, and import.
|
||||||
|
|||||||
1
internal/config/testdata/settings.yml
vendored
1
internal/config/testdata/settings.yml
vendored
@@ -44,5 +44,6 @@ Share:
|
|||||||
Title: ""
|
Title: ""
|
||||||
Download:
|
Download:
|
||||||
Name: file
|
Name: file
|
||||||
|
Raw: false
|
||||||
Templates:
|
Templates:
|
||||||
Default: index.tmpl
|
Default: index.tmpl
|
||||||
|
|||||||
@@ -18,7 +18,13 @@ func TestCaseInsensitive(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIgnoreCase(t *testing.T) {
|
func TestIgnoreCase(t *testing.T) {
|
||||||
assert.False(t, ignoreCase)
|
isCS, err := CaseInsensitive(os.TempDir())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, isCS, ignoreCase)
|
||||||
IgnoreCase()
|
IgnoreCase()
|
||||||
assert.True(t, ignoreCase)
|
assert.True(t, ignoreCase)
|
||||||
ignoreCase = false
|
ignoreCase = false
|
||||||
|
|||||||
140
pkg/fs/file_ext.go
Normal file
140
pkg/fs/file_ext.go
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileExt contains the filename extensions of file formats known to PhotoPrism.
|
||||||
|
var FileExt = FileExtensions{
|
||||||
|
".jpg": FormatJpeg,
|
||||||
|
".jpeg": FormatJpeg,
|
||||||
|
".jpe": FormatJpeg,
|
||||||
|
".jif": FormatJpeg,
|
||||||
|
".jfif": FormatJpeg,
|
||||||
|
".jfi": FormatJpeg,
|
||||||
|
".thm": FormatJpeg,
|
||||||
|
".3fr": FormatRaw,
|
||||||
|
".ari": FormatRaw,
|
||||||
|
".arw": FormatRaw,
|
||||||
|
".bay": FormatRaw,
|
||||||
|
".cap": FormatRaw,
|
||||||
|
".crw": FormatRaw,
|
||||||
|
".cr2": FormatRaw,
|
||||||
|
".cr3": FormatRaw,
|
||||||
|
".cr4": FormatRaw,
|
||||||
|
".data": FormatRaw,
|
||||||
|
".dcs": FormatRaw,
|
||||||
|
".dcr": FormatRaw,
|
||||||
|
".dng": FormatRaw,
|
||||||
|
".drf": FormatRaw,
|
||||||
|
".eip": FormatRaw,
|
||||||
|
".erf": FormatRaw,
|
||||||
|
".fff": FormatRaw,
|
||||||
|
".gpr": FormatRaw,
|
||||||
|
".iiq": FormatRaw,
|
||||||
|
".k25": FormatRaw,
|
||||||
|
".kdc": FormatRaw,
|
||||||
|
".mdc": FormatRaw,
|
||||||
|
".mef": FormatRaw,
|
||||||
|
".mos": FormatRaw,
|
||||||
|
".mrw": FormatRaw,
|
||||||
|
".nef": FormatRaw,
|
||||||
|
".nrw": FormatRaw,
|
||||||
|
".obm": FormatRaw,
|
||||||
|
".orf": FormatRaw,
|
||||||
|
".pef": FormatRaw,
|
||||||
|
".ptx": FormatRaw,
|
||||||
|
".pxn": FormatRaw,
|
||||||
|
".r3d": FormatRaw,
|
||||||
|
".raf": FormatRaw,
|
||||||
|
".raw": FormatRaw,
|
||||||
|
".rwl": FormatRaw,
|
||||||
|
".rwz": FormatRaw,
|
||||||
|
".rw2": FormatRaw,
|
||||||
|
".srf": FormatRaw,
|
||||||
|
".srw": FormatRaw,
|
||||||
|
".sr2": FormatRaw,
|
||||||
|
".x3f": FormatRaw,
|
||||||
|
".png": FormatPng,
|
||||||
|
".pn": FormatPng,
|
||||||
|
".tif": FormatTiff,
|
||||||
|
".tiff": FormatTiff,
|
||||||
|
".gif": FormatGif,
|
||||||
|
".bmp": FormatBitmap,
|
||||||
|
".heif": FormatHEIF,
|
||||||
|
".heic": FormatHEIF,
|
||||||
|
".hevc": FormatHEVC,
|
||||||
|
".mov": FormatMov,
|
||||||
|
".avi": FormatAvi,
|
||||||
|
".avc": FormatAvc,
|
||||||
|
".mp": FormatMp4,
|
||||||
|
".mp4": FormatMp4,
|
||||||
|
".m4v": FormatMp4,
|
||||||
|
".mpg": FormatMpg,
|
||||||
|
".mpeg": FormatMpg,
|
||||||
|
".3gp": Format3gp,
|
||||||
|
".3g2": Format3g2,
|
||||||
|
".flv": FormatFlv,
|
||||||
|
".mkv": FormatMkv,
|
||||||
|
".mpo": FormatMpo,
|
||||||
|
".mts": FormatMts,
|
||||||
|
".ogv": FormatOgv,
|
||||||
|
".webp": FormatWebP,
|
||||||
|
".webm": FormatWebM,
|
||||||
|
".wmv": FormatWMV,
|
||||||
|
".aae": FormatAAE,
|
||||||
|
".md": FormatMarkdown,
|
||||||
|
".json": FormatJson,
|
||||||
|
".txt": FormatText,
|
||||||
|
".yml": FormatYaml,
|
||||||
|
".yaml": FormatYaml,
|
||||||
|
".xmp": FormatXMP,
|
||||||
|
".xml": FormatXML,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeExt contains the default file type extensions.
|
||||||
|
var TypeExt = FileExt.TypeExt()
|
||||||
|
|
||||||
|
func (m FileExtensions) Known(name string) bool {
|
||||||
|
if name == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
ext := strings.ToLower(filepath.Ext(name))
|
||||||
|
|
||||||
|
if ext == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := m[ext]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m FileExtensions) TypeExt() TypeExtensions {
|
||||||
|
result := make(TypeExtensions)
|
||||||
|
|
||||||
|
if ignoreCase {
|
||||||
|
for ext, t := range m {
|
||||||
|
if _, ok := result[t]; ok {
|
||||||
|
result[t] = append(result[t], ext)
|
||||||
|
} else {
|
||||||
|
result[t] = []string{ext}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for ext, t := range m {
|
||||||
|
extUpper := strings.ToUpper(ext)
|
||||||
|
if _, ok := result[t]; ok {
|
||||||
|
result[t] = append(result[t], ext, extUpper)
|
||||||
|
} else {
|
||||||
|
result[t] = []string{ext, extUpper}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
31
pkg/fs/file_ext_test.go
Normal file
31
pkg/fs/file_ext_test.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFileExt_Known(t *testing.T) {
|
||||||
|
t.Run("Empty", func(t *testing.T) {
|
||||||
|
assert.False(t, FileExt.Known(""))
|
||||||
|
})
|
||||||
|
t.Run("jpg", func(t *testing.T) {
|
||||||
|
assert.True(t, FileExt.Known("testdata/test.jpg"))
|
||||||
|
})
|
||||||
|
t.Run("jpeg", func(t *testing.T) {
|
||||||
|
assert.True(t, FileExt.Known("testdata/test.jpeg"))
|
||||||
|
})
|
||||||
|
t.Run("cr2", func(t *testing.T) {
|
||||||
|
assert.True(t, FileExt.Known("testdata/.xxx/test (jpg).cr2"))
|
||||||
|
})
|
||||||
|
t.Run("CR2", func(t *testing.T) {
|
||||||
|
assert.True(t, FileExt.Known("testdata/test (jpg).CR2"))
|
||||||
|
})
|
||||||
|
t.Run("CR5", func(t *testing.T) {
|
||||||
|
assert.False(t, FileExt.Known("testdata/test (jpg).CR5"))
|
||||||
|
})
|
||||||
|
t.Run("mp", func(t *testing.T) {
|
||||||
|
assert.True(t, FileExt.Known("file.mp"))
|
||||||
|
})
|
||||||
|
}
|
||||||
156
pkg/fs/file_format.go
Normal file
156
pkg/fs/file_format.go
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileFormat represents a file format type.
|
||||||
|
type FileFormat string
|
||||||
|
|
||||||
|
// String returns the file format as string.
|
||||||
|
func (f FileFormat) String() string {
|
||||||
|
return string(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is checks if the format strings match.
|
||||||
|
func (f FileFormat) Is(s string) bool {
|
||||||
|
if s == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.String() == strings.ToLower(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find returns the first filename with the same base name and a given type.
|
||||||
|
func (f FileFormat) Find(fileName string, stripSequence bool) string {
|
||||||
|
base := BasePrefix(fileName, stripSequence)
|
||||||
|
dir := filepath.Dir(fileName)
|
||||||
|
|
||||||
|
prefix := filepath.Join(dir, base)
|
||||||
|
prefixLower := filepath.Join(dir, strings.ToLower(base))
|
||||||
|
prefixUpper := filepath.Join(dir, strings.ToUpper(base))
|
||||||
|
|
||||||
|
for _, ext := range TypeExt[f] {
|
||||||
|
if info, err := os.Stat(prefix + ext); err == nil && info.Mode().IsRegular() {
|
||||||
|
return filepath.Join(dir, info.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
if ignoreCase {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if info, err := os.Stat(prefixLower + ext); err == nil && info.Mode().IsRegular() {
|
||||||
|
return filepath.Join(dir, info.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
if info, err := os.Stat(prefixUpper + ext); err == nil && info.Mode().IsRegular() {
|
||||||
|
return filepath.Join(dir, info.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindFirst searches a list of directories for the first file with the same base name and a given type.
|
||||||
|
func (f FileFormat) FindFirst(fileName string, dirs []string, baseDir string, stripSequence bool) string {
|
||||||
|
fileBase := filepath.Base(fileName)
|
||||||
|
fileBasePrefix := BasePrefix(fileName, stripSequence)
|
||||||
|
fileBaseLower := strings.ToLower(fileBasePrefix)
|
||||||
|
fileBaseUpper := strings.ToUpper(fileBasePrefix)
|
||||||
|
|
||||||
|
fileDir := filepath.Dir(fileName)
|
||||||
|
search := append([]string{fileDir}, dirs...)
|
||||||
|
|
||||||
|
for _, ext := range TypeExt[f] {
|
||||||
|
lastDir := ""
|
||||||
|
|
||||||
|
for _, dir := range search {
|
||||||
|
if dir == "" || dir == lastDir {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
lastDir = dir
|
||||||
|
|
||||||
|
if dir != fileDir {
|
||||||
|
if filepath.IsAbs(dir) {
|
||||||
|
dir = filepath.Join(dir, RelName(fileDir, baseDir))
|
||||||
|
} else {
|
||||||
|
dir = filepath.Join(fileDir, dir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if info, err := os.Stat(filepath.Join(dir, fileBase) + ext); err == nil && info.Mode().IsRegular() {
|
||||||
|
return filepath.Join(dir, info.Name())
|
||||||
|
} else if info, err := os.Stat(filepath.Join(dir, fileBasePrefix) + ext); err == nil && info.Mode().IsRegular() {
|
||||||
|
return filepath.Join(dir, info.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
if ignoreCase {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if info, err := os.Stat(filepath.Join(dir, fileBaseLower) + ext); err == nil && info.Mode().IsRegular() {
|
||||||
|
return filepath.Join(dir, info.Name())
|
||||||
|
} else if info, err := os.Stat(filepath.Join(dir, fileBaseUpper) + ext); err == nil && info.Mode().IsRegular() {
|
||||||
|
return filepath.Join(dir, info.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindAll searches a list of directories for files with the same base name and a given type.
|
||||||
|
func (f FileFormat) FindAll(fileName string, dirs []string, baseDir string, stripSequence bool) (results []string) {
|
||||||
|
fileBase := filepath.Base(fileName)
|
||||||
|
fileBasePrefix := BasePrefix(fileName, stripSequence)
|
||||||
|
fileBaseLower := strings.ToLower(fileBasePrefix)
|
||||||
|
fileBaseUpper := strings.ToUpper(fileBasePrefix)
|
||||||
|
|
||||||
|
fileDir := filepath.Dir(fileName)
|
||||||
|
search := append([]string{fileDir}, dirs...)
|
||||||
|
|
||||||
|
for _, ext := range TypeExt[f] {
|
||||||
|
lastDir := ""
|
||||||
|
|
||||||
|
for _, dir := range search {
|
||||||
|
if dir == "" || dir == lastDir {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
lastDir = dir
|
||||||
|
|
||||||
|
if dir != fileDir {
|
||||||
|
if filepath.IsAbs(dir) {
|
||||||
|
dir = filepath.Join(dir, RelName(fileDir, baseDir))
|
||||||
|
} else {
|
||||||
|
dir = filepath.Join(fileDir, dir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if info, err := os.Stat(filepath.Join(dir, fileBase) + ext); err == nil && info.Mode().IsRegular() {
|
||||||
|
results = append(results, filepath.Join(dir, info.Name()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if info, err := os.Stat(filepath.Join(dir, fileBasePrefix) + ext); err == nil && info.Mode().IsRegular() {
|
||||||
|
results = append(results, filepath.Join(dir, info.Name()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if ignoreCase {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if info, err := os.Stat(filepath.Join(dir, fileBaseLower) + ext); err == nil && info.Mode().IsRegular() {
|
||||||
|
results = append(results, filepath.Join(dir, info.Name()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if info, err := os.Stat(filepath.Join(dir, fileBaseUpper) + ext); err == nil && info.Mode().IsRegular() {
|
||||||
|
results = append(results, filepath.Join(dir, info.Name()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
@@ -6,39 +6,40 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetFileType(t *testing.T) {
|
func TestFileFormat_String(t *testing.T) {
|
||||||
t.Run("jpeg", func(t *testing.T) {
|
t.Run("jpg", func(t *testing.T) {
|
||||||
result := GetFileFormat("testdata/test.jpg")
|
assert.Equal(t, "jpg", FormatJpeg.String())
|
||||||
assert.Equal(t, FormatJpeg, result)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("raw", func(t *testing.T) {
|
|
||||||
result := GetFileFormat("testdata/test (jpg).CR2")
|
|
||||||
assert.Equal(t, FormatRaw, result)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("empty", func(t *testing.T) {
|
|
||||||
result := GetFileFormat("")
|
|
||||||
assert.Equal(t, FormatOther, result)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFileType_Find(t *testing.T) {
|
func TestFileFormat_Is(t *testing.T) {
|
||||||
|
t.Run("Empty", func(t *testing.T) {
|
||||||
|
assert.False(t, FormatJpeg.Is(""))
|
||||||
|
})
|
||||||
|
t.Run("Upper", func(t *testing.T) {
|
||||||
|
assert.True(t, FormatJpeg.Is("JPG"))
|
||||||
|
})
|
||||||
|
t.Run("Lower", func(t *testing.T) {
|
||||||
|
assert.True(t, FormatJpeg.Is("jpg"))
|
||||||
|
})
|
||||||
|
t.Run("False", func(t *testing.T) {
|
||||||
|
assert.False(t, FormatJpeg.Is("raw"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFileFormat_Find(t *testing.T) {
|
||||||
t.Run("find jpg", func(t *testing.T) {
|
t.Run("find jpg", func(t *testing.T) {
|
||||||
result := FormatJpeg.Find("testdata/test.xmp", false)
|
result := FormatJpeg.Find("testdata/test.xmp", false)
|
||||||
assert.Equal(t, "testdata/test.jpg", result)
|
assert.Equal(t, "testdata/test.jpg", result)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("upper ext", func(t *testing.T) {
|
t.Run("upper ext", func(t *testing.T) {
|
||||||
result := FormatJpeg.Find("testdata/test.XMP", false)
|
result := FormatJpeg.Find("testdata/test.XMP", false)
|
||||||
assert.Equal(t, "testdata/test.jpg", result)
|
assert.Equal(t, "testdata/test.jpg", result)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("with sequence", func(t *testing.T) {
|
t.Run("with sequence", func(t *testing.T) {
|
||||||
result := FormatJpeg.Find("testdata/test (2).xmp", false)
|
result := FormatJpeg.Find("testdata/test (2).xmp", false)
|
||||||
assert.Equal(t, "", result)
|
assert.Equal(t, "", result)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("strip sequence", func(t *testing.T) {
|
t.Run("strip sequence", func(t *testing.T) {
|
||||||
result := FormatJpeg.Find("testdata/test (2).xmp", true)
|
result := FormatJpeg.Find("testdata/test (2).xmp", true)
|
||||||
assert.Equal(t, "testdata/test.jpg", result)
|
assert.Equal(t, "testdata/test.jpg", result)
|
||||||
@@ -55,86 +56,72 @@ func TestFileType_Find(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFileType_FindFirst(t *testing.T) {
|
func TestFileFormat_FindFirst(t *testing.T) {
|
||||||
dirs := []string{HiddenPath}
|
dirs := []string{HiddenPath}
|
||||||
|
|
||||||
t.Run("find xmp", func(t *testing.T) {
|
t.Run("find xmp", func(t *testing.T) {
|
||||||
result := FormatXMP.FindFirst("testdata/test.jpg", dirs, "", false)
|
result := FormatXMP.FindFirst("testdata/test.jpg", dirs, "", false)
|
||||||
assert.Equal(t, "testdata/.photoprism/test.xmp", result)
|
assert.Equal(t, "testdata/.photoprism/test.xmp", result)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("find xmp upper ext", func(t *testing.T) {
|
t.Run("find xmp upper ext", func(t *testing.T) {
|
||||||
result := FormatXMP.FindFirst("testdata/test.PNG", dirs, "", false)
|
result := FormatXMP.FindFirst("testdata/test.PNG", dirs, "", false)
|
||||||
assert.Equal(t, "testdata/.photoprism/test.xmp", result)
|
assert.Equal(t, "testdata/.photoprism/test.xmp", result)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("find xmp without sequence", func(t *testing.T) {
|
t.Run("find xmp without sequence", func(t *testing.T) {
|
||||||
result := FormatXMP.FindFirst("testdata/test (2).jpg", dirs, "", false)
|
result := FormatXMP.FindFirst("testdata/test (2).jpg", dirs, "", false)
|
||||||
assert.Equal(t, "", result)
|
assert.Equal(t, "", result)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("find xmp with sequence", func(t *testing.T) {
|
t.Run("find xmp with sequence", func(t *testing.T) {
|
||||||
result := FormatXMP.FindFirst("testdata/test (2).jpg", dirs, "", true)
|
result := FormatXMP.FindFirst("testdata/test (2).jpg", dirs, "", true)
|
||||||
assert.Equal(t, "testdata/.photoprism/test.xmp", result)
|
assert.Equal(t, "testdata/.photoprism/test.xmp", result)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("find jpg", func(t *testing.T) {
|
t.Run("find jpg", func(t *testing.T) {
|
||||||
result := FormatJpeg.FindFirst("testdata/test.xmp", dirs, "", false)
|
result := FormatJpeg.FindFirst("testdata/test.xmp", dirs, "", false)
|
||||||
assert.Equal(t, "testdata/test.jpg", result)
|
assert.Equal(t, "testdata/test.jpg", result)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("find jpg abs", func(t *testing.T) {
|
t.Run("find jpg abs", func(t *testing.T) {
|
||||||
result := FormatJpeg.FindFirst(Abs("testdata/test.xmp"), dirs, "", false)
|
result := FormatJpeg.FindFirst(Abs("testdata/test.xmp"), dirs, "", false)
|
||||||
assert.Equal(t, Abs("testdata/test.jpg"), result)
|
assert.Equal(t, Abs("testdata/test.jpg"), result)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("upper ext", func(t *testing.T) {
|
t.Run("upper ext", func(t *testing.T) {
|
||||||
result := FormatJpeg.FindFirst("testdata/test.XMP", dirs, "", false)
|
result := FormatJpeg.FindFirst("testdata/test.XMP", dirs, "", false)
|
||||||
assert.Equal(t, "testdata/test.jpg", result)
|
assert.Equal(t, "testdata/test.jpg", result)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("with sequence", func(t *testing.T) {
|
t.Run("with sequence", func(t *testing.T) {
|
||||||
result := FormatJpeg.FindFirst("testdata/test (2).xmp", dirs, "", false)
|
result := FormatJpeg.FindFirst("testdata/test (2).xmp", dirs, "", false)
|
||||||
assert.Equal(t, "", result)
|
assert.Equal(t, "", result)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("strip sequence", func(t *testing.T) {
|
t.Run("strip sequence", func(t *testing.T) {
|
||||||
result := FormatJpeg.FindFirst("testdata/test (2).xmp", dirs, "", true)
|
result := FormatJpeg.FindFirst("testdata/test (2).xmp", dirs, "", true)
|
||||||
assert.Equal(t, "testdata/test.jpg", result)
|
assert.Equal(t, "testdata/test.jpg", result)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("name upper", func(t *testing.T) {
|
t.Run("name upper", func(t *testing.T) {
|
||||||
result := FormatJpeg.FindFirst("testdata/CATYELLOW.xmp", dirs, "", true)
|
result := FormatJpeg.FindFirst("testdata/CATYELLOW.xmp", dirs, "", true)
|
||||||
assert.Equal(t, "testdata/CATYELLOW.jpg", result)
|
assert.Equal(t, "testdata/CATYELLOW.jpg", result)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("name lower", func(t *testing.T) {
|
t.Run("name lower", func(t *testing.T) {
|
||||||
result := FormatJpeg.FindFirst("testdata/chameleon_lime.xmp", dirs, "", true)
|
result := FormatJpeg.FindFirst("testdata/chameleon_lime.xmp", dirs, "", true)
|
||||||
assert.Equal(t, "testdata/chameleon_lime.jpg", result)
|
assert.Equal(t, "testdata/chameleon_lime.jpg", result)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("example_bmp_notfound", func(t *testing.T) {
|
t.Run("example_bmp_notfound", func(t *testing.T) {
|
||||||
result := FormatBitmap.FindFirst("testdata/example.00001.jpg", dirs, "", true)
|
result := FormatBitmap.FindFirst("testdata/example.00001.jpg", dirs, "", true)
|
||||||
assert.Equal(t, "", result)
|
assert.Equal(t, "", result)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("example_bmp_found", func(t *testing.T) {
|
t.Run("example_bmp_found", func(t *testing.T) {
|
||||||
result := FormatBitmap.FindFirst("testdata/example.00001.jpg", []string{"directory"}, "", true)
|
result := FormatBitmap.FindFirst("testdata/example.00001.jpg", []string{"directory"}, "", true)
|
||||||
assert.Equal(t, "testdata/directory/example.bmp", result)
|
assert.Equal(t, "testdata/directory/example.bmp", result)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("example_png_found", func(t *testing.T) {
|
t.Run("example_png_found", func(t *testing.T) {
|
||||||
result := FormatPng.FindFirst("testdata/example.00001.jpg", []string{"directory", "directory/subdirectory"}, "", true)
|
result := FormatPng.FindFirst("testdata/example.00001.jpg", []string{"directory", "directory/subdirectory"}, "", true)
|
||||||
assert.Equal(t, "testdata/directory/subdirectory/example.png", result)
|
assert.Equal(t, "testdata/directory/subdirectory/example.png", result)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("example_bmp_found", func(t *testing.T) {
|
t.Run("example_bmp_found", func(t *testing.T) {
|
||||||
result := FormatBitmap.FindFirst(Abs("testdata/example.00001.jpg"), []string{"directory"}, Abs("testdata"), true)
|
result := FormatBitmap.FindFirst(Abs("testdata/example.00001.jpg"), []string{"directory"}, Abs("testdata"), true)
|
||||||
assert.Equal(t, Abs("testdata/directory/example.bmp"), result)
|
assert.Equal(t, Abs("testdata/directory/example.bmp"), result)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFileType_FindAll(t *testing.T) {
|
func TestFileFormat_FindAll(t *testing.T) {
|
||||||
dirs := []string{HiddenPath}
|
dirs := []string{HiddenPath}
|
||||||
|
|
||||||
t.Run("CATYELLOW.jpg", func(t *testing.T) {
|
t.Run("CATYELLOW.jpg", func(t *testing.T) {
|
||||||
@@ -142,15 +129,3 @@ func TestFileType_FindAll(t *testing.T) {
|
|||||||
assert.Contains(t, result, "testdata/CATYELLOW.jpg")
|
assert.Contains(t, result, "testdata/CATYELLOW.jpg")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFileExt(t *testing.T) {
|
|
||||||
t.Run("mp", func(t *testing.T) {
|
|
||||||
assert.True(t, FileExt.Known("file.mp"))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetFileFormat(t *testing.T) {
|
|
||||||
t.Run("mp", func(t *testing.T) {
|
|
||||||
assert.Equal(t, FileFormat("mp4"), GetFileFormat("file.mp"))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
60
pkg/fs/file_formats.go
Normal file
60
pkg/fs/file_formats.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "image/gif"
|
||||||
|
_ "image/jpeg"
|
||||||
|
_ "image/png"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
_ "golang.org/x/image/bmp"
|
||||||
|
_ "golang.org/x/image/tiff"
|
||||||
|
_ "golang.org/x/image/webp"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
FormatJpeg FileFormat = "jpg" // JPEG image file.
|
||||||
|
FormatPng FileFormat = "png" // PNG image file.
|
||||||
|
FormatGif FileFormat = "gif" // GIF image file.
|
||||||
|
FormatTiff FileFormat = "tiff" // TIFF image file.
|
||||||
|
FormatBitmap FileFormat = "bmp" // BMP image file.
|
||||||
|
FormatRaw FileFormat = "raw" // RAW image file.
|
||||||
|
FormatMpo FileFormat = "mpo" // Stereoscopic Image that consists of two JPG images that are combined into one 3D image
|
||||||
|
FormatHEIF FileFormat = "heif" // High Efficiency Image File Format
|
||||||
|
FormatWebP FileFormat = "webp" // Google WebP Image
|
||||||
|
FormatWebM FileFormat = "webm" // Google WebM Video
|
||||||
|
FormatHEVC FileFormat = "hevc" // H.265, High Efficiency Video Coding (HEVC)
|
||||||
|
FormatAvc FileFormat = "avc" // H.264, Advanced Video Coding (AVC), MPEG-4 Part 10, used internally
|
||||||
|
FormatMov FileFormat = "mov" // QuickTime File Format, can contain AVC, HEVC,...
|
||||||
|
FormatMp4 FileFormat = "mp4" // Standard MPEG-4 Container based on QuickTime, can contain AVC, HEVC,...
|
||||||
|
FormatAvi FileFormat = "avi" // Microsoft Audio Video Interleave (AVI)
|
||||||
|
Format3gp FileFormat = "3gp" // Mobile Multimedia Container Format, MPEG-4 Part 12
|
||||||
|
Format3g2 FileFormat = "3g2" // Similar to 3GP, consumes less space & bandwidth
|
||||||
|
FormatFlv FileFormat = "flv" // Flash Video
|
||||||
|
FormatMkv FileFormat = "mkv" // Matroska Multimedia Container, free and open
|
||||||
|
FormatMpg FileFormat = "mpg" // Moving Picture Experts Group (MPEG)
|
||||||
|
FormatMts FileFormat = "mts" // AVCHD (Advanced Video Coding High Definition)
|
||||||
|
FormatOgv FileFormat = "ogv" // Ogg container format maintained by the Xiph.Org, free and open
|
||||||
|
FormatWMV FileFormat = "wmv" // Windows Media Video
|
||||||
|
FormatXMP FileFormat = "xmp" // Adobe XMP sidecar file (XML).
|
||||||
|
FormatAAE FileFormat = "aae" // Apple sidecar file (XML).
|
||||||
|
FormatXML FileFormat = "xml" // XML metadata / config / sidecar file.
|
||||||
|
FormatYaml FileFormat = "yml" // YAML metadata / config / sidecar file.
|
||||||
|
FormatToml FileFormat = "toml" // Tom's Obvious, Minimal Language sidecar file.
|
||||||
|
FormatJson FileFormat = "json" // JSON metadata / config / sidecar file.
|
||||||
|
FormatText FileFormat = "txt" // Text config / sidecar file.
|
||||||
|
FormatMarkdown FileFormat = "md" // Markdown text sidecar file.
|
||||||
|
FormatOther FileFormat = "" // Unknown file format.
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetFileFormat returns the (expected) type for a given file name.
|
||||||
|
func GetFileFormat(fileName string) FileFormat {
|
||||||
|
fileExt := strings.ToLower(filepath.Ext(fileName))
|
||||||
|
result, ok := FileExt[fileExt]
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
result = FormatOther
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
29
pkg/fs/file_formats_test.go
Normal file
29
pkg/fs/file_formats_test.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetFileFormat(t *testing.T) {
|
||||||
|
t.Run("Empty", func(t *testing.T) {
|
||||||
|
result := GetFileFormat("")
|
||||||
|
assert.Equal(t, FormatOther, result)
|
||||||
|
})
|
||||||
|
t.Run("JPEG", func(t *testing.T) {
|
||||||
|
result := GetFileFormat("testdata/test.jpg")
|
||||||
|
assert.Equal(t, FormatJpeg, result)
|
||||||
|
})
|
||||||
|
t.Run("RawCRw", func(t *testing.T) {
|
||||||
|
result := GetFileFormat("testdata/test (jpg).crw")
|
||||||
|
assert.Equal(t, FormatRaw, result)
|
||||||
|
})
|
||||||
|
t.Run("RawCR2", func(t *testing.T) {
|
||||||
|
result := GetFileFormat("testdata/test (jpg).CR2")
|
||||||
|
assert.Equal(t, FormatRaw, result)
|
||||||
|
})
|
||||||
|
t.Run("MP4", func(t *testing.T) {
|
||||||
|
assert.Equal(t, FileFormat("mp4"), GetFileFormat("file.mp"))
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,323 +0,0 @@
|
|||||||
package fs
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "image/gif" // Import for image.
|
|
||||||
_ "image/jpeg"
|
|
||||||
_ "image/png"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type FileFormat string
|
|
||||||
|
|
||||||
const (
|
|
||||||
FormatJpeg FileFormat = "jpg" // JPEG image file.
|
|
||||||
FormatPng FileFormat = "png" // PNG image file.
|
|
||||||
FormatGif FileFormat = "gif" // GIF image file.
|
|
||||||
FormatTiff FileFormat = "tiff" // TIFF image file.
|
|
||||||
FormatBitmap FileFormat = "bmp" // BMP image file.
|
|
||||||
FormatRaw FileFormat = "raw" // RAW image file.
|
|
||||||
FormatMpo FileFormat = "mpo" // Stereoscopic Image that consists of two JPG images that are combined into one 3D image
|
|
||||||
FormatHEIF FileFormat = "heif" // High Efficiency Image File Format
|
|
||||||
FormatWebP FileFormat = "webp" // Google WebP Image
|
|
||||||
FormatWebM FileFormat = "webm" // Google WebM Video
|
|
||||||
FormatHEVC FileFormat = "hevc" // H.265, High Efficiency Video Coding (HEVC)
|
|
||||||
FormatAvc FileFormat = "avc" // H.264, Advanced Video Coding (AVC), MPEG-4 Part 10, used internally
|
|
||||||
FormatMov FileFormat = "mov" // QuickTime File Format, can contain AVC, HEVC,...
|
|
||||||
FormatMp4 FileFormat = "mp4" // Standard MPEG-4 Container based on QuickTime, can contain AVC, HEVC,...
|
|
||||||
FormatAvi FileFormat = "avi" // Microsoft Audio Video Interleave (AVI)
|
|
||||||
Format3gp FileFormat = "3gp" // Mobile Multimedia Container Format, MPEG-4 Part 12
|
|
||||||
Format3g2 FileFormat = "3g2" // Similar to 3GP, consumes less space & bandwidth
|
|
||||||
FormatFlv FileFormat = "flv" // Flash Video
|
|
||||||
FormatMkv FileFormat = "mkv" // Matroska Multimedia Container, free and open
|
|
||||||
FormatMpg FileFormat = "mpg" // Moving Picture Experts Group (MPEG)
|
|
||||||
FormatMts FileFormat = "mts" // AVCHD (Advanced Video Coding High Definition)
|
|
||||||
FormatOgv FileFormat = "ogv" // Ogg container format maintained by the Xiph.Org, free and open
|
|
||||||
FormatWMV FileFormat = "wmv" // Windows Media Video
|
|
||||||
FormatXMP FileFormat = "xmp" // Adobe XMP sidecar file (XML).
|
|
||||||
FormatAAE FileFormat = "aae" // Apple sidecar file (XML).
|
|
||||||
FormatXML FileFormat = "xml" // XML metadata / config / sidecar file.
|
|
||||||
FormatYaml FileFormat = "yml" // YAML metadata / config / sidecar file.
|
|
||||||
FormatToml FileFormat = "toml" // Tom's Obvious, Minimal Language sidecar file.
|
|
||||||
FormatJson FileFormat = "json" // JSON metadata / config / sidecar file.
|
|
||||||
FormatText FileFormat = "txt" // Text config / sidecar file.
|
|
||||||
FormatMarkdown FileFormat = "md" // Markdown text sidecar file.
|
|
||||||
FormatOther FileFormat = "" // Unknown file format.
|
|
||||||
)
|
|
||||||
|
|
||||||
// FileExt contains the filename extensions of file formats known to PhotoPrism.
|
|
||||||
var FileExt = FileExtensions{
|
|
||||||
".bmp": FormatBitmap,
|
|
||||||
".gif": FormatGif,
|
|
||||||
".tif": FormatTiff,
|
|
||||||
".tiff": FormatTiff,
|
|
||||||
".png": FormatPng,
|
|
||||||
".pn": FormatPng,
|
|
||||||
".crw": FormatRaw,
|
|
||||||
".cr2": FormatRaw,
|
|
||||||
".cr3": FormatRaw,
|
|
||||||
".nef": FormatRaw,
|
|
||||||
".arw": FormatRaw,
|
|
||||||
".dng": FormatRaw,
|
|
||||||
".mov": FormatMov,
|
|
||||||
".avi": FormatAvi,
|
|
||||||
".mp": FormatMp4,
|
|
||||||
".mp4": FormatMp4,
|
|
||||||
".m4v": FormatMp4,
|
|
||||||
".avc": FormatAvc,
|
|
||||||
".hevc": FormatHEVC,
|
|
||||||
".3gp": Format3gp,
|
|
||||||
".3g2": Format3g2,
|
|
||||||
".flv": FormatFlv,
|
|
||||||
".mkv": FormatMkv,
|
|
||||||
".mpg": FormatMpg,
|
|
||||||
".mpeg": FormatMpg,
|
|
||||||
".mpo": FormatMpo,
|
|
||||||
".mts": FormatMts,
|
|
||||||
".ogv": FormatOgv,
|
|
||||||
".webp": FormatWebP,
|
|
||||||
".webm": FormatWebM,
|
|
||||||
".wmv": FormatWMV,
|
|
||||||
".yml": FormatYaml,
|
|
||||||
".yaml": FormatYaml,
|
|
||||||
".jpg": FormatJpeg,
|
|
||||||
".jpeg": FormatJpeg,
|
|
||||||
".jpe": FormatJpeg,
|
|
||||||
".jif": FormatJpeg,
|
|
||||||
".jfif": FormatJpeg,
|
|
||||||
".jfi": FormatJpeg,
|
|
||||||
".thm": FormatJpeg,
|
|
||||||
".xmp": FormatXMP,
|
|
||||||
".aae": FormatAAE,
|
|
||||||
".heif": FormatHEIF,
|
|
||||||
".heic": FormatHEIF,
|
|
||||||
".3fr": FormatRaw,
|
|
||||||
".ari": FormatRaw,
|
|
||||||
".bay": FormatRaw,
|
|
||||||
".cap": FormatRaw,
|
|
||||||
".data": FormatRaw,
|
|
||||||
".dcs": FormatRaw,
|
|
||||||
".dcr": FormatRaw,
|
|
||||||
".drf": FormatRaw,
|
|
||||||
".eip": FormatRaw,
|
|
||||||
".erf": FormatRaw,
|
|
||||||
".fff": FormatRaw,
|
|
||||||
".gpr": FormatRaw,
|
|
||||||
".iiq": FormatRaw,
|
|
||||||
".k25": FormatRaw,
|
|
||||||
".kdc": FormatRaw,
|
|
||||||
".mdc": FormatRaw,
|
|
||||||
".mef": FormatRaw,
|
|
||||||
".mos": FormatRaw,
|
|
||||||
".mrw": FormatRaw,
|
|
||||||
".nrw": FormatRaw,
|
|
||||||
".obm": FormatRaw,
|
|
||||||
".orf": FormatRaw,
|
|
||||||
".pef": FormatRaw,
|
|
||||||
".ptx": FormatRaw,
|
|
||||||
".pxn": FormatRaw,
|
|
||||||
".r3d": FormatRaw,
|
|
||||||
".raf": FormatRaw,
|
|
||||||
".raw": FormatRaw,
|
|
||||||
".rwl": FormatRaw,
|
|
||||||
".rw2": FormatRaw,
|
|
||||||
".rwz": FormatRaw,
|
|
||||||
".sr2": FormatRaw,
|
|
||||||
".srf": FormatRaw,
|
|
||||||
".srw": FormatRaw,
|
|
||||||
".x3f": FormatRaw,
|
|
||||||
".xml": FormatXML,
|
|
||||||
".txt": FormatText,
|
|
||||||
".md": FormatMarkdown,
|
|
||||||
".json": FormatJson,
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m FileExtensions) Known(name string) bool {
|
|
||||||
if name == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
ext := strings.ToLower(filepath.Ext(name))
|
|
||||||
|
|
||||||
if ext == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := m[ext]; ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m FileExtensions) TypeExt() TypeExtensions {
|
|
||||||
result := make(TypeExtensions)
|
|
||||||
|
|
||||||
if ignoreCase {
|
|
||||||
for ext, t := range m {
|
|
||||||
if _, ok := result[t]; ok {
|
|
||||||
result[t] = append(result[t], ext)
|
|
||||||
} else {
|
|
||||||
result[t] = []string{ext}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for ext, t := range m {
|
|
||||||
extUpper := strings.ToUpper(ext)
|
|
||||||
if _, ok := result[t]; ok {
|
|
||||||
result[t] = append(result[t], ext, extUpper)
|
|
||||||
} else {
|
|
||||||
result[t] = []string{ext, extUpper}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
var TypeExt = FileExt.TypeExt()
|
|
||||||
|
|
||||||
// Find returns the first filename with the same base name and a given type.
|
|
||||||
func (t FileFormat) Find(fileName string, stripSequence bool) string {
|
|
||||||
base := BasePrefix(fileName, stripSequence)
|
|
||||||
dir := filepath.Dir(fileName)
|
|
||||||
|
|
||||||
prefix := filepath.Join(dir, base)
|
|
||||||
prefixLower := filepath.Join(dir, strings.ToLower(base))
|
|
||||||
prefixUpper := filepath.Join(dir, strings.ToUpper(base))
|
|
||||||
|
|
||||||
for _, ext := range TypeExt[t] {
|
|
||||||
if info, err := os.Stat(prefix + ext); err == nil && info.Mode().IsRegular() {
|
|
||||||
return filepath.Join(dir, info.Name())
|
|
||||||
}
|
|
||||||
|
|
||||||
if ignoreCase {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if info, err := os.Stat(prefixLower + ext); err == nil && info.Mode().IsRegular() {
|
|
||||||
return filepath.Join(dir, info.Name())
|
|
||||||
}
|
|
||||||
|
|
||||||
if info, err := os.Stat(prefixUpper + ext); err == nil && info.Mode().IsRegular() {
|
|
||||||
return filepath.Join(dir, info.Name())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFileFormat returns the (expected) type for a given file name.
|
|
||||||
func GetFileFormat(fileName string) FileFormat {
|
|
||||||
fileExt := strings.ToLower(filepath.Ext(fileName))
|
|
||||||
result, ok := FileExt[fileExt]
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
result = FormatOther
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindFirst searches a list of directories for the first file with the same base name and a given type.
|
|
||||||
func (t FileFormat) FindFirst(fileName string, dirs []string, baseDir string, stripSequence bool) string {
|
|
||||||
fileBase := filepath.Base(fileName)
|
|
||||||
fileBasePrefix := BasePrefix(fileName, stripSequence)
|
|
||||||
fileBaseLower := strings.ToLower(fileBasePrefix)
|
|
||||||
fileBaseUpper := strings.ToUpper(fileBasePrefix)
|
|
||||||
|
|
||||||
fileDir := filepath.Dir(fileName)
|
|
||||||
search := append([]string{fileDir}, dirs...)
|
|
||||||
|
|
||||||
for _, ext := range TypeExt[t] {
|
|
||||||
lastDir := ""
|
|
||||||
|
|
||||||
for _, dir := range search {
|
|
||||||
if dir == "" || dir == lastDir {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
lastDir = dir
|
|
||||||
|
|
||||||
if dir != fileDir {
|
|
||||||
if filepath.IsAbs(dir) {
|
|
||||||
dir = filepath.Join(dir, RelName(fileDir, baseDir))
|
|
||||||
} else {
|
|
||||||
dir = filepath.Join(fileDir, dir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if info, err := os.Stat(filepath.Join(dir, fileBase) + ext); err == nil && info.Mode().IsRegular() {
|
|
||||||
return filepath.Join(dir, info.Name())
|
|
||||||
} else if info, err := os.Stat(filepath.Join(dir, fileBasePrefix) + ext); err == nil && info.Mode().IsRegular() {
|
|
||||||
return filepath.Join(dir, info.Name())
|
|
||||||
}
|
|
||||||
|
|
||||||
if ignoreCase {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if info, err := os.Stat(filepath.Join(dir, fileBaseLower) + ext); err == nil && info.Mode().IsRegular() {
|
|
||||||
return filepath.Join(dir, info.Name())
|
|
||||||
} else if info, err := os.Stat(filepath.Join(dir, fileBaseUpper) + ext); err == nil && info.Mode().IsRegular() {
|
|
||||||
return filepath.Join(dir, info.Name())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindAll searches a list of directories for files with the same base name and a given type.
|
|
||||||
func (t FileFormat) FindAll(fileName string, dirs []string, baseDir string, stripSequence bool) (results []string) {
|
|
||||||
fileBase := filepath.Base(fileName)
|
|
||||||
fileBasePrefix := BasePrefix(fileName, stripSequence)
|
|
||||||
fileBaseLower := strings.ToLower(fileBasePrefix)
|
|
||||||
fileBaseUpper := strings.ToUpper(fileBasePrefix)
|
|
||||||
|
|
||||||
fileDir := filepath.Dir(fileName)
|
|
||||||
search := append([]string{fileDir}, dirs...)
|
|
||||||
|
|
||||||
for _, ext := range TypeExt[t] {
|
|
||||||
lastDir := ""
|
|
||||||
|
|
||||||
for _, dir := range search {
|
|
||||||
if dir == "" || dir == lastDir {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
lastDir = dir
|
|
||||||
|
|
||||||
if dir != fileDir {
|
|
||||||
if filepath.IsAbs(dir) {
|
|
||||||
dir = filepath.Join(dir, RelName(fileDir, baseDir))
|
|
||||||
} else {
|
|
||||||
dir = filepath.Join(fileDir, dir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if info, err := os.Stat(filepath.Join(dir, fileBase) + ext); err == nil && info.Mode().IsRegular() {
|
|
||||||
results = append(results, filepath.Join(dir, info.Name()))
|
|
||||||
}
|
|
||||||
|
|
||||||
if info, err := os.Stat(filepath.Join(dir, fileBasePrefix) + ext); err == nil && info.Mode().IsRegular() {
|
|
||||||
results = append(results, filepath.Join(dir, info.Name()))
|
|
||||||
}
|
|
||||||
|
|
||||||
if ignoreCase {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if info, err := os.Stat(filepath.Join(dir, fileBaseLower) + ext); err == nil && info.Mode().IsRegular() {
|
|
||||||
results = append(results, filepath.Join(dir, info.Name()))
|
|
||||||
}
|
|
||||||
|
|
||||||
if info, err := os.Stat(filepath.Join(dir, fileBaseUpper) + ext); err == nil && info.Mode().IsRegular() {
|
|
||||||
results = append(results, filepath.Join(dir, info.Name()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user