Stacks: Add advanced indexing options #681 #667 #593

This commit is contained in:
Michael Mayer
2020-12-07 16:19:03 +01:00
parent 335b7ff246
commit ae0af699c6
19 changed files with 369 additions and 170 deletions

Binary file not shown.

View File

@@ -78,7 +78,7 @@ msgstr "Aktionen"
msgid "Add Album"
msgstr "Neues Album erstellen"
#: src/pages/settings/general.vue:269
#: src/pages/settings/general.vue:377
msgid "Add files to your library via Web Upload."
msgstr "Dateien über den Browser hochladen und indizieren."
@@ -230,7 +230,7 @@ msgstr "Höhe (m)"
msgid "An error occurred - are you offline?"
msgstr "Server nicht erreichbar - offline?"
#: src/pages/settings/general.vue:590
#: src/pages/settings/general.vue:698
msgid "Animation"
msgstr "Animation"
@@ -254,7 +254,7 @@ msgid "Approve"
msgstr "Übernehmen"
#: src/component/navigation.vue:114 src/component/photo/cards.vue:20
#: src/component/photo/clipboard.vue:211 src/pages/settings/general.vue:334
#: src/component/photo/clipboard.vue:211 src/pages/settings/general.vue:442
#: src/routes.js:185
msgid "Archive"
msgstr "Archiv"
@@ -295,13 +295,13 @@ msgstr "Seitenverhältnis"
msgid "At least 6 characters."
msgstr "Mindestens 6 Zeichen."
#: src/pages/settings/general.vue:121
#: src/pages/settings/general.vue:123
msgid ""
"Automatically create JPEGs for other file types so that they can be "
"displayed in a browser."
msgstr ""
"RAW-Bilder und Videos müssen zu JPEGs konvertiert werden, damit sie in der "
"Suche angezeigt werden können."
"Videos und andere Bild-Formate nach JPEG konvertieren, damit sie indexiert "
"und angezeigt werden können."
#: src/pages/settings.vue:8
msgid "Backup"
@@ -319,11 +319,11 @@ msgstr "Blau"
msgid "Brown"
msgstr "Braun"
#: src/pages/settings/general.vue:423
#: src/pages/settings/general.vue:531
msgid "Browse and edit image classification labels."
msgstr "Automatische Bild-Kategorisierung sehen und bearbeiten."
#: src/pages/settings/general.vue:379
#: src/pages/settings/general.vue:487
msgid "Browse indexed files and folders in Library."
msgstr "Durchsuche indizierte Dateien und Verzeichnisse."
@@ -384,7 +384,7 @@ msgstr "Kategorie"
msgid "Change"
msgstr "Ändern"
#: src/pages/settings/general.vue:357
#: src/pages/settings/general.vue:465
msgid "Change photo titles, locations and other metadata."
msgstr "Titel, Datum, Ort und andere Metadaten können geändert werden."
@@ -453,7 +453,7 @@ msgstr "Enthält einen Eintrag."
msgid "Contributors"
msgstr "Contributors"
#: src/pages/settings/general.vue:120
#: src/pages/settings/general.vue:122
msgid "Convert to JPEG"
msgstr "Automatisch konvertieren"
@@ -587,7 +587,7 @@ msgstr "Fertig."
#: src/component/album/clipboard.vue:111 src/component/album/toolbar.vue:106
#: src/component/file/clipboard.vue:64 src/component/photo/clipboard.vue:165
#: src/component/photo/viewer.vue:72 src/dialog/photo/files.vue:33
#: src/pages/settings/general.vue:290 src/share/album/clipboard.vue:60
#: src/pages/settings/general.vue:398 src/share/album/clipboard.vue:60
#: src/share/photo/clipboard.vue:62 src/share/photos.vue:75
msgid "Download"
msgstr "Download"
@@ -596,7 +596,7 @@ msgstr "Download"
msgid "Download remote files"
msgstr "Dateien herunterladen"
#: src/pages/settings/general.vue:291
#: src/pages/settings/general.vue:399
msgid "Download single files and zip archives."
msgstr ""
"Einzelne Fotos, Videos sowie Zip-Archive können heruntergeladen werden."
@@ -623,7 +623,7 @@ msgstr "E-Mail"
#: src/component/album/clipboard.vue:89 src/component/album/toolbar.vue:69
#: src/component/photo/clipboard.vue:119 src/component/photo/viewer.vue:89
#: src/pages/settings/general.vue:356
#: src/pages/settings/general.vue:464
msgid "Edit"
msgstr "Bearbeiten"
@@ -663,7 +663,7 @@ msgstr "Fehler"
msgid "Every two days"
msgstr "Jeden zweiten Tag"
#: src/pages/settings/general.vue:77
#: src/pages/settings/general.vue:79
msgid ""
"Exclude content marked as private from search results, shared albums, labels "
"and places."
@@ -731,17 +731,21 @@ msgstr "Datei"
msgid "File Browser"
msgstr "Datei Browser"
#: src/pages/settings/general.vue:248
msgid "File Name Prefix"
msgstr "Fortlaufender Name"
#: src/dialog/photo/edit.vue:31
msgid "Files"
msgstr "Dateien"
#: src/pages/settings/general.vue:143
#: src/pages/settings/general.vue:249
msgid ""
"Files with sequential names like 'IMG_1234 (2)' or 'IMG_1234 copy 2' belong "
"to the same photo."
"Files with sequential names like 'IMG_1234 (2)' and 'IMG_1234 (3)' belong to "
"the same picture."
msgstr ""
"Dateien mit Namen wie IMG_1234 (2) oder IMG_1234 copy 2 werden als "
"Stapel zusammengefasst."
"Dateien mit fortlaufenden Namen wie IMG_1234 (2) und IMG_1234 (3) "
"gehören zum gleichen Bild."
#: src/dialog/photo/details.vue:476
msgid "Focal Length"
@@ -814,11 +818,11 @@ msgstr "Unsichtbar"
msgid "Hidden Files"
msgstr "Unsichtbare Dateien"
#: src/pages/settings/general.vue:335
#: src/pages/settings/general.vue:443
msgid "Hide photos that have been moved to archive."
msgstr "Archivierte Inhalte werden nicht in den Suchergebnissen angezeigt."
#: src/pages/settings/general.vue:76
#: src/pages/settings/general.vue:78
msgid "Hide Private"
msgstr "Privates ausblenden"
@@ -852,7 +856,7 @@ msgstr "Bild"
msgid "Image"
msgstr "Bild"
#: src/pages/library/import.vue:43 src/pages/settings/general.vue:466
#: src/pages/library/import.vue:43 src/pages/settings/general.vue:574
msgid "Import"
msgstr "Import"
@@ -868,7 +872,7 @@ msgstr ""
"Import kopiert neue Dateien und sortiert sie nach Datum, um Duplikate zu "
"vermeiden."
#: src/pages/settings/general.vue:467
#: src/pages/settings/general.vue:575
msgid "Imported files will be sorted by date and given a unique name."
msgstr ""
"Import kopiert neue Dateien und sortiert sie nach Datum, um Duplikate zu "
@@ -935,7 +939,7 @@ msgid "Label Name"
msgstr "Name"
#: src/component/navigation.vue:263 src/dialog/photo/edit.vue:27
#: src/pages/settings/general.vue:422 src/routes.js:236
#: src/pages/settings/general.vue:530 src/routes.js:236
msgid "Labels"
msgstr "Kategorien"
@@ -943,7 +947,7 @@ msgstr "Kategorien"
msgid "Labels deleted"
msgstr "Kategorien gelöscht"
#: src/pages/settings/general.vue:220
#: src/pages/settings/general.vue:328
msgid "Language"
msgstr "Sprache"
@@ -963,14 +967,14 @@ msgstr "Lavendel"
msgid "Lens"
msgstr "Objektiv"
#: src/pages/settings/general.vue:401
#: src/pages/settings/general.vue:509
msgid "Let PhotoPrism create albums from past events."
msgstr ""
"PhotoPrism erstellt automatisch Alben mit besonderen Momenten, Reisen und "
"Orten."
#: src/component/navigation.vue:276 src/component/navigation.vue:285
#: src/pages/settings/general.vue:6 src/pages/settings/general.vue:444
#: src/pages/settings/general.vue:6 src/pages/settings/general.vue:552
#: src/routes.js:255 src/routes.js:262 src/routes.js:269
msgid "Library"
msgstr "Dateien"
@@ -1033,7 +1037,7 @@ msgstr "Anmelden"
msgid "Logout"
msgstr "Abmelden"
#: src/pages/library.vue:17 src/pages/settings/general.vue:488
#: src/pages/library.vue:17 src/pages/settings/general.vue:596
msgid "Logs"
msgstr "Logs"
@@ -1069,7 +1073,7 @@ msgstr "Nachricht versendet"
msgid "Missing"
msgstr "Fehlend"
#: src/component/navigation.vue:218 src/pages/settings/general.vue:400
#: src/component/navigation.vue:218 src/pages/settings/general.vue:508
#: src/routes.js:98 src/routes.js:105
msgid "Moments"
msgstr "Erlebnisse"
@@ -1196,7 +1200,7 @@ msgstr ""
#: src/component/photo/list.vue:15 src/component/photo/list.vue:1
#: src/component/photo/list.vue:19 src/component/photo/mosaic.vue:15
#: src/component/photo/mosaic.vue:1 src/dialog/upload.vue:50
#: src/pages/settings/general.vue:99
#: src/pages/settings/general.vue:101
msgid ""
"Non-photographic and low-quality images require a review before they appear "
"in search results."
@@ -1305,7 +1309,7 @@ msgid "Original Name"
msgstr "Originalname"
#: src/component/navigation.vue:293 src/pages/library/files.vue:6
#: src/pages/settings/general.vue:378
#: src/pages/settings/general.vue:486
msgid "Originals"
msgstr "Originale"
@@ -1374,8 +1378,12 @@ msgstr "Pink"
msgid "Place"
msgstr "Ort"
#: src/pages/settings/general.vue:204
msgid "Place & Time"
msgstr "Gleicher Ort & Zeit"
#: src/component/navigation.vue:231 src/component/navigation.vue:240
#: src/pages/settings/general.vue:124 src/pages/settings/general.vue:510
#: src/pages/settings/general.vue:152 src/pages/settings/general.vue:618
#: src/routes.js:192 src/routes.js:198 src/routes.js:204 src/routes.js:211
msgid "Places"
msgstr "Karten"
@@ -1442,7 +1450,7 @@ msgstr "Projektion"
msgid "Purple"
msgstr "Purpur"
#: src/pages/settings/general.vue:98
#: src/pages/settings/general.vue:100
msgid "Quality Filter"
msgstr "Qualitätsfilter"
@@ -1562,7 +1570,7 @@ msgstr "Scans"
msgid "Search"
msgstr "Suche"
#: src/pages/settings/general.vue:511
#: src/pages/settings/general.vue:619
msgid "Search and display photos on a map."
msgstr "Fotos und Videos auf verschiedenen Weltkarten anzeigen und filtern."
@@ -1629,7 +1637,7 @@ msgid "Setup"
msgstr "Einrichtung"
#: src/component/album/clipboard.vue:68 src/component/album/toolbar.vue:87
#: src/component/photo/clipboard.vue:73 src/pages/settings/general.vue:312
#: src/component/photo/clipboard.vue:73 src/pages/settings/general.vue:420
msgid "Share"
msgstr "Teilen"
@@ -1645,7 +1653,7 @@ msgstr "Mit dir geteilt."
msgid "Show less"
msgstr "Weniger zeigen"
#: src/pages/settings/general.vue:445
#: src/pages/settings/general.vue:553
msgid "Show Library in navigation menu."
msgstr "Datei-Verwaltung in der Navigation anzeigen."
@@ -1653,7 +1661,7 @@ msgstr "Datei-Verwaltung in der Navigation anzeigen."
msgid "Show more"
msgstr "Mehr zeigen"
#: src/pages/settings/general.vue:489
#: src/pages/settings/general.vue:597
msgid "Show server logs in Library."
msgstr "Server-Ereignisprotokoll anzeigen, um Fehler zu finden."
@@ -1699,14 +1707,32 @@ msgstr "Quelle"
msgid "Spanish"
msgstr "Spanisch"
#: src/pages/settings/general.vue:142
msgid "Stack Sequences"
msgstr "Sequenzen gruppieren"
#: src/pages/settings/general.vue:227
msgid "Stack files sharing the same unique image or instance identifier."
msgstr "Bilder mit identischer ID als Stapel indexieren und anzeigen."
#: src/component/navigation.vue:89
#: src/pages/settings/general.vue:38
msgid "Stack files with matching..."
msgstr "Als Stapel zusammenfassen..."
#: src/pages/settings/general.vue:205
msgid ""
"Stack pictures taken at the exact same time and location based on their "
"metadata."
msgstr "Aufnahmen mit übereinstimmenden Metadaten gruppieren."
#: src/component/navigation.vue:89 src/pages/settings/general.vue:144
msgid "Stacks"
msgstr "Bildstapel"
#: src/pages/settings/general.vue:145
msgid ""
"Stacks group files with a similar frame of reference, but differences of "
"quality, format, size or color."
msgstr ""
"Bildstapel enthalten zusammengehörige Aufnahmen, die jedoch Unterschiede in "
"Qualität, Format, Größe oder Farben aufweisen können."
#: src/pages/library/index.vue:30
msgid "Start"
msgstr "Start"
@@ -1731,7 +1757,7 @@ msgstr "Ablageverzeichnis"
msgid "Streets"
msgstr "Straßen"
#: src/pages/settings/general.vue:568
#: src/pages/settings/general.vue:676
msgid "Style"
msgstr "Style"
@@ -1777,7 +1803,7 @@ msgstr ""
"Ihr Format wird möglicherweise nicht unterstützt, es handelt sich um "
"Duplikate oder sie wurden noch nicht nach JPEG konvertiert."
#: src/pages/settings/general.vue:198
#: src/pages/settings/general.vue:306
msgid "Theme"
msgstr "Theme"
@@ -1844,6 +1870,10 @@ msgstr "Versuche es mit anderen Filtern oder Suchbegriffen."
msgid "Type"
msgstr "Typ"
#: src/pages/settings/general.vue:226
msgid "Unique ID"
msgstr "Eindeutige Bild-ID"
#: src/dialog/photo/details.vue:16 src/dialog/photo/info.vue:21
#: src/model/album.js:122 src/model/photo.js:401 src/model/photo.js:415
#: src/model/photo.js:438 src/model/photo.js:452 src/model/photo.js:529
@@ -1869,7 +1899,7 @@ msgstr "Geändert"
#: src/component/album/toolbar.vue:175 src/component/navigation.vue:130
#: src/component/photo/toolbar.vue:123 src/dialog/share/upload.vue:35
#: src/dialog/upload.vue:8 src/dialog/upload.vue:54 src/pages/albums.vue:134
#: src/pages/library/import.vue:38 src/pages/settings/general.vue:268
#: src/pages/library/import.vue:38 src/pages/settings/general.vue:376
#: src/pages/settings/sync.vue:24
msgid "Upload"
msgstr "Upload"
@@ -1890,7 +1920,7 @@ msgstr "Upload fehlgeschlagen"
msgid "Upload local files"
msgstr "Dateien hochladen"
#: src/pages/settings/general.vue:313
#: src/pages/settings/general.vue:421
msgid "Upload to WebDAV and share links with friends."
msgstr "Teile Fotos, Videos und Alben mit Freunden."
@@ -1914,7 +1944,7 @@ msgstr "URL"
msgid "User"
msgstr "Benutzer"
#: src/pages/settings/general.vue:38
#: src/pages/settings/general.vue:66
msgid "User Interface"
msgstr "Benutzeroberfläche"
@@ -2003,6 +2033,9 @@ msgstr "Ihre Nachricht wurde gesendet"
msgid "Zoom in/out"
msgstr "Zoom in/out"
#~ msgid "Stack Sequences"
#~ msgstr "Sequenzen gruppieren"
#~ msgid "Brazilian Portuguese"
#~ msgstr "Brasilianisches Portugiesisch"

