HEIF/AVIF/DNG: Update file format descriptions in the UI #2726

Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
Michael Mayer 2022-10-07 16:37:47 +02:00
parent 97fff0bfd3
commit 2034110c5d
13 changed files with 286 additions and 276 deletions

View file

@ -2350,9 +2350,9 @@
}
},
"node_modules/@types/node": {
"version": "18.8.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.2.tgz",
"integrity": "sha512-cRMwIgdDN43GO4xMWAfJAecYn8wV4JbsOGHNfNUIDiuYkUYAR5ec4Rj7IO2SAhFPEfpPtLtUTbbny/TCT7aDwA=="
"version": "18.8.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.3.tgz",
"integrity": "sha512-0os9vz6BpGwxGe9LOhgP/ncvYN5Tx1fNcd2TM3rD/aCGBkysb+ZWpXEocG24h6ZzOi13+VB8HndAQFezsSOw1w=="
},
"node_modules/@types/parse-json": {
"version": "4.0.0",
@ -3070,9 +3070,9 @@
}
},
"node_modules/axios": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.0.0.tgz",
"integrity": "sha512-SsHsGFN1qNPFT5QhSoSD37SHDfGyLSW5AESmyLk2JeCMHv5g0I9g0Hz/zQHx2KNe0jGXh2q2hAm7OdkXm360CA==",
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.1.2.tgz",
"integrity": "sha512-bznQyETwElsXl2RK7HLLwb5GPpOLlycxHCtrpDR/4RqqBzjARaOTo3jz4IgtntWUYee7Ne4S8UHd92VCuzPaWA==",
"dependencies": {
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",
@ -3503,9 +3503,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001416",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001416.tgz",
"integrity": "sha512-06wzzdAkCPZO+Qm4e/eNghZBDfVNDsCgw33T27OwBH9unE9S478OYw//Q2L7Npf/zBzs7rjZOszIFQkwQKAEqA==",
"version": "1.0.30001418",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz",
"integrity": "sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg==",
"funding": [
{
"type": "opencollective",
@ -4662,9 +4662,9 @@
}
},
"node_modules/electron-to-chromium": {
"version": "1.4.274",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.274.tgz",
"integrity": "sha512-Fgn7JZQzq85I81FpKUNxVLAzoghy8JZJ4NIue+YfUYBbu1AkpgzFvNwzF/ZNZH9ElkmJD0TSWu1F2gTpw/zZlg=="
"version": "1.4.275",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.275.tgz",
"integrity": "sha512-aJeQQ+Hl9Jyyzv4chBqYJwmVRY46N5i2BEX5Cuyk/5gFCUZ5F3i7Hnba6snZftWla7Gglwc5pIgcd+E7cW+rPg=="
},
"node_modules/emoji-regex": {
"version": "8.0.0",
@ -4823,9 +4823,9 @@
}
},
"node_modules/es-abstract": {
"version": "1.20.3",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.3.tgz",
"integrity": "sha512-AyrnaKVpMzljIdwjzrj+LxGmj8ik2LckwXacHqrJJ/jxz6dDDBcZ7I7nlHM0FvEW8MfbWJwOd+yT2XzYW49Frw==",
"version": "1.20.4",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz",
"integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==",
"dependencies": {
"call-bind": "^1.0.2",
"es-to-primitive": "^1.2.1",
@ -4837,7 +4837,7 @@
"has-property-descriptors": "^1.0.0",
"has-symbols": "^1.0.3",
"internal-slot": "^1.0.3",
"is-callable": "^1.2.6",
"is-callable": "^1.2.7",
"is-negative-zero": "^2.0.2",
"is-regex": "^1.1.4",
"is-shared-array-buffer": "^1.0.2",
@ -11128,9 +11128,9 @@
}
},
"node_modules/sass-loader": {
"version": "13.0.2",
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.0.2.tgz",
"integrity": "sha512-BbiqbVmbfJaWVeOOAu2o7DhYWtcNmTfvroVgFXa6k2hHheMxNAeDHLNoDy/Q5aoaVlz0LH+MbMktKwm9vN/j8Q==",
"version": "13.1.0",
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.1.0.tgz",
"integrity": "sha512-tZS1RJQ2n2+QNyf3CCAo1H562WjL/5AM6Gi8YcPVVoNxQX8d19mx8E+8fRrMWsyc93ZL6Q8vZDSM0FHVTJaVnQ==",
"dependencies": {
"klona": "^2.0.4",
"neo-async": "^2.6.2"
@ -14952,9 +14952,9 @@
}
},
"@types/node": {
"version": "18.8.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.2.tgz",
"integrity": "sha512-cRMwIgdDN43GO4xMWAfJAecYn8wV4JbsOGHNfNUIDiuYkUYAR5ec4Rj7IO2SAhFPEfpPtLtUTbbny/TCT7aDwA=="
"version": "18.8.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.3.tgz",
"integrity": "sha512-0os9vz6BpGwxGe9LOhgP/ncvYN5Tx1fNcd2TM3rD/aCGBkysb+ZWpXEocG24h6ZzOi13+VB8HndAQFezsSOw1w=="
},
"@types/parse-json": {
"version": "4.0.0",
@ -15516,9 +15516,9 @@
"integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw=="
},
"axios": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.0.0.tgz",
"integrity": "sha512-SsHsGFN1qNPFT5QhSoSD37SHDfGyLSW5AESmyLk2JeCMHv5g0I9g0Hz/zQHx2KNe0jGXh2q2hAm7OdkXm360CA==",
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.1.2.tgz",
"integrity": "sha512-bznQyETwElsXl2RK7HLLwb5GPpOLlycxHCtrpDR/4RqqBzjARaOTo3jz4IgtntWUYee7Ne4S8UHd92VCuzPaWA==",
"requires": {
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",
@ -15850,9 +15850,9 @@
}
},
"caniuse-lite": {
"version": "1.0.30001416",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001416.tgz",
"integrity": "sha512-06wzzdAkCPZO+Qm4e/eNghZBDfVNDsCgw33T27OwBH9unE9S478OYw//Q2L7Npf/zBzs7rjZOszIFQkwQKAEqA=="
"version": "1.0.30001418",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz",
"integrity": "sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg=="
},
"chai": {
"version": "4.3.6",
@ -16676,9 +16676,9 @@
}
},
"electron-to-chromium": {
"version": "1.4.274",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.274.tgz",
"integrity": "sha512-Fgn7JZQzq85I81FpKUNxVLAzoghy8JZJ4NIue+YfUYBbu1AkpgzFvNwzF/ZNZH9ElkmJD0TSWu1F2gTpw/zZlg=="
"version": "1.4.275",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.275.tgz",
"integrity": "sha512-aJeQQ+Hl9Jyyzv4chBqYJwmVRY46N5i2BEX5Cuyk/5gFCUZ5F3i7Hnba6snZftWla7Gglwc5pIgcd+E7cW+rPg=="
},
"emoji-regex": {
"version": "8.0.0",
@ -16801,9 +16801,9 @@
}
},
"es-abstract": {
"version": "1.20.3",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.3.tgz",
"integrity": "sha512-AyrnaKVpMzljIdwjzrj+LxGmj8ik2LckwXacHqrJJ/jxz6dDDBcZ7I7nlHM0FvEW8MfbWJwOd+yT2XzYW49Frw==",
"version": "1.20.4",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz",
"integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==",
"requires": {
"call-bind": "^1.0.2",
"es-to-primitive": "^1.2.1",
@ -16815,7 +16815,7 @@
"has-property-descriptors": "^1.0.0",
"has-symbols": "^1.0.3",
"internal-slot": "^1.0.3",
"is-callable": "^1.2.6",
"is-callable": "^1.2.7",
"is-negative-zero": "^2.0.2",
"is-regex": "^1.1.4",
"is-shared-array-buffer": "^1.0.2",
@ -21213,9 +21213,9 @@
}
},
"sass-loader": {
"version": "13.0.2",
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.0.2.tgz",
"integrity": "sha512-BbiqbVmbfJaWVeOOAu2o7DhYWtcNmTfvroVgFXa6k2hHheMxNAeDHLNoDy/Q5aoaVlz0LH+MbMktKwm9vN/j8Q==",
"version": "13.1.0",
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.1.0.tgz",
"integrity": "sha512-tZS1RJQ2n2+QNyf3CCAo1H562WjL/5AM6Gi8YcPVVoNxQX8d19mx8E+8fRrMWsyc93ZL6Q8vZDSM0FHVTJaVnQ==",
"requires": {
"klona": "^2.0.4",
"neo-async": "^2.6.2"

View file

@ -163,6 +163,8 @@ export default class Util {
}
switch (value) {
case "jpg":
return "JPEG";
case "raw":
return "Unprocessed Sensor Data (RAW)";
case "mov":
@ -176,11 +178,13 @@ export default class Util {
return "TIFF";
case "gif":
return "GIF";
case "dng":
return "Adobe Digital Negative";
case "avc":
case "avc1":
return "Advanced Video Coding (AVC) / H.264";
case "avif":
return "AV1 Image File Format (AVIF)";
return "AOMedia Video 1 (AV1)";
case "hevc":
case "hvc":
case "hvc1":
@ -257,6 +261,7 @@ export default class Util {
return "T6/Group 4 Fax";
case "5":
return "LZW";
case "jpg":
case "jpeg":
case "6":
case "7":

View file

@ -266,9 +266,9 @@ msgstr ""
#: src/component/navigation.vue:101
#: src/component/photo/cards.vue:301
#: src/component/photo/cards.vue:480
#: src/component/photo/cards.vue:481
#: src/component/photo/list.vue:191
#: src/component/photo/mosaic.vue:224
#: src/component/photo/mosaic.vue:225
#: src/model/file.js:210
#: src/options/options.js:290
msgid "Animated"
@ -290,7 +290,7 @@ msgstr ""
msgid "Apply"
msgstr ""
#: src/component/photo/cards.vue:410
#: src/component/photo/cards.vue:411
#: src/component/photo/clipboard.vue:88
#: src/dialog/photo/details.vue:116
msgid "Approve"
@ -298,7 +298,7 @@ msgstr ""
#: src/app/routes.js:232
#: src/component/navigation.vue:134
#: src/component/photo/cards.vue:381
#: src/component/photo/cards.vue:382
#: src/component/photo/clipboard.vue:204
#: src/pages/settings/general.vue:294
msgid "Archive"
@ -415,7 +415,7 @@ msgstr ""
msgid "Calendar"
msgstr ""
#: src/component/photo/cards.vue:488
#: src/component/photo/cards.vue:489
#: src/component/photo/list.vue:137
#: src/component/photo/toolbar.vue:202
#: src/dialog/photo/details.vue:331
@ -469,7 +469,7 @@ msgstr ""
msgid "Cards"
msgstr ""
#: src/component/photo/toolbar.vue:337
#: src/component/photo/toolbar.vue:338
#: src/dialog/album/edit.vue:163
#: src/pages/about/feedback.vue:121
#: src/pages/albums.vue:113
@ -509,7 +509,7 @@ msgstr ""
msgid "Codec"
msgstr ""
#: src/component/photo/toolbar.vue:314
#: src/component/photo/toolbar.vue:315
msgid "Color"
msgstr ""
@ -657,13 +657,13 @@ msgstr ""
#: src/dialog/photo/delete.vue:18
#: src/dialog/photo/files.vue:41
#: src/dialog/photo/files.vue:38
#: src/dialog/share.vue:222
#: src/dialog/share.vue:223
#: src/pages/library/errors.vue:88
#: src/pages/settings/general.vue:206
msgid "Delete"
msgstr ""
#: src/component/photo/cards.vue:449
#: src/component/photo/cards.vue:450
#: src/dialog/album/edit.vue:141
#: src/dialog/photo/details.vue:562
#: src/pages/albums.vue:428
@ -1389,7 +1389,7 @@ msgstr ""
#: src/component/navigation.vue:200
#: src/component/photo/cards.vue:292
#: src/component/photo/list.vue:182
#: src/component/photo/mosaic.vue:214
#: src/component/photo/mosaic.vue:215
#: src/options/options.js:298
msgid "Live"
msgstr ""
@ -1403,7 +1403,7 @@ msgstr ""
msgid "location"
msgstr ""
#: src/component/photo/cards.vue:512
#: src/component/photo/cards.vue:513
#: src/component/photo/list.vue:138
#: src/dialog/album/edit.vue:121
msgid "Location"
@ -1540,7 +1540,7 @@ msgstr ""
msgid "Must have at least 8 characters."
msgstr ""
#: src/component/photo/cards.vue:500
#: src/component/photo/cards.vue:501
#: src/component/photo/list.vue:138
#: src/component/photo/list.vue:227
#: src/dialog/account/edit.vue:399
@ -1991,7 +1991,7 @@ msgid "Raw"
msgstr ""
#: src/component/photo/cards.vue:285
#: src/component/photo/mosaic.vue:206
#: src/component/photo/mosaic.vue:207
msgid "RAW"
msgstr ""
@ -2316,7 +2316,7 @@ msgid "Sponsors get access to additional features, receive direct technical supp
msgstr ""
#: src/component/photo/cards.vue:315
#: src/component/photo/mosaic.vue:240
#: src/component/photo/mosaic.vue:241
msgid "Stack"
msgstr ""
@ -2400,7 +2400,7 @@ msgstr ""
msgid "Sync raw and video files"
msgstr ""
#: src/component/photo/cards.vue:463
#: src/component/photo/cards.vue:464
#: src/component/photo/list.vue:136
#: src/dialog/photo/info.vue:52
msgid "Taken"
@ -2467,9 +2467,9 @@ msgstr ""
msgid "Title too long"
msgstr ""
#: src/component/album/toolbar.vue:138
#: src/component/album/toolbar.vue:155
#: src/component/album/toolbar.vue:171
#: src/component/album/toolbar.vue:139
#: src/component/album/toolbar.vue:157
#: src/component/album/toolbar.vue:174
#: src/component/photo/toolbar.vue:71
#: src/component/photo/toolbar.vue:88
#: src/component/photo/toolbar.vue:104
@ -2582,7 +2582,7 @@ msgstr ""
msgid "Updating stacks"
msgstr ""
#: src/component/album/toolbar.vue:189
#: src/component/album/toolbar.vue:192
#: src/component/navigation.vue:1958
#: src/component/photo/toolbar.vue:122
#: src/dialog/share/upload.vue:35
@ -2666,9 +2666,9 @@ msgid "Verified"
msgstr ""
#: src/component/photo/cards.vue:308
#: src/component/photo/cards.vue:471
#: src/component/photo/cards.vue:472
#: src/component/photo/list.vue:198
#: src/component/photo/mosaic.vue:232
#: src/component/photo/mosaic.vue:233
#: src/model/file.js:219
#: src/model/photo.js:794
#: src/model/photo.js:810
@ -2685,7 +2685,7 @@ msgid "Videos"
msgstr ""
#: src/component/photo/cards.vue:324
#: src/component/photo/mosaic.vue:249
#: src/component/photo/mosaic.vue:250
#: src/component/photo/toolbar.vue:226
msgid "View"
msgstr ""

View file

@ -46,8 +46,8 @@ func (b Blacklist) Contains(ext string) bool {
return false
}
// Ok tests if a file extension is NOT blacklisted.
func (b Blacklist) Ok(ext string) bool {
// Allow tests if a file extension is NOT blacklisted.
func (b Blacklist) Allow(ext string) bool {
return !b.Contains(ext)
}

View file

@ -34,16 +34,16 @@ func TestNewBlacklist(t *testing.T) {
func TestBlacklist_Ok(t *testing.T) {
t.Run("CanonCR2", func(t *testing.T) {
list := NewBlacklist("cr2")
assert.False(t, list.Ok(".cr2"))
assert.False(t, list.Allow(".cr2"))
assert.True(t, list.Contains(".cr2"))
})
t.Run("Raw", func(t *testing.T) {
list := NewBlacklist("RAF, Cr3, aaf ")
assert.False(t, list.Ok(".raf"))
assert.False(t, list.Ok("cr3"))
assert.False(t, list.Ok("AAF"))
assert.True(t, list.Ok(""))
assert.True(t, list.Ok(".raw"))
assert.True(t, list.Ok("raw"))
assert.False(t, list.Allow(".raf"))
assert.False(t, list.Allow("cr3"))
assert.False(t, list.Allow("AAF"))
assert.True(t, list.Allow(""))
assert.True(t, list.Allow(".raw"))
assert.True(t, list.Allow("raw"))
})
}

View file

@ -8,6 +8,8 @@ import (
const (
ExtYAML = ".yml"
ExtJPEG = ".jpg"
ExtDNG = ".dng"
ExtTHM = ".thm"
ExtAVC = ".avc"
)

View file

@ -17,6 +17,13 @@ var Extensions = FileExtensions{
".jfif": ImageJPEG,
".jfi": ImageJPEG,
".thm": ImageJPEG,
".tif": ImageTIFF,
".tiff": ImageTIFF,
".png": ImagePNG,
".pn": ImagePNG,
".gif": ImageGIF,
".bmp": ImageBMP,
".dng": ImageDNG,
".avif": ImageAVIF,
".avifs": ImageAVIF,
".heif": ImageHEIF,
@ -27,54 +34,47 @@ var Extensions = FileExtensions{
".avci": ImageHEIF,
".avcs": ImageHEIF,
".webp": ImageWebP,
".tif": ImageTIFF,
".tiff": ImageTIFF,
".png": ImagePNG,
".pn": ImagePNG,
".mpo": ImageMPO,
".gif": ImageGIF,
".bmp": ImageBMP,
".3fr": RawImage,
".ari": RawImage,
".arw": RawImage,
".bay": RawImage,
".cap": RawImage,
".crw": RawImage,
".cr2": RawImage,
".cr3": RawImage,
".data": RawImage,
".dcs": RawImage,
".dcr": RawImage,
".dng": RawImage,
".drf": RawImage,
".eip": RawImage,
".erf": RawImage,
".fff": RawImage,
".gpr": RawImage,
".iiq": RawImage,
".k25": RawImage,
".kdc": RawImage,
".mdc": RawImage,
".mef": RawImage,
".mos": RawImage,
".mrw": RawImage,
".nef": RawImage,
".nrw": RawImage,
".obm": RawImage,
".orf": RawImage,
".pef": RawImage,
".ptx": RawImage,
".pxn": RawImage,
".r3d": RawImage,
".raf": RawImage,
".raw": RawImage,
".rwl": RawImage,
".rwz": RawImage,
".rw2": RawImage,
".srf": RawImage,
".srw": RawImage,
".sr2": RawImage,
".x3f": RawImage,
".3fr": ImageRaw,
".ari": ImageRaw,
".arw": ImageRaw,
".bay": ImageRaw,
".cap": ImageRaw,
".crw": ImageRaw,
".cr2": ImageRaw,
".cr3": ImageRaw,
".data": ImageRaw,
".dcs": ImageRaw,
".dcr": ImageRaw,
".drf": ImageRaw,
".eip": ImageRaw,
".erf": ImageRaw,
".fff": ImageRaw,
".gpr": ImageRaw,
".iiq": ImageRaw,
".k25": ImageRaw,
".kdc": ImageRaw,
".mdc": ImageRaw,
".mef": ImageRaw,
".mos": ImageRaw,
".mrw": ImageRaw,
".nef": ImageRaw,
".nrw": ImageRaw,
".obm": ImageRaw,
".orf": ImageRaw,
".pef": ImageRaw,
".ptx": ImageRaw,
".pxn": ImageRaw,
".r3d": ImageRaw,
".raf": ImageRaw,
".raw": ImageRaw,
".rwl": ImageRaw,
".rwz": ImageRaw,
".rw2": ImageRaw,
".srf": ImageRaw,
".srw": ImageRaw,
".sr2": ImageRaw,
".x3f": ImageRaw,
".hevc": VideoHEVC,
".mov": VideoMOV,
".qt": VideoMOV,
@ -103,15 +103,15 @@ var Extensions = FileExtensions{
".webm": VideoWebM,
".asf": VideoASF,
".wmv": VideoWMV,
".xmp": XmpFile,
".aae": AaeFile,
".xml": XmlFile,
".yml": YamlFile,
".yaml": YamlFile,
".json": JsonFile,
".txt": TextFile,
".md": MarkdownFile,
".markdown": MarkdownFile,
".xmp": SidecarXMP,
".aae": SidecarAAE,
".xml": SidecarXML,
".yml": SidecarYAML,
".yaml": SidecarYAML,
".json": SidecarJSON,
".txt": SidecarText,
".md": SidecarMarkdown,
".markdown": SidecarMarkdown,
}
// Known tests if the file extension is known (supported).

View file

@ -78,19 +78,19 @@ func TestType_FindFirst(t *testing.T) {
dirs := []string{HiddenPath}
t.Run("find xmp", func(t *testing.T) {
result := XmpFile.FindFirst("testdata/test.jpg", dirs, "", false)
result := SidecarXMP.FindFirst("testdata/test.jpg", dirs, "", false)
assert.Equal(t, "testdata/.photoprism/test.xmp", result)
})
t.Run("find xmp upper ext", func(t *testing.T) {
result := XmpFile.FindFirst("testdata/test.PNG", dirs, "", false)
result := SidecarXMP.FindFirst("testdata/test.PNG", dirs, "", false)
assert.Equal(t, "testdata/.photoprism/test.xmp", result)
})
t.Run("find xmp without sequence", func(t *testing.T) {
result := XmpFile.FindFirst("testdata/test (2).jpg", dirs, "", false)
result := SidecarXMP.FindFirst("testdata/test (2).jpg", dirs, "", false)
assert.Equal(t, "", result)
})
t.Run("find xmp with sequence", func(t *testing.T) {
result := XmpFile.FindFirst("testdata/test (2).jpg", dirs, "", true)
result := SidecarXMP.FindFirst("testdata/test (2).jpg", dirs, "", true)
assert.Equal(t, "testdata/.photoprism/test.xmp", result)
})
t.Run("find jpg", func(t *testing.T) {
@ -159,11 +159,11 @@ func TestType(t *testing.T) {
})
t.Run("RawCRw", func(t *testing.T) {
result := FileType("testdata/test (jpg).crw")
assert.Equal(t, RawImage, result)
assert.Equal(t, ImageRaw, result)
})
t.Run("RawCR2", func(t *testing.T) {
result := FileType("testdata/test (jpg).CR2")
assert.Equal(t, RawImage, result)
assert.Equal(t, ImageRaw, result)
})
t.Run("MP4", func(t *testing.T) {
assert.Equal(t, Type("mp4"), FileType("file.mp"))

View file

@ -12,41 +12,42 @@ import (
// File types.
const (
RawImage Type = "raw" // RAW image
ImageJPEG Type = "jpg" // JPEG image
ImageAVIF Type = "avif" // AV1 Image File Format (AVIF)
ImageHEIF Type = "heif" // High Efficiency Image File Format (HEIF/HEIC)
ImageTIFF Type = "tiff" // TIFF image
ImagePNG Type = "png" // PNG image
ImageGIF Type = "gif" // GIF image
ImageBMP Type = "bmp" // BMP image
ImageMPO Type = "mpo" // Stereoscopic Image that consists of two JPG images that are combined into one 3D image
ImageWebP Type = "webp" // Google WebP Image
VideoWebM Type = "webm" // Google WebM Video
VideoAVC Type = "avc" // H.264, Advanced Video Coding (AVC, MPEG-4 Part 10)
VideoHEVC Type = "hevc" // H.265, High Efficiency Video Coding (HEVC)
VideoVVC Type = "vvc" // H.266, Versatile Video Coding (VVC)
VideoAV1 Type = "av1" // Alliance for Open Media Video
VideoMPG Type = "mpg" // Moving Picture Experts Group (MPEG)
VideoMJPG Type = "mjpg" // Motion JPEG (M-JPEG)
VideoMOV Type = "mov" // QuickTime File Format, can contain AVC, HEVC,...
VideoMP2 Type = "mp2" // MPEG-2, H.222/H.262
VideoMP4 Type = "mp4" // MPEG-4 Container based on QuickTime, can contain AVC, HEVC,...
VideoAVI Type = "avi" // Microsoft Audio Video Interleave (AVI)
Video3GP Type = "3gp" // Mobile Multimedia Container, MPEG-4 Part 12
Video3G2 Type = "3g2" // Similar to 3GP, consumes less space & bandwidth
VideoFlash Type = "flv" // Flash Video
VideoMKV Type = "mkv" // Matroska Multimedia Container, free and open
VideoAVCHD Type = "mts" // AVCHD (Advanced Video Coding High Definition)
VideoOGV Type = "ogv" // Ogg container format maintained by the Xiph.Org, free and open
VideoASF Type = "asf" // Advanced Systems/Streaming Format (ASF)
VideoWMV Type = "wmv" // Windows Media Video (based on ASF)
XmpFile Type = "xmp" // Adobe XMP sidecar file (XML)
AaeFile Type = "aae" // Apple image edits sidecar file (based on XML)
XmlFile Type = "xml" // XML metadata / config / sidecar file
YamlFile Type = "yml" // YAML metadata / config / sidecar file
JsonFile Type = "json" // JSON metadata / config / sidecar file
TextFile Type = "txt" // Text config / sidecar file
MarkdownFile Type = "md" // Markdown text sidecar file
UnknownType Type = "" // Unknown file
ImageRaw Type = "raw" // RAW image
ImageJPEG Type = "jpg" // JPEG image
ImagePNG Type = "png" // PNG image
ImageGIF Type = "gif" // GIF image
ImageTIFF Type = "tiff" // TIFF image
ImageDNG Type = "dng" // Adobe Digital Negative image
ImageAVIF Type = "avif" // AV1 Image File Format (AVIF)
ImageHEIF Type = "heif" // High Efficiency Image File Format (HEIF/HEIC)
ImageBMP Type = "bmp" // BMP image
ImageMPO Type = "mpo" // Stereoscopic Image that consists of two JPG images that are combined into one 3D image
ImageWebP Type = "webp" // Google WebP Image
VideoWebM Type = "webm" // Google WebM Video
VideoAVC Type = "avc" // H.264, Advanced Video Coding (AVC, MPEG-4 Part 10)
VideoHEVC Type = "hevc" // H.265, High Efficiency Video Coding (HEVC)
VideoVVC Type = "vvc" // H.266, Versatile Video Coding (VVC)
VideoAV1 Type = "av1" // Alliance for Open Media Video
VideoMPG Type = "mpg" // Moving Picture Experts Group (MPEG)
VideoMJPG Type = "mjpg" // Motion JPEG (M-JPEG)
VideoMOV Type = "mov" // QuickTime File Format, can contain AVC, HEVC,...
VideoMP2 Type = "mp2" // MPEG-2, H.222/H.262
VideoMP4 Type = "mp4" // MPEG-4 Container based on QuickTime, can contain AVC, HEVC,...
VideoAVI Type = "avi" // Microsoft Audio Video Interleave (AVI)
Video3GP Type = "3gp" // Mobile Multimedia Container, MPEG-4 Part 12
Video3G2 Type = "3g2" // Similar to 3GP, consumes less space & bandwidth
VideoFlash Type = "flv" // Flash Video
VideoMKV Type = "mkv" // Matroska Multimedia Container, free and open
VideoAVCHD Type = "mts" // AVCHD (Advanced Video Coding High Definition)
VideoOGV Type = "ogv" // Ogg container format maintained by the Xiph.Org, free and open
VideoASF Type = "asf" // Advanced Systems/Streaming Format (ASF)
VideoWMV Type = "wmv" // Windows Media Video (based on ASF)
SidecarXMP Type = "xmp" // Adobe XMP sidecar file (XML)
SidecarAAE Type = "aae" // Apple image edits sidecar file (based on XML)
SidecarXML Type = "xml" // XML metadata / config / sidecar file
SidecarYAML Type = "yml" // YAML metadata / config / sidecar file
SidecarJSON Type = "json" // JSON metadata / config / sidecar file
SidecarText Type = "txt" // Text config / sidecar file
SidecarMarkdown Type = "md" // Markdown text sidecar file
UnknownType Type = "" // Unknown file
)

View file

@ -2,41 +2,42 @@ package fs
// TypeInfo contains human-readable descriptions for supported file formats
var TypeInfo = map[Type]string{
RawImage: "Unprocessed Sensor Data",
ImageJPEG: "Joint Photographic Experts Group (JPEG)",
ImagePNG: "Portable Network Graphics",
ImageGIF: "Graphics Interchange Format",
ImageTIFF: "Tag Image File Format",
ImageBMP: "Bitmap",
ImageMPO: "Stereoscopic JPEG (3D)",
ImageAVIF: "AV1 Image File Format",
ImageHEIF: "High Efficiency Image File Format",
ImageWebP: "Google WebP",
VideoWebM: "Google WebM",
VideoMP2: "MPEG 2 (H.262)",
VideoAVC: "Advanced Video Coding (H.264, MPEG-4 Part 10)",
VideoHEVC: "High Efficiency Video Coding (H.265)",
VideoVVC: "Versatile Video Coding (H.266)",
VideoAV1: "AOMedia Video 1",
VideoMOV: "Apple QuickTime",
VideoMP4: "Multimedia Container (MPEG-4 Part 14)",
VideoAVI: "Microsoft Audio Video Interleave",
VideoASF: "Advanced Systems Format ",
VideoWMV: "Windows Media",
Video3GP: "Mobile Multimedia Container (3G)",
Video3G2: "Mobile Multimedia Container (CDMA2000)",
VideoFlash: "Adobe Flash",
VideoMKV: "Matroska Multimedia Container",
VideoMPG: "Moving Picture Experts Group (MPEG)",
VideoMJPG: "Motion JPEG",
VideoAVCHD: "Advanced Video Coding High Definition (AVCHD)",
VideoOGV: "Ogg Media (OGG)",
XmpFile: "Adobe Extensible Metadata Platform",
AaeFile: "Apple Image Edits XML",
XmlFile: "Extensible Markup Language",
JsonFile: "Serialized JSON Data (Exiftool, Google Photos)",
YamlFile: "Serialized YAML Data (Config, Metadata)",
TextFile: "Plain Text",
MarkdownFile: "Markdown Formatted Text",
UnknownType: "Other",
ImageRaw: "Unprocessed Sensor Data",
ImageDNG: "Adobe Digital Negative",
ImageJPEG: "Joint Photographic Experts Group (JPEG)",
ImagePNG: "Portable Network Graphics",
ImageGIF: "Graphics Interchange Format",
ImageTIFF: "Tag Image File Format",
ImageBMP: "Bitmap",
ImageMPO: "Stereoscopic JPEG (3D)",
ImageAVIF: "AV1 Image File Format",
ImageHEIF: "High Efficiency Image File Format",
ImageWebP: "Google WebP",
VideoWebM: "Google WebM",
VideoMP2: "MPEG 2 (H.262)",
VideoAVC: "Advanced Video Coding (H.264, MPEG-4 Part 10)",
VideoHEVC: "High Efficiency Video Coding (H.265)",
VideoVVC: "Versatile Video Coding (H.266)",
VideoAV1: "AOMedia Video 1",
VideoMOV: "Apple QuickTime",
VideoMP4: "Multimedia Container (MPEG-4 Part 14)",
VideoAVI: "Microsoft Audio Video Interleave",
VideoASF: "Advanced Systems Format ",
VideoWMV: "Windows Media",
Video3GP: "Mobile Multimedia Container (3G)",
Video3G2: "Mobile Multimedia Container (CDMA2000)",
VideoFlash: "Adobe Flash",
VideoMKV: "Matroska Multimedia Container",
VideoMPG: "Moving Picture Experts Group (MPEG)",
VideoMJPG: "Motion JPEG",
VideoAVCHD: "Advanced Video Coding High Definition (AVCHD)",
VideoOGV: "Ogg Media (OGG)",
SidecarXMP: "Adobe Extensible Metadata Platform",
SidecarAAE: "Apple Image Edits XML",
SidecarXML: "Extensible Markup Language",
SidecarJSON: "Serialized JSON Data (Exiftool, Google Photos)",
SidecarYAML: "Serialized YAML Data (Config, Metadata)",
SidecarText: "Plain Text",
SidecarMarkdown: "Markdown Formatted Text",
UnknownType: "Other",
}

View file

@ -1,48 +1,48 @@
package fs
import (
"os"
"path/filepath"
"strings"
"github.com/h2non/filetype"
"github.com/gabriel-vasile/mimetype"
)
const (
MimeTypeJpeg = "image/jpeg"
MimeTypePng = "image/png"
MimeTypeGif = "image/gif"
MimeTypeBitmap = "image/bmp"
MimeTypeWebP = "image/webp"
MimeTypeTiff = "image/tiff"
MimeTypeAVIF = "image/avif"
MimeTypeHEIF = "image/heif"
MimeTypeUnknown = ""
MimeTypeJpeg = "image/jpeg"
MimeTypePng = "image/png"
MimeTypeGif = "image/gif"
MimeTypeBitmap = "image/bmp"
MimeTypeTiff = "image/tiff"
MimeTypeDNG = "image/dng"
MimeTypeAVIF = "image/avif"
MimeTypeHEIC = "image/heic"
MimeTypeWebP = "image/webp"
MimeTypeXML = "text/xml"
MimeTypeJSON = "application/json"
)
// MimeType returns the mime type of a file, an empty string if it is unknown.
func MimeType(filename string) string {
// Workaround, since "image/avif " cannot be recognized yet.
if Extensions[filepath.Ext(filename)] == ImageAVIF {
// Set mime detection read limit.
func init() {
mimetype.SetLimit(1024)
}
// MimeType returns the mime type of a file, or an empty string if it could not be detected.
func MimeType(filename string) (mimeType string) {
// Workaround, since "image/dng" cannot be recognized yet.
if ext := strings.ToLower(filepath.Ext(filename)); ext == "" {
// Continue.
} else if Extensions[ext] == ImageDNG {
return MimeTypeDNG
} else if Extensions[ext] == ImageAVIF {
return MimeTypeAVIF
}
handle, err := os.Open(filename)
if err != nil {
return ""
}
defer handle.Close()
// Only the first 261 bytes are used to sniff the content type.
buffer := make([]byte, 261)
if _, err := handle.Read(buffer); err != nil {
return ""
} else if t, err := filetype.Get(buffer); err == nil && t != filetype.Unknown {
return t.MIME.Value
} else if t := filetype.GetType(NormalizedExt(filename)); t != filetype.Unknown {
return t.MIME.Value
if t, err := mimetype.DetectFile(filename); err != nil {
return MimeTypeUnknown
} else {
return ""
mimeType, _, _ = strings.Cut(t.String(), ";")
}
return mimeType
}

View file

@ -77,7 +77,7 @@ func TestSkipWalk(t *testing.T) {
done[fileName] = Found
if textName := TextFile.Find(fileName, false); textName != "" {
if textName := SidecarText.Find(fileName, false); textName != "" {
done[textName] = Found
}

View file

@ -4,41 +4,42 @@ import "github.com/photoprism/photoprism/pkg/fs"
// Formats maps file formats to general media types.
var Formats = map[fs.Type]Type{
fs.RawImage: Raw,
fs.ImageJPEG: Image,
fs.ImagePNG: Image,
fs.ImageGIF: Image,
fs.ImageTIFF: Image,
fs.ImageBMP: Image,
fs.ImageMPO: Image,
fs.ImageAVIF: Image,
fs.ImageHEIF: Image,
fs.VideoHEVC: Video,
fs.ImageWebP: Image,
fs.VideoWebM: Video,
fs.VideoAVI: Video,
fs.VideoAVC: Video,
fs.VideoVVC: Video,
fs.VideoAV1: Video,
fs.VideoMPG: Video,
fs.VideoMJPG: Video,
fs.VideoMP2: Video,
fs.VideoMP4: Video,
fs.VideoMKV: Video,
fs.VideoMOV: Video,
fs.Video3GP: Video,
fs.Video3G2: Video,
fs.VideoFlash: Video,
fs.VideoAVCHD: Video,
fs.VideoOGV: Video,
fs.VideoASF: Video,
fs.VideoWMV: Video,
fs.XmpFile: Sidecar,
fs.XmlFile: Sidecar,
fs.AaeFile: Sidecar,
fs.YamlFile: Sidecar,
fs.TextFile: Sidecar,
fs.JsonFile: Sidecar,
fs.MarkdownFile: Sidecar,
fs.UnknownType: Other,
fs.ImageRaw: Raw,
fs.ImageDNG: Raw,
fs.ImageJPEG: Image,
fs.ImagePNG: Image,
fs.ImageGIF: Image,
fs.ImageTIFF: Image,
fs.ImageBMP: Image,
fs.ImageMPO: Image,
fs.ImageAVIF: Image,
fs.ImageHEIF: Image,
fs.VideoHEVC: Video,
fs.ImageWebP: Image,
fs.VideoWebM: Video,
fs.VideoAVI: Video,
fs.VideoAVC: Video,
fs.VideoVVC: Video,
fs.VideoAV1: Video,
fs.VideoMPG: Video,
fs.VideoMJPG: Video,
fs.VideoMP2: Video,
fs.VideoMP4: Video,
fs.VideoMKV: Video,
fs.VideoMOV: Video,
fs.Video3GP: Video,
fs.Video3G2: Video,
fs.VideoFlash: Video,
fs.VideoAVCHD: Video,
fs.VideoOGV: Video,
fs.VideoASF: Video,
fs.VideoWMV: Video,
fs.SidecarXMP: Sidecar,
fs.SidecarXML: Sidecar,
fs.SidecarAAE: Sidecar,
fs.SidecarYAML: Sidecar,
fs.SidecarText: Sidecar,
fs.SidecarJSON: Sidecar,
fs.SidecarMarkdown: Sidecar,
fs.UnknownType: Other,
}