From f7153cdd219d487733a7a3a8809b333bdace6294 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Tue, 5 Oct 2021 18:42:39 +0200 Subject: [PATCH] People: Improve logging, command help, and handling of broken files #22 --- frontend/package-lock.json | 876 +++++++++++++------------ frontend/src/dialog/photo/files.vue | 16 +- internal/api/covers.go | 4 +- internal/api/covers_test.go | 2 +- internal/api/folder_cover.go | 2 +- internal/api/import.go | 2 +- internal/api/photo_unstack.go | 4 +- internal/commands/config.go | 2 +- internal/commands/faces.go | 6 +- internal/commands/index_test.go | 4 +- internal/commands/reset.go | 30 +- internal/config/config.go | 23 +- internal/config/flags.go | 159 ++--- internal/config/options.go | 2 +- internal/entity/face.go | 4 +- internal/entity/file.go | 30 +- internal/entity/file_test.go | 2 +- internal/entity/marker.go | 99 ++- internal/entity/marker_test.go | 80 ++- internal/entity/markers.go | 24 +- internal/entity/photo.go | 14 +- internal/entity/photo_title.go | 18 +- internal/entity/subject.go | 8 +- internal/entity/subject_test.go | 4 +- internal/face/thresholds.go | 22 +- internal/meta/exif_parser.go | 10 +- internal/meta/exif_test.go | 6 +- internal/photoprism/faces.go | 28 +- internal/photoprism/faces_audit.go | 16 +- internal/photoprism/faces_cluster.go | 8 +- internal/photoprism/faces_stats.go | 4 +- internal/photoprism/import_worker.go | 4 +- internal/photoprism/index.go | 6 +- internal/photoprism/index_mediafile.go | 7 +- internal/photoprism/index_related.go | 10 +- internal/photoprism/location.go | 2 +- internal/photoprism/location_test.go | 4 +- internal/photoprism/mediafile.go | 6 +- internal/photoprism/mediafile_meta.go | 2 +- internal/query/faces.go | 12 +- internal/query/photo.go | 29 +- internal/thumb/create.go | 2 +- pkg/fs/ignore.go | 2 +- 43 files changed, 873 insertions(+), 722 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index a480d70e5..0e4682e83 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1982,11 +1982,11 @@ } }, "node_modules/@vue/component-compiler-utils/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -2035,9 +2035,9 @@ "integrity": "sha512-Knqhx7WieLdVgwCAZgTVrDCXZ50uItuecLh9JdLC8O+a5ayaSyIQYveUK3hCRNC7ws5zalHmZwfdLMGaS8r4Ew==" }, "node_modules/@vvo/tzdb": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@vvo/tzdb/-/tzdb-6.21.0.tgz", - "integrity": "sha512-yW6tRzUihnW2we3yvsTmb9+iDHawkiABl1uaFVorrGwy4K5WAoPWGnNBR7o0uIRNqqitXGEFm66voXOlLYvR+g==" + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/@vvo/tzdb/-/tzdb-6.26.0.tgz", + "integrity": "sha512-i9vUOdHWKkiIF/MvKR217gTGTsEoscZIZvPmu4QnLqJtHYbRdGDEbDbGKLFmIsmvU4ou4yVC8pch9Mix/bIVig==" }, "node_modules/@webassemblyjs/ast": { "version": "1.11.1", @@ -2488,15 +2488,15 @@ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, "node_modules/array-includes": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", - "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", + "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", + "es-abstract": "^1.19.1", "get-intrinsic": "^1.1.1", - "is-string": "^1.0.5" + "is-string": "^1.0.7" }, "engines": { "node": ">= 0.4" @@ -2506,13 +2506,13 @@ } }, "node_modules/array.prototype.flat": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", - "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", + "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", "dependencies": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" + "es-abstract": "^1.19.0" }, "engines": { "node": ">= 0.4" @@ -2564,15 +2564,15 @@ } }, "node_modules/autoprefixer": { - "version": "9.8.7", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.7.tgz", - "integrity": "sha512-7Hg99B1eTH5+LgmUBUSmov1Z3bsggQJS7v3IMGo6wcScnbRuvtMc871J9J+4bSbIqa9LSX/zypFXJ8sXHpMJeQ==", + "version": "9.8.8", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz", + "integrity": "sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==", "dependencies": { "browserslist": "^4.12.0", "caniuse-lite": "^1.0.30001109", - "nanocolors": "^0.2.8", "normalize-range": "^0.1.2", "num2fraction": "^1.2.2", + "picocolors": "^0.2.1", "postcss": "^7.0.32", "postcss-value-parser": "^4.1.0" }, @@ -2585,11 +2585,11 @@ } }, "node_modules/autoprefixer/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -2935,15 +2935,15 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" }, "node_modules/browserslist": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.2.tgz", - "integrity": "sha512-jSDZyqJmkKMEMi7SZAgX5UltFdR5NAO43vY0AwTpu4X3sGH7GLLQ83KiUomgrnvZRCeW0yPPnKqnxPqQOER9zQ==", + "version": "4.17.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.3.tgz", + "integrity": "sha512-59IqHJV5VGdcJZ+GZ2hU5n4Kv3YiASzW6Xk5g9tf5a/MAzGeFwgGWU39fVzNIOVcgB3+Gp+kiQu0HEfTVU/3VQ==", "dependencies": { - "caniuse-lite": "^1.0.30001261", - "electron-to-chromium": "^1.3.854", + "caniuse-lite": "^1.0.30001264", + "electron-to-chromium": "^1.3.857", "escalade": "^3.1.1", - "nanocolors": "^0.2.12", - "node-releases": "^1.1.76" + "node-releases": "^1.1.77", + "picocolors": "^0.2.1" }, "bin": { "browserslist": "cli.js" @@ -3019,9 +3019,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001263", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001263.tgz", - "integrity": "sha512-doiV5dft6yzWO1WwU19kt8Qz8R0/8DgEziz6/9n2FxUasteZNwNNYSmJO3GLBH8lCVE73AB1RPDPAeYbcO5Cvw==", + "version": "1.0.30001264", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001264.tgz", + "integrity": "sha512-Ftfqqfcs/ePiUmyaySsQ4PUsdcYyXG2rfoBVsk3iY1ahHaJEw65vfb7Suzqm+cEkwwPIv/XWkg27iCpRavH4zA==", "funding": { "type": "opencollective", "url": "https://opencollective.com/browserslist" @@ -3562,11 +3562,11 @@ } }, "node_modules/css-blank-pseudo/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -3634,11 +3634,11 @@ } }, "node_modules/css-has-pseudo/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -3783,11 +3783,11 @@ } }, "node_modules/css-prefers-color-scheme/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -4281,9 +4281,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.3.856", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.856.tgz", - "integrity": "sha512-lSezYIe1/p5qkEswAfaQUseOBiwGwuCvRl/MKzOEVe++DcmQ92+43dznDl4rFJ4Zpu+kevhwyIf7KjJevyDA/A==" + "version": "1.3.859", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.859.tgz", + "integrity": "sha512-gXRXKNWedfdiKIzwr0Mg/VGCvxXzy+4SuK9hp1BDvfbCwx0O5Ot+2f4CoqQkqEJ3Zj/eAV/GoAFgBVFgkBLXuQ==" }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -4441,9 +4441,9 @@ } }, "node_modules/es-abstract": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.0.tgz", - "integrity": "sha512-oWPrF+7P1nGv/rw9oIInwdkmI1qediEJSvVfHFryBd8mWllCKB5tke3aKyf51J6chgyKmi6mODqdnin2yb88Nw==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", "dependencies": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", @@ -5075,9 +5075,9 @@ } }, "node_modules/eslint-plugin-vue": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-7.18.0.tgz", - "integrity": "sha512-ceDXlXYMMPMSXw7tdKUR42w9jlzthJGJ3Kvm3YrZ0zuQfvAySNxe8sm6VHuksBW0+060GzYXhHJG6IHVOfF83Q==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-7.19.0.tgz", + "integrity": "sha512-pqsJY1q0cjdQerWSlGHo+NfnWZ8Xuc5tetddljJJ7726auWsnHty7F5qgj/EcjkPgyj8s5Lw4YGuhsFHkzIrkQ==", "dependencies": { "eslint-utils": "^2.1.0", "natural-compare": "^1.4.0", @@ -6088,9 +6088,10 @@ } }, "node_modules/gl-matrix": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.3.0.tgz", - "integrity": "sha512-COb7LDz+SXaHtl/h4LeaFcNdJdAQSDeVqjiIihSXNrkWObZLhDI4hIkZC11Aeqp7bcE72clzB0BnDXr2SmslRA==" + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.0.tgz", + "integrity": "sha512-n7fF4meQ6jbBSw91jGmP83I/wkQud5kIRSW/dr5z+9YJdQz61GWpgmRK9k7c4O/1QsXaIXu9o2vf/q2Gu3Ybow==", + "deprecated": "Broke various systems. Will investigate and likely republish as a new major version" }, "node_modules/glob": { "version": "7.2.0", @@ -6529,9 +6530,9 @@ } }, "node_modules/import-local": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", - "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.3.tgz", + "integrity": "sha512-bE9iaUY3CXH8Cwfan/abDKAxe1KGT9kyGsBPqf6DMK/z0a2OzAsrukeYNgIH6cH5Xr452jb1TUL8rSfCLjZ9uA==", "dependencies": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" @@ -7875,24 +7876,16 @@ } }, "node_modules/mime-types": { - "version": "2.1.32", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz", - "integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==", + "version": "2.1.33", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.33.tgz", + "integrity": "sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==", "dependencies": { - "mime-db": "1.49.0" + "mime-db": "1.50.0" }, "engines": { "node": ">= 0.6" } }, - "node_modules/mime-types/node_modules/mime-db": { - "version": "1.49.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz", - "integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -8234,9 +8227,9 @@ } }, "node_modules/node-releases": { - "version": "1.1.76", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.76.tgz", - "integrity": "sha512-9/IECtNr8dXNmPWmFXepT0/7o5eolGesHUa3mtr0KlgnCvnZxwh2qensKL42JJY2vQKC3nIBXetFAqR+PW1CmA==" + "version": "1.1.77", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.77.tgz", + "integrity": "sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ==" }, "node_modules/node-storage-shim": { "version": "2.0.1", @@ -8361,13 +8354,13 @@ } }, "node_modules/object.values": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.4.tgz", - "integrity": "sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.2" + "es-abstract": "^1.19.1" }, "engines": { "node": ">= 0.4" @@ -8603,6 +8596,11 @@ "node": ">= 0.8.0" } }, + "node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + }, "node_modules/picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", @@ -8797,12 +8795,12 @@ "integrity": "sha512-RVAzFGo1Mx9+YukVKSgTLut6r4ZVBW8IVrqGHAPfEsVJN93WSp5HRD6+qNa7av1q/joPKDNJd55m5AJl9GBQGA==" }, "node_modules/postcss": { - "version": "8.3.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.8.tgz", - "integrity": "sha512-GT5bTjjZnwDifajzczOC+r3FI3Cu+PgPvrsjhQdRqa2kTJ4968/X9CUce9xttIB0xOs5c6xf0TCWZo/y9lF6bA==", + "version": "8.3.9", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.9.tgz", + "integrity": "sha512-f/ZFyAKh9Dnqytx5X62jgjhhzttjZS7hMsohcI7HEI5tjELX/HxCy3EFhsRxyzGvrzFF+82XPvCS8T9TFleVJw==", "dependencies": { - "nanocolors": "^0.2.2", - "nanoid": "^3.1.25", + "nanoid": "^3.1.28", + "picocolors": "^0.2.1", "source-map-js": "^0.6.2" }, "engines": { @@ -8823,11 +8821,11 @@ } }, "node_modules/postcss-attribute-case-insensitive/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -8855,11 +8853,11 @@ } }, "node_modules/postcss-browser-reporter/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -8903,11 +8901,11 @@ } }, "node_modules/postcss-color-functional-notation/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -8940,11 +8938,11 @@ } }, "node_modules/postcss-color-gray/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -8976,11 +8974,11 @@ } }, "node_modules/postcss-color-hex-alpha/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -9013,11 +9011,11 @@ } }, "node_modules/postcss-color-mod-function/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -9049,11 +9047,11 @@ } }, "node_modules/postcss-color-rebeccapurple/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -9115,11 +9113,11 @@ } }, "node_modules/postcss-custom-media/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -9151,11 +9149,11 @@ } }, "node_modules/postcss-custom-properties/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -9198,11 +9196,11 @@ } }, "node_modules/postcss-custom-selectors/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -9258,11 +9256,11 @@ } }, "node_modules/postcss-dir-pseudo-class/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -9351,11 +9349,11 @@ } }, "node_modules/postcss-double-position-gradients/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -9387,11 +9385,11 @@ } }, "node_modules/postcss-env-function/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -9422,11 +9420,11 @@ } }, "node_modules/postcss-focus-visible/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -9457,11 +9455,11 @@ } }, "node_modules/postcss-focus-within/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -9489,11 +9487,11 @@ } }, "node_modules/postcss-font-variant/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -9524,11 +9522,11 @@ } }, "node_modules/postcss-gap-properties/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -9560,11 +9558,11 @@ } }, "node_modules/postcss-image-set-function/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -9608,11 +9606,11 @@ } }, "node_modules/postcss-initial/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -9645,11 +9643,11 @@ } }, "node_modules/postcss-lab-function/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -9726,11 +9724,11 @@ } }, "node_modules/postcss-logical/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -9761,11 +9759,11 @@ } }, "node_modules/postcss-media-minmax/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -9948,11 +9946,11 @@ } }, "node_modules/postcss-nesting/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -10127,11 +10125,11 @@ } }, "node_modules/postcss-overflow-shorthand/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -10159,11 +10157,11 @@ } }, "node_modules/postcss-page-break/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -10195,11 +10193,11 @@ } }, "node_modules/postcss-place/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -10266,11 +10264,11 @@ } }, "node_modules/postcss-preset-env/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -10313,11 +10311,11 @@ } }, "node_modules/postcss-pseudo-class-any-link/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -10388,11 +10386,11 @@ } }, "node_modules/postcss-replace-overflow-wrap/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -10444,11 +10442,11 @@ } }, "node_modules/postcss-selector-matches/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -10477,11 +10475,11 @@ } }, "node_modules/postcss-selector-not/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" }, "engines": { @@ -10592,6 +10590,17 @@ "node": ">=6.14.4" } }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.1.28", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.28.tgz", + "integrity": "sha512-gSu9VZ2HtmoKYe/lmyPFES5nknFrHa+/DT9muUFWFMi6Jh9E1I7bkvlQ8xxf1Kos9pi9o8lBnIOkatMhKX/YUw==", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/potpack": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.1.tgz", @@ -13154,9 +13163,9 @@ } }, "node_modules/webpack": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.56.0.tgz", - "integrity": "sha512-pJ7esw2AGkpZL0jqsEAKnDEfRZdrc9NVjAWA+d1mFkwj68ng9VQ6+Wnrl+kS5dlDHvrat5ASK5vd7wp6I7f53Q==", + "version": "5.57.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.57.0.tgz", + "integrity": "sha512-kbKX1HOJpEhX9GZDFgHauU/7HfgGeGzUzjSUV+wZjGxP3PFeau7BgYFtm5+UTtJJSqmXYKFuBpWRDrSdQ3d8zA==", "dependencies": { "@types/eslint-scope": "^3.7.0", "@types/estree": "^0.0.50", @@ -13406,9 +13415,9 @@ } }, "node_modules/webpack/node_modules/acorn-import-assertions": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.7.6.tgz", - "integrity": "sha512-FlVvVFA1TX6l3lp8VjDnYYq7R1nyW6x3svAt4nDgrWQ9SBaSh9CnbwgSUTasgfNfOG5HlM1ehugCvM+hjo56LA==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", "peerDependencies": { "acorn": "^8" } @@ -15034,11 +15043,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -15073,9 +15082,9 @@ "integrity": "sha512-Knqhx7WieLdVgwCAZgTVrDCXZ50uItuecLh9JdLC8O+a5ayaSyIQYveUK3hCRNC7ws5zalHmZwfdLMGaS8r4Ew==" }, "@vvo/tzdb": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@vvo/tzdb/-/tzdb-6.21.0.tgz", - "integrity": "sha512-yW6tRzUihnW2we3yvsTmb9+iDHawkiABl1uaFVorrGwy4K5WAoPWGnNBR7o0uIRNqqitXGEFm66voXOlLYvR+g==" + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/@vvo/tzdb/-/tzdb-6.26.0.tgz", + "integrity": "sha512-i9vUOdHWKkiIF/MvKR217gTGTsEoscZIZvPmu4QnLqJtHYbRdGDEbDbGKLFmIsmvU4ou4yVC8pch9Mix/bIVig==" }, "@webassemblyjs/ast": { "version": "1.11.1", @@ -15430,25 +15439,25 @@ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, "array-includes": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", - "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", + "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", + "es-abstract": "^1.19.1", "get-intrinsic": "^1.1.1", - "is-string": "^1.0.5" + "is-string": "^1.0.7" } }, "array.prototype.flat": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", - "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", + "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", "requires": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" + "es-abstract": "^1.19.0" } }, "arraybuffer.slice": { @@ -15482,25 +15491,25 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" }, "autoprefixer": { - "version": "9.8.7", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.7.tgz", - "integrity": "sha512-7Hg99B1eTH5+LgmUBUSmov1Z3bsggQJS7v3IMGo6wcScnbRuvtMc871J9J+4bSbIqa9LSX/zypFXJ8sXHpMJeQ==", + "version": "9.8.8", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz", + "integrity": "sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==", "requires": { "browserslist": "^4.12.0", "caniuse-lite": "^1.0.30001109", - "nanocolors": "^0.2.8", "normalize-range": "^0.1.2", "num2fraction": "^1.2.2", + "picocolors": "^0.2.1", "postcss": "^7.0.32", "postcss-value-parser": "^4.1.0" }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -15766,15 +15775,15 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" }, "browserslist": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.2.tgz", - "integrity": "sha512-jSDZyqJmkKMEMi7SZAgX5UltFdR5NAO43vY0AwTpu4X3sGH7GLLQ83KiUomgrnvZRCeW0yPPnKqnxPqQOER9zQ==", + "version": "4.17.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.3.tgz", + "integrity": "sha512-59IqHJV5VGdcJZ+GZ2hU5n4Kv3YiASzW6Xk5g9tf5a/MAzGeFwgGWU39fVzNIOVcgB3+Gp+kiQu0HEfTVU/3VQ==", "requires": { - "caniuse-lite": "^1.0.30001261", - "electron-to-chromium": "^1.3.854", + "caniuse-lite": "^1.0.30001264", + "electron-to-chromium": "^1.3.857", "escalade": "^3.1.1", - "nanocolors": "^0.2.12", - "node-releases": "^1.1.76" + "node-releases": "^1.1.77", + "picocolors": "^0.2.1" } }, "buffer-from": { @@ -15828,9 +15837,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001263", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001263.tgz", - "integrity": "sha512-doiV5dft6yzWO1WwU19kt8Qz8R0/8DgEziz6/9n2FxUasteZNwNNYSmJO3GLBH8lCVE73AB1RPDPAeYbcO5Cvw==" + "version": "1.0.30001264", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001264.tgz", + "integrity": "sha512-Ftfqqfcs/ePiUmyaySsQ4PUsdcYyXG2rfoBVsk3iY1ahHaJEw65vfb7Suzqm+cEkwwPIv/XWkg27iCpRavH4zA==" }, "chai": { "version": "4.3.4", @@ -16261,11 +16270,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -16304,11 +16313,11 @@ "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" }, "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -16403,11 +16412,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -16762,9 +16771,9 @@ "integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==" }, "electron-to-chromium": { - "version": "1.3.856", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.856.tgz", - "integrity": "sha512-lSezYIe1/p5qkEswAfaQUseOBiwGwuCvRl/MKzOEVe++DcmQ92+43dznDl4rFJ4Zpu+kevhwyIf7KjJevyDA/A==" + "version": "1.3.859", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.859.tgz", + "integrity": "sha512-gXRXKNWedfdiKIzwr0Mg/VGCvxXzy+4SuK9hp1BDvfbCwx0O5Ot+2f4CoqQkqEJ3Zj/eAV/GoAFgBVFgkBLXuQ==" }, "emoji-regex": { "version": "8.0.0", @@ -16896,9 +16905,9 @@ } }, "es-abstract": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.0.tgz", - "integrity": "sha512-oWPrF+7P1nGv/rw9oIInwdkmI1qediEJSvVfHFryBd8mWllCKB5tke3aKyf51J6chgyKmi6mODqdnin2yb88Nw==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", "requires": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", @@ -17459,9 +17468,9 @@ "requires": {} }, "eslint-plugin-vue": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-7.18.0.tgz", - "integrity": "sha512-ceDXlXYMMPMSXw7tdKUR42w9jlzthJGJ3Kvm3YrZ0zuQfvAySNxe8sm6VHuksBW0+060GzYXhHJG6IHVOfF83Q==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-7.19.0.tgz", + "integrity": "sha512-pqsJY1q0cjdQerWSlGHo+NfnWZ8Xuc5tetddljJJ7726auWsnHty7F5qgj/EcjkPgyj8s5Lw4YGuhsFHkzIrkQ==", "requires": { "eslint-utils": "^2.1.0", "natural-compare": "^1.4.0", @@ -18113,9 +18122,9 @@ } }, "gl-matrix": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.3.0.tgz", - "integrity": "sha512-COb7LDz+SXaHtl/h4LeaFcNdJdAQSDeVqjiIihSXNrkWObZLhDI4hIkZC11Aeqp7bcE72clzB0BnDXr2SmslRA==" + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.0.tgz", + "integrity": "sha512-n7fF4meQ6jbBSw91jGmP83I/wkQud5kIRSW/dr5z+9YJdQz61GWpgmRK9k7c4O/1QsXaIXu9o2vf/q2Gu3Ybow==" }, "glob": { "version": "7.2.0", @@ -18430,9 +18439,9 @@ } }, "import-local": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", - "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.3.tgz", + "integrity": "sha512-bE9iaUY3CXH8Cwfan/abDKAxe1KGT9kyGsBPqf6DMK/z0a2OzAsrukeYNgIH6cH5Xr452jb1TUL8rSfCLjZ9uA==", "requires": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" @@ -19449,18 +19458,11 @@ "integrity": "sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A==" }, "mime-types": { - "version": "2.1.32", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz", - "integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==", + "version": "2.1.33", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.33.tgz", + "integrity": "sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==", "requires": { - "mime-db": "1.49.0" - }, - "dependencies": { - "mime-db": { - "version": "1.49.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz", - "integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==" - } + "mime-db": "1.50.0" } }, "mimic-fn": { @@ -19699,9 +19701,9 @@ "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=" }, "node-releases": { - "version": "1.1.76", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.76.tgz", - "integrity": "sha512-9/IECtNr8dXNmPWmFXepT0/7o5eolGesHUa3mtr0KlgnCvnZxwh2qensKL42JJY2vQKC3nIBXetFAqR+PW1CmA==" + "version": "1.1.77", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.77.tgz", + "integrity": "sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ==" }, "node-storage-shim": { "version": "2.0.1", @@ -19789,13 +19791,13 @@ } }, "object.values": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.4.tgz", - "integrity": "sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.2" + "es-abstract": "^1.19.1" } }, "on-finished": { @@ -19962,6 +19964,11 @@ "resolved": "https://registry.npmjs.org/photoswipe/-/photoswipe-4.1.3.tgz", "integrity": "sha512-89Z43IRUyw7ycTolo+AaiDn3W1EEIfox54hERmm9bI12IB9cvRfHSHez3XhAyU8XW2EAFrC+2sKMhh7SJwn0bA==" }, + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + }, "picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", @@ -20100,13 +20107,20 @@ "integrity": "sha512-RVAzFGo1Mx9+YukVKSgTLut6r4ZVBW8IVrqGHAPfEsVJN93WSp5HRD6+qNa7av1q/joPKDNJd55m5AJl9GBQGA==" }, "postcss": { - "version": "8.3.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.8.tgz", - "integrity": "sha512-GT5bTjjZnwDifajzczOC+r3FI3Cu+PgPvrsjhQdRqa2kTJ4968/X9CUce9xttIB0xOs5c6xf0TCWZo/y9lF6bA==", + "version": "8.3.9", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.9.tgz", + "integrity": "sha512-f/ZFyAKh9Dnqytx5X62jgjhhzttjZS7hMsohcI7HEI5tjELX/HxCy3EFhsRxyzGvrzFF+82XPvCS8T9TFleVJw==", "requires": { - "nanocolors": "^0.2.2", - "nanoid": "^3.1.25", + "nanoid": "^3.1.28", + "picocolors": "^0.2.1", "source-map-js": "^0.6.2" + }, + "dependencies": { + "nanoid": { + "version": "3.1.28", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.28.tgz", + "integrity": "sha512-gSu9VZ2HtmoKYe/lmyPFES5nknFrHa+/DT9muUFWFMi6Jh9E1I7bkvlQ8xxf1Kos9pi9o8lBnIOkatMhKX/YUw==" + } } }, "postcss-attribute-case-insensitive": { @@ -20119,11 +20133,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -20143,11 +20157,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -20177,11 +20191,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -20203,11 +20217,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -20228,11 +20242,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -20254,11 +20268,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -20279,11 +20293,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -20322,11 +20336,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -20347,11 +20361,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -20377,11 +20391,11 @@ "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" }, "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -20417,11 +20431,11 @@ "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" }, "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -20476,11 +20490,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -20501,11 +20515,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -20525,11 +20539,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -20549,11 +20563,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -20573,11 +20587,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -20597,11 +20611,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -20622,11 +20636,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -20656,11 +20670,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -20682,11 +20696,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -20734,11 +20748,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -20758,11 +20772,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -20875,11 +20889,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -20984,11 +20998,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -21008,11 +21022,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -21033,11 +21047,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -21093,11 +21107,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -21123,11 +21137,11 @@ "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" }, "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -21175,11 +21189,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -21213,11 +21227,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -21238,11 +21252,11 @@ }, "dependencies": { "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "nanocolors": "^0.2.2", + "picocolors": "^0.2.1", "source-map": "^0.6.1" } }, @@ -23309,9 +23323,9 @@ } }, "webpack": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.56.0.tgz", - "integrity": "sha512-pJ7esw2AGkpZL0jqsEAKnDEfRZdrc9NVjAWA+d1mFkwj68ng9VQ6+Wnrl+kS5dlDHvrat5ASK5vd7wp6I7f53Q==", + "version": "5.57.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.57.0.tgz", + "integrity": "sha512-kbKX1HOJpEhX9GZDFgHauU/7HfgGeGzUzjSUV+wZjGxP3PFeau7BgYFtm5+UTtJJSqmXYKFuBpWRDrSdQ3d8zA==", "requires": { "@types/eslint-scope": "^3.7.0", "@types/estree": "^0.0.50", @@ -23345,9 +23359,9 @@ "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==" }, "acorn-import-assertions": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.7.6.tgz", - "integrity": "sha512-FlVvVFA1TX6l3lp8VjDnYYq7R1nyW6x3svAt4nDgrWQ9SBaSh9CnbwgSUTasgfNfOG5HlM1ehugCvM+hjo56LA==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", "requires": {} }, "schema-utils": { diff --git a/frontend/src/dialog/photo/files.vue b/frontend/src/dialog/photo/files.vue index 8ae27012b..20315329f 100644 --- a/frontend/src/dialog/photo/files.vue +++ b/frontend/src/dialog/photo/files.vue @@ -41,13 +41,13 @@ @click.stop.prevent="downloadFile(file)"> Download - Primary - @@ -66,6 +66,12 @@ {{ file.UID | uppercase }} + + + Error + + {{ file.Error | uppercase }} + Instance ID @@ -162,12 +168,6 @@ {{ file.Chroma }} / 100 - - - Error - - {{ file.Error }} - Missing diff --git a/internal/api/covers.go b/internal/api/covers.go index 64efa4f92..9cc81ba9e 100644 --- a/internal/api/covers.go +++ b/internal/api/covers.go @@ -76,7 +76,7 @@ func AlbumCover(router *gin.RouterGroup) { f, err := query.AlbumCoverByUID(uid) if err != nil { - log.Debugf("%s: no photos yet, using generic image for %s", albumCover, uid) + log.Debugf("%s: %s contains no photos, using generic cover", albumCover, uid) c.Data(http.StatusOK, "image/svg+xml", albumIconSvg) return } @@ -84,7 +84,7 @@ func AlbumCover(router *gin.RouterGroup) { fileName := photoprism.FileName(f.FileRoot, f.FileName) if !fs.FileExists(fileName) { - log.Errorf("%s: could not find original for %s", albumCover, fileName) + log.Errorf("%s: found no original for %s", albumCover, fileName) c.Data(http.StatusOK, "image/svg+xml", albumIconSvg) // Set missing flag so that the file doesn't show up in search results anymore. diff --git a/internal/api/covers_test.go b/internal/api/covers_test.go index a59815942..82561d50f 100644 --- a/internal/api/covers_test.go +++ b/internal/api/covers_test.go @@ -15,7 +15,7 @@ func TestAlbumCover(t *testing.T) { assert.Equal(t, http.StatusOK, r.Code) }) - t.Run("album has no photo (because is not existing)", func(t *testing.T) { + t.Run("album contains no photos (because is not existing)", func(t *testing.T) { app, router, conf := NewApiTest() AlbumCover(router) r := PerformRequest(app, "GET", "/api/v1/albums/987-986435/t/"+conf.PreviewToken()+"/tile_500") diff --git a/internal/api/folder_cover.go b/internal/api/folder_cover.go index cf2469b76..38b1db288 100644 --- a/internal/api/folder_cover.go +++ b/internal/api/folder_cover.go @@ -85,7 +85,7 @@ func FolderCover(router *gin.RouterGroup) { f, err := query.FolderCoverByUID(uid) if err != nil { - log.Debugf("%s: no photos yet, using generic image for %s", folderCover, uid) + log.Debugf("%s: %s contains no photos, using generic cover", folderCover, uid) c.Data(http.StatusOK, "image/svg+xml", folderIconSvg) return } diff --git a/internal/api/import.go b/internal/api/import.go index 00f4880b2..a6ce4cc1b 100644 --- a/internal/api/import.go +++ b/internal/api/import.go @@ -77,7 +77,7 @@ func StartImport(router *gin.RouterGroup) { } if len(f.Albums) > 0 { - log.Debugf("import: files will be added to album %s", strings.Join(f.Albums, " and ")) + log.Debugf("import: adding files to album %s", strings.Join(f.Albums, " and ")) opt.Albums = f.Albums } diff --git a/internal/api/photo_unstack.go b/internal/api/photo_unstack.go index 5cc66de92..79736f934 100644 --- a/internal/api/photo_unstack.go +++ b/internal/api/photo_unstack.go @@ -81,11 +81,11 @@ func PhotoUnstack(router *gin.RouterGroup) { AbortEntityNotFound(c) return } else if related.Len() == 0 { - log.Errorf("photo: no files found for %s (unstack)", txt.Quote(baseName)) + log.Errorf("photo: found no files for %s (unstack)", txt.Quote(baseName)) AbortEntityNotFound(c) return } else if related.Main == nil { - log.Errorf("photo: no main file found for %s (unstack)", txt.Quote(baseName)) + log.Errorf("photo: found no main file for %s (unstack)", txt.Quote(baseName)) AbortEntityNotFound(c) return } diff --git a/internal/commands/config.go b/internal/commands/config.go index 08299ea57..595ab24f9 100644 --- a/internal/commands/config.go +++ b/internal/commands/config.go @@ -85,9 +85,9 @@ func configAction(ctx *cli.Context) error { fmt.Printf("%-25s %s\n", "site-url", conf.SiteUrl()) fmt.Printf("%-25s %s\n", "site-preview", conf.SitePreview()) fmt.Printf("%-25s %s\n", "site-title", conf.SiteTitle()) + fmt.Printf("%-25s %s\n", "site-author", conf.SiteAuthor()) fmt.Printf("%-25s %s\n", "site-caption", conf.SiteCaption()) fmt.Printf("%-25s %s\n", "site-description", conf.SiteDescription()) - fmt.Printf("%-25s %s\n", "site-author", conf.SiteAuthor()) fmt.Printf("%-25s %s\n", "cdn-url", conf.CdnUrl("/")) fmt.Printf("%-25s %s\n", "content-uri", conf.ContentUri()) fmt.Printf("%-25s %s\n", "static-uri", conf.StaticUri()) diff --git a/internal/commands/faces.go b/internal/commands/faces.go index 1c2e26a05..5e8759a0b 100644 --- a/internal/commands/faces.go +++ b/internal/commands/faces.go @@ -35,7 +35,7 @@ var FacesCommand = cli.Command{ Flags: []cli.Flag{ cli.BoolFlag{ Name: "fix, f", - Usage: "issues will be fixed automatically", + Usage: "automatically fixes issues", }, }, Action: facesAuditAction, @@ -46,7 +46,7 @@ var FacesCommand = cli.Command{ Flags: []cli.Flag{ cli.BoolFlag{ Name: "force, f", - Usage: "remove all people and faces", + Usage: "removes all people and faces", }, }, Action: facesResetAction, @@ -63,7 +63,7 @@ var FacesCommand = cli.Command{ Flags: []cli.Flag{ cli.BoolFlag{ Name: "force, f", - Usage: "update all faces", + Usage: "updates all faces", }, }, Action: facesUpdateAction, diff --git a/internal/commands/index_test.go b/internal/commands/index_test.go index 13f257b2c..6f3c54907 100644 --- a/internal/commands/index_test.go +++ b/internal/commands/index_test.go @@ -48,9 +48,7 @@ func TestIndexCommand(t *testing.T) { // Expected index command output. assert.Contains(t, output, "indexing originals") assert.Contains(t, output, "classify: loading labels") - assert.Contains(t, output, "index: no .ppignore file found") - assert.Contains(t, output, "index: updating primary files") - assert.Contains(t, output, "index: flagging hidden files") + assert.Contains(t, output, "index: found no .ppignore file") } else { t.Fatal("log output missing") } diff --git a/internal/commands/reset.go b/internal/commands/reset.go index 888a12845..d19a76871 100644 --- a/internal/commands/reset.go +++ b/internal/commands/reset.go @@ -63,7 +63,7 @@ func resetAction(ctx *cli.Context) error { } removeSidecarJsonPrompt := promptui.Prompt{ - Label: "Permanently delete all *.json photo sidecar files?", + Label: "Permanently remove all JSON photo sidecar files?", IsConfirm: true, } @@ -77,7 +77,7 @@ func resetAction(ctx *cli.Context) error { } if len(matches) > 0 { - log.Infof("%d json photo sidecar files will be removed", len(matches)) + log.Infof("removing %d JSON photo sidecar files", len(matches)) for _, name := range matches { if err := os.Remove(name); err != nil { @@ -89,16 +89,16 @@ func resetAction(ctx *cli.Context) error { fmt.Println("") - log.Infof("removed json files [%s]", time.Since(start)) + log.Infof("removed JSON sidecar files [%s]", time.Since(start)) } else { - log.Infof("no json files found") + log.Infof("found no JSON sidecar files") } } else { - log.Infof("keeping json sidecar files") + log.Infof("keeping JSON sidecar files") } removeSidecarYamlPrompt := promptui.Prompt{ - Label: "Permanently delete all *.yml photo metadata backups?", + Label: "Permanently remove all YAML photo metadata backup files?", IsConfirm: true, } @@ -112,7 +112,7 @@ func resetAction(ctx *cli.Context) error { } if len(matches) > 0 { - log.Infof("%d photo metadata backups will be removed", len(matches)) + log.Infof("%d YAML photo metadata backup files will be removed", len(matches)) for _, name := range matches { if err := os.Remove(name); err != nil { @@ -124,16 +124,16 @@ func resetAction(ctx *cli.Context) error { fmt.Println("") - log.Infof("removed files [%s]", time.Since(start)) + log.Infof("removed YAML photo metadata backup files [%s]", time.Since(start)) } else { - log.Infof("no metadata backups found for removal") + log.Infof("found no YAML photo metadata backup files") } } else { - log.Infof("keeping backup files") + log.Infof("keeping YAML photo metadata backup files") } removeAlbumYamlPrompt := promptui.Prompt{ - Label: "Permanently delete all *.yml album backups?", + Label: "Permanently remove all YAML album backup files?", IsConfirm: true, } @@ -147,7 +147,7 @@ func resetAction(ctx *cli.Context) error { } if len(matches) > 0 { - log.Infof("%d album backups will be removed", len(matches)) + log.Infof("%d YAML album backups will be removed", len(matches)) for _, name := range matches { if err := os.Remove(name); err != nil { @@ -159,12 +159,12 @@ func resetAction(ctx *cli.Context) error { fmt.Println("") - log.Infof("removed files [%s]", time.Since(start)) + log.Infof("removed YAML album backup files [%s]", time.Since(start)) } else { - log.Infof("no album backups found for removal") + log.Infof("found no YAML album backup files") } } else { - log.Infof("keeping backup files") + log.Infof("keeping YAML album backup files") } conf.Shutdown() diff --git a/internal/config/config.go b/internal/config/config.go index d7310d0fe..28cc5e3c2 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -42,6 +42,9 @@ var TotalMem uint64 const ApiUri = "/api/v1" const StaticUri = "/static" +const DefaultWakeupInterval = int(15 * 60) +const DefaultAutoIndexDelay = int(5 * 60) +const DefaultAutoImportDelay = int(3 * 60) // Megabyte in bytes. const Megabyte = 1000 * 1000 @@ -328,6 +331,11 @@ func (c *Config) SiteTitle() string { return c.options.SiteTitle } +// SiteAuthor returns the site author / copyright. +func (c *Config) SiteAuthor() string { + return c.options.SiteAuthor +} + // SiteCaption returns a short site caption. func (c *Config) SiteCaption() string { return c.options.SiteCaption @@ -338,11 +346,6 @@ func (c *Config) SiteDescription() string { return c.options.SiteDescription } -// SiteAuthor returns the site author / copyright. -func (c *Config) SiteAuthor() string { - return c.options.SiteAuthor -} - // Debug tests if debug mode is enabled. func (c *Config) Debug() bool { return c.options.Debug @@ -372,7 +375,7 @@ func (c *Config) Public() bool { return c.options.Public } -// Modify Public state while running. For testing purposes only. +// SetPublic changes authentication while instance is running, for testing purposes only. func (c *Config) SetPublic(p bool) { if c.Debug() { c.options.Public = p @@ -472,7 +475,7 @@ func (c *Config) Workers() int { // WakeupInterval returns the background worker wakeup interval duration. func (c *Config) WakeupInterval() time.Duration { if c.options.WakeupInterval <= 0 || c.options.WakeupInterval > 86400 { - return 15 * time.Minute + return time.Duration(DefaultWakeupInterval) * time.Second } return time.Duration(c.options.WakeupInterval) * time.Second @@ -483,7 +486,7 @@ func (c *Config) AutoIndex() time.Duration { if c.options.AutoIndex < 0 { return time.Duration(0) } else if c.options.AutoIndex == 0 || c.options.AutoIndex > 86400 { - return 5 * time.Minute + return time.Duration(DefaultAutoIndexDelay) * time.Second } return time.Duration(c.options.AutoIndex) * time.Second @@ -494,13 +497,13 @@ func (c *Config) AutoImport() time.Duration { if c.options.AutoImport < 0 || c.ReadOnly() { return time.Duration(0) } else if c.options.AutoImport == 0 || c.options.AutoImport > 86400 { - return 3 * time.Minute + return time.Duration(DefaultAutoImportDelay) * time.Second } return time.Duration(c.options.AutoImport) * time.Second } -// GeoApi returns the preferred geo coding api (none or places). +// GeoApi returns the preferred geocoding api (none or places). func (c *Config) GeoApi() string { if c.options.DisablePlaces { return "" diff --git a/internal/config/flags.go b/internal/config/flags.go index e9b2fe585..7b045995a 100644 --- a/internal/config/flags.go +++ b/internal/config/flags.go @@ -11,18 +11,18 @@ import ( var GlobalFlags = []cli.Flag{ cli.BoolFlag{ Name: "debug", - Usage: "run in debug mode, shows additional log messages", + Usage: "enables the debug mode, shows additional log messages", EnvVar: "PHOTOPRISM_DEBUG", }, cli.BoolFlag{ Name: "test", Hidden: true, - Usage: "run in test mode", + Usage: "enables the test mode", }, cli.BoolFlag{ Name: "demo", Hidden: true, - Usage: "run in demo mode", + Usage: "enables the demo mode", EnvVar: "PHOTOPRISM_DEMO", }, cli.BoolFlag{ @@ -33,99 +33,102 @@ var GlobalFlags = []cli.Flag{ }, cli.BoolFlag{ Name: "public, p", - Usage: "no authentication required, disables password protection", + Usage: "disables password authentication", EnvVar: "PHOTOPRISM_PUBLIC", }, cli.BoolFlag{ Name: "read-only, r", - Usage: "don't modify originals folder; disables import, upload, and delete", + Usage: "disables import, upload, and delete", EnvVar: "PHOTOPRISM_READONLY", }, cli.BoolFlag{ Name: "experimental, e", - Usage: "enable experimental features", + Usage: "enables experimental features", EnvVar: "PHOTOPRISM_EXPERIMENTAL", }, cli.StringFlag{ Name: "admin-password", - Usage: "initial admin `PASSWORD`, min 4 characters", + Usage: "sets the initial admin `PASSWORD`, min 4 characters", EnvVar: "PHOTOPRISM_ADMIN_PASSWORD", }, cli.StringFlag{ Name: "config-file, c", - Usage: "load initial config options from `FILENAME`", + Usage: "loads config options from `FILENAME`", EnvVar: "PHOTOPRISM_CONFIG_FILE", }, cli.StringFlag{ Name: "config-path", - Usage: "config `PATH` containing application settings", + Usage: "sets the config `PATH` for storing settings and other config files", EnvVar: "PHOTOPRISM_CONFIG_PATH", }, cli.StringFlag{ Name: "originals-path", - Usage: "originals `PATH` containing your photo and video collection", + Usage: "sets the originals `PATH` containing your photo and video files", EnvVar: "PHOTOPRISM_ORIGINALS_PATH", }, cli.IntFlag{ Name: "originals-limit", Value: 1000, - Usage: "file size limit for originals in `MB`", + Usage: "limits the file size in `MB`", EnvVar: "PHOTOPRISM_ORIGINALS_LIMIT", }, cli.StringFlag{ Name: "import-path", - Usage: "optional `PATH` for importing files to originals", + Usage: "sets an optional import `PATH` from which files can be added to originals", EnvVar: "PHOTOPRISM_IMPORT_PATH", }, cli.StringFlag{ Name: "storage-path", - Usage: "storage `PATH` for cache, database and sidecar files", + Usage: "sets the storage base `PATH` for cache, database, and sidecar files", EnvVar: "PHOTOPRISM_STORAGE_PATH", }, cli.StringFlag{ Name: "sidecar-path", - Usage: "relative or absolute storage `PATH` for sidecar files", + Usage: "sets a custom relative or absolute sidecar `PATH`", EnvVar: "PHOTOPRISM_SIDECAR_PATH", }, cli.StringFlag{ Name: "cache-path", - Usage: "cache storage `PATH` for sessions and thumbnails", + Usage: "sets a custom cache `PATH` for storing sessions and thumbnails", EnvVar: "PHOTOPRISM_CACHE_PATH", }, cli.StringFlag{ Name: "temp-path", - Usage: "temporary `PATH` for storing uploads and downloads", + Usage: "sets a custom temp `PATH` for storing temporary files", EnvVar: "PHOTOPRISM_TEMP_PATH", }, cli.StringFlag{ Name: "backup-path", - Usage: "backup storage `PATH`", + Usage: "sets a custom backup `PATH`", EnvVar: "PHOTOPRISM_BACKUP_PATH", }, cli.StringFlag{ Name: "assets-path", - Usage: "assets `PATH` for static resources like models and templates", + Usage: "sets the assets `PATH` containing static resources like icons and templates", EnvVar: "PHOTOPRISM_ASSETS_PATH", }, cli.IntFlag{ Name: "workers, w", - Usage: "limits `NUMBER` of indexing workers", + Usage: "limits the `NUMBER` of indexing workers", EnvVar: "PHOTOPRISM_WORKERS", Value: cpuid.CPU.PhysicalCores / 2, }, cli.IntFlag{ Name: "wakeup-interval", - Usage: "background worker wakeup interval in `SECONDS`", + Usage: "adjusts the background worker wakeup interval in `SECONDS`", + Value: DefaultWakeupInterval, EnvVar: "PHOTOPRISM_WAKEUP_INTERVAL", }, cli.IntFlag{ Name: "auto-index", - Usage: "auto indexing safety delay in `SECONDS` (WebDAV)", + Usage: "adjusts the WebDAV auto indexing safety delay in `SECONDS` (-1 to disable)", + Value: DefaultAutoIndexDelay, EnvVar: "PHOTOPRISM_AUTO_INDEX", }, cli.IntFlag{ Name: "auto-import", - Usage: "auto importing safety delay in `SECONDS` (WebDAV)", + Usage: "adjusts the WebDAV auto import safety delay in `SECONDS` (-1 to disable)", + Value: DefaultAutoImportDelay, EnvVar: "PHOTOPRISM_AUTO_IMPORT", }, cli.BoolFlag{ @@ -135,12 +138,12 @@ var GlobalFlags = []cli.Flag{ }, cli.BoolFlag{ Name: "disable-webdav", - Usage: "disables built-in WebDAV server", + Usage: "disables the built-in WebDAV server", EnvVar: "PHOTOPRISM_DISABLE_WEBDAV", }, cli.BoolFlag{ Name: "disable-settings", - Usage: "disables settings UI and API", + Usage: "disables the settings UI and API", EnvVar: "PHOTOPRISM_DISABLE_SETTINGS", }, cli.BoolFlag{ @@ -195,83 +198,83 @@ var GlobalFlags = []cli.Flag{ }, cli.BoolFlag{ Name: "detect-nsfw", - Usage: "flag photos as private that may be offensive (requires TensorFlow)", + Usage: "flags photos as private that may be offensive (requires TensorFlow)", EnvVar: "PHOTOPRISM_DETECT_NSFW", }, cli.BoolFlag{ Name: "upload-nsfw", - Usage: "allow uploads that may be offensive", + Usage: "allows uploads that may be offensive", EnvVar: "PHOTOPRISM_UPLOAD_NSFW", }, cli.StringFlag{ Name: "log-level, l", - Usage: "trace, debug, info, warning, error, fatal or panic", + Usage: "adjusts the log level to trace, debug, info, warning, error, fatal or panic", Value: "info", EnvVar: "PHOTOPRISM_LOG_LEVEL", }, cli.StringFlag{ Name: "log-filename", - Usage: "server log `FILENAME`", + Usage: "sets the server log `FILENAME`", EnvVar: "PHOTOPRISM_LOG_FILENAME", Value: "", }, cli.StringFlag{ Name: "pid-filename", - Usage: "server process id `FILENAME`", + Usage: "sets the server process id `FILENAME`", EnvVar: "PHOTOPRISM_PID_FILENAME", }, cli.StringFlag{ Name: "cdn-url", - Usage: "content delivery network `URL` (optional)", + Usage: "sets an optional content delivery network `URL` (optional)", EnvVar: "PHOTOPRISM_CDN_URL", }, cli.StringFlag{ Name: "site-url", - Usage: "public site `URL`", + Usage: "sets the public site `URL`", Value: "http://localhost:2342/", EnvVar: "PHOTOPRISM_SITE_URL", }, cli.StringFlag{ Name: "site-preview", - Usage: "public preview image `URL`", + Usage: "sets the public preview image `URL`", EnvVar: "PHOTOPRISM_SITE_PREVIEW", }, cli.StringFlag{ Name: "site-title", - Usage: "site title", + Usage: "sets the site title", Value: "PhotoPrism", EnvVar: "PHOTOPRISM_SITE_TITLE", }, + cli.StringFlag{ + Name: "site-author", + Usage: "sets the artist name or copyright", + EnvVar: "PHOTOPRISM_SITE_AUTHOR", + }, cli.StringFlag{ Name: "site-caption", - Usage: "short site caption", + Usage: "sets the site caption", Value: "Browse Your Life", EnvVar: "PHOTOPRISM_SITE_CAPTION", }, cli.StringFlag{ Name: "site-description", - Usage: "long site description", + Usage: "sets an optional site description", EnvVar: "PHOTOPRISM_SITE_DESCRIPTION", }, - cli.StringFlag{ - Name: "site-author", - Usage: "site artist or copyright", - EnvVar: "PHOTOPRISM_SITE_AUTHOR", - }, cli.IntFlag{ Name: "http-port", Value: 2342, - Usage: "http server port `NUMBER`", + Usage: "sets the http server port `NUMBER`", EnvVar: "PHOTOPRISM_HTTP_PORT", }, cli.StringFlag{ Name: "http-host", - Usage: "http server `IP` address", + Usage: "sets the http server `IP` address", EnvVar: "PHOTOPRISM_HTTP_HOST", }, cli.StringFlag{ Name: "http-mode, m", - Usage: "debug, release or test", + Usage: "selects the http server `MODE` (debug, release, or test)", EnvVar: "PHOTOPRISM_HTTP_MODE", }, cli.StringFlag{ @@ -281,45 +284,45 @@ var GlobalFlags = []cli.Flag{ }, cli.StringFlag{ Name: "database-driver", - Usage: "database driver `NAME` (sqlite or mysql)", + Usage: "selects the database `DRIVER` (sqlite or mysql)", Value: "sqlite", EnvVar: "PHOTOPRISM_DATABASE_DRIVER", }, cli.StringFlag{ Name: "database-dsn", - Usage: "sqlite file name, specifying a `DSN` is optional for mariadb and mysql", + Usage: "sets the sqlite file name, specifying a `DSN` is optional other drivers", EnvVar: "PHOTOPRISM_DATABASE_DSN", }, cli.StringFlag{ Name: "database-server", - Usage: "database server `HOST`, specifying a :port is optional", + Usage: "sets the database server `HOST` and port e.g. mysql:3306", EnvVar: "PHOTOPRISM_DATABASE_SERVER", }, cli.StringFlag{ Name: "database-name", Value: "photoprism", - Usage: "database schema `NAME`", + Usage: "sets the database schema `NAME`", EnvVar: "PHOTOPRISM_DATABASE_NAME", }, cli.StringFlag{ Name: "database-user", Value: "photoprism", - Usage: "database user `NAME`", + Usage: "sets the database user `NAME`", EnvVar: "PHOTOPRISM_DATABASE_USER", }, cli.StringFlag{ Name: "database-password", - Usage: "database user `PASSWORD`", + Usage: "sets the database user `PASSWORD`", EnvVar: "PHOTOPRISM_DATABASE_PASSWORD", }, cli.IntFlag{ Name: "database-conns", - Usage: "`LIMIT` for open database connections", + Usage: "limits the `NUMBER` of open database connections", EnvVar: "PHOTOPRISM_DATABASE_CONNS", }, cli.IntFlag{ Name: "database-conns-idle", - Usage: "`LIMIT` for idle database connections", + Usage: "limits the `NUMBER` of idle database connections", EnvVar: "PHOTOPRISM_DATABASE_CONNS_IDLE", }, cli.BoolFlag{ @@ -329,160 +332,160 @@ var GlobalFlags = []cli.Flag{ }, cli.StringFlag{ Name: "darktable-bin", - Usage: "Darktable CLI `COMMAND` for RAW file conversion", + Usage: "sets the Darktable CLI `COMMAND` for RAW file conversion", Value: "darktable-cli", EnvVar: "PHOTOPRISM_DARKTABLE_BIN", }, cli.StringFlag{ Name: "darktable-blacklist", - Usage: "Comma-separated file extension `BLACKLIST`", + Usage: "disables using Darktable for specific file `EXTENSIONS`", Value: "raf,cr3,dng", EnvVar: "PHOTOPRISM_DARKTABLE_BLACKLIST", }, cli.StringFlag{ Name: "rawtherapee-bin", - Usage: "RawTherapee CLI `COMMAND` for RAW file conversion", + Usage: "sets the RawTherapee CLI `COMMAND` for RAW file conversion", Value: "rawtherapee-cli", EnvVar: "PHOTOPRISM_RAWTHERAPEE_BIN", }, cli.StringFlag{ Name: "rawtherapee-blacklist", - Usage: "Comma-separated file extension `BLACKLIST`", + Usage: "disables using RawTherapee for specific file `EXTENSIONS`", Value: "", EnvVar: "PHOTOPRISM_RAWTHERAPEE_BLACKLIST", }, cli.StringFlag{ Name: "sips-bin", - Usage: "Sips `COMMAND` for RAW file conversion on macOS", + Usage: "sets the Sips `COMMAND` for RAW file conversion on macOS", Value: "sips", EnvVar: "PHOTOPRISM_SIPS_BIN", }, cli.StringFlag{ Name: "heifconvert-bin", - Usage: "HEIC/HEIF image convert `COMMAND`", + Usage: "sets the HEIC/HEIF image convert `COMMAND`", Value: "heif-convert", EnvVar: "PHOTOPRISM_HEIFCONVERT_BIN", }, cli.StringFlag{ Name: "ffmpeg-bin", - Usage: "FFmpeg `COMMAND` for video transcoding and cover images", + Usage: "sets the FFmpeg `COMMAND` for video transcoding and cover images", Value: "ffmpeg", EnvVar: "PHOTOPRISM_FFMPEG_BIN", }, cli.StringFlag{ Name: "ffmpeg-encoder", - Usage: "FFmpeg AVC encoder `NAME`", + Usage: "sets the FFmpeg AVC encoder `NAME`", Value: "libx264", EnvVar: "PHOTOPRISM_FFMPEG_ENCODER", }, cli.IntFlag{ Name: "ffmpeg-bitrate", - Usage: "FFmpeg encoding bitrate `LIMIT` in Mbit/s", + Usage: "limits the FFmpeg encoding `BITRATE` (Mbit/s)", Value: 50, EnvVar: "PHOTOPRISM_FFMPEG_BITRATE", }, cli.IntFlag{ Name: "ffmpeg-buffers", - Usage: "FFmpeg capture buffers", + Usage: "adjusts the number of FFmpeg capture buffers", Value: 32, EnvVar: "PHOTOPRISM_FFMPEG_BUFFERS", }, cli.StringFlag{ Name: "exiftool-bin", - Usage: "ExifTool `COMMAND` for enhanced metadata extraction", + Usage: "sets the ExifTool `COMMAND` for extracting metadata", Value: "exiftool", EnvVar: "PHOTOPRISM_EXIFTOOL_BIN", }, cli.StringFlag{ Name: "download-token", - Usage: "static url `TOKEN` for original file downloads", + Usage: "sets a custom download url `TOKEN`", EnvVar: "PHOTOPRISM_DOWNLOAD_TOKEN", }, cli.StringFlag{ Name: "preview-token", - Usage: "static url `TOKEN` for thumbnails and video streaming", + Usage: "sets a custom thumbnail and video streaming url `TOKEN`", EnvVar: "PHOTOPRISM_PREVIEW_TOKEN", }, cli.StringFlag{ Name: "thumb-filter", - Usage: "image downscaling `FILTER` (best to worst: blackman, lanczos, cubic, linear)", + Usage: "selects the thumbnail downscaling `FILTER` (best to worst: blackman, lanczos, cubic, linear)", Value: "lanczos", EnvVar: "PHOTOPRISM_THUMB_FILTER", }, cli.IntFlag{ Name: "thumb-size, s", - Usage: "max pre-cached thumbnail size in `PIXELS` (720-7680)", + Usage: "limits the pre-cached thumbnail size in `PIXELS` (720-7680)", Value: 2048, EnvVar: "PHOTOPRISM_THUMB_SIZE", }, cli.BoolFlag{ Name: "thumb-uncached, u", - Usage: "enable on-demand thumbnail generation (high memory and cpu usage)", + Usage: "enables on-demand thumbnail generation (high memory and cpu usage)", EnvVar: "PHOTOPRISM_THUMB_UNCACHED", }, cli.IntFlag{ Name: "thumb-size-uncached, x", - Usage: "on-demand thumbnail generation size limit in `PIXELS` (720-7680)", + Usage: "limits the on-demand thumbnail size in `PIXELS` (720-7680)", Value: 7680, EnvVar: "PHOTOPRISM_THUMB_SIZE_UNCACHED", }, cli.IntFlag{ Name: "jpeg-size", - Usage: "size limit for converted image files in `PIXELS` (720-30000)", + Usage: "limits the size of generated JPEG files in `PIXELS` (720-30000)", Value: 7680, EnvVar: "PHOTOPRISM_JPEG_SIZE", }, cli.IntFlag{ Name: "jpeg-quality, q", - Usage: "set to 90+ for high-quality thumbnails (25-100)", + Usage: "adjusts the `QUALITY` of generated JPEG files, a higher value reduces compression (25-100)", Value: 92, EnvVar: "PHOTOPRISM_JPEG_QUALITY", }, cli.IntFlag{ Name: "face-size", - Usage: "face size threshold in `PIXELS`", + Usage: "sets the min face size in `PIXELS`", Value: face.SizeThreshold, EnvVar: "PHOTOPRISM_FACE_SIZE", }, cli.Float64Flag{ Name: "face-score", - Usage: "face `QUALITY` threshold", + Usage: "adjusts the quality `THRESHOLD` for faces", Value: face.ScoreThreshold, EnvVar: "PHOTOPRISM_FACE_SCORE", }, cli.IntFlag{ Name: "face-overlap", - Usage: "face area overlap threshold in `PERCENT`", + Usage: "adjusts the face area overlap threshold in `PERCENT`", Value: face.OverlapThreshold, EnvVar: "PHOTOPRISM_FACE_OVERLAP", }, cli.IntFlag{ Name: "face-cluster-size", - Usage: "size threshold for faces forming a cluster in `PIXELS`", + Usage: "sets the min size of faces forming a cluster in `PIXELS`", Value: face.ClusterSizeThreshold, EnvVar: "PHOTOPRISM_FACE_CLUSTER_SIZE", }, cli.IntFlag{ Name: "face-cluster-score", - Usage: "`QUALITY` threshold for faces forming a cluster", + Usage: "adjusts the quality `THRESHOLD` for faces forming a cluster", Value: face.ClusterScoreThreshold, EnvVar: "PHOTOPRISM_FACE_CLUSTER_SCORE", }, cli.IntFlag{ Name: "face-cluster-core", - Usage: "`NUMBER` of faces forming a cluster core", + Usage: "determines the `NUMBER` of faces forming a cluster core", Value: face.ClusterCore, EnvVar: "PHOTOPRISM_FACE_CLUSTER_CORE", }, cli.Float64Flag{ Name: "face-cluster-dist", - Usage: "similarity `DISTANCE` of faces forming a cluster core", + Usage: "determines how close faces forming a cluster core must be to each other", Value: face.ClusterDist, EnvVar: "PHOTOPRISM_FACE_CLUSTER_DIST", }, cli.Float64Flag{ Name: "face-match-dist", - Usage: "`DISTANCE` offset for matching new faces with clusters", + Usage: "adjusts the similarity offset for matching faces with existing clusters", Value: face.MatchDist, EnvVar: "PHOTOPRISM_FACE_MATCH_DIST", }, diff --git a/internal/config/options.go b/internal/config/options.go index 3add1dd2e..404e4d1a1 100644 --- a/internal/config/options.go +++ b/internal/config/options.go @@ -78,9 +78,9 @@ type Options struct { SiteUrl string `yaml:"SiteUrl" json:"SiteUrl" flag:"site-url"` SitePreview string `yaml:"SitePreview" json:"SitePreview" flag:"site-preview"` SiteTitle string `yaml:"SiteTitle" json:"SiteTitle" flag:"site-title"` + SiteAuthor string `yaml:"SiteAuthor" json:"SiteAuthor" flag:"site-author"` SiteCaption string `yaml:"SiteCaption" json:"SiteCaption" flag:"site-caption"` SiteDescription string `yaml:"SiteDescription" json:"SiteDescription" flag:"site-description"` - SiteAuthor string `yaml:"SiteAuthor" json:"SiteAuthor" flag:"site-author"` DatabaseDriver string `yaml:"DatabaseDriver" json:"-" flag:"database-driver"` DatabaseDsn string `yaml:"DatabaseDsn" json:"-" flag:"database-dsn"` DatabaseServer string `yaml:"DatabaseServer" json:"-" flag:"database-server"` diff --git a/internal/entity/face.go b/internal/entity/face.go index 220f2a56a..d695ffaff 100644 --- a/internal/entity/face.go +++ b/internal/entity/face.go @@ -161,7 +161,7 @@ func (m *Face) ResolveCollision(embeddings face.Embeddings) (resolved bool, err return false, fmt.Errorf("collision distance must be positive") } else if dist < 0.02 { // Ignore if distance is very small as faces may belong to the same person. - log.Infof("faces: %s collision at dist %f reported, same person?", m.ID, dist) + log.Warnf("face %s: clearing ambiguous subject %s, similar face at dist %f with source %s", m.ID, m.SubjUID, dist, SrcString(m.FaceSrc)) // Reset subject UID just in case. m.SubjUID = "" @@ -182,7 +182,7 @@ func (m *Face) ResolveCollision(embeddings face.Embeddings) (resolved bool, err if revised, err := m.ReviseMatches(); err != nil { return true, err } else if r := len(revised); r > 0 { - log.Infof("faces: revised %d matches after collision", r) + log.Infof("faces: revised %d matches after conflict", r) } return true, nil diff --git a/internal/entity/file.go b/internal/entity/file.go index 2d4f706e2..8f2b9666b 100644 --- a/internal/entity/file.go +++ b/internal/entity/file.go @@ -209,19 +209,19 @@ func (m *File) DeletePermanently() error { } if err := UnscopedDb().Delete(Marker{}, "file_uid = ?", m.FileUID).Error; err != nil { - log.Errorf("file: %s (remove markers)", err) + log.Errorf("file %s: %s while removing markers", txt.Quote(m.FileUID), err) } if err := UnscopedDb().Delete(FileShare{}, "file_id = ?", m.ID).Error; err != nil { - log.Errorf("file: %s (remove shares)", err) + log.Errorf("file %s: %s while removing share info", txt.Quote(m.FileUID), err) } if err := UnscopedDb().Delete(FileSync{}, "file_id = ?", m.ID).Error; err != nil { - log.Errorf("file: %s (remove sync)", err) + log.Errorf("file %s: %s while removing sync info", txt.Quote(m.FileUID), err) } if err := m.ReplaceHash(""); err != nil { - log.Errorf("file: %s (remove covers)", err) + log.Errorf("file %s: %s while removing covers", txt.Quote(m.FileUID), err) } return UnscopedDb().Delete(m).Error @@ -236,9 +236,9 @@ func (m *File) ReplaceHash(newHash string) error { // Log values. if m.FileHash != "" && newHash == "" { - log.Tracef("file: removing hash %s", txt.Quote(m.FileHash)) + log.Tracef("file %s: removing hash %s", txt.Quote(m.FileUID), txt.Quote(m.FileHash)) } else if m.FileHash != "" && newHash != "" { - log.Tracef("file: hash %s changed to %s", txt.Quote(m.FileHash), txt.Quote(newHash)) + log.Tracef("file %s: hash %s changed to %s", txt.Quote(m.FileUID), txt.Quote(m.FileHash), txt.Quote(newHash)) } // Set file hash to new value. @@ -314,16 +314,16 @@ func (m *File) AllFilesMissing() bool { // Create inserts a new row to the database. func (m *File) Create() error { if m.PhotoID == 0 { - return fmt.Errorf("file: photo id must not be empty (create)") + return fmt.Errorf("file: can't create file with empty photo id") } if err := UnscopedDb().Create(m).Error; err != nil { - log.Errorf("file: %s (create)", err) + log.Errorf("file: %s while saving", err) return err } if _, err := m.SaveMarkers(); err != nil { - log.Errorf("file: %s (create markers for %s)", err, m.FileUID) + log.Errorf("file %s: %s while saving markers", txt.Quote(m.FileUID), err) return err } @@ -342,16 +342,16 @@ func (m *File) ResolvePrimary() error { // Save stores the file in the database. func (m *File) Save() error { if m.PhotoID == 0 { - return fmt.Errorf("file: photo id must not be empty (save %s)", m.FileUID) + return fmt.Errorf("file %s: can't save file with empty photo id", m.FileUID) } if err := UnscopedDb().Save(m).Error; err != nil { - log.Errorf("file: %s (save %s)", err, m.FileUID) + log.Errorf("file %s: %s while saving", txt.Quote(m.FileUID), err) return err } if _, err := m.SaveMarkers(); err != nil { - log.Errorf("file: %s (save markers for %s)", err, m.FileUID) + log.Errorf("file %s: %s while saving markers", txt.Quote(m.FileUID), err) return err } @@ -381,7 +381,7 @@ func (m *File) Updates(values interface{}) error { // Rename updates the name and path of this file. func (m *File) Rename(fileName, rootName, filePath, fileBase string) error { - log.Debugf("file: renaming %s to %s", txt.Quote(m.FileName), txt.Quote(fileName)) + log.Debugf("file %s: renaming %s to %s", txt.Quote(m.FileUID), txt.Quote(m.FileName), txt.Quote(fileName)) // Update database row. if err := m.Updates(map[string]interface{}{ @@ -425,7 +425,7 @@ func (m *File) Undelete() error { return err } - log.Debugf("file: removed missing flag from %s", txt.Quote(m.FileName)) + log.Debugf("file %s: removed missing flag from %s", txt.Quote(m.FileUID), txt.Quote(m.FileName)) m.FileMissing = false m.DeletedAt = nil @@ -544,7 +544,7 @@ func (m *File) Markers() *Markers { } else if m.FileUID == "" { m.markers = &Markers{} } else if res, err := FindMarkers(m.FileUID); err != nil { - log.Warnf("file: %s (load markers)", err) + log.Warnf("file %s: %s while loading markers", txt.Quote(m.FileUID), err) m.markers = &Markers{} } else { m.markers = &res diff --git a/internal/entity/file_test.go b/internal/entity/file_test.go index aee3db69b..0d5eca6b9 100644 --- a/internal/entity/file_test.go +++ b/internal/entity/file_test.go @@ -188,7 +188,7 @@ func TestFile_Save(t *testing.T) { t.Fatalf("file id should be 0: %d", file.ID) } - assert.Equal(t, "file: photo id must not be empty (save 123)", err.Error()) + assert.Equal(t, "file 123: can't save file with empty photo id", err.Error()) }) t.Run("success", func(t *testing.T) { photo := &Photo{TakenAtLocal: time.Date(2019, 01, 15, 0, 0, 0, 0, time.UTC), PhotoTitle: "Berlin / Morning Mood"} diff --git a/internal/entity/marker.go b/internal/entity/marker.go index 0dfa8720c..e359832f5 100644 --- a/internal/entity/marker.go +++ b/internal/entity/marker.go @@ -4,8 +4,11 @@ import ( "encoding/json" "fmt" "math" + "strings" "time" + "github.com/dustin/go-humanize/english" + "github.com/jinzhu/gorm" "github.com/photoprism/photoprism/internal/crop" @@ -69,7 +72,7 @@ func (m *Marker) BeforeCreate(scope *gorm.Scope) error { // NewMarker creates a new entity. func NewMarker(file File, area crop.Area, subjUID, markerSrc, markerType string, size, score int) *Marker { if file.FileHash == "" { - log.Errorf("marker: file hash is empty - you might have found a bug") + log.Errorf("markers: file hash is empty - you might have found a bug") return nil } @@ -116,6 +119,28 @@ func (m *Marker) SetEmbeddings(e face.Embeddings) { m.EmbeddingsJSON = e.JSON() } +// UpdateFile sets the file uid and thumb and updates the index if the marker already exists. +func (m *Marker) UpdateFile(file *File) (updated bool) { + if file.FileUID != "" && m.FileUID != file.FileUID { + m.FileUID = file.FileUID + updated = true + } + + if file.FileHash != "" && !strings.HasPrefix(m.Thumb, file.FileHash) { + m.Thumb = crop.NewArea("crop", m.X, m.Y, m.W, m.H).Thumb(file.FileHash) + updated = true + } + + if !updated || m.MarkerUID == "" { + return false + } else if res := UnscopedDb().Model(m).UpdateColumns(Values{"file_uid": m.FileUID, "thumb": m.Thumb}); res.Error != nil { + log.Errorf("marker %s: %s (set file)", m.MarkerUID, res.Error) + return false + } else { + return true + } +} + // Updates multiple columns in the database. func (m *Marker) Updates(values interface{}) error { return UnscopedDb().Model(m).Updates(values).Error @@ -207,7 +232,7 @@ func (m *Marker) SetFace(f *Face, dist float64) (updated bool, err error) { } else if reported, err := f.ResolveCollision(m.Embeddings()); err != nil { return false, err } else if reported { - log.Infof("marker: collision reported for %s, face %s, source %s, subject %s <> %s", m.MarkerUID, f.ID, m.SubjSrc, m.SubjUID, f.SubjUID) + log.Warnf("marker %s: face %s has ambiguous subjects %s <> %s, subject source %s", txt.Quote(m.MarkerUID), txt.Quote(f.ID), txt.Quote(m.SubjUID), txt.Quote(f.SubjUID), SrcString(m.SubjSrc)) return false, nil } else { return false, nil @@ -329,17 +354,37 @@ func (m *Marker) SyncSubject(updateRelated bool) (err error) { UpdateColumns(Values{"subj_uid": m.SubjUID, "subj_src": SrcAuto, "marker_review": false}).Error; err != nil { return fmt.Errorf("%s (update related markers)", err) } else if res.RowsAffected > 0 && m.face != nil { - log.Debugf("marker: matched %s with %s", subj.SubjName, m.FaceID) + log.Debugf("markers: matched %s with %s", subj.SubjName, m.FaceID) return m.face.RefreshPhotos() } return nil } +// InvalidArea tests if the marker area is invalid or out of range. +func (m *Marker) InvalidArea() error { + if m.MarkerType != MarkerFace { + return nil + } + + // Ok? + if false == (m.X > 1 || m.Y > 1 || m.X < 0 || m.Y < 0 || m.W <= 0 || m.H <= 0 || m.W > 1 || m.H > 1) { + return nil + } + + msg := fmt.Sprintf("invalid %s area x=%d%% y=%d%% w=%d%% h=%d%%", txt.Quote(m.MarkerType), int(m.X*100), int(m.Y*100), int(m.W*100), int(m.H*100)) + + if m.MarkerUID == "" { + return fmt.Errorf("markers: %s", msg) + } else { + return fmt.Errorf("marker %s: %s", m.MarkerUID, msg) + } +} + // Save updates the existing or inserts a new row. func (m *Marker) Save() error { - if m.X == 0 || m.Y == 0 || m.X > 1 || m.Y > 1 || m.X < -1 || m.Y < -1 { - return fmt.Errorf("marker: invalid position") + if err := m.InvalidArea(); err != nil { + return err } return Db().Save(m).Error @@ -347,8 +392,8 @@ func (m *Marker) Save() error { // Create inserts a new row to the database. func (m *Marker) Create() error { - if m.X == 0 || m.Y == 0 || m.X > 1 || m.Y > 1 || m.X < -1 || m.Y < -1 { - return fmt.Errorf("marker: invalid position") + if err := m.InvalidArea(); err != nil { + return err } return Db().Create(m).Error @@ -361,7 +406,7 @@ func (m *Marker) Embeddings() face.Embeddings { } else if len(m.embeddings) > 0 { return m.embeddings } else if err := json.Unmarshal(m.EmbeddingsJSON, &m.embeddings); err != nil { - log.Errorf("marker: %s while parsing embeddings json", err) + log.Errorf("markers: %s while parsing embeddings json", err) } return m.embeddings @@ -389,10 +434,10 @@ func (m *Marker) Subject() (subj *Subject) { // Create subject? if m.SubjSrc != SrcAuto && m.MarkerName != "" && m.SubjUID == "" { if subj = NewSubject(m.MarkerName, SubjPerson, m.SubjSrc); subj == nil { - log.Errorf("marker: invalid subject %s", txt.Quote(m.MarkerName)) + log.Errorf("marker %s: invalid subject %s", txt.Quote(m.MarkerUID), txt.Quote(m.MarkerName)) return nil } else if subj = FirstOrCreateSubject(subj); subj == nil { - log.Debugf("marker: invalid subject %s", txt.Quote(m.MarkerName)) + log.Debugf("marker %s: invalid subject %s", txt.Quote(m.MarkerUID), txt.Quote(m.MarkerName)) return nil } else { m.subject = subj @@ -418,9 +463,9 @@ func (m *Marker) ClearSubject(src string) error { // Find and (soft) delete unused subjects. start := time.Now() if count, err := DeleteOrphanPeople(); err != nil { - log.Errorf("marker: %s while removing unused subjects [%s]", err, time.Since(start)) + log.Errorf("marker %s: %s while removing unused subjects [%s]", txt.Quote(m.MarkerUID), err, time.Since(start)) } else if count > 0 { - log.Debugf("marker: removed %d people [%s]", count, time.Since(start)) + log.Debugf("marker %s: removed %s [%s]", txt.Quote(m.MarkerUID), english.Plural(count, "person", "people"), time.Since(start)) } }() @@ -433,7 +478,7 @@ func (m *Marker) ClearSubject(src string) error { } else if resolved, err := m.face.ResolveCollision(m.Embeddings()); err != nil { return err } else if resolved { - log.Debugf("marker: resolved collision with face %s", m.face.ID) + log.Debugf("marker %s: resolved ambiguous subjects for face %s", txt.Quote(m.MarkerUID), txt.Quote(m.face.ID)) } // Clear references. @@ -446,7 +491,7 @@ func (m *Marker) ClearSubject(src string) error { // Face returns a matching face entity if possible. func (m *Marker) Face() (f *Face) { if m.MarkerUID == "" { - log.Debugf("marker: empty uid while finding face") + log.Debugf("markers: can't find face when uid is empty") return nil } @@ -459,19 +504,19 @@ func (m *Marker) Face() (f *Face) { // Add face if size if m.SubjSrc != SrcAuto && m.FaceID == "" { if m.Size < face.ClusterSizeThreshold || m.Score < face.ClusterScoreThreshold { - log.Debugf("marker: skipped adding face due to low-quality (uid %s, size %d, score %d)", txt.Quote(m.MarkerUID), m.Size, m.Score) + log.Debugf("marker %s: skipped adding face due to low-quality (size %d, score %d)", txt.Quote(m.MarkerUID), m.Size, m.Score) return nil } else if emb := m.Embeddings(); emb.Empty() { - log.Warnf("marker: %s has no embeddings", m.MarkerUID) + log.Warnf("marker %s: found no face embeddings", txt.Quote(m.MarkerUID)) return nil } else if f = NewFace(m.SubjUID, m.SubjSrc, emb); f == nil { - log.Warnf("marker: failed adding face for id %s", m.MarkerUID) + log.Warnf("marker %s: failed assigning face", txt.Quote(m.MarkerUID)) return nil } else if f = FirstOrCreateFace(f); f == nil { - log.Warnf("marker: failed adding face for id %s", m.MarkerUID) + log.Warnf("marker %s: failed assigning face", txt.Quote(m.MarkerUID)) return nil } else if err := f.MatchMarkers(Faceless); err != nil { - log.Errorf("marker: %s (match faces)", err) + log.Errorf("marker %s: %s while matching with faces", txt.Quote(m.MarkerUID), err) } m.face = f @@ -654,30 +699,28 @@ func FindFaceMarker(faceId string) *Marker { if err := Db().Where("face_id = ?", faceId). Where("thumb <> '' AND marker_invalid = 0"). Order("face_dist ASC, q DESC").First(&result).Error; err != nil { - log.Warnf("marker: face %s not found", txt.Quote(faceId)) + log.Warnf("markers: found no marker for face %s", txt.Quote(faceId)) return nil } return &result } -// UpdateOrCreateMarker updates a marker in the database or creates a new one if needed. -func UpdateOrCreateMarker(m *Marker) (*Marker, error) { +// CreateMarkerIfNotExists updates a marker in the database or creates a new one if needed. +func CreateMarkerIfNotExists(m *Marker) (*Marker, error) { result := Marker{} if m.MarkerUID != "" { - err := m.Save() - log.Debugf("marker: updated existing %s %s for %s", txt.Quote(m.MarkerType), txt.Quote(m.MarkerUID), txt.Quote(m.FileUID)) - return m, err + return m, nil } else if err := Db().Where(`file_uid = ? AND thumb = ? AND marker_type = ?`, m.FileUID, m.Thumb, m.MarkerType).First(&result).Error; err == nil { - log.Infof("marker: found existing %s %s for %s", txt.Quote(m.MarkerType), txt.Quote(result.MarkerUID), txt.Quote(result.FileUID)) + log.Infof("markers: %s marker %s already exists for %s", txt.Quote(m.MarkerType), txt.Quote(result.MarkerUID), txt.Quote(result.FileUID)) return &result, err } else if err := m.Create(); err != nil { - log.Warnf("marker: %s while creating %s for %s", err, txt.Quote(m.MarkerType), txt.Quote(m.FileUID)) + log.Warnf("markers: %s while adding %s", err, txt.Quote(m.MarkerType)) return m, err } else { - log.Debugf("marker: added %s %s for %s", txt.Quote(m.MarkerType), txt.Quote(m.MarkerUID), txt.Quote(m.FileUID)) + log.Debugf("markers: added %s marker %s for %s", txt.Quote(m.MarkerType), txt.Quote(m.MarkerUID), txt.Quote(m.FileUID)) } return m, nil diff --git a/internal/entity/marker_test.go b/internal/entity/marker_test.go index 3d7c470b6..2e9e5f8e8 100644 --- a/internal/entity/marker_test.go +++ b/internal/entity/marker_test.go @@ -16,6 +16,30 @@ var testArea = crop.Area{ H: 0.355556, } +var invalidArea1 = crop.Area{ + Name: "face", + X: -1, + Y: 0.206944, + W: 0.355556, + H: 0.355556, +} + +var invalidArea2 = crop.Area{ + Name: "face", + X: 0.1, + Y: 0.206944, + W: 0, + H: 0.355556, +} + +var invalidArea3 = crop.Area{ + Name: "face", + X: 0.1, + Y: -0.206944, + W: 0.1, + H: 0.355556, +} + func TestMarker_TableName(t *testing.T) { m := &Marker{} assert.Contains(t, m.TableName(), "markers") @@ -127,7 +151,7 @@ func TestUpdateOrCreateMarker(t *testing.T) { assert.Equal(t, SrcImage, m.MarkerSrc) assert.Equal(t, MarkerLabel, m.MarkerType) - m, err := UpdateOrCreateMarker(m) + m, err := CreateMarkerIfNotExists(m) if err != nil { t.Fatal(err) @@ -146,7 +170,7 @@ func TestUpdateOrCreateMarker(t *testing.T) { func TestMarker_Updates(t *testing.T) { t.Run("success", func(t *testing.T) { m := NewMarker(FileFixtures.Get("exampleFileName.jpg"), testArea, "lt9k3pw1wowuy3c4", SrcImage, MarkerLabel, 100, 65) - m, err := UpdateOrCreateMarker(m) + m, err := CreateMarkerIfNotExists(m) if err != nil { t.Fatal(err) @@ -171,7 +195,7 @@ func TestMarker_Updates(t *testing.T) { func TestMarker_Update(t *testing.T) { t.Run("success", func(t *testing.T) { m := NewMarker(FileFixtures.Get("exampleFileName.jpg"), testArea, "lt9k3pw1wowuy3c4", SrcImage, MarkerLabel, 100, 65) - m, err := UpdateOrCreateMarker(m) + m, err := CreateMarkerIfNotExists(m) if err != nil { t.Fatal(err) @@ -192,11 +216,40 @@ func TestMarker_Update(t *testing.T) { }) } +func TestMarker_InvalidArea(t *testing.T) { + t.Run("TestArea", func(t *testing.T) { + m := NewMarker(FileFixtures.Get("exampleFileName.jpg"), testArea, "lt9k3pw1wowuy3c4", SrcImage, MarkerFace, 100, 65) + assert.Nil(t, m.InvalidArea()) + m.MarkerType = MarkerUnknown + assert.Nil(t, m.InvalidArea()) + }) + t.Run("InvalidArea1", func(t *testing.T) { + m := NewMarker(FileFixtures.Get("exampleFileName.jpg"), invalidArea1, "lt9k3pw1wowuy3c4", SrcImage, MarkerFace, 100, 65) + assert.EqualError(t, m.InvalidArea(), "markers: invalid face area x=-100% y=20% w=35% h=35%") + m.MarkerUID = "m345634636" + assert.EqualError(t, m.InvalidArea(), "marker m345634636: invalid face area x=-100% y=20% w=35% h=35%") + m.MarkerType = MarkerUnknown + assert.Nil(t, m.InvalidArea()) + }) + t.Run("InvalidArea2", func(t *testing.T) { + m := NewMarker(FileFixtures.Get("exampleFileName.jpg"), invalidArea2, "lt9k3pw1wowuy3c4", SrcImage, MarkerFace, 100, 65) + assert.Error(t, m.InvalidArea()) + m.MarkerType = MarkerUnknown + assert.Nil(t, m.InvalidArea()) + }) + t.Run("InvalidArea3", func(t *testing.T) { + m := NewMarker(FileFixtures.Get("exampleFileName.jpg"), invalidArea3, "lt9k3pw1wowuy3c4", SrcImage, MarkerFace, 100, 65) + assert.Error(t, m.InvalidArea()) + m.MarkerType = MarkerUnknown + assert.Nil(t, m.InvalidArea()) + }) +} + func TestMarker_Save(t *testing.T) { t.Run("success", func(t *testing.T) { m := NewMarker(FileFixtures.Get("exampleFileName.jpg"), testArea, "lt9k3pw1wowuy3c4", SrcImage, MarkerLabel, 100, 65) - m, err := UpdateOrCreateMarker(m) + m, err := CreateMarkerIfNotExists(m) if err != nil { t.Fatal(err) @@ -229,9 +282,14 @@ func TestMarker_Save(t *testing.T) { assert.NotEmpty(t, p.Files) }) t.Run("invalid position", func(t *testing.T) { - m := Marker{X: 0, Y: 0} - err := m.Save() - assert.Equal(t, "marker: invalid position", err.Error()) + m := Marker{X: -1, Y: 0, W: 0.2, H: 0.133, MarkerType: MarkerFace} + + if err := m.Save(); err == nil { + t.Fatal("error expected") + } else { + assert.Equal(t, "markers: invalid face area x=-100% y=0% w=20% h=13%", err.Error()) + } + }) } @@ -357,9 +415,13 @@ func TestMarker_SyncSubject(t *testing.T) { func TestMarker_Create(t *testing.T) { t.Run("invalid position", func(t *testing.T) { - m := Marker{X: 0, Y: 0} + m := Marker{X: 0, Y: 0, MarkerType: MarkerFace} err := m.Create() - assert.Equal(t, "marker: invalid position", err.Error()) + if err == nil { + t.Fatal("error expected") + } else { + assert.Equal(t, "markers: invalid face area x=0% y=0% w=0% h=0%", err.Error()) + } }) } diff --git a/internal/entity/markers.go b/internal/entity/markers.go index 3b94d6941..02d0c87a0 100644 --- a/internal/entity/markers.go +++ b/internal/entity/markers.go @@ -1,6 +1,8 @@ package entity import ( + "fmt" + "github.com/photoprism/photoprism/internal/classify" "github.com/photoprism/photoprism/internal/face" "github.com/photoprism/photoprism/pkg/txt" @@ -11,18 +13,20 @@ type Markers []Marker // Save stores the markers in the database. func (m Markers) Save(file *File) (count int, err error) { - for i := range m { - if file != nil { - m[i].FileUID = file.FileUID - } - - if _, err := UpdateOrCreateMarker(&m[i]); err != nil { - log.Errorf("markers: %s (save)", err) - } + if file == nil { + return 0, fmt.Errorf("file required for saving markers") } - if file == nil { - return len(m), nil + for i := range m { + if m[i].UpdateFile(file) { + continue + } + + if created, err := CreateMarkerIfNotExists(&m[i]); err != nil { + log.Errorf("markers: %s (save)", err) + } else { + m[i] = *created + } } return file.UpdatePhotoFaceCount() diff --git a/internal/entity/photo.go b/internal/entity/photo.go index 6ffbd8739..de60d8b54 100644 --- a/internal/entity/photo.go +++ b/internal/entity/photo.go @@ -140,7 +140,7 @@ func SavePhotoForm(model Photo, form form.Photo) error { } if !model.HasID() { - return errors.New("photo: can't save form, id is empty") + return errors.New("can't save form when photo id is missing") } model.UpdateDateFields() @@ -167,7 +167,7 @@ func SavePhotoForm(model Photo, form form.Photo) error { } if err := model.SyncKeywordLabels(); err != nil { - log.Errorf("photo: %s", err) + log.Errorf("photo %s: %s while syncing keywords and labels", model.PhotoUID, err) } if err := model.UpdateTitle(model.ClassifyLabels()); err != nil { @@ -175,7 +175,7 @@ func SavePhotoForm(model Photo, form form.Photo) error { } if err := model.IndexKeywords(); err != nil { - log.Errorf("photo: %s", err.Error()) + log.Errorf("photo %s: %s while indexing keywords", model.PhotoUID, err.Error()) } edited := TimeStamp() @@ -825,7 +825,7 @@ func (m *Photo) SetCoordinates(lat, lng float32, altitude int, source string) { // SetCamera updates the camera. func (m *Photo) SetCamera(camera *Camera, source string) { if camera == nil { - log.Warnf("photo: failed updating camera from source '%s'", source) + log.Warnf("photo %s: failed updating camera from source %s", txt.Quote(m.PhotoUID), SrcString(source)) return } @@ -845,7 +845,7 @@ func (m *Photo) SetCamera(camera *Camera, source string) { // SetLens updates the lens. func (m *Photo) SetLens(lens *Lens, source string) { if lens == nil { - log.Warnf("photo: failed updating lens from source '%s'", source) + log.Warnf("photo %s: failed updating lens from source %s", txt.Quote(m.PhotoUID), SrcString(source)) return } @@ -1073,12 +1073,12 @@ func (m *Photo) SetPrimary(fileUID string) error { if fileUID != "" { // Do nothing. } else if err := Db().Model(File{}). - Where("photo_uid = ? AND file_missing = 0 AND file_type = 'jpg'", m.PhotoUID). + Where("photo_uid = ? AND file_type = 'jpg' AND file_missing = 0 AND file_error = ''", m.PhotoUID). Order("file_width DESC").Limit(1). Pluck("file_uid", &files).Error; err != nil { return err } else if len(files) == 0 { - return fmt.Errorf("%s has no jpeg", m.PhotoUID) + return fmt.Errorf("found no valid jpeg for %s", m.PhotoUID) } else { fileUID = files[0] } diff --git a/internal/entity/photo_title.go b/internal/entity/photo_title.go index dbce34591..c967875f0 100644 --- a/internal/entity/photo_title.go +++ b/internal/entity/photo_title.go @@ -4,6 +4,9 @@ import ( "fmt" "path/filepath" "strings" + "time" + + "github.com/dustin/go-humanize/english" "github.com/photoprism/photoprism/internal/classify" "github.com/photoprism/photoprism/pkg/fs" @@ -39,12 +42,13 @@ func (m *Photo) SetTitle(title, source string) { // UpdateTitle updated the photo title based on location and labels. func (m *Photo) UpdateTitle(labels classify.Labels) error { if m.TitleSrc != SrcAuto && m.HasTitle() { - return fmt.Errorf("photo: won't update title, %s was modified", m.PhotoUID) + return fmt.Errorf("photo %s: keeping %s title %s", txt.Quote(m.PhotoUID), SrcString(m.TitleSrc), txt.Quote(m.PhotoTitle)) } var names string var knownLocation bool + start := time.Now() oldTitle := m.PhotoTitle fileTitle := m.FileTitle() @@ -62,7 +66,7 @@ func (m *Photo) UpdateTitle(labels classify.Labels) error { // TODO: User defined title format if names != "" { - log.Debugf("photo: using %s to create title for %s", txt.Quote(names), m.PhotoUID) + log.Debugf("photo %s: generating title from %s (%s)", txt.Quote(m.PhotoUID), english.Plural(len(people), "person", "people"), txt.Quote(names)) if l := len([]rune(names)); l > 35 { m.SetTitle(names, SrcAuto) @@ -76,7 +80,7 @@ func (m *Photo) UpdateTitle(labels classify.Labels) error { m.SetTitle(fmt.Sprintf("%s / %s / %s", names, loc.City(), m.TakenAt.Format("2006")), SrcAuto) } } else if title := labels.Title(loc.Name()); title != "" { - log.Debugf("photo: using label %s to create title for %s", txt.Quote(title), m.PhotoUID) + log.Debugf("photo %s: generating title from label %s", txt.Quote(m.PhotoUID), txt.Quote(title)) if loc.NoCity() || loc.LongCity() || loc.CityContains(title) { m.SetTitle(fmt.Sprintf("%s / %s / %s", txt.Title(title), loc.CountryName(), m.TakenAt.Format("2006")), SrcAuto) } else { @@ -101,7 +105,7 @@ func (m *Photo) UpdateTitle(labels classify.Labels) error { knownLocation = true if names != "" { - log.Debugf("photo: using %s to create title for %s", txt.Quote(names), m.PhotoUID) + log.Debugf("photo %s: generating title from %s (%s)", txt.Quote(m.PhotoUID), english.Plural(len(people), "person", "people"), txt.Quote(names)) if l := len([]rune(names)); l > 35 { m.SetTitle(names, SrcAuto) @@ -115,7 +119,7 @@ func (m *Photo) UpdateTitle(labels classify.Labels) error { m.SetTitle(fmt.Sprintf("%s / %s / %s", names, m.Place.City(), m.TakenAt.Format("2006")), SrcAuto) } } else if title := labels.Title(fileTitle); title != "" { - log.Debugf("photo: using label %s to create title for %s", txt.Quote(title), m.PhotoUID) + log.Debugf("photo %s: generating title from label %s", txt.Quote(m.PhotoUID), txt.Quote(title)) if m.Place.NoCity() || m.Place.LongCity() || m.Place.CityContains(title) { m.SetTitle(fmt.Sprintf("%s / %s / %s", txt.Title(title), m.Place.CountryName(), m.TakenAt.Format("2006")), SrcAuto) } else { @@ -157,7 +161,7 @@ func (m *Photo) UpdateTitle(labels classify.Labels) error { } if m.PhotoTitle != oldTitle { - log.Debugf("photo: changed title of %s to %s", m.PhotoUID, txt.Quote(m.PhotoTitle)) + log.Debugf("photo %s: changed title to %s [%s]", txt.Quote(m.PhotoUID), txt.Quote(m.PhotoTitle), time.Since(start)) } return nil @@ -166,7 +170,7 @@ func (m *Photo) UpdateTitle(labels classify.Labels) error { // UpdateAndSaveTitle updates the photo title and saves it. func (m *Photo) UpdateAndSaveTitle() error { if !m.HasID() { - return fmt.Errorf("photo: can't save to database, id is empty") + return fmt.Errorf("can't save photo whithout id") } m.PhotoFaces = m.FaceCount() diff --git a/internal/entity/subject.go b/internal/entity/subject.go index a2523ad8a..fa106dec1 100644 --- a/internal/entity/subject.go +++ b/internal/entity/subject.go @@ -101,7 +101,7 @@ func (m *Subject) Delete() error { subjectMutex.Lock() defer subjectMutex.Unlock() - log.Infof("subject: deleting %s %s", m.SubjType, txt.Quote(m.SubjName)) + log.Infof("subject: deleting %s %s", txt.Quote(m.SubjType), txt.Quote(m.SubjName)) event.EntitiesDeleted("subjects", []string{m.SubjUID}) @@ -129,7 +129,7 @@ func (m *Subject) Restore() error { if m.Deleted() { m.DeletedAt = nil - log.Infof("subject: restoring %s %s", m.SubjType, txt.Quote(m.SubjName)) + log.Infof("subject: restoring %s %s", txt.Quote(m.SubjType), txt.Quote(m.SubjName)) event.EntitiesCreated("subjects", []*Subject{m}) @@ -167,7 +167,7 @@ func FirstOrCreateSubject(m *Subject) *Subject { if found := FindSubjectByName(m.SubjName); found != nil { return found } else if createErr := m.Create(); createErr == nil { - log.Infof("subject: added %s %s", m.SubjType, txt.Quote(m.SubjName)) + log.Infof("subject: added %s %s", txt.Quote(m.SubjType), txt.Quote(m.SubjName)) event.EntitiesCreated("subjects", []*Subject{m}) @@ -257,7 +257,7 @@ func (m *Subject) UpdateName(name string) (*Subject, error) { if err := m.SetName(name); err != nil { return m, err } else if err := m.Updates(Values{"SubjName": m.SubjName, "SubjSlug": m.SubjSlug}); err == nil { - log.Infof("subject: renamed %s %s", m.SubjType, txt.Quote(m.SubjName)) + log.Infof("subject: renamed %s %s", txt.Quote(m.SubjType), txt.Quote(m.SubjName)) event.EntitiesUpdated("subjects", []*Subject{m}) diff --git a/internal/entity/subject_test.go b/internal/entity/subject_test.go index 8b28d5ed1..12591e58f 100644 --- a/internal/entity/subject_test.go +++ b/internal/entity/subject_test.go @@ -136,7 +136,7 @@ func TestSubject_Restore(t *testing.T) { t.Run("success", func(t *testing.T) { var deleteTime = time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) - m := &Subject{DeletedAt: &deleteTime, SubjName: "ToBeRestored"} + m := &Subject{DeletedAt: &deleteTime, SubjType: SubjPerson, SubjName: "ToBeRestored"} err := m.Save() if err != nil { t.Fatal(err) @@ -150,7 +150,7 @@ func TestSubject_Restore(t *testing.T) { assert.False(t, m.Deleted()) }) t.Run("subject not deleted", func(t *testing.T) { - m := &Subject{DeletedAt: nil, SubjName: "NotDeleted1234"} + m := &Subject{DeletedAt: nil, SubjType: SubjPerson, SubjName: "NotDeleted1234"} err := m.Restore() if err != nil { t.Fatal(err) diff --git a/internal/face/thresholds.go b/internal/face/thresholds.go index 602e4d53e..c61b677a9 100644 --- a/internal/face/thresholds.go +++ b/internal/face/thresholds.go @@ -4,17 +4,17 @@ import ( "github.com/photoprism/photoprism/internal/crop" ) -var CropSize = crop.Sizes[crop.Tile160] -var MatchDist = 0.46 -var ClusterDist = 0.64 -var ClusterCore = 4 -var ClusterScoreThreshold = 15 -var ClusterSizeThreshold = 80 -var SampleThreshold = 2 * ClusterCore -var OverlapThreshold = 42 -var OverlapThresholdFloor = OverlapThreshold - 1 -var ScoreThreshold = 9.0 -var SizeThreshold = 40 +var CropSize = crop.Sizes[crop.Tile160] // Face image crop size for FaceNet. +var OverlapThreshold = 42 // Face area overlap threshold in percent. +var OverlapThresholdFloor = OverlapThreshold - 1 // Reduced overlap area to avoid rounding inconsistencies. +var ScoreThreshold = 9.0 // Min face score. +var ClusterScoreThreshold = 15 // Min score for faces forming a cluster. +var SizeThreshold = 40 // Min face size in pixels. +var ClusterSizeThreshold = 80 // Min size for faces forming a cluster in pixels. +var ClusterDist = 0.64 // Similarity distance threshold of faces forming a cluster core. +var MatchDist = 0.46 // Distance offset threshold for matching new faces with clusters. +var ClusterCore = 4 // Min number of faces forming a cluster core. +var SampleThreshold = 2 * ClusterCore // Threshold for automatic clustering to start. // QualityThreshold returns the scale adjusted quality score threshold. func QualityThreshold(scale int) (score float32) { diff --git a/internal/meta/exif_parser.go b/internal/meta/exif_parser.go index 656254e26..a0857ef72 100644 --- a/internal/meta/exif_parser.go +++ b/internal/meta/exif_parser.go @@ -39,7 +39,7 @@ func RawExif(fileName string, fileType fs.FileFormat) (rawExif []byte, err error if err != nil { if strings.HasPrefix(err.Error(), "no exif header") { - return rawExif, fmt.Errorf("metadata: no exif header in %s (parse jpeg)", logName) + return rawExif, fmt.Errorf("metadata: found no exif header in %s (parse jpeg)", logName) } else if strings.HasPrefix(err.Error(), "no exif data") { log.Debugf("metadata: failed parsing %s, starting brute-force search (parse jpeg)", logName) } else { @@ -62,7 +62,7 @@ func RawExif(fileName string, fileType fs.FileFormat) (rawExif []byte, err error if err != nil { if err.Error() == "file does not have EXIF" { - return rawExif, fmt.Errorf("metadata: no exif header in %s (parse png)", logName) + return rawExif, fmt.Errorf("metadata: found no exif header in %s (parse png)", logName) } else { log.Warnf("metadata: %s in %s (parse png)", err, logName) } @@ -82,7 +82,7 @@ func RawExif(fileName string, fileType fs.FileFormat) (rawExif []byte, err error if err != nil { if err.Error() == "file does not have EXIF" { - return rawExif, fmt.Errorf("metadata: no exif header in %s (parse heic)", logName) + return rawExif, fmt.Errorf("metadata: found no exif header in %s (parse heic)", logName) } else { log.Warnf("metadata: %s in %s (parse heic)", err, logName) } @@ -102,7 +102,7 @@ func RawExif(fileName string, fileType fs.FileFormat) (rawExif []byte, err error if err != nil { if err.Error() == "file does not have EXIF" { - return rawExif, fmt.Errorf("metadata: no exif header in %s (parse tiff)", logName) + return rawExif, fmt.Errorf("metadata: found no exif header in %s (parse tiff)", logName) } else { log.Warnf("metadata: %s in %s (parse tiff)", err, logName) } @@ -115,7 +115,7 @@ func RawExif(fileName string, fileType fs.FileFormat) (rawExif []byte, err error rawExif, err = exif.SearchFileAndExtractExif(fileName) if err != nil { - return rawExif, fmt.Errorf("metadata: no exif header in %s (search and extract)", logName) + return rawExif, fmt.Errorf("metadata: found no exif header in %s (search and extract)", logName) } } diff --git a/internal/meta/exif_test.go b/internal/meta/exif_test.go index 4062bffc5..671336482 100644 --- a/internal/meta/exif_test.go +++ b/internal/meta/exif_test.go @@ -108,7 +108,7 @@ func TestExif(t *testing.T) { t.Fatal("err should NOT be nil") } - assert.Equal(t, "metadata: no exif header in tweethog.png (search and extract)", err.Error()) + assert.Equal(t, "metadata: found no exif header in tweethog.png (search and extract)", err.Error()) }) t.Run("iphone_7.heic", func(t *testing.T) { @@ -223,7 +223,7 @@ func TestExif(t *testing.T) { t.Fatal("err should NOT be nil") } - assert.Equal(t, "metadata: no exif header in no-exif-data.jpg (search and extract)", err.Error()) + assert.Equal(t, "metadata: found no exif header in no-exif-data.jpg (search and extract)", err.Error()) }) t.Run("screenshot.png", func(t *testing.T) { @@ -268,7 +268,7 @@ func TestExif(t *testing.T) { t.Run("gopher-preview.jpg", func(t *testing.T) { _, err := Exif("testdata/gopher-preview.jpg", fs.FormatJpeg) - assert.EqualError(t, err, "metadata: no exif header in gopher-preview.jpg (search and extract)") + assert.EqualError(t, err, "metadata: found no exif header in gopher-preview.jpg (search and extract)") }) t.Run("huawei-gps-error.jpg", func(t *testing.T) { diff --git a/internal/photoprism/faces.go b/internal/photoprism/faces.go index 378f5b2fb..4c2ac21dc 100644 --- a/internal/photoprism/faces.go +++ b/internal/photoprism/faces.go @@ -5,6 +5,8 @@ import ( "runtime/debug" "time" + "github.com/dustin/go-humanize/english" + "github.com/photoprism/photoprism/internal/config" "github.com/photoprism/photoprism/internal/entity" "github.com/photoprism/photoprism/internal/mutex" @@ -60,37 +62,37 @@ func (w *Faces) Start(opt FacesOptions) (err error) { } else if removed > 0 { log.Infof("faces: removed %d orphan markers [%s]", removed, time.Since(start)) } else { - log.Debugf("faces: no orphan markers [%s]", time.Since(start)) + log.Debugf("faces: found no orphan markers [%s]", time.Since(start)) } // Repair invalid marker face and subject references. start = time.Now() if removed, err := query.FixMarkerReferences(); err != nil { - log.Errorf("faces: %s (fix references)", err) + log.Errorf("markers: %s (fix references)", err) } else if removed > 0 { - log.Infof("faces: fixed %d marker references [%s]", removed, time.Since(start)) + log.Infof("markers: fixed %d references [%s]", removed, time.Since(start)) } else { - log.Debugf("faces: no invalid marker references [%s]", time.Since(start)) + log.Debugf("markers: found no invalid references [%s]", time.Since(start)) } // Create known marker subjects if needed. start = time.Now() if affected, err := query.CreateMarkerSubjects(); err != nil { - log.Errorf("faces: %s (create subjects)", err) + log.Errorf("markers: %s (create subjects)", err) } else if affected > 0 { - log.Infof("faces: added %d known marker subjects [%s]", affected, time.Since(start)) + log.Infof("markers: added %d known subjects [%s]", affected, time.Since(start)) } else { - log.Debugf("faces: marker subjects already exist [%s]", time.Since(start)) + log.Debugf("markers: found no missing subjects [%s]", time.Since(start)) } // Resolve collisions of different subject's faces. start = time.Now() if c, r, err := query.ResolveFaceCollisions(); err != nil { - log.Errorf("faces: %s (resolve collisions)", err) + log.Errorf("faces: %s (resolve ambiguous subjects)", err) } else if c > 0 { - log.Infof("faces: resolved %d / %d collisions [%s]", r, c, time.Since(start)) + log.Infof("faces: resolved %d / %d ambiguous subjects [%s]", r, c, time.Since(start)) } else { - log.Debugf("faces: no collisions detected [%s]", time.Since(start)) + log.Debugf("faces: found no ambiguous subjects [%s]", time.Since(start)) } // Optimize existing face clusters. @@ -100,7 +102,7 @@ func (w *Faces) Start(opt FacesOptions) (err error) { } else if res.Merged > 0 { log.Infof("faces: merged %d clusters [%s]", res.Merged, time.Since(start)) } else { - log.Debugf("faces: no clusters could be merged [%s]", time.Since(start)) + log.Debugf("faces: found no clusters to be merged [%s]", time.Since(start)) } var added entity.Faces @@ -125,9 +127,9 @@ func (w *Faces) Start(opt FacesOptions) (err error) { // Log face matching results. if matches.Updated > 0 { - log.Infof("faces: updated %d markers, recognized %d faces, %d unknown [%s]", matches.Updated, matches.Recognized, matches.Unknown, time.Since(start)) + log.Infof("faces: updated %s, recognized %s, %d unknown [%s]", english.Plural(int(matches.Updated), "marker", "markers"), english.Plural(int(matches.Recognized), "face", "faces"), matches.Unknown, time.Since(start)) } else { - log.Debugf("faces: updated %d markers, recognized %d faces, %d unknown [%s]", matches.Updated, matches.Recognized, matches.Unknown, time.Since(start)) + log.Debugf("faces: updated %s, recognized %s, %d unknown [%s]", english.Plural(int(matches.Updated), "marker", "markers"), english.Plural(int(matches.Recognized), "face", "faces"), matches.Unknown, time.Since(start)) } // Remove unused people. diff --git a/internal/photoprism/faces_audit.go b/internal/photoprism/faces_audit.go index 6ecf0e887..cde063989 100644 --- a/internal/photoprism/faces_audit.go +++ b/internal/photoprism/faces_audit.go @@ -74,18 +74,18 @@ func (w *Faces) Audit(fix bool) (err error) { r := f1.SampleRadius + face.MatchDist - log.Infof("face %s: conflict at dist %f, Ø %f from %d samples, collision Ø %f", f1.ID, dist, r, f1.Samples, f1.CollisionRadius) + log.Infof("face %s: ambiguous subject at dist %f, Ø %f from %d samples, collision Ø %f", f1.ID, dist, r, f1.Samples, f1.CollisionRadius) if f1.SubjUID != "" { log.Infof("face %s: subject %s (%s %s)", f1.ID, txt.Quote(subj[f1.SubjUID].SubjName), f1.SubjUID, entity.SrcString(f1.FaceSrc)) } else { - log.Infof("face %s: no subject (%s)", f1.ID, entity.SrcString(f1.FaceSrc)) + log.Infof("face %s: has no subject (%s)", f1.ID, entity.SrcString(f1.FaceSrc)) } if f2.SubjUID != "" { log.Infof("face %s: subject %s (%s %s)", f2.ID, txt.Quote(subj[f2.SubjUID].SubjName), f2.SubjUID, entity.SrcString(f2.FaceSrc)) } else { - log.Infof("face %s: no subject (%s)", f2.ID, entity.SrcString(f2.FaceSrc)) + log.Infof("face %s: has no subject (%s)", f2.ID, entity.SrcString(f2.FaceSrc)) } if !fix { @@ -93,21 +93,21 @@ func (w *Faces) Audit(fix bool) (err error) { } else if ok, err := f1.ResolveCollision(face.Embeddings{f2.Embedding()}); err != nil { log.Errorf("face %s: %s", f1.ID, err) } else if ok { - log.Infof("face %s: collision has been resolved", f1.ID) + log.Infof("face %s: ambiguous subject has been resolved", f1.ID) resolved++ } else { - log.Infof("face %s: collision could not be resolved", f1.ID) + log.Infof("face %s: ambiguous subject could not be resolved", f1.ID) } } } } if conflicts == 0 { - log.Infof("found no conflicting face clusters") + log.Infof("found no ambiguous subjects") } else if !fix { - log.Infof("%d conflicting face clusters", conflicts) + log.Infof("%s", english.Plural(conflicts, "ambiguous subject", "ambiguous subjects")) } else { - log.Infof("%d conflicting face clusters, %d resolved", conflicts, resolved) + log.Infof("%s, %d resolved", english.Plural(conflicts, "ambiguous subject", "ambiguous subjects"), resolved) } if markers, err := query.MarkersWithSubjectConflict(); err != nil { diff --git a/internal/photoprism/faces_cluster.go b/internal/photoprism/faces_cluster.go index f6dd58e78..01b12bae4 100644 --- a/internal/photoprism/faces_cluster.go +++ b/internal/photoprism/faces_cluster.go @@ -19,16 +19,16 @@ func (w *Faces) Cluster(opt FacesOptions) (added entity.Faces, err error) { // Skip clustering if index contains no new face markers, and force option isn't set. if opt.Force { - log.Infof("faces: forced clustering") + log.Infof("faces: enforced clustering") } else if n := query.CountNewFaceMarkers(face.ClusterSizeThreshold, face.ClusterScoreThreshold); n < opt.SampleThreshold() { - log.Debugf("faces: skipping clustering") + log.Debugf("faces: skipped clustering") return added, nil } // Fetch unclustered face embeddings. embeddings, err := query.Embeddings(false, true, face.ClusterSizeThreshold, face.ClusterScoreThreshold) - log.Debugf("faces: %d unclustered samples found", len(embeddings)) + log.Debugf("faces: found %s", english.Plural(len(embeddings), "unclustered sample", "unclustered samples")) // Anything that keeps us from doing this? if err != nil { @@ -75,7 +75,7 @@ func (w *Faces) Cluster(opt FacesOptions) (added entity.Faces, err error) { log.Errorf("faces: face should not be nil - bug?") } else if err := f.Create(); err == nil { added = append(added, *f) - log.Debugf("faces: added cluster %s based on %d samples, radius %f", f.ID, f.Samples, f.SampleRadius) + log.Debugf("faces: added cluster %s based on %s, radius %f", f.ID, english.Plural(f.Samples, "sample", "samples"), f.SampleRadius) } else if err := f.Updates(entity.Values{"UpdatedAt": entity.TimeStamp()}); err != nil { log.Errorf("faces: %s", err) } else { diff --git a/internal/photoprism/faces_stats.go b/internal/photoprism/faces_stats.go index 49414dba8..821cf454c 100644 --- a/internal/photoprism/faces_stats.go +++ b/internal/photoprism/faces_stats.go @@ -10,7 +10,7 @@ func (w *Faces) Stats() (err error) { if embeddings, err := query.Embeddings(true, false, 0, 0); err != nil { return err } else if samples := len(embeddings); samples == 0 { - log.Infof("faces: no samples found") + log.Infof("faces: found no samples") } else { log.Infof("faces: computing distance of %d samples", samples) @@ -101,7 +101,7 @@ func (w *Faces) Stats() (err error) { } if l := len(dist); l == 0 { - log.Infof("faces: analyzed %d clusters, no matches", samples) + log.Infof("faces: analyzed %d clusters, found no matches", samples) } else { log.Infof("faces: %d faces match to the same person", l) } diff --git a/internal/photoprism/import_worker.go b/internal/photoprism/import_worker.go index 4a78bd0aa..42ea557f1 100644 --- a/internal/photoprism/import_worker.go +++ b/internal/photoprism/import_worker.go @@ -178,7 +178,7 @@ func ImportWorker(jobs <-chan ImportJob) { continue } } else { - log.Warnf("import: no main file for %s, conversion to jpeg failed?", fs.RelName(destMainFileName, imp.originalsPath())) + log.Warnf("import: found no main file for %s, conversion to jpeg may have failed", fs.RelName(destMainFileName, imp.originalsPath())) } for _, f := range related.Files { @@ -210,7 +210,7 @@ func ImportWorker(jobs <-chan ImportJob) { if res.Indexed() && f.IsJpeg() { if err := f.ResampleDefault(ind.thumbPath(), false); err != nil { - log.Errorf("import: failed creating thumbs for %s (%s)", txt.Quote(f.BaseName()), err.Error()) + log.Errorf("import: failed creating thumbnails for %s (%s)", txt.Quote(f.BaseName()), err.Error()) query.SetFileError(res.FileUID, err.Error()) } } diff --git a/internal/photoprism/index.go b/internal/photoprism/index.go index 1e61acad7..3cf58820f 100644 --- a/internal/photoprism/index.go +++ b/internal/photoprism/index.go @@ -9,13 +9,13 @@ import ( "strings" "sync" - "github.com/photoprism/photoprism/internal/face" - "github.com/karrick/godirwalk" + "github.com/photoprism/photoprism/internal/classify" "github.com/photoprism/photoprism/internal/config" "github.com/photoprism/photoprism/internal/entity" "github.com/photoprism/photoprism/internal/event" + "github.com/photoprism/photoprism/internal/face" "github.com/photoprism/photoprism/internal/mutex" "github.com/photoprism/photoprism/internal/nsfw" "github.com/photoprism/photoprism/pkg/fs" @@ -265,7 +265,7 @@ func (ind *Index) Start(opt IndexOptions) fs.Done { log.Warnf("index: %s (update counts)", err) } } else { - log.Infof("index: no new or modified files") + log.Infof("index: found no new or modified files") } runtime.GC() diff --git a/internal/photoprism/index_mediafile.go b/internal/photoprism/index_mediafile.go index b9ec95fa5..a1992cbb6 100644 --- a/internal/photoprism/index_mediafile.go +++ b/internal/photoprism/index_mediafile.go @@ -298,8 +298,9 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) ( case m.IsJpeg(): // Color information if p, err := m.Colors(Config().ThumbPath()); err != nil { - log.Errorf("index: %s in %s (detect colors)", err.Error(), logName) + log.Debugf("%s while detecting colors", err.Error()) file.FileError = err.Error() + file.FilePrimary = false } else { file.FileMainColor = p.MainColor.Name() file.FileColors = p.Colors.Hex() @@ -633,9 +634,9 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) ( details.Keywords = strings.Join(txt.UniqueWords(w), ", ") if details.Keywords != "" { - log.Tracef("index: set keywords %s for %s", details.Keywords, logName) + log.Tracef("index: using keywords %s for %s", details.Keywords, logName) } else { - log.Tracef("index: no keywords for %s", logName) + log.Tracef("index: found no keywords for %s", logName) } photo.PhotoQuality = photo.QualityScore() diff --git a/internal/photoprism/index_related.go b/internal/photoprism/index_related.go index 2d4dcdde8..6527d8092 100644 --- a/internal/photoprism/index_related.go +++ b/internal/photoprism/index_related.go @@ -12,7 +12,7 @@ import ( func IndexMain(related *RelatedFiles, ind *Index, opt IndexOptions) (result IndexResult) { // Skip sidecar files without related media file. if related.Main == nil { - result.Err = fmt.Errorf("index: no main file found for %s", txt.Quote(related.String())) + result.Err = fmt.Errorf("index: found no main file for %s", txt.Quote(related.String())) result.Status = IndexFailed return result } @@ -45,7 +45,7 @@ func IndexMain(related *RelatedFiles, ind *Index, opt IndexOptions) (result Inde log.Debugf("index: created %s", txt.Quote(jpegFile.BaseName())) if err := jpegFile.ResampleDefault(ind.thumbPath(), false); err != nil { - result.Err = fmt.Errorf("index: failed creating thumbs for %s (%s)", txt.Quote(f.BaseName()), err.Error()) + result.Err = fmt.Errorf("index: failed creating thumbnails for %s (%s)", txt.Quote(f.BaseName()), err.Error()) result.Status = IndexFailed return result @@ -59,7 +59,7 @@ func IndexMain(related *RelatedFiles, ind *Index, opt IndexOptions) (result Inde if result.Indexed() && f.IsJpeg() { if err := f.ResampleDefault(ind.thumbPath(), false); err != nil { - log.Errorf("index: failed creating thumbs for %s (%s)", txt.Quote(f.BaseName()), err.Error()) + log.Errorf("index: failed creating thumbnails for %s (%s)", txt.Quote(f.BaseName()), err.Error()) query.SetFileError(result.FileUID, err.Error()) } } @@ -126,7 +126,7 @@ func IndexRelated(related RelatedFiles, ind *Index, opt IndexOptions) (result In log.Debugf("index: created %s", txt.Quote(jpegFile.BaseName())) if err := jpegFile.ResampleDefault(ind.thumbPath(), false); err != nil { - result.Err = fmt.Errorf("index: failed creating thumbs for %s (%s)", txt.Quote(f.BaseName()), err.Error()) + result.Err = fmt.Errorf("index: failed creating thumbnails for %s (%s)", txt.Quote(f.BaseName()), err.Error()) result.Status = IndexFailed return result @@ -140,7 +140,7 @@ func IndexRelated(related RelatedFiles, ind *Index, opt IndexOptions) (result In if res.Indexed() && f.IsJpeg() { if err := f.ResampleDefault(ind.thumbPath(), false); err != nil { - log.Errorf("index: failed creating thumbs for %s (%s)", txt.Quote(f.BaseName()), err.Error()) + log.Errorf("index: failed creating thumbnails for %s (%s)", txt.Quote(f.BaseName()), err.Error()) query.SetFileError(res.FileUID, err.Error()) } } diff --git a/internal/photoprism/location.go b/internal/photoprism/location.go index 65c7609ee..8faa48e20 100644 --- a/internal/photoprism/location.go +++ b/internal/photoprism/location.go @@ -15,7 +15,7 @@ func (m *MediaFile) Location() (*entity.Cell, error) { data := m.MetaData() if data.Lat == 0 && data.Lng == 0 { - return nil, errors.New("media: no latitude and longitude in metadata") + return nil, errors.New("media: found no latitude and longitude") } m.location = entity.NewCell(data.Lat, data.Lng) diff --git a/internal/photoprism/location_test.go b/internal/photoprism/location_test.go index 4a45a5d9c..cbe413b3e 100644 --- a/internal/photoprism/location_test.go +++ b/internal/photoprism/location_test.go @@ -83,7 +83,7 @@ func TestMediaFile_Location(t *testing.T) { if _, err := mediaFile.Location(); err == nil { t.Fatal("mediaFile.Location() should return error") } else { - assert.Equal(t, "media: no latitude and longitude in metadata", err.Error()) + assert.Equal(t, "media: found no latitude and longitude", err.Error()) } }) t.Run("Random.docx", func(t *testing.T) { @@ -97,7 +97,7 @@ func TestMediaFile_Location(t *testing.T) { location, err := mediaFile.Location() - assert.Error(t, err, "metadata: no exif header in Random.docx") + assert.Error(t, err, "metadata: found no exif header in Random.docx") assert.Nil(t, location) }) } diff --git a/internal/photoprism/mediafile.go b/internal/photoprism/mediafile.go index 9062d14c7..8644c31f6 100644 --- a/internal/photoprism/mediafile.go +++ b/internal/photoprism/mediafile.go @@ -902,7 +902,7 @@ func (m *MediaFile) Thumbnail(path string, sizeName thumb.Name) (filename string if err != nil { err = fmt.Errorf("media: failed creating thumbnail for %s (%s)", txt.Quote(m.BaseName()), err) - log.Error(err) + log.Debug(err) return "", err } @@ -928,7 +928,7 @@ func (m *MediaFile) ResampleDefault(thumbPath string, force bool) (err error) { defer func() { switch count { case 0: - log.Debug(capture.Time(start, fmt.Sprintf("media: no new thumbnails created for %s", m.BasePrefix(false)))) + log.Debug(capture.Time(start, fmt.Sprintf("media: created no new thumbnails for %s", m.BasePrefix(false)))) default: log.Info(capture.Time(start, fmt.Sprintf("media: created %s for %s", english.Plural(count, "thumbnail", "thumbnails"), m.BasePrefix(false)))) } @@ -961,7 +961,7 @@ func (m *MediaFile) ResampleDefault(thumbPath string, force bool) (err error) { img, err := imaging.Open(m.FileName()) if err != nil { - log.Errorf("media: %s in %s", err.Error(), txt.Quote(m.BaseName())) + log.Debugf("media: %s in %s", err.Error(), txt.Quote(m.BaseName())) return err } diff --git a/internal/photoprism/mediafile_meta.go b/internal/photoprism/mediafile_meta.go index fcb83d560..efd6ca912 100644 --- a/internal/photoprism/mediafile_meta.go +++ b/internal/photoprism/mediafile_meta.go @@ -80,7 +80,7 @@ func (m *MediaFile) MetaData() (result meta.Data) { // Parse regular JSON sidecar files ("img_1234.json") if !m.IsSidecar() { if jsonFiles := fs.FormatJson.FindAll(m.FileName(), []string{Config().SidecarPath(), fs.HiddenPath}, Config().OriginalsPath(), false); len(jsonFiles) == 0 { - log.Tracef("metadata: no additional sidecar file found for %s", txt.Quote(filepath.Base(m.FileName()))) + log.Tracef("metadata: found no additional sidecar file for %s", txt.Quote(filepath.Base(m.FileName()))) } else { for _, jsonFile := range jsonFiles { jsonErr := m.metaData.JSON(jsonFile, m.BaseName()) diff --git a/internal/query/faces.go b/internal/query/faces.go index 1e5dd11b4..9669afc36 100644 --- a/internal/query/faces.go +++ b/internal/query/faces.go @@ -84,7 +84,7 @@ func CountNewFaceMarkers(size, score int) (n int) { if err := Db().Where("face_src = ?", entity.SrcAuto). Order("created_at DESC").Limit(1).Take(&f).Error; err != nil { - log.Debugf("faces: no existing clusters") + log.Debugf("faces: found no existing clusters") } q := Db().Model(&entity.Markers{}). @@ -181,27 +181,27 @@ func ResolveFaceCollisions() (conflicts, resolved int, err error) { r := f1.SampleRadius + face.MatchDist - log.Infof("face %s: conflict at dist %f, Ø %f from %d samples, collision Ø %f", f1.ID, dist, r, f1.Samples, f1.CollisionRadius) + log.Infof("face %s: ambiguous subject at dist %f, Ø %f from %d samples, collision Ø %f", f1.ID, dist, r, f1.Samples, f1.CollisionRadius) if f1.SubjUID != "" { log.Debugf("face %s: subject %s (%s %s)", f1.ID, txt.Quote(f1.SubjUID), f1.SubjUID, entity.SrcString(f1.FaceSrc)) } else { - log.Debugf("face %s: no subject (%s)", f1.ID, entity.SrcString(f1.FaceSrc)) + log.Debugf("face %s: has no subject (%s)", f1.ID, entity.SrcString(f1.FaceSrc)) } if f2.SubjUID != "" { log.Debugf("face %s: subject %s (%s %s)", f2.ID, txt.Quote(f2.SubjUID), f2.SubjUID, entity.SrcString(f2.FaceSrc)) } else { - log.Debugf("face %s: no subject (%s)", f2.ID, entity.SrcString(f2.FaceSrc)) + log.Debugf("face %s: has no subject (%s)", f2.ID, entity.SrcString(f2.FaceSrc)) } if ok, err := f1.ResolveCollision(face.Embeddings{f2.Embedding()}); err != nil { log.Errorf("face %s: %s", f1.ID, err) } else if ok { - log.Infof("face %s: collision has been resolved", f1.ID) + log.Infof("face %s: conflict has been resolved", f1.ID) resolved++ } else { - log.Debugf("face %s: collision could not be resolved", f1.ID) + log.Debugf("face %s: conflict could not be resolved", f1.ID) } } } diff --git a/internal/query/photo.go b/internal/query/photo.go index 19388fe00..8bf13f782 100644 --- a/internal/query/photo.go +++ b/internal/query/photo.go @@ -3,6 +3,8 @@ package query import ( "time" + "github.com/dustin/go-humanize/english" + "github.com/jinzhu/gorm" "github.com/photoprism/photoprism/internal/entity" ) @@ -83,11 +85,19 @@ func PhotosMissing(limit int, offset int) (entities entity.Photos, err error) { // ResetPhotoQuality sets photo quality scores to -1 if files are missing. func ResetPhotoQuality() error { - log.Info("index: flagging hidden files") + start := time.Now() - return Db().Table("photos"). - Where("id NOT IN (SELECT photo_id FROM files WHERE file_primary = 1 AND file_missing = 0 AND deleted_at IS NULL)"). - Update("photo_quality", -1).Error + res := Db().Table("photos"). + Where("id NOT IN (SELECT photo_id FROM files WHERE file_primary = 1 AND file_missing = 0 AND file_error = '' AND deleted_at IS NULL)"). + Update("photo_quality", -1) + + if res.RowsAffected == 0 { + log.Debugf("index: found no additional broken photos [%s]", time.Since(start)) + } else { + log.Infof("index: flagged %s as hidden [%s]", english.Plural(int(res.RowsAffected), "broken photo", "broken photos"), time.Since(start)) + } + + return res.Error } // PhotosCheck returns photos selected for maintenance. @@ -124,7 +134,7 @@ func OrphanPhotos() (photos entity.Photos, err error) { // FixPrimaries tries to set a primary file for photos that have none. func FixPrimaries() error { - log.Info("index: updating primary files") + start := time.Now() var photos entity.Photos @@ -136,13 +146,20 @@ func FixPrimaries() error { return err } + if len(photos) == 0 { + log.Debugf("index: found no photos without primary file [%s]", time.Since(start)) + return nil + } + for _, p := range photos { - log.Debugf("index: finding new primary file for photo %s", p.PhotoUID) + log.Debugf("index: searching primary file for %s", p.PhotoUID) if err := p.SetPrimary(""); err != nil { log.Infof("index: %s (set primary)", err) } } + log.Infof("index: updated primary files [%s]", time.Since(start)) + return nil } diff --git a/internal/thumb/create.go b/internal/thumb/create.go index 82b350e5d..b61fc492f 100644 --- a/internal/thumb/create.go +++ b/internal/thumb/create.go @@ -96,7 +96,7 @@ func FromFile(imageFilename, hash, thumbPath string, width, height, orientation img, err := imaging.Open(imageFilename) if err != nil { - log.Errorf("resample: %s in %s", err, txt.Quote(filepath.Base(imageFilename))) + log.Debugf("resample: %s in %s", err, txt.Quote(filepath.Base(imageFilename))) return "", err } diff --git a/pkg/fs/ignore.go b/pkg/fs/ignore.go index fa721728f..aa715920d 100644 --- a/pkg/fs/ignore.go +++ b/pkg/fs/ignore.go @@ -122,7 +122,7 @@ func (l *IgnoreList) Dir(dir string) error { } if !FileExists(fileName) { - return fmt.Errorf("no %s file found", l.configFile) + return fmt.Errorf("found no %s file", l.configFile) } return l.ConfigFile(fileName)