File diff suppressed because one or more lines are too long

View File

@@ -78,7 +78,7 @@ msgstr ""
msgid "Add Album"
msgstr ""
#: src/pages/settings/general.vue:269
#: src/pages/settings/general.vue:377
msgid "Add files to your library via Web Upload."
msgstr ""
@@ -234,7 +234,7 @@ msgstr ""
msgid "An error occurred - are you offline?"
msgstr ""
#: src/pages/settings/general.vue:590
#: src/pages/settings/general.vue:698
msgid "Animation"
msgstr ""
@@ -259,7 +259,7 @@ msgstr ""
#: src/component/navigation.vue:114
#: src/component/photo/cards.vue:20
#: src/component/photo/clipboard.vue:211
#: src/pages/settings/general.vue:334
#: src/pages/settings/general.vue:442
#: src/routes.js:185
msgid "Archive"
msgstr ""
@@ -300,7 +300,7 @@ msgstr ""
msgid "At least 6 characters."
msgstr ""
#: src/pages/settings/general.vue:121
#: src/pages/settings/general.vue:123
msgid "Automatically create JPEGs for other file types so that they can be displayed in a browser."
msgstr ""
@@ -320,11 +320,11 @@ msgstr ""
msgid "Brown"
msgstr ""
#: src/pages/settings/general.vue:423
#: src/pages/settings/general.vue:531
msgid "Browse and edit image classification labels."
msgstr ""
#: src/pages/settings/general.vue:379
#: src/pages/settings/general.vue:487
msgid "Browse indexed files and folders in Library."
msgstr ""
@@ -404,7 +404,7 @@ msgstr ""
msgid "Change"
msgstr ""
#: src/pages/settings/general.vue:357
#: src/pages/settings/general.vue:465
msgid "Change photo titles, locations and other metadata."
msgstr ""
@@ -476,7 +476,7 @@ msgstr ""
msgid "Contributors"
msgstr ""
#: src/pages/settings/general.vue:120
#: src/pages/settings/general.vue:122
msgid "Convert to JPEG"
msgstr ""
@@ -636,7 +636,7 @@ msgstr ""
#: src/component/photo/clipboard.vue:165
#: src/component/photo/viewer.vue:72
#: src/dialog/photo/files.vue:33
#: src/pages/settings/general.vue:290
#: src/pages/settings/general.vue:398
#: src/share/album/clipboard.vue:60
#: src/share/photo/clipboard.vue:62
#: src/share/photos.vue:75
@@ -647,7 +647,7 @@ msgstr ""
msgid "Download remote files"
msgstr ""
#: src/pages/settings/general.vue:291
#: src/pages/settings/general.vue:399
msgid "Download single files and zip archives."
msgstr ""
@@ -679,7 +679,7 @@ msgstr ""
#: src/component/album/toolbar.vue:69
#: src/component/photo/clipboard.vue:119
#: src/component/photo/viewer.vue:89
#: src/pages/settings/general.vue:356
#: src/pages/settings/general.vue:464
msgid "Edit"
msgstr ""
@@ -720,7 +720,7 @@ msgstr ""
msgid "Every two days"
msgstr ""
#: src/pages/settings/general.vue:77
#: src/pages/settings/general.vue:79
msgid "Exclude content marked as private from search results, shared albums, labels and places."
msgstr ""
@@ -788,12 +788,16 @@ msgstr ""
msgid "File Browser"
msgstr ""
#: src/pages/settings/general.vue:248
msgid "File Name Prefix"
msgstr ""
#: src/dialog/photo/edit.vue:31
msgid "Files"
msgstr ""
#: src/pages/settings/general.vue:143
msgid "Files with sequential names like 'IMG_1234 (2)' or 'IMG_1234 copy 2' belong to the same photo."
#: src/pages/settings/general.vue:249
msgid "Files with sequential names like 'IMG_1234 (2)' and 'IMG_1234 (3)' belong to the same picture."
msgstr ""
#: src/dialog/photo/details.vue:476
@@ -873,11 +877,11 @@ msgstr ""
msgid "Hidden Files"
msgstr ""
#: src/pages/settings/general.vue:335
#: src/pages/settings/general.vue:443
msgid "Hide photos that have been moved to archive."
msgstr ""
#: src/pages/settings/general.vue:76
#: src/pages/settings/general.vue:78
msgid "Hide Private"
msgstr ""
@@ -909,7 +913,7 @@ msgid "Image"
msgstr ""
#: src/pages/library/import.vue:43
#: src/pages/settings/general.vue:466
#: src/pages/settings/general.vue:574
msgid "Import"
msgstr ""
@@ -921,7 +925,7 @@ msgstr ""
msgid "Imported files will be sorted by date and given a unique name to avoid duplicates."
msgstr ""
#: src/pages/settings/general.vue:467
#: src/pages/settings/general.vue:575
msgid "Imported files will be sorted by date and given a unique name."
msgstr ""
@@ -990,7 +994,7 @@ msgstr ""
#: src/component/navigation.vue:263
#: src/dialog/photo/edit.vue:27
#: src/pages/settings/general.vue:422
#: src/pages/settings/general.vue:530
#: src/routes.js:236
msgid "Labels"
msgstr ""
@@ -999,7 +1003,7 @@ msgstr ""
msgid "Labels deleted"
msgstr ""
#: src/pages/settings/general.vue:220
#: src/pages/settings/general.vue:328
msgid "Language"
msgstr ""
@@ -1020,14 +1024,14 @@ msgstr ""
msgid "Lens"
msgstr ""
#: src/pages/settings/general.vue:401
#: src/pages/settings/general.vue:509
msgid "Let PhotoPrism create albums from past events."
msgstr ""
#: src/component/navigation.vue:276
#: src/component/navigation.vue:285
#: src/pages/settings/general.vue:6
#: src/pages/settings/general.vue:444
#: src/pages/settings/general.vue:552
#: src/routes.js:255
#: src/routes.js:262
#: src/routes.js:269
@@ -1093,7 +1097,7 @@ msgid "Logout"
msgstr ""
#: src/pages/library.vue:17
#: src/pages/settings/general.vue:488
#: src/pages/settings/general.vue:596
msgid "Logs"
msgstr ""
@@ -1131,7 +1135,7 @@ msgid "Missing"
msgstr ""
#: src/component/navigation.vue:218
#: src/pages/settings/general.vue:400
#: src/pages/settings/general.vue:508
#: src/routes.js:98
#: src/routes.js:105
msgid "Moments"
@@ -1280,7 +1284,7 @@ msgstr ""
#: src/component/photo/mosaic.vue:15
#: src/component/photo/mosaic.vue:1
#: src/dialog/upload.vue:50
#: src/pages/settings/general.vue:99
#: src/pages/settings/general.vue:101
msgid "Non-photographic and low-quality images require a review before they appear in search results."
msgstr ""
@@ -1380,7 +1384,7 @@ msgstr ""
#: src/component/navigation.vue:293
#: src/pages/library/files.vue:6
#: src/pages/settings/general.vue:378
#: src/pages/settings/general.vue:486
msgid "Originals"
msgstr ""
@@ -1448,10 +1452,14 @@ msgstr ""
msgid "Place"
msgstr ""
#: src/pages/settings/general.vue:204
msgid "Place & Time"
msgstr ""
#: src/component/navigation.vue:231
#: src/component/navigation.vue:240
#: src/pages/settings/general.vue:124
#: src/pages/settings/general.vue:510
#: src/pages/settings/general.vue:152
#: src/pages/settings/general.vue:618
#: src/routes.js:192
#: src/routes.js:198
#: src/routes.js:204
@@ -1524,7 +1532,7 @@ msgstr ""
msgid "Purple"
msgstr ""
#: src/pages/settings/general.vue:98
#: src/pages/settings/general.vue:100
msgid "Quality Filter"
msgstr ""
@@ -1653,7 +1661,7 @@ msgstr ""
msgid "Search"
msgstr ""
#: src/pages/settings/general.vue:511
#: src/pages/settings/general.vue:619
msgid "Search and display photos on a map."
msgstr ""
@@ -1732,7 +1740,7 @@ msgstr ""
#: src/component/album/clipboard.vue:68
#: src/component/album/toolbar.vue:87
#: src/component/photo/clipboard.vue:73
#: src/pages/settings/general.vue:312
#: src/pages/settings/general.vue:420
msgid "Share"
msgstr ""
@@ -1748,7 +1756,7 @@ msgstr ""
msgid "Show less"
msgstr ""
#: src/pages/settings/general.vue:445
#: src/pages/settings/general.vue:553
msgid "Show Library in navigation menu."
msgstr ""
@@ -1756,7 +1764,7 @@ msgstr ""
msgid "Show more"
msgstr ""
#: src/pages/settings/general.vue:489
#: src/pages/settings/general.vue:597
msgid "Show server logs in Library."
msgstr ""
@@ -1805,14 +1813,27 @@ msgstr ""
msgid "Spanish"
msgstr ""
#: src/pages/settings/general.vue:142
msgid "Stack Sequences"
#: src/pages/settings/general.vue:227
msgid "Stack files sharing the same unique image or instance identifier."
msgstr ""
#: src/pages/settings/general.vue:38
msgid "Stack files with matching..."
msgstr ""
#: src/pages/settings/general.vue:205
msgid "Stack pictures taken at the exact same time and location based on their metadata."
msgstr ""
#: src/component/navigation.vue:89
#: src/pages/settings/general.vue:144
msgid "Stacks"
msgstr ""
#: src/pages/settings/general.vue:145
msgid "Stacks group files with a similar frame of reference, but differences of quality, format, size or color."
msgstr ""
#: src/pages/library/index.vue:30
msgid "Start"
msgstr ""
@@ -1837,7 +1858,7 @@ msgstr ""
msgid "Streets"
msgstr ""
#: src/pages/settings/general.vue:568
#: src/pages/settings/general.vue:676
msgid "Style"
msgstr ""
@@ -1879,7 +1900,7 @@ msgstr ""
msgid "Their format may not be supported, they haven't been converted to JPEG yet or there are duplicates."
msgstr ""
#: src/pages/settings/general.vue:198
#: src/pages/settings/general.vue:306
msgid "Theme"
msgstr ""
@@ -1956,6 +1977,10 @@ msgstr ""
msgid "Type"
msgstr ""
#: src/pages/settings/general.vue:226
msgid "Unique ID"
msgstr ""
#: src/dialog/photo/details.vue:16
#: src/dialog/photo/info.vue:21
#: src/model/album.js:122
@@ -1998,7 +2023,7 @@ msgstr ""
#: src/dialog/upload.vue:54
#: src/pages/albums.vue:134
#: src/pages/library/import.vue:38
#: src/pages/settings/general.vue:268
#: src/pages/settings/general.vue:376
#: src/pages/settings/sync.vue:24
msgid "Upload"
msgstr ""
@@ -2019,7 +2044,7 @@ msgstr ""
msgid "Upload local files"
msgstr ""
#: src/pages/settings/general.vue:313
#: src/pages/settings/general.vue:421
msgid "Upload to WebDAV and share links with friends."
msgstr ""
@@ -2045,7 +2070,7 @@ msgstr ""
msgid "User"
msgstr ""
#: src/pages/settings/general.vue:38
#: src/pages/settings/general.vue:66
msgid "User Interface"
msgstr ""

