This commit is contained in:
parent
b195b7e4f8
commit
444c94bf9e
113
frontend/package-lock.json
generated
113
frontend/package-lock.json
generated
|
@ -1887,9 +1887,9 @@
|
|||
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4="
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "16.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.2.tgz",
|
||||
"integrity": "sha512-zCclL4/rx+W5SQTzFs9wyvvyCwoK9QtBpratqz2IYJ3O8Umrn0m3nsTv0wQBk9sRGpvUe9CwPDrQFB10f1FIjQ=="
|
||||
"version": "16.10.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.3.tgz",
|
||||
"integrity": "sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ=="
|
||||
},
|
||||
"node_modules/@types/parse-json": {
|
||||
"version": "4.0.0",
|
||||
|
@ -3019,9 +3019,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001264",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001264.tgz",
|
||||
"integrity": "sha512-Ftfqqfcs/ePiUmyaySsQ4PUsdcYyXG2rfoBVsk3iY1ahHaJEw65vfb7Suzqm+cEkwwPIv/XWkg27iCpRavH4zA==",
|
||||
"version": "1.0.30001265",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001265.tgz",
|
||||
"integrity": "sha512-YzBnspggWV5hep1m9Z6sZVLOt7vrju8xWooFAgN6BA5qvy98qPAPb7vNUzypFaoh2pb3vlfzbDO8tB57UPGbtw==",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/browserslist"
|
||||
|
@ -3428,9 +3428,9 @@
|
|||
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
|
||||
},
|
||||
"node_modules/core-js": {
|
||||
"version": "3.18.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.18.1.tgz",
|
||||
"integrity": "sha512-vJlUi/7YdlCZeL6fXvWNaLUPh/id12WXj3MbkMw5uOyF0PfWPBNOCNbs53YqgrvtujLNlt9JQpruyIKkUZ+PKA==",
|
||||
"version": "3.18.2",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.18.2.tgz",
|
||||
"integrity": "sha512-zNhPOUoSgoizoSQFdX1MeZO16ORRb9FFQLts8gSYbZU5FcgXhp24iMWMxnOQo5uIaIG7/6FA/IqJPwev1o9ZXQ==",
|
||||
"hasInstallScript": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
|
@ -3438,11 +3438,11 @@
|
|||
}
|
||||
},
|
||||
"node_modules/core-js-compat": {
|
||||
"version": "3.18.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.18.1.tgz",
|
||||
"integrity": "sha512-XJMYx58zo4W0kLPmIingVZA10+7TuKrMLPt83+EzDmxFJQUMcTVVmQ+n5JP4r6Z14qSzhQBRi3NSWoeVyKKXUg==",
|
||||
"version": "3.18.2",
|
||||
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.18.2.tgz",
|
||||
"integrity": "sha512-25VJYCJtGjZwLguj7d66oiHfmnVw3TMOZ0zV8DyMJp/aeQ3OjR519iOOeck08HMyVVRAqXxafc2Hl+5QstJrsQ==",
|
||||
"dependencies": {
|
||||
"browserslist": "^4.17.1",
|
||||
"browserslist": "^4.17.3",
|
||||
"semver": "7.0.0"
|
||||
},
|
||||
"funding": {
|
||||
|
@ -4281,9 +4281,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"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=="
|
||||
"version": "1.3.860",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.860.tgz",
|
||||
"integrity": "sha512-gWwGZ+Wv4Mou2SJRH6JQzhTPjL5f95SX7n6VkLTQ/Q/INsZLZNQ1vH2GlZjozKyvT0kkFuCmWTwIoCj+/hUDPw=="
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
|
@ -6088,10 +6088,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/gl-matrix": {
|
||||
"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"
|
||||
"version": "3.4.3",
|
||||
"resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz",
|
||||
"integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA=="
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "7.2.0",
|
||||
|
@ -7895,9 +7894,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/mini-css-extract-plugin": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.3.0.tgz",
|
||||
"integrity": "sha512-uzWaOwC+gJrnKbr23J1ZRWx/Wd9W9Ce1mKPlsBGBV/r8zG7/G7oKMxGmxbI65pVGbae2cR7CUx9Ulk0HQt8BfQ==",
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.1.tgz",
|
||||
"integrity": "sha512-97R1JD3GCG7wN5DNM6JrJMxnWNGf2oDlwRgoDc4HOQ5GprahF98mvcPvDMTb5eI1n3vmmMzCpbXH8tpcnr/Nmw==",
|
||||
"dependencies": {
|
||||
"schema-utils": "^3.1.0"
|
||||
},
|
||||
|
@ -10591,9 +10590,9 @@
|
|||
}
|
||||
},
|
||||
"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==",
|
||||
"version": "3.1.29",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.29.tgz",
|
||||
"integrity": "sha512-dW2pUSGZ8ZnCFIlBIA31SV8huOGCHb6OwzVCc7A69rb/a+SgPBwfmLvK5TKQ3INPbRkcI8a/Owo0XbiTNH19wg==",
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
|
@ -13163,9 +13162,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/webpack": {
|
||||
"version": "5.57.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.57.0.tgz",
|
||||
"integrity": "sha512-kbKX1HOJpEhX9GZDFgHauU/7HfgGeGzUzjSUV+wZjGxP3PFeau7BgYFtm5+UTtJJSqmXYKFuBpWRDrSdQ3d8zA==",
|
||||
"version": "5.57.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.57.1.tgz",
|
||||
"integrity": "sha512-kHszukYjTPVfCOEyrUthA3jqJwduY/P3eO8I0gMNOZGIQWKAwZftxmp5hq6paophvwo9NoUrcZOecs9ulOyyTg==",
|
||||
"dependencies": {
|
||||
"@types/eslint-scope": "^3.7.0",
|
||||
"@types/estree": "^0.0.50",
|
||||
|
@ -14952,9 +14951,9 @@
|
|||
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4="
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "16.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.2.tgz",
|
||||
"integrity": "sha512-zCclL4/rx+W5SQTzFs9wyvvyCwoK9QtBpratqz2IYJ3O8Umrn0m3nsTv0wQBk9sRGpvUe9CwPDrQFB10f1FIjQ=="
|
||||
"version": "16.10.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.3.tgz",
|
||||
"integrity": "sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ=="
|
||||
},
|
||||
"@types/parse-json": {
|
||||
"version": "4.0.0",
|
||||
|
@ -15837,9 +15836,9 @@
|
|||
}
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001264",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001264.tgz",
|
||||
"integrity": "sha512-Ftfqqfcs/ePiUmyaySsQ4PUsdcYyXG2rfoBVsk3iY1ahHaJEw65vfb7Suzqm+cEkwwPIv/XWkg27iCpRavH4zA=="
|
||||
"version": "1.0.30001265",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001265.tgz",
|
||||
"integrity": "sha512-YzBnspggWV5hep1m9Z6sZVLOt7vrju8xWooFAgN6BA5qvy98qPAPb7vNUzypFaoh2pb3vlfzbDO8tB57UPGbtw=="
|
||||
},
|
||||
"chai": {
|
||||
"version": "4.3.4",
|
||||
|
@ -16169,16 +16168,16 @@
|
|||
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
|
||||
},
|
||||
"core-js": {
|
||||
"version": "3.18.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.18.1.tgz",
|
||||
"integrity": "sha512-vJlUi/7YdlCZeL6fXvWNaLUPh/id12WXj3MbkMw5uOyF0PfWPBNOCNbs53YqgrvtujLNlt9JQpruyIKkUZ+PKA=="
|
||||
"version": "3.18.2",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.18.2.tgz",
|
||||
"integrity": "sha512-zNhPOUoSgoizoSQFdX1MeZO16ORRb9FFQLts8gSYbZU5FcgXhp24iMWMxnOQo5uIaIG7/6FA/IqJPwev1o9ZXQ=="
|
||||
},
|
||||
"core-js-compat": {
|
||||
"version": "3.18.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.18.1.tgz",
|
||||
"integrity": "sha512-XJMYx58zo4W0kLPmIingVZA10+7TuKrMLPt83+EzDmxFJQUMcTVVmQ+n5JP4r6Z14qSzhQBRi3NSWoeVyKKXUg==",
|
||||
"version": "3.18.2",
|
||||
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.18.2.tgz",
|
||||
"integrity": "sha512-25VJYCJtGjZwLguj7d66oiHfmnVw3TMOZ0zV8DyMJp/aeQ3OjR519iOOeck08HMyVVRAqXxafc2Hl+5QstJrsQ==",
|
||||
"requires": {
|
||||
"browserslist": "^4.17.1",
|
||||
"browserslist": "^4.17.3",
|
||||
"semver": "7.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -16771,9 +16770,9 @@
|
|||
"integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA=="
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"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=="
|
||||
"version": "1.3.860",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.860.tgz",
|
||||
"integrity": "sha512-gWwGZ+Wv4Mou2SJRH6JQzhTPjL5f95SX7n6VkLTQ/Q/INsZLZNQ1vH2GlZjozKyvT0kkFuCmWTwIoCj+/hUDPw=="
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
|
@ -18122,9 +18121,9 @@
|
|||
}
|
||||
},
|
||||
"gl-matrix": {
|
||||
"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=="
|
||||
"version": "3.4.3",
|
||||
"resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz",
|
||||
"integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA=="
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.2.0",
|
||||
|
@ -19471,9 +19470,9 @@
|
|||
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
|
||||
},
|
||||
"mini-css-extract-plugin": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.3.0.tgz",
|
||||
"integrity": "sha512-uzWaOwC+gJrnKbr23J1ZRWx/Wd9W9Ce1mKPlsBGBV/r8zG7/G7oKMxGmxbI65pVGbae2cR7CUx9Ulk0HQt8BfQ==",
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.1.tgz",
|
||||
"integrity": "sha512-97R1JD3GCG7wN5DNM6JrJMxnWNGf2oDlwRgoDc4HOQ5GprahF98mvcPvDMTb5eI1n3vmmMzCpbXH8tpcnr/Nmw==",
|
||||
"requires": {
|
||||
"schema-utils": "^3.1.0"
|
||||
},
|
||||
|
@ -20117,9 +20116,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"nanoid": {
|
||||
"version": "3.1.28",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.28.tgz",
|
||||
"integrity": "sha512-gSu9VZ2HtmoKYe/lmyPFES5nknFrHa+/DT9muUFWFMi6Jh9E1I7bkvlQ8xxf1Kos9pi9o8lBnIOkatMhKX/YUw=="
|
||||
"version": "3.1.29",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.29.tgz",
|
||||
"integrity": "sha512-dW2pUSGZ8ZnCFIlBIA31SV8huOGCHb6OwzVCc7A69rb/a+SgPBwfmLvK5TKQ3INPbRkcI8a/Owo0XbiTNH19wg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -23323,9 +23322,9 @@
|
|||
}
|
||||
},
|
||||
"webpack": {
|
||||
"version": "5.57.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.57.0.tgz",
|
||||
"integrity": "sha512-kbKX1HOJpEhX9GZDFgHauU/7HfgGeGzUzjSUV+wZjGxP3PFeau7BgYFtm5+UTtJJSqmXYKFuBpWRDrSdQ3d8zA==",
|
||||
"version": "5.57.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.57.1.tgz",
|
||||
"integrity": "sha512-kHszukYjTPVfCOEyrUthA3jqJwduY/P3eO8I0gMNOZGIQWKAwZftxmp5hq6paophvwo9NoUrcZOecs9ulOyyTg==",
|
||||
"requires": {
|
||||
"@types/eslint-scope": "^3.7.0",
|
||||
"@types/estree": "^0.0.50",
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<v-layout row wrap class="search-results photo-results cards-view" :class="{'select-results': selectMode}">
|
||||
<v-flex
|
||||
v-for="(photo, index) in photos"
|
||||
:key="index"
|
||||
:key="photo.ID"
|
||||
xs12 sm6 md4 lg3 xlg2 xxxl1 d-flex
|
||||
>
|
||||
<v-card tile
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<v-layout row wrap class="search-results photo-results mosaic-view" :class="{'select-results': selectMode}">
|
||||
<v-flex
|
||||
v-for="(photo, index) in photos"
|
||||
:key="index"
|
||||
:key="photo.ID"
|
||||
xs4 sm3 md2 lg1 d-flex
|
||||
>
|
||||
<v-card tile
|
||||
|
|
|
@ -202,6 +202,8 @@ main {
|
|||
#photoprism .v-tabs .v-badge__badge {
|
||||
right: -22px;
|
||||
font-size: 9px;
|
||||
width: 1.1rem;
|
||||
width: auto;
|
||||
padding: 0 0.2rem;
|
||||
min-width: 1.1rem;
|
||||
height: 1.1rem;
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
<template>
|
||||
<div class="p-tab p-tab-photo-files">
|
||||
<v-expansion-panel expand class="pa-0 elevation-0 secondary" :value="state">
|
||||
<template v-for="(file, index) in model.fileModels()">
|
||||
<v-expansion-panel-content v-if="!file.Missing" :key="index" class="pa-0 elevation-0 secondary-light"
|
||||
<template v-for="file in model.fileModels()">
|
||||
<v-expansion-panel-content v-if="!file.Missing" :key="file.UID" class="pa-0 elevation-0 secondary-light"
|
||||
style="margin-top: 1px;">
|
||||
<template #header>
|
||||
<div class="caption">{{ file.baseName(70) }}</div>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-expansion-panel class="pa-0 elevation-0">
|
||||
<v-expansion-panel-content v-for="(link, index) in links" :key="index"
|
||||
<v-expansion-panel-content v-for="(link, index) in links" :key="link.UID"
|
||||
class="pa-0 elevation-0 secondary mb-1">
|
||||
<template #header>
|
||||
<button :class="`text-xs-${!rtl ? 'left' : 'right'} action-url ml-0 mt-0 mb-0 pa-0 mr-2`" style="user-select: none;"
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -9,8 +9,8 @@ msgstr ""
|
|||
msgid ""
|
||||
msgstr ""
|
||||
|
||||
#: src/dialog/photo/files.vue:131
|
||||
#: src/dialog/photo/files.vue:128
|
||||
#: src/dialog/photo/files.vue:137
|
||||
#: src/dialog/photo/files.vue:134
|
||||
msgid "{{ file.Orientation }}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -36,7 +36,8 @@ msgstr ""
|
|||
msgid "%{n} labels found"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/people/faces.vue:366
|
||||
#: src/pages/people/faces.vue:267
|
||||
#: src/pages/people/faces.vue:353
|
||||
#: src/pages/people/subjects.vue:392
|
||||
msgid "%{n} people found"
|
||||
msgstr ""
|
||||
|
@ -161,7 +162,7 @@ msgstr ""
|
|||
msgid "After two weeks"
|
||||
msgstr ""
|
||||
|
||||
#: src/model/album.js:189
|
||||
#: src/model/album.js:179
|
||||
msgid "Album"
|
||||
msgstr ""
|
||||
|
||||
|
@ -198,7 +199,6 @@ msgstr ""
|
|||
msgid "All %{n} labels loaded"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/people/faces.vue:269
|
||||
#: src/pages/people/subjects.vue:295
|
||||
msgid "All %{n} people loaded"
|
||||
msgstr ""
|
||||
|
@ -329,8 +329,8 @@ msgstr ""
|
|||
msgid "Artist"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialog/photo/files.vue:122
|
||||
#: src/dialog/photo/files.vue:119
|
||||
#: src/dialog/photo/files.vue:128
|
||||
#: src/dialog/photo/files.vue:125
|
||||
msgid "Aspect Ratio"
|
||||
msgstr ""
|
||||
|
||||
|
@ -420,8 +420,8 @@ msgstr ""
|
|||
#: src/pages/labels.vue:198
|
||||
#: src/pages/library/files.vue:193
|
||||
#: src/pages/library/files.vue:209
|
||||
#: src/pages/people/faces.vue:203
|
||||
#: src/pages/people/faces.vue:219
|
||||
#: src/pages/people/faces.vue:196
|
||||
#: src/pages/people/faces.vue:212
|
||||
#: src/pages/people/subjects.vue:229
|
||||
#: src/pages/people/subjects.vue:245
|
||||
#: src/share/albums.vue:345
|
||||
|
@ -482,8 +482,8 @@ msgstr ""
|
|||
msgid "Chinese Traditional"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialog/photo/files.vue:142
|
||||
#: src/dialog/photo/files.vue:139
|
||||
#: src/dialog/photo/files.vue:148
|
||||
#: src/dialog/photo/files.vue:145
|
||||
msgid "Chroma"
|
||||
msgstr ""
|
||||
|
||||
|
@ -494,8 +494,8 @@ msgstr ""
|
|||
msgid "Close"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialog/photo/files.vue:94
|
||||
#: src/dialog/photo/files.vue:91
|
||||
#: src/dialog/photo/files.vue:100
|
||||
#: src/dialog/photo/files.vue:97
|
||||
msgid "Codec"
|
||||
msgstr ""
|
||||
|
||||
|
@ -894,8 +894,8 @@ msgstr ""
|
|||
msgid "English"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialog/photo/files.vue:148
|
||||
#: src/dialog/photo/files.vue:145
|
||||
#: src/dialog/photo/files.vue:52
|
||||
#: src/dialog/photo/files.vue:49
|
||||
msgid "Error"
|
||||
msgstr ""
|
||||
|
||||
|
@ -944,7 +944,7 @@ msgstr ""
|
|||
msgid "F Number"
|
||||
msgstr ""
|
||||
|
||||
#: src/model/face.js:115
|
||||
#: src/model/face.js:153
|
||||
msgid "Face"
|
||||
msgstr ""
|
||||
|
||||
|
@ -992,7 +992,7 @@ msgstr ""
|
|||
msgid "Feedback"
|
||||
msgstr ""
|
||||
|
||||
#: src/model/file.js:245
|
||||
#: src/model/file.js:244
|
||||
msgid "File"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1076,8 +1076,8 @@ msgstr ""
|
|||
msgid "Group by similarity"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialog/photo/files.vue:58
|
||||
#: src/dialog/photo/files.vue:55
|
||||
#: src/dialog/photo/files.vue:64
|
||||
#: src/dialog/photo/files.vue:61
|
||||
msgid "Hash"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1097,7 +1097,7 @@ msgstr ""
|
|||
msgid "Hidden Files"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/people/faces.vue:192
|
||||
#: src/pages/people/faces.vue:186
|
||||
msgid "Hide"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1184,8 +1184,8 @@ msgstr ""
|
|||
msgid "Indexing media and sidecar files…"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialog/photo/files.vue:52
|
||||
#: src/dialog/photo/files.vue:49
|
||||
#: src/dialog/photo/files.vue:58
|
||||
#: src/dialog/photo/files.vue:55
|
||||
msgid "Instance ID"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1381,8 +1381,8 @@ msgstr ""
|
|||
msgid "Magenta"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialog/photo/files.vue:136
|
||||
#: src/dialog/photo/files.vue:133
|
||||
#: src/dialog/photo/files.vue:142
|
||||
#: src/dialog/photo/files.vue:139
|
||||
msgid "Main Color"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1445,10 +1445,6 @@ msgstr ""
|
|||
msgid "More than 20 albums found"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/people/faces.vue:369
|
||||
msgid "More than 20 faces found"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/labels.vue:342
|
||||
msgid "More than 20 labels found"
|
||||
msgstr ""
|
||||
|
@ -1486,8 +1482,8 @@ msgstr ""
|
|||
#: src/component/photo/list.vue:237
|
||||
#: src/dialog/account/edit.vue:397
|
||||
#: src/dialog/album/edit.vue:106
|
||||
#: src/dialog/photo/files.vue:70
|
||||
#: src/dialog/photo/files.vue:67
|
||||
#: src/dialog/photo/files.vue:76
|
||||
#: src/dialog/photo/files.vue:73
|
||||
#: src/dialog/photo/files.vue:30
|
||||
#: src/dialog/photo/info.vue:31
|
||||
#: src/dialog/photo/labels.vue:48
|
||||
|
@ -1496,8 +1492,8 @@ msgstr ""
|
|||
#: src/pages/about/feedback.vue:144
|
||||
#: src/pages/labels.vue:337
|
||||
#: src/pages/login.vue:73
|
||||
#: src/pages/people/faces.vue:47
|
||||
#: src/pages/people/faces.vue:310
|
||||
#: src/pages/people/faces.vue:48
|
||||
#: src/pages/people/faces.vue:304
|
||||
#: src/pages/people/subjects.vue:315
|
||||
#: src/share/photo/cards.vue:30
|
||||
#: src/share/photo/list.vue:34
|
||||
|
@ -1511,7 +1507,7 @@ msgstr ""
|
|||
#: src/dialog/photo/people.vue:21
|
||||
#: src/pages/labels.vue:38
|
||||
#: src/pages/library/files.vue:40
|
||||
#: src/pages/people/faces.vue:41
|
||||
#: src/pages/people/faces.vue:42
|
||||
#: src/pages/people/subjects.vue:40
|
||||
msgid "Name too long"
|
||||
msgstr ""
|
||||
|
@ -1566,7 +1562,8 @@ msgstr ""
|
|||
|
||||
#: src/dialog/photo/people.vue:5
|
||||
#: src/pages/people/faces.vue:23
|
||||
#: src/pages/people/faces.vue:362
|
||||
#: src/pages/people/faces.vue:263
|
||||
#: src/pages/people/faces.vue:349
|
||||
#: src/pages/people/subjects.vue:26
|
||||
#: src/pages/people/subjects.vue:388
|
||||
msgid "No people found"
|
||||
|
@ -1680,7 +1677,8 @@ msgstr ""
|
|||
msgid "One label found"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/people/faces.vue:364
|
||||
#: src/pages/people/faces.vue:265
|
||||
#: src/pages/people/faces.vue:351
|
||||
#: src/pages/people/subjects.vue:390
|
||||
msgid "One person found"
|
||||
msgstr ""
|
||||
|
@ -1701,8 +1699,8 @@ msgstr ""
|
|||
msgid "Orange"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialog/photo/files.vue:128
|
||||
#: src/dialog/photo/files.vue:125
|
||||
#: src/dialog/photo/files.vue:134
|
||||
#: src/dialog/photo/files.vue:131
|
||||
msgid "Orientation"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1714,8 +1712,8 @@ msgstr ""
|
|||
msgid "Original file names will be stored and indexed."
|
||||
msgstr ""
|
||||
|
||||
#: src/dialog/photo/files.vue:76
|
||||
#: src/dialog/photo/files.vue:73
|
||||
#: src/dialog/photo/files.vue:82
|
||||
#: src/dialog/photo/files.vue:79
|
||||
#: src/dialog/photo/info.vue:37
|
||||
msgid "Original Name"
|
||||
msgstr ""
|
||||
|
@ -1848,8 +1846,8 @@ msgstr ""
|
|||
msgid "Polish"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialog/photo/files.vue:108
|
||||
#: src/dialog/photo/files.vue:105
|
||||
#: src/dialog/photo/files.vue:114
|
||||
#: src/dialog/photo/files.vue:111
|
||||
msgid "Portrait"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1880,9 +1878,9 @@ msgid "Preview"
|
|||
msgstr ""
|
||||
|
||||
#: src/dialog/photo/files.vue:34
|
||||
#: src/dialog/photo/files.vue:100
|
||||
#: src/dialog/photo/files.vue:106
|
||||
#: src/dialog/photo/files.vue:31
|
||||
#: src/dialog/photo/files.vue:97
|
||||
#: src/dialog/photo/files.vue:103
|
||||
#: src/dialog/photo/files.vue:24
|
||||
msgid "Primary"
|
||||
msgstr ""
|
||||
|
@ -1899,8 +1897,8 @@ msgstr ""
|
|||
msgid "Product Feedback"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialog/photo/files.vue:116
|
||||
#: src/dialog/photo/files.vue:113
|
||||
#: src/dialog/photo/files.vue:122
|
||||
#: src/dialog/photo/files.vue:119
|
||||
msgid "Projection"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1984,7 +1982,7 @@ msgstr ""
|
|||
#: src/pages/labels.vue:91
|
||||
#: src/pages/library/errors.vue:69
|
||||
#: src/pages/library/files.vue:100
|
||||
#: src/pages/people/faces.vue:67
|
||||
#: src/pages/people/faces.vue:63
|
||||
#: src/pages/people/subjects.vue:98
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
@ -2203,7 +2201,7 @@ msgstr ""
|
|||
msgid "Shows more detailed log messages. Requires a restart."
|
||||
msgstr ""
|
||||
|
||||
#: src/model/file.js:186
|
||||
#: src/model/file.js:185
|
||||
msgid "Sidecar"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2220,8 +2218,8 @@ msgid "Similar"
|
|||
msgstr ""
|
||||
|
||||
#: src/dialog/account/edit.vue:220
|
||||
#: src/dialog/photo/files.vue:82
|
||||
#: src/dialog/photo/files.vue:79
|
||||
#: src/dialog/photo/files.vue:88
|
||||
#: src/dialog/photo/files.vue:85
|
||||
#: src/dialog/photo/files.vue:32
|
||||
msgid "Size"
|
||||
msgstr ""
|
||||
|
@ -2301,8 +2299,8 @@ msgstr ""
|
|||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialog/photo/files.vue:64
|
||||
#: src/dialog/photo/files.vue:61
|
||||
#: src/dialog/photo/files.vue:70
|
||||
#: src/dialog/photo/files.vue:67
|
||||
msgid "Storage Folder"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2315,7 +2313,7 @@ msgid "Style"
|
|||
msgstr ""
|
||||
|
||||
#: src/dialog/photo/details.vue:482
|
||||
#: src/model/subject.js:135
|
||||
#: src/model/subject.js:136
|
||||
msgid "Subject"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2345,7 +2343,7 @@ msgid "Teal"
|
|||
msgstr ""
|
||||
|
||||
#: src/dialog/photo/details.vue:26
|
||||
#: src/pages/people/faces.vue:50
|
||||
#: src/pages/people/faces.vue:51
|
||||
msgid "Text too long"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2436,15 +2434,15 @@ msgid "Try again using other filters or keywords."
|
|||
msgstr ""
|
||||
|
||||
#: src/dialog/account/edit.vue:490
|
||||
#: src/dialog/photo/files.vue:88
|
||||
#: src/dialog/photo/files.vue:85
|
||||
#: src/dialog/photo/files.vue:94
|
||||
#: src/dialog/photo/files.vue:91
|
||||
#: src/dialog/photo/files.vue:33
|
||||
#: src/dialog/photo/info.vue:15
|
||||
msgid "Type"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialog/photo/people.vue:151
|
||||
#: src/pages/people/faces.vue:235
|
||||
#: src/pages/people/faces.vue:229
|
||||
msgid "Undo"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2454,7 +2452,7 @@ msgstr ""
|
|||
|
||||
#: src/dialog/photo/details.vue:16
|
||||
#: src/dialog/photo/info.vue:21
|
||||
#: src/model/album.js:146
|
||||
#: src/model/album.js:136
|
||||
#: src/model/photo.js:527
|
||||
#: src/model/photo.js:544
|
||||
#: src/model/photo.js:567
|
||||
|
@ -2581,7 +2579,7 @@ msgstr ""
|
|||
#: src/component/photo/cards.vue:225
|
||||
#: src/component/photo/list.vue:198
|
||||
#: src/component/photo/mosaic.vue:200
|
||||
#: src/model/file.js:184
|
||||
#: src/model/file.js:183
|
||||
#: src/model/photo.js:618
|
||||
#: src/model/photo.js:632
|
||||
#: src/options/options.js:300
|
||||
|
@ -2650,11 +2648,11 @@ msgstr ""
|
|||
|
||||
#: src/dialog/people/merge.vue:18
|
||||
#: src/dialog/photo/archive.vue:18
|
||||
#: src/dialog/photo/files.vue:103
|
||||
#: src/dialog/photo/files.vue:111
|
||||
#: src/dialog/photo/files.vue:109
|
||||
#: src/dialog/photo/files.vue:117
|
||||
#: src/dialog/photo/files.vue:157
|
||||
#: src/dialog/photo/files.vue:100
|
||||
#: src/dialog/photo/files.vue:108
|
||||
#: src/dialog/photo/files.vue:106
|
||||
#: src/dialog/photo/files.vue:114
|
||||
#: src/dialog/photo/files.vue:154
|
||||
#: src/dialog/photo/info.vue:284
|
||||
#: src/dialog/photo/info.vue:305
|
||||
|
|
|
@ -33,13 +33,11 @@ import RestModel from "model/rest";
|
|||
import { DateTime } from "luxon";
|
||||
import { config } from "../session";
|
||||
import { $gettext } from "common/vm";
|
||||
import * as src from "../common/src";
|
||||
import Api from "../common/api";
|
||||
|
||||
export class Face extends RestModel {
|
||||
constructor(values) {
|
||||
if (values && values.Marker) {
|
||||
values.Marker = new Marker(values.Marker);
|
||||
}
|
||||
|
||||
super(values);
|
||||
}
|
||||
|
||||
|
@ -48,15 +46,24 @@ export class Face extends RestModel {
|
|||
ID: "",
|
||||
Src: "",
|
||||
SubjUID: "",
|
||||
SubjSrc: "",
|
||||
FileUID: "",
|
||||
MarkerUID: "",
|
||||
Samples: 0,
|
||||
SampleRadius: 0.0,
|
||||
Collisions: 0,
|
||||
CollisionRadius: 0.0,
|
||||
Marker: new Marker(),
|
||||
Hidden: false,
|
||||
MatchedAt: "",
|
||||
CreatedAt: "",
|
||||
UpdatedAt: "",
|
||||
Name: "",
|
||||
FaceDist: 0.0,
|
||||
Size: 0,
|
||||
Score: 0,
|
||||
Review: false,
|
||||
Invalid: false,
|
||||
Thumb: "",
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -82,11 +89,15 @@ export class Face extends RestModel {
|
|||
}
|
||||
|
||||
thumbnailUrl(size) {
|
||||
if (!this.Marker) {
|
||||
return `${config.contentUri}/svg/portrait`;
|
||||
if (!size) {
|
||||
size = "tile_160";
|
||||
}
|
||||
|
||||
return this.Marker.thumbnailUrl(size);
|
||||
if (this.Thumb) {
|
||||
return `${config.contentUri}/t/${this.Thumb}/${config.previewToken()}/${size}`;
|
||||
} else {
|
||||
return `${config.contentUri}/svg/portrait`;
|
||||
}
|
||||
}
|
||||
|
||||
getDateString() {
|
||||
|
@ -103,6 +114,33 @@ export class Face extends RestModel {
|
|||
return this.update();
|
||||
}
|
||||
|
||||
setName() {
|
||||
if (!this.Name || this.Name.trim() === "") {
|
||||
// Can't save an empty name.
|
||||
return Promise.resolve(this);
|
||||
}
|
||||
|
||||
this.SubjSrc = src.Manual;
|
||||
|
||||
const payload = { SubjSrc: this.SubjSrc, Name: this.Name };
|
||||
|
||||
return Api.put(Marker.getCollectionResource() + "/" + this.MarkerUID, payload).then((resp) => {
|
||||
if (resp && resp.data && resp.data.Name) {
|
||||
const data = resp.data;
|
||||
this.setValues({
|
||||
Name: data.Name,
|
||||
SubjSrc: data.SubjSrc,
|
||||
SubjUID: data.SubjUID,
|
||||
Review: data.Review,
|
||||
Invalid: data.Invalid,
|
||||
Thumb: data.Thumb,
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.resolve(this);
|
||||
});
|
||||
}
|
||||
|
||||
static batchSize() {
|
||||
return 24;
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
<v-layout row wrap class="search-results album-results cards-view" :class="{'select-results': selection.length > 0}">
|
||||
<v-flex
|
||||
v-for="(album, index) in results"
|
||||
:key="index"
|
||||
:key="album.UID"
|
||||
xs6 sm4 md3 xlg2 xxl1 d-flex
|
||||
>
|
||||
<v-card tile
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
<v-layout row wrap class="search-results label-results cards-view" :class="{'select-results': selection.length > 0}">
|
||||
<v-flex
|
||||
v-for="(label, index) in results"
|
||||
:key="index"
|
||||
:key="label.UID"
|
||||
xs6 sm4 md3 lg2 xxl1 d-flex
|
||||
>
|
||||
<v-card tile
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
</v-container>
|
||||
<v-list v-else-if="errors.length > 0" dense two-line class="transparent">
|
||||
<v-list-tile
|
||||
v-for="(err, index) in errors" :key="index"
|
||||
v-for="err in errors" :key="err.ID"
|
||||
avatar
|
||||
@click="showDetails(err)"
|
||||
>
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
<v-layout row wrap class="search-results file-results cards-view" :class="{'select-results': selection.length > 0}">
|
||||
<v-flex
|
||||
v-for="(model, index) in results"
|
||||
:key="index"
|
||||
:key="model.UID"
|
||||
xs6 sm4 md3 lg2 xxl1 d-flex
|
||||
>
|
||||
<v-card tile
|
||||
|
|
|
@ -12,13 +12,19 @@
|
|||
ripple @click="changePath(item.path)">
|
||||
<v-icon v-if="$vuetify.breakpoint.smAndDown" :title="item.label">{{ item.icon }}</v-icon>
|
||||
<template v-else>
|
||||
<v-icon :size="18" :left="!rtl" :right="rtl">{{ item.icon }}</v-icon> {{ item.label }}
|
||||
<v-icon :size="18" :left="!rtl" :right="rtl">{{ item.icon }}</v-icon>
|
||||
<v-badge color="secondary-dark" :left="rtl" :right="!rtl">
|
||||
<template #badge>
|
||||
<span v-if="item.count">{{ item.count }}</span>
|
||||
</template>
|
||||
{{ item.label }}
|
||||
</v-badge>
|
||||
</template>
|
||||
</v-tab>
|
||||
|
||||
<v-tabs-items touchless>
|
||||
<v-tab-item v-for="(item, index) in tabs" :key="index" lazy>
|
||||
<component :is="item.component" :static-filter="item.filter" :active="active === index"></component>
|
||||
<component :is="item.component" :static-filter="item.filter" :active="active === index" @updateFaceCount="onUpdateFaceCount"></component>
|
||||
</v-tab-item>
|
||||
</v-tabs-items>
|
||||
</v-tabs>
|
||||
|
@ -59,6 +65,7 @@ export default {
|
|||
'class': '',
|
||||
'path': '/people/new',
|
||||
'icon': 'person_add',
|
||||
'count': 0,
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -79,7 +86,10 @@ export default {
|
|||
};
|
||||
},
|
||||
methods: {
|
||||
changePath: function (path) {
|
||||
onUpdateFaceCount(count) {
|
||||
this.tabs[1].count = count;
|
||||
},
|
||||
changePath (path) {
|
||||
if (this.$route.path !== path) {
|
||||
this.$router.replace(path);
|
||||
}
|
||||
|
|
|
@ -37,18 +37,17 @@
|
|||
:key="model.ID"
|
||||
xs12 sm6 md4 lg3 xl2 xxl1 d-flex
|
||||
>
|
||||
<v-card v-if="model.Marker"
|
||||
:data-id="model.ID"
|
||||
<v-card :data-id="model.ID"
|
||||
tile style="user-select: none;"
|
||||
:class="model.classes()"
|
||||
class="result accent lighten-3">
|
||||
<div class="card-background accent lighten-3"></div>
|
||||
<v-img :src="model.Marker.thumbnailUrl('tile_320')"
|
||||
<v-img :src="model.thumbnailUrl('tile_320')"
|
||||
:transition="false"
|
||||
aspect-ratio="1"
|
||||
class="accent lighten-2 clickable"
|
||||
@click.stop.prevent="$router.push(model.route(view))">
|
||||
<v-btn v-if="!model.Marker.SubjUID && !model.Hidden" :ripple="false" :depressed="false" class="input-hide"
|
||||
<v-btn v-if="!model.SubjUID && !model.Hidden" :ripple="false" :depressed="false" class="input-hide"
|
||||
icon flat small absolute :title="$gettext('Hide')"
|
||||
@click.stop.prevent="onHide(model)">
|
||||
<v-icon color="white" class="action-hide">clear</v-icon>
|
||||
|
@ -66,10 +65,10 @@
|
|||
</v-btn>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
<v-layout v-else-if="model.Marker.SubjUID" row wrap align-center>
|
||||
<v-layout v-else-if="model.SubjUID" row wrap align-center>
|
||||
<v-flex xs12 class="text-xs-left pa-0">
|
||||
<v-text-field
|
||||
v-model="model.Marker.Name"
|
||||
v-model="model.Name"
|
||||
:rules="[textRule]"
|
||||
:disabled="busy"
|
||||
:readonly="false"
|
||||
|
@ -78,15 +77,15 @@
|
|||
hide-details
|
||||
single-line
|
||||
solo-inverted
|
||||
@change="onRename(model.Marker)"
|
||||
@keyup.enter.native="onRename(model.Marker)"
|
||||
@change="onRename(model)"
|
||||
@keyup.enter.native="onRename(model)"
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
<v-layout v-else row wrap align-center>
|
||||
<v-flex xs12 class="text-xs-left pa-0">
|
||||
<v-combobox
|
||||
v-model="model.Marker.Name"
|
||||
v-model="model.Name"
|
||||
style="z-index: 250"
|
||||
:items="$config.values.people"
|
||||
item-value="Name"
|
||||
|
@ -104,8 +103,8 @@
|
|||
prepend-inner-icon="person_add"
|
||||
browser-autocomplete="off"
|
||||
class="input-name pa-0 ma-0"
|
||||
@change="onRename(model.Marker)"
|
||||
@keyup.enter.native="onRename(model.Marker)"
|
||||
@change="onRename(model)"
|
||||
@keyup.enter.native="onRename(model)"
|
||||
>
|
||||
</v-combobox>
|
||||
</v-flex>
|
||||
|
@ -163,6 +162,7 @@ export default {
|
|||
batchSize: 999,
|
||||
offset: 0,
|
||||
page: 0,
|
||||
faceCount: 0,
|
||||
selection: [],
|
||||
settings: settings,
|
||||
filter: filter,
|
||||
|
@ -386,6 +386,8 @@ export default {
|
|||
Face.search(params).then(resp => {
|
||||
this.results = this.dirty ? resp.models : this.results.concat(resp.models);
|
||||
|
||||
this.setFaceCount(this.results.length);
|
||||
|
||||
if (!this.results.length) {
|
||||
this.$notify.warn(this.$gettext("No people found"));
|
||||
} else if (this.results.length === 1) {
|
||||
|
@ -443,7 +445,7 @@ export default {
|
|||
|
||||
this.loading = true;
|
||||
this.page = 0;
|
||||
// this.dirty = true;
|
||||
this.dirty = true;
|
||||
this.scrollDisabled = false;
|
||||
|
||||
this.loadMore();
|
||||
|
@ -470,6 +472,8 @@ export default {
|
|||
this.offset = resp.limit;
|
||||
this.results = resp.models;
|
||||
|
||||
this.setFaceCount(this.results.length);
|
||||
|
||||
if (!this.results.length) {
|
||||
this.$notify.warn(this.$gettext("No people found"));
|
||||
} else if (this.results.length === 1) {
|
||||
|
@ -485,32 +489,36 @@ export default {
|
|||
},
|
||||
onShow(face) {
|
||||
this.busy = true;
|
||||
// this.dirty = true;
|
||||
face.show().finally(() => this.busy = false);
|
||||
face.show().finally(() => {
|
||||
this.busy = false;
|
||||
this.changeFaceCount(1);
|
||||
});
|
||||
},
|
||||
onHide(face) {
|
||||
this.busy = true;
|
||||
// this.dirty = true;
|
||||
face.hide().finally(() => this.busy = false);
|
||||
},
|
||||
onClearSubject(marker) {
|
||||
this.busy = true;
|
||||
// this.dirty = true;
|
||||
this.$notify.blockUI();
|
||||
marker.clearSubject(marker).finally(() => {
|
||||
this.$notify.unblockUI();
|
||||
face.hide().finally(() => {
|
||||
this.busy = false;
|
||||
this.changeFaceCount(-1);
|
||||
});
|
||||
},
|
||||
onRename(marker) {
|
||||
onRename(model) {
|
||||
this.busy = true;
|
||||
// this.dirty = true;
|
||||
this.$notify.blockUI();
|
||||
marker.rename().finally(() => {
|
||||
|
||||
model.setName().finally(() => {
|
||||
this.$notify.unblockUI();
|
||||
this.busy = false;
|
||||
this.changeFaceCount(-1);
|
||||
});
|
||||
},
|
||||
changeFaceCount(count) {
|
||||
this.faceCount = this.faceCount + count;
|
||||
this.$emit('updateFaceCount', this.faceCount);
|
||||
},
|
||||
setFaceCount(count) {
|
||||
this.faceCount = count;
|
||||
this.$emit('updateFaceCount', this.faceCount);
|
||||
},
|
||||
onUpdate(ev, data) {
|
||||
if (!this.listen) return;
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
<v-layout row wrap class="search-results subject-results cards-view" :class="{'select-results': selection.length > 0}">
|
||||
<v-flex
|
||||
v-for="(model, index) in results"
|
||||
:key="index"
|
||||
:key="model.UID"
|
||||
xs6 sm4 md3 lg2 xxl1 d-flex
|
||||
>
|
||||
<v-card tile
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
<v-layout row wrap class="search-results album-results cards-view" :class="{'select-results': selection.length > 0}">
|
||||
<v-flex
|
||||
v-for="(album, index) in results"
|
||||
:key="index"
|
||||
:key="album.UID"
|
||||
xs6 sm4 md3 xlg2 xxl1 d-flex
|
||||
>
|
||||
<v-card tile
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<v-layout row wrap class="search-results photo-results cards-view" :class="{'select-results': selectMode}">
|
||||
<v-flex
|
||||
v-for="(photo, index) in photos"
|
||||
:key="index"
|
||||
:key="photo.ID"
|
||||
xs12 sm6 md4 lg3 xlg2 xxxl1 d-flex
|
||||
>
|
||||
<v-card tile
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<v-layout row wrap class="search-results photo-results mosaic-view" :class="{'select-results': selectMode}">
|
||||
<v-flex
|
||||
v-for="(photo, index) in photos"
|
||||
:key="index"
|
||||
:key="photo.ID"
|
||||
xs4 sm3 md2 lg1 d-flex
|
||||
>
|
||||
<v-card tile
|
||||
|
|
|
@ -61,22 +61,22 @@ describe("model/face", () => {
|
|||
const values = {
|
||||
ID: "f123ghytrfggd",
|
||||
Samples: 5,
|
||||
Marker: {
|
||||
UID: "ABC123ghytr",
|
||||
FileUID: "fhjouohnnmnd",
|
||||
Type: "face",
|
||||
Src: "image",
|
||||
Thumb: "nicethumbuid",
|
||||
},
|
||||
MarkerUID: "ABC123ghytr",
|
||||
FileUID: "fhjouohnnmnd",
|
||||
Name: "",
|
||||
Thumb: "7ca759a2b788cc5bcc08dbbce9854ff94a2f94d1",
|
||||
};
|
||||
|
||||
const face = new Face(values);
|
||||
const result = face.thumbnailUrl("xyz");
|
||||
assert.equal(result, "/api/v1/t/nicethumbuid/public/xyz");
|
||||
|
||||
const values2 = { ID: "f123ghytrfggd", Samples: 5 };
|
||||
assert.equal(result, "/api/v1/t/7ca759a2b788cc5bcc08dbbce9854ff94a2f94d1/public/xyz");
|
||||
|
||||
const values2 = { ID: "f123ghytrfggd", Samples: 5, Thumb: "7ca759a2b788cc5bcc08dbbce9854ff94a2f94d1" };
|
||||
const face2 = new Face(values2);
|
||||
const result2 = face2.thumbnailUrl();
|
||||
assert.equal(result2, "/api/v1/svg/portrait");
|
||||
|
||||
assert.equal(result2, "/api/v1/t/7ca759a2b788cc5bcc08dbbce9854ff94a2f94d1/public/tile_160");
|
||||
});
|
||||
|
||||
it("should get date string", () => {
|
||||
|
|
|
@ -119,6 +119,15 @@ func (m *Subject) Delete() error {
|
|||
return Db().Delete(m).Error
|
||||
}
|
||||
|
||||
// AfterDelete resets file and photo counters when the entity was deleted.
|
||||
func (m *Subject) AfterDelete(tx *gorm.DB) (err error) {
|
||||
tx.Model(m).Updates(Values{
|
||||
"FileCount": 0,
|
||||
"PhotoCount": 0,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Deleted returns true if the entity is deleted.
|
||||
func (m *Subject) Deleted() bool {
|
||||
return m.DeletedAt != nil
|
||||
|
@ -327,6 +336,14 @@ func (m *Subject) MergeWith(other *Subject) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Update file and photo counts.
|
||||
if err := Db().Model(other).Updates(Values{
|
||||
"FileCount": other.FileCount + m.FileCount,
|
||||
"PhotoCount": other.PhotoCount + m.PhotoCount,
|
||||
}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return m.Delete()
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,6 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
|
@ -17,8 +15,42 @@ func Faces(f form.FaceSearch) (results FaceResults, err error) {
|
|||
return results, err
|
||||
}
|
||||
|
||||
facesTable := entity.Face{}.TableName()
|
||||
|
||||
// Base query.
|
||||
s := UnscopedDb().Table(entity.Face{}.TableName())
|
||||
s := UnscopedDb().Table(facesTable)
|
||||
|
||||
if f.Markers {
|
||||
s = s.Select(fmt.Sprintf(`%s.*, m.marker_uid, m.file_uid, m.marker_name, m.subj_src, m.marker_src,
|
||||
m.marker_type, m.marker_review, m.marker_invalid, m.size, m.score, m.thumb, m.face_dist`, facesTable))
|
||||
|
||||
if txt.Yes(f.Unknown) {
|
||||
s = s.Joins(`JOIN (
|
||||
SELECT face_id, MIN(marker_uid) AS marker_uid FROM markers
|
||||
WHERE face_id <> '' AND subj_uid = '' AND marker_name = '' AND marker_type = 'face' AND marker_src = 'image'
|
||||
AND marker_invalid = 0 AND face_dist <= 0.64 AND size >= 80 AND score >= 15
|
||||
GROUP BY face_id) fm
|
||||
ON faces.id = fm.face_id`)
|
||||
} else if txt.No(f.Unknown) {
|
||||
s = s.Joins(`JOIN (
|
||||
SELECT face_id, MIN(marker_uid) AS marker_uid FROM markers
|
||||
WHERE face_id <> '' AND subj_uid <> '' AND marker_name <> '' AND marker_type = 'face' AND marker_src = 'image'
|
||||
AND marker_invalid = 0 AND face_dist <= 0.64 AND size >= 80 AND score >= 15
|
||||
GROUP BY face_id) fm
|
||||
ON faces.id = fm.face_id`)
|
||||
} else {
|
||||
s = s.Joins(`JOIN (
|
||||
SELECT face_id, MIN(marker_uid) AS marker_uid FROM markers
|
||||
WHERE face_id <> '' AND marker_type = 'face' AND marker_src = 'image'
|
||||
AND marker_invalid = 0 AND face_dist <= 0.64 AND size >= 80 AND score >= 15
|
||||
GROUP BY face_id) fm
|
||||
ON faces.id = fm.face_id`)
|
||||
}
|
||||
|
||||
s = s.Joins("JOIN markers m ON m.marker_uid = fm.marker_uid")
|
||||
} else {
|
||||
s = s.Select(fmt.Sprintf(`%s.*`, facesTable))
|
||||
}
|
||||
|
||||
// Limit result count.
|
||||
if f.Count > 0 && f.Count <= MaxResults {
|
||||
|
@ -30,46 +62,21 @@ func Faces(f form.FaceSearch) (results FaceResults, err error) {
|
|||
// Set sort order.
|
||||
switch f.Order {
|
||||
case "subject":
|
||||
s = s.Order("subj_uid")
|
||||
s = s.Order(fmt.Sprintf("%s.subj_uid", facesTable))
|
||||
case "added":
|
||||
s = s.Order(fmt.Sprintf("%s.created_at DESC", entity.Face{}.TableName()))
|
||||
s = s.Order(fmt.Sprintf("%s.created_at DESC", facesTable))
|
||||
case "samples":
|
||||
s = s.Order("samples DESC")
|
||||
s = s.Order(fmt.Sprintf("%s.samples DESC, %s.id", facesTable, facesTable))
|
||||
default:
|
||||
s = s.Order("samples DESC")
|
||||
}
|
||||
|
||||
// Make sure at least one marker exists.
|
||||
if f.Markers || txt.Yes(f.Unknown) {
|
||||
s = s.Where("id IN (SELECT face_id FROM ? WHERE "+
|
||||
"face_id IS NOT NULL AND face_id <> '' AND marker_type = ? AND marker_src = ? AND marker_invalid = 0)",
|
||||
gorm.Expr(entity.Marker{}.TableName()), entity.MarkerFace, entity.SrcImage)
|
||||
}
|
||||
|
||||
// Adds markers to search results if requested.
|
||||
addMarkers := func(results FaceResults) FaceResults {
|
||||
r := make(FaceResults, 0, len(results))
|
||||
|
||||
// Add markers to results.
|
||||
for i := range results {
|
||||
if marker := entity.FindFaceMarker(results[i].ID); marker != nil {
|
||||
m := results[i]
|
||||
m.Marker = marker
|
||||
r = append(r, m)
|
||||
}
|
||||
}
|
||||
|
||||
return r
|
||||
s = s.Order(fmt.Sprintf("%s.samples DESC, %s.id", facesTable, facesTable))
|
||||
}
|
||||
|
||||
// Find specific IDs?
|
||||
if f.ID != "" {
|
||||
s = s.Where(fmt.Sprintf("%s.id IN (?)", entity.Face{}.TableName()), strings.Split(strings.ToUpper(f.ID), txt.Or))
|
||||
s = s.Where(fmt.Sprintf("%s.id IN (?)", facesTable), strings.Split(strings.ToUpper(f.ID), txt.Or))
|
||||
|
||||
if result := s.Scan(&results); result.Error != nil {
|
||||
return results, result.Error
|
||||
} else if f.Markers {
|
||||
return addMarkers(results), nil
|
||||
}
|
||||
|
||||
return results, nil
|
||||
|
@ -77,23 +84,21 @@ func Faces(f form.FaceSearch) (results FaceResults, err error) {
|
|||
|
||||
// Exclude unknown faces?
|
||||
if txt.Yes(f.Unknown) {
|
||||
s = s.Where("subj_uid = '' OR subj_uid IS NULL")
|
||||
s = s.Where(fmt.Sprintf("%s.subj_uid = '' OR %s.subj_uid IS NULL", facesTable, facesTable))
|
||||
} else if txt.No(f.Unknown) {
|
||||
s = s.Where("subj_uid <> '' AND subj_uid IS NOT NULL")
|
||||
s = s.Where(fmt.Sprintf("%s.subj_uid <> '' AND %s.subj_uid IS NOT NULL", facesTable, facesTable))
|
||||
}
|
||||
|
||||
// Exclude hidden faces?
|
||||
if f.Hidden == "" || txt.No(f.Hidden) {
|
||||
s = s.Where("face_hidden = 0 OR face_hidden IS NULL")
|
||||
s = s.Where(fmt.Sprintf("%s.face_hidden = 0 OR %s.face_hidden IS NULL", facesTable, facesTable))
|
||||
} else if txt.Yes(f.Hidden) {
|
||||
s = s.Where("face_hidden = 1")
|
||||
s = s.Where(fmt.Sprintf("%s.face_hidden = 1", facesTable))
|
||||
}
|
||||
|
||||
// Perform query.
|
||||
if res := s.Scan(&results); res.Error != nil {
|
||||
return results, res.Error
|
||||
} else if f.Markers {
|
||||
return addMarkers(results), nil
|
||||
}
|
||||
|
||||
return results, nil
|
||||
|
|
|
@ -2,24 +2,31 @@ package search
|
|||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
)
|
||||
|
||||
// Face represents a face search result.
|
||||
type Face struct {
|
||||
ID string `json:"ID"`
|
||||
FaceSrc string `json:"Src"`
|
||||
FaceHidden bool `json:"Hidden"`
|
||||
SubjUID string `json:"SubjUID"`
|
||||
Samples int `json:"Samples"`
|
||||
SampleRadius float64 `json:"SampleRadius"`
|
||||
Collisions int `json:"Collisions"`
|
||||
CollisionRadius float64 `json:"CollisionRadius"`
|
||||
Marker *entity.Marker `json:"Marker,omitempty"`
|
||||
MatchedAt *time.Time `json:"MatchedAt" yaml:"MatchedAt,omitempty"`
|
||||
CreatedAt time.Time `json:"CreatedAt" yaml:"CreatedAt,omitempty"`
|
||||
UpdatedAt time.Time `json:"UpdatedAt" yaml:"UpdatedAt,omitempty"`
|
||||
ID string `json:"ID"`
|
||||
FaceSrc string `json:"Src"`
|
||||
FaceHidden bool `json:"Hidden"`
|
||||
FaceDist float64 `json:"FaceDist,omitempty"`
|
||||
SubjUID string `json:"SubjUID"`
|
||||
SubjSrc string `json:"SubjSrc,omitempty"`
|
||||
FileUID string `json:"FileUID,omitempty"`
|
||||
MarkerUID string `json:"MarkerUID,omitempty"`
|
||||
Samples int `json:"Samples"`
|
||||
SampleRadius float64 `json:"SampleRadius"`
|
||||
Collisions int `json:"Collisions"`
|
||||
CollisionRadius float64 `json:"CollisionRadius"`
|
||||
MarkerName string `json:"Name"`
|
||||
Size int `json:"Size,omitempty"`
|
||||
Score int `json:"Score,omitempty"`
|
||||
MarkerReview bool `json:"Review"`
|
||||
MarkerInvalid bool `json:"Invalid"`
|
||||
Thumb string `json:"Thumb"`
|
||||
MatchedAt *time.Time `json:"MatchedAt" yaml:"MatchedAt,omitempty"`
|
||||
CreatedAt time.Time `json:"CreatedAt" yaml:"CreatedAt,omitempty"`
|
||||
UpdatedAt time.Time `json:"UpdatedAt" yaml:"UpdatedAt,omitempty"`
|
||||
}
|
||||
|
||||
// FaceResults represents face search results.
|
||||
|
|
|
@ -15,9 +15,7 @@ func TestFaces(t *testing.T) {
|
|||
t.Logf("Faces: %#v", results)
|
||||
if len(results) == 0 {
|
||||
t.Fatal("results are empty")
|
||||
} else if results[0].Marker == nil {
|
||||
t.Fatal("marker is nil")
|
||||
} else if results[0].Marker.MarkerUID == "" {
|
||||
} else if results[0].MarkerUID == "" {
|
||||
t.Fatal("marker uid is empty")
|
||||
}
|
||||
})
|
||||
|
@ -25,7 +23,7 @@ func TestFaces(t *testing.T) {
|
|||
results, err := Faces(form.FaceSearch{Offset: 3, Order: "subject", Markers: true})
|
||||
assert.NoError(t, err)
|
||||
t.Logf("Faces: %#v", results)
|
||||
assert.LessOrEqual(t, 3, len(results))
|
||||
assert.LessOrEqual(t, 1, len(results))
|
||||
})
|
||||
t.Run("Find specific id", func(t *testing.T) {
|
||||
results, err := Faces(form.FaceSearch{ID: "PN6QO5INYTUSAATOFL43LL2ABAV5ACZK", Markers: true})
|
||||
|
|
Loading…
Reference in a new issue