View File

@@ -59,15 +59,73 @@
<v-flex xs12 sm6 lg3 class="px-2 pb-2 pt-2">
<v-checkbox
@change="onChange"
:disabled="busy"
class="ma-0 pa-0 input-sequences"
v-model="settings.index.sequences"
color="secondary-dark"
:label="$gettext('Stack Sequences')"
:hint="$gettext('Files with sequential names like \'IMG_1234 (2)\' or \'IMG_1234 copy 2\' belong to the same photo.')"
prepend-icon="photo_library"
persistent-hint
@change="onChange"
:disabled="busy"
class="ma-0 pa-0 input-stacks"
v-model="settings.index.stacks"
color="secondary-dark"
:label="$gettext('Stacks')"
:hint="$gettext('Stacks group files with a similar frame of reference, but differences of quality, format, size or color.')"
prepend-icon="burst_mode"
persistent-hint
>
</v-checkbox>
</v-flex>
</v-layout>
</v-card-actions>
</v-card>
<v-card flat tile class="mt-0 px-1 application" v-show="settings.index.stacks">
<v-card-title primary-title class="pb-0">
<h3 class="body-2 mb-0">
<translate>Stack files with matching...</translate>
</h3>
</v-card-title>
<v-card-actions>
<v-layout wrap align-top>
<v-flex xs12 sm6 lg4 class="px-2 pb-2 pt-2">
<v-checkbox
@change="onChange"
:disabled="busy"
class="ma-0 pa-0 input-stack-meta"
v-model="settings.stack.meta"
color="secondary-dark"
:label="$gettext('Place & Time')"
:hint="$gettext('Stack pictures taken at the exact same time and location based on their metadata.')"
prepend-icon="schedule"
persistent-hint
>
</v-checkbox>
</v-flex>
<v-flex xs12 sm6 lg4 class="px-2 pb-2 pt-2">
<v-checkbox
@change="onChange"
:disabled="busy"
class="ma-0 pa-0 input-stack-uuid"
v-model="settings.stack.uuid"
color="secondary-dark"
:label="$gettext('Unique ID')"
:hint="$gettext('Stack files sharing the same unique image or instance identifier.')"
prepend-icon="fingerprint"
persistent-hint
>
</v-checkbox>
</v-flex>
<v-flex xs12 sm6 lg4 class="px-2 pb-2 pt-2">
<v-checkbox
@change="onChange"
:disabled="busy"
class="ma-0 pa-0 input-stack-sequences"
v-model="settings.stack.sequences"
color="secondary-dark"
:label="$gettext('File Name Prefix')"
:hint="$gettext('Files with sequential names like \'IMG_1234 (2)\' and \'IMG_1234 (3)\' belong to the same picture.')"
prepend-icon="spellcheck"
persistent-hint
>
</v-checkbox>
</v-flex>

View File

@@ -49,7 +49,7 @@ func StartIndexing(router *gin.RouterGroup) {
indOpt := photoprism.IndexOptions{
Rescan: f.Rescan,
Convert: conf.Settings().Index.Convert && conf.SidecarWritable(),
Stack: conf.Settings().Index.Stack,
Stack: conf.Settings().Index.Stacks,
Path: filepath.Clean(f.Path),
}

View File

@@ -63,7 +63,7 @@ func indexAction(ctx *cli.Context) error {
Path: subPath,
Rescan: ctx.Bool("all"),
Convert: conf.Settings().Index.Convert && conf.SidecarWritable(),
Stack: conf.Settings().Index.Stack,
Stack: conf.Settings().Index.Stacks,
}
indexed := ind.Start(indOpt)

View File

@@ -27,21 +27,6 @@ type MapsSettings struct {
Style string `json:"style" yaml:"style"`
}
// IndexSettings represents indexing settings.
type IndexSettings struct {
Path string `json:"path" yaml:"path"`
Convert bool `json:"convert" yaml:"convert"`
Rescan bool `json:"rescan" yaml:"rescan"`
Sequences bool `json:"sequences" yaml:"sequences"`
Stack bool `json:"stack" yaml:"stack"`
}
// ImportSettings represents import settings.
type ImportSettings struct {
Path string `json:"path" yaml:"path"`
Move bool `json:"move" yaml:"move"`
}
// FeatureSettings represents feature flags, mainly for the Web UI.
type FeatureSettings struct {
Upload bool `json:"upload" yaml:"upload"`
@@ -60,6 +45,27 @@ type FeatureSettings struct {
Logs bool `json:"logs" yaml:"logs"`
}
// ImportSettings represents import settings.
type ImportSettings struct {
Path string `json:"path" yaml:"path"`
Move bool `json:"move" yaml:"move"`
}
// IndexSettings represents indexing settings.
type IndexSettings struct {
Path string `json:"path" yaml:"path"`
Convert bool `json:"convert" yaml:"convert"`
Rescan bool `json:"rescan" yaml:"rescan"`
Stacks bool `json:"stacks" yaml:"stacks"`
}
// StackSettings represents file stack settings.
type StackSettings struct {
UUID bool `json:"uuid" yaml:"uuid"`
Meta bool `json:"meta" yaml:"meta"`
Sequences bool `json:"sequences" yaml:"sequences"`
}
// Settings represents user settings for Web UI, indexing, and import.
type Settings struct {
Theme string `json:"theme" yaml:"theme"`
@@ -69,6 +75,7 @@ type Settings struct {
Features FeatureSettings `json:"features" yaml:"features"`
Import ImportSettings `json:"import" yaml:"import"`
Index IndexSettings `json:"index" yaml:"index"`
Stack StackSettings `json:"stack" yaml:"stack"`
}
// NewSettings creates a new Settings instance.
@@ -104,11 +111,15 @@ func NewSettings() *Settings {
Move: false,
},
Index: IndexSettings{
Path: "/",
Rescan: false,
Convert: true,
Sequences: true,
Stack: true,
Path: "/",
Rescan: false,
Convert: true,
Stacks: true,
},
Stack: StackSettings{
UUID: true,
Meta: true,
Sequences: false,
},
}
}

View File

@@ -27,5 +27,8 @@ index:
path: /
convert: true
rescan: false
sequences: true
stack: true
stacks: true
stack:
uuid: true
meta: true
sequences: false

View File

@@ -1012,13 +1012,27 @@ func (m *Photo) MapKey() string {
}
// Stack merges the photo with identical ones.
func (m *Photo) Stack() (identical Photos, err error) {
if err := Db().
func (m *Photo) Stack(meta, uuid bool) (identical Photos, err error) {
if !meta && !uuid {
return identical, nil
}
stmt := Db().
Where("id <> ?", m.ID).
Where("photo_single = 0").
Where("(taken_at = ? AND cell_id = ? AND camera_serial = ? AND camera_id = ?) OR (uuid <> '' AND uuid = ?)",
m.TakenAt, m.CellID, m.CameraSerial, m.CameraID, m.UUID).
Find(&identical).Error; err != nil {
Where("photo_single = 0")
switch {
case meta && uuid:
stmt.Where("(taken_at = ? AND taken_src = 'meta' AND cell_id = ? AND camera_serial = ? AND camera_id = ?) OR (uuid <> '' AND uuid = ?)",
m.TakenAt, m.CellID, m.CameraSerial, m.CameraID, m.UUID)
case meta:
stmt.Where("taken_at = ? AND taken_src = 'meta' AND cell_id = ? AND camera_serial = ? AND camera_id = ?",
m.TakenAt, m.CellID, m.CameraSerial, m.CameraID)
case uuid:
stmt.Where("uuid <> '' AND uuid = ?", m.UUID)
}
if err := stmt.Find(&identical).Error; err != nil {
return identical, err
}

View File

@@ -115,7 +115,7 @@ func (c *Convert) Start(path string) error {
// ToJson uses exiftool to export metadata to a json file.
func (c *Convert) ToJson(mf *MediaFile) (*MediaFile, error) {
jsonName := fs.TypeJson.FindFirst(mf.FileName(), []string{c.conf.SidecarPath(), fs.HiddenPath}, c.conf.OriginalsPath(), c.conf.Settings().Index.Sequences)
jsonName := fs.TypeJson.FindFirst(mf.FileName(), []string{c.conf.SidecarPath(), fs.HiddenPath}, c.conf.OriginalsPath(), c.conf.Settings().Stack.Sequences)
result, err := NewMediaFile(jsonName)
@@ -127,7 +127,7 @@ func (c *Convert) ToJson(mf *MediaFile) (*MediaFile, error) {
return nil, fmt.Errorf("convert: can't create json sidecar file for %s in read only mode", txt.Quote(mf.BaseName()))
}
jsonName = fs.FileName(mf.FileName(), c.conf.SidecarPath(), c.conf.OriginalsPath(), ".json", c.conf.Settings().Index.Sequences)
jsonName = fs.FileName(mf.FileName(), c.conf.SidecarPath(), c.conf.OriginalsPath(), ".json", c.conf.Settings().Stack.Sequences)
fileName := mf.RelName(c.conf.OriginalsPath())
@@ -220,7 +220,7 @@ func (c *Convert) ToJpeg(image *MediaFile) (*MediaFile, error) {
return image, nil
}
jpegName := fs.TypeJpeg.FindFirst(image.FileName(), []string{c.conf.SidecarPath(), fs.HiddenPath}, c.conf.OriginalsPath(), c.conf.Settings().Index.Sequences)
jpegName := fs.TypeJpeg.FindFirst(image.FileName(), []string{c.conf.SidecarPath(), fs.HiddenPath}, c.conf.OriginalsPath(), c.conf.Settings().Stack.Sequences)
mediaFile, err := NewMediaFile(jpegName)
@@ -232,12 +232,12 @@ func (c *Convert) ToJpeg(image *MediaFile) (*MediaFile, error) {
return nil, fmt.Errorf("convert: disabled in read only mode (%s)", image.RelName(c.conf.OriginalsPath()))
}
jpegName = fs.FileName(image.FileName(), c.conf.SidecarPath(), c.conf.OriginalsPath(), fs.JpegExt, c.conf.Settings().Index.Sequences)
jpegName = fs.FileName(image.FileName(), c.conf.SidecarPath(), c.conf.OriginalsPath(), fs.JpegExt, c.conf.Settings().Stack.Sequences)
fileName := image.RelName(c.conf.OriginalsPath())
log.Debugf("convert: %s -> %s", fileName, filepath.Base(jpegName))
xmpName := fs.TypeXMP.Find(image.FileName(), c.conf.Settings().Index.Sequences)
xmpName := fs.TypeXMP.Find(image.FileName(), c.conf.Settings().Stack.Sequences)
event.Publish("index.converting", event.Data{
"fileType": image.FileType(),
@@ -308,7 +308,7 @@ func (c *Convert) ToAvc1(video *MediaFile) (*MediaFile, error) {
return video, nil
}
avcName := fs.TypeMp4.FindFirst(video.FileName(), []string{c.conf.SidecarPath(), fs.HiddenPath}, c.conf.OriginalsPath(), c.conf.Settings().Index.Sequences)
avcName := fs.TypeMp4.FindFirst(video.FileName(), []string{c.conf.SidecarPath(), fs.HiddenPath}, c.conf.OriginalsPath(), c.conf.Settings().Stack.Sequences)
mediaFile, err := NewMediaFile(avcName)
@@ -320,7 +320,7 @@ func (c *Convert) ToAvc1(video *MediaFile) (*MediaFile, error) {
return nil, fmt.Errorf("convert: disabled in read only mode (%s)", video.RelName(c.conf.OriginalsPath()))
}
avcName = fs.FileName(video.FileName(), c.conf.SidecarPath(), c.conf.OriginalsPath(), fs.AvcExt, c.conf.Settings().Index.Sequences)
avcName = fs.FileName(video.FileName(), c.conf.SidecarPath(), c.conf.OriginalsPath(), fs.AvcExt, c.conf.Settings().Stack.Sequences)
fileName := video.RelName(c.conf.OriginalsPath())
log.Debugf("convert: %s -> %s", fileName, filepath.Base(avcName))

View File

@@ -151,7 +151,7 @@ func (imp *Import) Start(opt ImportOptions) fs.Done {
return nil
}
related, err := mf.RelatedFiles(imp.conf.Settings().Index.Sequences)
related, err := mf.RelatedFiles(imp.conf.Settings().Stack.Sequences)
if err != nil {
event.Error(fmt.Sprintf("import: %s", err.Error()))

View File

@@ -113,7 +113,7 @@ func ImportWorker(jobs <-chan ImportJob) {
}
}
related, err := f.RelatedFiles(imp.conf.Settings().Index.Sequences)
related, err := f.RelatedFiles(imp.conf.Settings().Stack.Sequences)
if err != nil {
log.Errorf("import: %s in %s (find related files)", err.Error(), txt.Quote(fs.RelName(destinationMainFilename, imp.originalsPath())))

View File

@@ -167,7 +167,7 @@ func (ind *Index) Start(opt IndexOptions) fs.Done {
return nil
}
related, err := mf.RelatedFiles(ind.conf.Settings().Index.Sequences)
related, err := mf.RelatedFiles(ind.conf.Settings().Stack.Sequences)
if err != nil {
log.Warnf("index: %s", err.Error())
@@ -222,7 +222,7 @@ func (ind *Index) Start(opt IndexOptions) fs.Done {
}
if opt.Stack {
if err := ind.StackIdenticalPhotos(); err != nil {
if err := ind.StackPhotos(); err != nil {
log.Errorf("index: %s", err)
}
}
@@ -240,16 +240,16 @@ func (ind *Index) Start(opt IndexOptions) fs.Done {
return done
}
// StackIdenticalPhotos stacks files that belong to the same photo.
func (ind *Index) StackIdenticalPhotos() error {
photos, err := query.IdenticalPhotos()
// StackPhotos stacks files that belong to the same photo.
func (ind *Index) StackPhotos() error {
photos, err := query.MatchingPhotos(ind.conf.Settings().Stack.Meta, ind.conf.Settings().Stack.UUID)
if err != nil {
return err
}
for _, photo := range photos {
if merged, err := photo.Stack(); err != nil {
if merged, err := photo.Stack(ind.conf.Settings().Stack.Meta, ind.conf.Settings().Stack.UUID); err != nil {
log.Errorf("index: %s", err)
} else {
log.Infof("index: merged photo uid %s with %s", photo.PhotoUID, merged.UIDs())

View File

@@ -114,7 +114,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
photoExists := false
stripSequence := Config().Settings().Index.Sequences
stripSequence := Config().Settings().Stack.Sequences
event.Publish("index.indexing", event.Data{
"fileHash": fileHash,
@@ -156,23 +156,24 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
}
}
// Try to find existing photo by file path and name.
// Look for existing photo if file wasn't indexed yet...
if !fileExists {
photoQuery = entity.UnscopedDb().First(&photo, "photo_path = ? AND photo_name = ?", filePath, fileBase)
// Add file to existing photo (file stack)?
if o.Stack {
// Try to find existing photo by exact time and location.
// Stack file based on matching location and time metadata?
if o.Stack && Config().Settings().Stack.Meta {
if photoQuery.Error != nil && m.MetaData().HasTimeAndPlace() {
metaData = m.MetaData()
photoQuery = entity.UnscopedDb().First(&photo, "photo_lat = ? AND photo_lng = ? AND taken_at = ? AND camera_serial = ?", metaData.Lat, metaData.Lng, metaData.TakenAt, metaData.CameraSerial)
photoQuery = entity.UnscopedDb().First(&photo, "photo_lat = ? AND photo_lng = ? AND taken_at = ? AND taken_src = 'meta' AND camera_serial = ?", metaData.Lat, metaData.Lng, metaData.TakenAt, metaData.CameraSerial)
if photoQuery.Error == nil {
fileStacked = true
}
}
}
// Try to find existing photo by unique image id.
// Stack file based on the same unique ID?
if o.Stack && Config().Settings().Stack.UUID {
if photoQuery.Error != nil && m.MetaData().HasDocumentID() {
photoQuery = entity.UnscopedDb().First(&photo, "uuid = ?", m.MetaData().DocumentID)

View File

@@ -379,7 +379,7 @@ func (m *MediaFile) PathNameInfo() (fileRoot, fileBase, relativePath, relativeNa
rootPath = Config().OriginalsPath()
}
fileBase = m.BasePrefix(Config().Settings().Index.Sequences)
fileBase = m.BasePrefix(Config().Settings().Stack.Sequences)
relativePath = m.RelPath(rootPath)
relativeName = m.RelName(rootPath)

View File

@@ -110,23 +110,46 @@ func PhotosCheck(limit int, offset int) (entities entity.Photos, err error) {
return entities, err
}
// IdenticalPhotos returns photos sharing the same exact time, location and camera serial.
func IdenticalPhotos() (entities entity.Photos, err error) {
err = UnscopedDb().Table("photos").
Select("photos.*").
Joins(`JOIN photos dup ON photos.id < dup.id
AND photos.photo_single = 0
AND dup.photo_single = 0
AND photos.deleted_at IS NULL
AND dup.deleted_at IS NULL
AND ((photos.photo_lat = dup.photo_lat
// MatchingPhotos returns photos sharing the same exact time and location, or unique image id.
func MatchingPhotos(meta, uuid bool) (entities entity.Photos, err error) {
if !meta && !uuid {
return entities, nil
}
stmt := UnscopedDb().Table("photos").
Select("photos.*")
switch {
case meta && uuid:
stmt.Joins(`JOIN photos dup ON photos.id < dup.id
AND photos.photo_single = 0 AND dup.photo_single = 0
AND photos.deleted_at IS NULL AND dup.deleted_at IS NULL
AND ((photos.taken_src = 'meta' AND dup.taken_src = 'meta'
AND photos.photo_lat = dup.photo_lat
AND photos.photo_lng = dup.photo_lng
AND photos.taken_at = dup.taken_at
AND photos.camera_id = dup.camera_id
AND photos.camera_serial = dup.camera_serial) OR
(photos.uuid <> '' AND photos.uuid = dup.uuid))`).
Group("photos.id").
Find(&entities).Error
(photos.uuid <> '' AND photos.uuid = dup.uuid))`)
case meta:
stmt.Joins(`JOIN photos dup ON photos.id < dup.id
AND photos.photo_single = 0
AND dup.photo_single = 0
AND photos.deleted_at IS NULL AND dup.deleted_at IS NULL
AND ((photos.taken_src = 'meta' AND dup.taken_src = 'meta'
AND photos.photo_lat = dup.photo_lat
AND photos.photo_lng = dup.photo_lng
AND photos.taken_at = dup.taken_at
AND photos.camera_id = dup.camera_id
AND photos.camera_serial = dup.camera_serial))`)
case uuid:
stmt.Joins(`JOIN photos dup ON photos.id < dup.id
AND photos.photo_single = 0 AND dup.photo_single = 0
AND photos.deleted_at IS NULL AND dup.deleted_at IS NULL
AND (photos.uuid <> '' AND photos.uuid = dup.uuid)`)
}
err = stmt.Group("photos.id").Find(&entities).Error
return entities, err
}

View File

@@ -80,12 +80,43 @@ func TestPhotosCheck(t *testing.T) {
}
func TestIdenticalPhotos(t *testing.T) {
result, err := IdenticalPhotos()
t.Run("all", func(t *testing.T) {
result, err := MatchingPhotos(true, true)
if err != nil {
t.Fatal(err)
}
if err != nil {
t.Fatal(err)
}
assert.IsType(t, entity.Photos{}, result)
// t.Logf("%+v", result)
assert.IsType(t, entity.Photos{}, result)
})
t.Run("meta", func(t *testing.T) {
result, err := MatchingPhotos(true, false)
if err != nil {
t.Fatal(err)
}
assert.IsType(t, entity.Photos{}, result)
})
t.Run("uuid", func(t *testing.T) {
result, err := MatchingPhotos(false, true)
if err != nil {
t.Fatal(err)
}
assert.IsType(t, entity.Photos{}, result)
})
t.Run("none", func(t *testing.T) {
result, err := MatchingPhotos(false, false)
if err != nil {
t.Fatal(err)
}
assert.IsType(t, entity.Photos{}, result)
})
}

View File

@@ -35,7 +35,7 @@ func (worker *Sync) relatedDownloads(a entity.Account) (result Downloads, err er
// Group results by directory and base name
for i, file := range files {
k := fs.AbsPrefix(file.RemoteName, worker.conf.Settings().Index.Sequences)
k := fs.AbsPrefix(file.RemoteName, worker.conf.Settings().Stack.Sequences)
result[k] = append(result[k], file)
@@ -137,7 +137,7 @@ func (worker *Sync) download(a entity.Account) (complete bool, err error) {
continue
}
related, err := mf.RelatedFiles(worker.conf.Settings().Index.Sequences)
related, err := mf.RelatedFiles(worker.conf.Settings().Stack.Sequences)
if err != nil {
worker.logWarn(err)