Index image projection and add panorama filter

Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
Michael Mayer 2020-07-16 13:02:48 +02:00
parent a4c68fb34a
commit 0e5fe5eb43
31 changed files with 612 additions and 153 deletions

View file

@ -65,7 +65,7 @@
<v-list-tile-content>
<v-list-tile-title>
<translate key="Photos">Photos</translate>
<translate>Photos</translate>
</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
@ -83,7 +83,7 @@
<v-list-tile :to="{name: 'photos', query: { q: 'mono:true quality:3 photo:true' }}" :exact="true" @click="">
<v-list-tile-content>
<v-list-tile-title>
<translate key="Monochrome">Monochrome</translate>
<translate>Monochrome</translate>
</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
@ -91,7 +91,15 @@
<v-list-tile :to="{name: 'photos', query: { q: 'stack:true' }}" :exact="true" @click="" class="nav-stacks">
<v-list-tile-content>
<v-list-tile-title>
<translate key="Stacks">Stacks</translate>
<translate>Stacks</translate>
</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
<v-list-tile :to="{name: 'photos', query: { q: 'panorama:true' }}" :exact="true" @click="" class="nav-panoramas">
<v-list-tile-content>
<v-list-tile-title>
<translate>Panoramas</translate>
</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
@ -99,7 +107,7 @@
<v-list-tile :to="{name: 'photos', query: { q: 'scan:true' }}" :exact="true" @click="" class="nav-scans">
<v-list-tile-content>
<v-list-tile-title>
<translate key="Scans">Scans</translate>
<translate>Scans</translate>
</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
@ -108,7 +116,7 @@
class="nav-review">
<v-list-tile-content>
<v-list-tile-title>
<translate key="Review">Review</translate>
<translate>Review</translate>
<span v-show="config.count.review > 0" class="nav-count">{{ config.count.review }}</span>
</v-list-tile-title>
</v-list-tile-content>
@ -117,7 +125,7 @@
<v-list-tile to="/archive" @click="" class="nav-archive" v-show="$config.feature('archive')">
<v-list-tile-content>
<v-list-tile-title>
<translate key="Archive">Archive</translate>
<translate>Archive</translate>
</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>

View file

@ -116,6 +116,12 @@
</td>
<td><translate>Yes</translate></td>
</tr>
<tr v-if="file.Projection">
<td>
<translate>Projection</translate>
</td>
<td>{{ file.Projection | capitalize }}</td>
</tr>
<tr v-if="file.AspectRatio">
<td>
<translate>Aspect Ratio</translate>

View file

@ -126,14 +126,14 @@
</tr>
<tr>
<td>
<translate>360°</translate>
<translate>Panorama</translate>
</td>
<td>
<v-switch
@change="save"
hide-details
v-model="model.P360"
:label="model.P360 ? $gettext('Yes') : $gettext('No')"
v-model="model.Panorama"
:label="model.Panorama ? $gettext('Yes') : $gettext('No')"
></v-switch>
</td>
</tr>

Binary file not shown.

View file

@ -58,7 +58,7 @@ msgstr "Auf den Link klicken, um ihn zu kopieren."
msgid "Account"
msgstr "Zugang"
#: src/dialog/photo/info.vue:133
#: src/dialog/photo/info.vue:141
msgid "Accuracy"
msgstr "Genauigkeit"
@ -93,7 +93,7 @@ msgstr "Server hinzufügen"
msgid "Add to album"
msgstr "Hinzufügen"
#: src/dialog/photo/files.vue:138
#: src/dialog/photo/files.vue:144
msgid "Added"
msgstr "Hinzugefügt"
@ -142,7 +142,7 @@ msgstr "Album"
msgid "Album Name"
msgstr "Album Name"
#: src/component/navigation.vue:155 src/component/navigation.vue:164
#: src/component/navigation.vue:163 src/component/navigation.vue:172
#: src/routes.js:104 src/share/routes.js:15 src/share/routes.js:22
msgid "Albums"
msgstr "Alben"
@ -208,7 +208,7 @@ msgstr ""
"Alternativ können Dateien auch direkt auf kompatible WebDAV-Server "
"hochgeladen z.B. Nextcloud."
#: src/dialog/photo/info.vue:125
#: src/dialog/photo/info.vue:133
msgid "Altitude"
msgstr "Höhe"
@ -242,13 +242,13 @@ msgstr "Speichern"
msgid "Approve"
msgstr "Übernehmen"
#: src/component/navigation.vue:103 src/component/photo/cards.vue:124
#: src/component/navigation.vue:111 src/component/photo/cards.vue:124
#: src/component/photo/clipboard.vue:155 src/pages/settings/general.vue:415
#: src/routes.js:178 src/share/photo/clipboard.vue:74
msgid "Archive"
msgstr "Archiv"
#: src/dialog/photo/info.vue:173
#: src/dialog/photo/info.vue:181
msgid "Archived"
msgstr "Archiviert"
@ -272,7 +272,7 @@ msgstr "Diesen Account wirklich löschen?"
msgid "Artist"
msgstr "Ersteller"
#: src/dialog/photo/files.vue:102
#: src/dialog/photo/files.vue:108
msgid "Aspect Ratio"
msgstr "Seitenverhältnis"
@ -308,7 +308,7 @@ msgstr "Durchsuche indizierte Dateien und Verzeichnisse."
msgid "Busy, please wait…"
msgstr "Bitte warten…"
#: src/component/navigation.vue:194 src/routes.js:117 src/routes.js:124
#: src/component/navigation.vue:202 src/routes.js:117 src/routes.js:124
msgid "Calendar"
msgstr "Kalender"
@ -358,11 +358,11 @@ msgstr "Titel, Datum, Ort und andere Metadaten können geändert werden."
msgid "Change private flag"
msgstr "Als privat markieren"
#: src/dialog/photo/info.vue:165
#: src/dialog/photo/info.vue:173
msgid "Checked"
msgstr "Geprüft"
#: src/dialog/photo/files.vue:114
#: src/dialog/photo/files.vue:120
msgid "Chroma"
msgstr "Farbsättigung"
@ -455,7 +455,7 @@ msgstr "Land"
msgid "Create album"
msgstr "Album erstellen"
#: src/dialog/photo/info.vue:141
#: src/dialog/photo/info.vue:149
msgid "Created"
msgstr "Hinzugefügt"
@ -511,7 +511,7 @@ msgstr "Unser englischer User Guide enthält eine detaillierte Anleitung."
msgid "Details"
msgstr "Details"
#: src/dialog/photo/files.vue:205
#: src/dialog/photo/files.vue:211
msgid "Dimensions"
msgstr "Auflösung"
@ -555,7 +555,7 @@ msgstr ""
msgid "Downloading…"
msgstr "Wird heruntergeladen…"
#: src/dialog/photo/files.vue:132
#: src/dialog/photo/files.vue:138
msgid "Duplicate"
msgstr "Duplikat"
@ -584,7 +584,7 @@ msgstr "Account bearbeiten"
msgid "Edit Photo"
msgstr "Foto bearbeiten"
#: src/dialog/photo/info.vue:157
#: src/dialog/photo/info.vue:165
msgid "Edited"
msgstr "Bearbeitet"
@ -596,11 +596,11 @@ msgstr "Aktiv"
msgid "English"
msgstr "Englisch"
#: src/dialog/photo/files.vue:120
#: src/dialog/photo/files.vue:126
msgid "Error"
msgstr "Fehler"
#: src/component/navigation.vue:300
#: src/component/navigation.vue:308
msgid "Errors"
msgstr "Fehler"
@ -652,11 +652,11 @@ msgstr "Schnell"
msgid "Favorite"
msgstr "Favorit"
#: src/component/navigation.vue:116 src/routes.js:150
#: src/component/navigation.vue:124 src/routes.js:150
msgid "Favorites"
msgstr "Favoriten"
#: src/model/file.js:233
#: src/model/file.js:234
msgid "File"
msgstr "Datei"
@ -709,7 +709,7 @@ msgstr "Ordner ist leer"
msgid "Folder Name"
msgstr "Ordnername"
#: src/component/navigation.vue:173 src/routes.js:130 src/routes.js:137
#: src/component/navigation.vue:181 src/routes.js:130 src/routes.js:137
msgid "Folders"
msgstr "Ordner"
@ -750,12 +750,12 @@ msgstr "Sequenzen gruppieren"
msgid "Hash"
msgstr "Prüfsumme"
#: src/component/navigation.vue:331 src/component/navigation.vue:25
#: src/component/navigation.vue:339 src/component/navigation.vue:25
#: src/pages/help.vue:4
msgid "Help"
msgstr "Hilfe"
#: src/component/navigation.vue:291
#: src/component/navigation.vue:299
msgid "Hidden"
msgstr "Unsichtbar"
@ -823,7 +823,7 @@ msgstr "Importiere %{name}…"
msgid "Importing files to originals…"
msgstr "Importiere Dateien nach Originals…"
#: src/dialog/photo/files.vue:140 src/dialog/photo/files.vue:146
#: src/dialog/photo/files.vue:146 src/dialog/photo/files.vue:152
msgid "in"
msgstr "in"
@ -871,7 +871,7 @@ msgstr "Kategorie"
msgid "Label Name"
msgstr "Name"
#: src/component/navigation.vue:252 src/dialog/photo/edit.vue:27
#: src/component/navigation.vue:260 src/dialog/photo/edit.vue:27
#: src/pages/settings/general.vue:543 src/routes.js:229
msgid "Labels"
msgstr "Kategorien"
@ -888,7 +888,7 @@ msgstr "Sprache"
msgid "Last Backup"
msgstr "Letztes Backup"
#: src/dialog/photo/details.vue:451 src/dialog/photo/info.vue:109
#: src/dialog/photo/details.vue:451 src/dialog/photo/info.vue:117
msgid "Latitude"
msgstr "Breitengrad"
@ -906,13 +906,13 @@ msgstr ""
"PhotoPrism erstellt automatisch Alben mit besonderen Momenten, Reisen und "
"Orten."
#: src/component/navigation.vue:265 src/component/navigation.vue:274
#: src/component/navigation.vue:273 src/component/navigation.vue:282
#: src/pages/settings/general.vue:6 src/pages/settings/general.vue:575
#: src/routes.js:248 src/routes.js:255 src/routes.js:262
msgid "Library"
msgstr "Dateien"
#: src/component/navigation.vue:339 src/component/navigation.vue:33
#: src/component/navigation.vue:347 src/component/navigation.vue:33
#: src/dialog/photo/details.vue:460 src/pages/about/license.vue:4
msgid "License"
msgstr "Lizenz"
@ -958,11 +958,11 @@ msgstr ""
"Warnungen und Fehler erscheinen hier, sobald PhotoPrism beschädigte Dateien "
"findet oder andere Probleme aufgetreten sind."
#: src/component/navigation.vue:365
#: src/component/navigation.vue:373
msgid "Login"
msgstr "Anmelden"
#: src/component/navigation.vue:353
#: src/component/navigation.vue:361
msgid "Logout"
msgstr "Abmelden"
@ -970,7 +970,7 @@ msgstr "Abmelden"
msgid "Logs"
msgstr "Logs"
#: src/dialog/photo/details.vue:452 src/dialog/photo/info.vue:117
#: src/dialog/photo/details.vue:452 src/dialog/photo/info.vue:125
msgid "Longitude"
msgstr "Längengrad"
@ -978,7 +978,7 @@ msgstr "Längengrad"
msgid "Magenta"
msgstr "Magenta"
#: src/dialog/photo/files.vue:108
#: src/dialog/photo/files.vue:114
msgid "Main Color"
msgstr "Hauptfarbe"
@ -994,11 +994,11 @@ msgstr "Manueller Upload"
msgid "Medium"
msgstr "Mittel"
#: src/dialog/photo/files.vue:126
#: src/dialog/photo/files.vue:132
msgid "Missing"
msgstr "Fehlend"
#: src/component/navigation.vue:207 src/pages/settings/general.vue:511
#: src/component/navigation.vue:215 src/pages/settings/general.vue:511
#: src/routes.js:91 src/routes.js:98
msgid "Moments"
msgstr "Erlebnisse"
@ -1057,7 +1057,7 @@ msgstr[1] ""
#: src/component/photo/list.vue:140 src/dialog/account/edit.vue:488
#: src/dialog/album/edit.vue:121 src/dialog/photo/files.vue:60
#: src/dialog/photo/files.vue:204 src/dialog/photo/info.vue:30
#: src/dialog/photo/files.vue:210 src/dialog/photo/info.vue:30
#: src/pages/login.vue:72 src/share/photo/list.vue:117
msgid "Name"
msgstr "Name"
@ -1084,6 +1084,7 @@ msgstr "Neueste zuerst"
#: src/dialog/photo/archive.vue:13 src/dialog/photo/info.vue:192
#: src/dialog/photo/info.vue:219 src/dialog/photo/info.vue:246
#: src/dialog/photo/info.vue:273
msgid "No"
msgstr "Nein"
@ -1178,7 +1179,7 @@ msgstr "Notizen"
msgid "Nothing to see here yet. Be patient."
msgstr "Noch nichts zu sehen. Gedulde dich noch ein bisschen."
#: src/component/navigation.vue:377 src/options/options.js:156
#: src/component/navigation.vue:385 src/options/options.js:156
msgid "Offline"
msgstr "Offline"
@ -1231,11 +1232,19 @@ msgstr "Die ursprünglichen Dateinamen werden im Index gespeichert."
msgid "Original Name"
msgstr "Originalname"
#: src/component/navigation.vue:282 src/pages/library/files.vue:6
#: src/component/navigation.vue:290 src/pages/library/files.vue:6
#: src/pages/settings/general.vue:479
msgid "Originals"
msgstr "Originale"
#: src/dialog/photo/info.vue:101
msgid "Panorama"
msgstr "Panorama"
#: src/component/navigation.vue:86
msgid "Panoramas"
msgstr "Panoramas"
#: src/dialog/account/add.vue:80 src/dialog/account/edit.vue:561
#: src/dialog/share.vue:154 src/pages/login.vue:73
msgid "Password"
@ -1253,7 +1262,7 @@ msgstr "Personen"
msgid "People you share a link with will be able to view public contents."
msgstr "Jeder mit diesem Link wird öffentliche Fotos und Videos sehen können."
#: src/model/photo.js:647
#: src/model/photo.js:648
msgid "Photo"
msgstr "Foto"
@ -1281,11 +1290,11 @@ msgstr "Fotos"
msgid "Pink"
msgstr "Pink"
#: src/dialog/photo/info.vue:101
#: src/dialog/photo/info.vue:109
msgid "Place"
msgstr "Ort"
#: src/component/navigation.vue:220 src/component/navigation.vue:229
#: src/component/navigation.vue:228 src/component/navigation.vue:237
#: src/pages/settings/general.vue:124 src/pages/settings/general.vue:671
#: src/routes.js:185 src/routes.js:191 src/routes.js:197 src/routes.js:204
msgid "Places"
@ -1332,15 +1341,21 @@ msgid "Preview"
msgstr "Voransicht"
#: src/dialog/photo/files.vue:33 src/dialog/photo/files.vue:90
#: src/dialog/photo/files.vue:198
#: src/dialog/photo/files.vue:204
msgid "Primary"
msgstr "Primärdatei"
#: src/component/navigation.vue:129 src/dialog/photo/info.vue:85
#: src/component/navigation.vue:137 src/dialog/photo/info.vue:85
#: src/routes.js:171
msgid "Private"
msgstr "Privat"
#: src/dialog/photo/files.vue:102
#, fuzzy
#| msgid "location"
msgid "Projection"
msgstr "Ort"
#: src/options/options.js:218
msgid "Purple"
msgstr "Purpur"
@ -1387,7 +1402,7 @@ msgstr "Rot"
msgid "Reload"
msgstr "Neu laden"
#: src/dialog/reload.vue:57 src/pages/settings/general.vue:377
#: src/dialog/reload.vue:58 src/pages/settings/general.vue:377
msgid "Reloading…"
msgstr "Wird neu geladen…"
@ -1428,7 +1443,7 @@ msgstr "Wiederherstellen"
msgid "Retype Password"
msgstr "Passwort wiederholen"
#: src/component/navigation.vue:94 src/routes.js:164
#: src/component/navigation.vue:102 src/routes.js:164
msgid "Review"
msgstr "Bestätigen"
@ -1445,7 +1460,7 @@ msgstr "Speichern"
msgid "Scan"
msgstr "Scan"
#: src/component/navigation.vue:86
#: src/component/navigation.vue:94
msgid "Scans"
msgstr "Scans"
@ -1502,7 +1517,7 @@ msgstr "Server"
msgid "Service URL"
msgstr "Service URL"
#: src/component/navigation.vue:314 src/component/navigation.vue:323
#: src/component/navigation.vue:322 src/component/navigation.vue:331
#: src/component/navigation.vue:8 src/component/navigation.vue:17
#: src/routes.js:269 src/routes.js:276 src/routes.js:283
msgid "Settings"
@ -1542,7 +1557,7 @@ msgstr "Server-Ereignisprotokoll anzeigen, um Fehler zu finden."
msgid "Showing all %{n} results"
msgstr "Alle %{n} Ergebnisse werden angezeigt"
#: src/model/file.js:174
#: src/model/file.js:175
msgid "Sidecar"
msgstr "Sidecar"
@ -1555,7 +1570,7 @@ msgid "Similar"
msgstr "Ähnlich"
#: src/dialog/account/edit.vue:261 src/dialog/photo/files.vue:72
#: src/dialog/photo/files.vue:206
#: src/dialog/photo/files.vue:212
msgid "Size"
msgstr "Größe"
@ -1589,11 +1604,11 @@ msgstr "Bildstapel"
msgid "Start"
msgstr "Start"
#: src/component/navigation.vue:238
#: src/component/navigation.vue:246
msgid "States"
msgstr "Länder"
#: src/dialog/photo/files.vue:208
#: src/dialog/photo/files.vue:214
msgid "Status"
msgstr "Status"
@ -1712,7 +1727,7 @@ msgid "Try again using other filters or keywords."
msgstr "Versuche es mit anderen Filtern oder Suchbegriffen."
#: src/dialog/account/edit.vue:620 src/dialog/photo/files.vue:78
#: src/dialog/photo/files.vue:207 src/dialog/photo/info.vue:15
#: src/dialog/photo/files.vue:213 src/dialog/photo/info.vue:15
msgid "Type"
msgstr "Typ"
@ -1720,17 +1735,17 @@ msgstr "Typ"
msgid "UID"
msgstr "UID"
#: src/dialog/photo/details.vue:431 src/dialog/photo/info.vue:241
#: src/model/album.js:122 src/model/photo.js:400 src/model/photo.js:414
#: src/model/photo.js:437 src/model/photo.js:451 src/model/photo.js:528
#: src/model/photo.js:541 src/options/options.js:15 src/options/options.js:29
#: src/dialog/photo/details.vue:431 src/dialog/photo/info.vue:254
#: 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
#: src/model/photo.js:542 src/options/options.js:15 src/options/options.js:29
#: src/options/options.js:43 src/options/options.js:57
#: src/options/options.js:69 src/pages/library/errors.vue:203
#: src/pages/library/errors.vue:210 src/pages/library/logs.vue:32
msgid "Unknown"
msgstr "Unbekannt"
#: src/component/navigation.vue:181 src/routes.js:143
#: src/component/navigation.vue:189 src/routes.js:143
msgid "Unsorted"
msgstr "Unsortiert"
@ -1738,7 +1753,7 @@ msgstr "Unsortiert"
msgid "Unstack"
msgstr "Entfernen"
#: src/dialog/photo/files.vue:144 src/dialog/photo/info.vue:149
#: src/dialog/photo/files.vue:150 src/dialog/photo/info.vue:157
msgid "Updated"
msgstr "Geändert"
@ -1798,12 +1813,12 @@ msgstr "Benutzeroberfläche"
msgid "Username"
msgstr "Nutzername"
#: src/model/file.js:172 src/model/photo.js:488 src/model/photo.js:502
#: src/model/file.js:173 src/model/photo.js:489 src/model/photo.js:503
#: src/options/options.js:191
msgid "Video"
msgstr "Video"
#: src/component/navigation.vue:142 src/routes.js:157
#: src/component/navigation.vue:150 src/routes.js:157
msgid "Videos"
msgstr "Videos"
@ -1851,9 +1866,10 @@ msgid "Yellow"
msgstr "Gelb"
#: src/dialog/photo/archive.vue:15 src/dialog/photo/files.vue:92
#: src/dialog/photo/files.vue:98 src/dialog/photo/files.vue:128
#: src/dialog/photo/files.vue:134 src/dialog/photo/info.vue:191
#: src/dialog/photo/files.vue:98 src/dialog/photo/files.vue:134
#: src/dialog/photo/files.vue:140 src/dialog/photo/info.vue:191
#: src/dialog/photo/info.vue:218 src/dialog/photo/info.vue:245
#: src/dialog/photo/info.vue:272
msgid "Yes"
msgstr "Ja"

File diff suppressed because one or more lines are too long

View file

@ -53,7 +53,7 @@ msgstr ""
msgid "Account"
msgstr ""
#: src/dialog/photo/info.vue:133
#: src/dialog/photo/info.vue:141
msgid "Accuracy"
msgstr ""
@ -92,7 +92,7 @@ msgstr ""
msgid "Add to album"
msgstr ""
#: src/dialog/photo/files.vue:138
#: src/dialog/photo/files.vue:144
msgid "Added"
msgstr ""
@ -139,8 +139,8 @@ msgstr ""
msgid "Album Name"
msgstr ""
#: src/component/navigation.vue:155
#: src/component/navigation.vue:164
#: src/component/navigation.vue:163
#: src/component/navigation.vue:172
#: src/routes.js:104
#: src/share/routes.js:15
#: src/share/routes.js:22
@ -210,7 +210,7 @@ msgstr ""
msgid "Alternatively, you can upload files directly to WebDAV servers like Nextcloud."
msgstr ""
#: src/dialog/photo/info.vue:125
#: src/dialog/photo/info.vue:133
msgid "Altitude"
msgstr ""
@ -243,7 +243,7 @@ msgstr ""
msgid "Approve"
msgstr ""
#: src/component/navigation.vue:103
#: src/component/navigation.vue:111
#: src/component/photo/cards.vue:124
#: src/component/photo/clipboard.vue:155
#: src/pages/settings/general.vue:415
@ -252,7 +252,7 @@ msgstr ""
msgid "Archive"
msgstr ""
#: src/dialog/photo/info.vue:173
#: src/dialog/photo/info.vue:181
msgid "Archived"
msgstr ""
@ -276,7 +276,7 @@ msgstr ""
msgid "Artist"
msgstr ""
#: src/dialog/photo/files.vue:102
#: src/dialog/photo/files.vue:108
msgid "Aspect Ratio"
msgstr ""
@ -312,7 +312,7 @@ msgstr ""
msgid "Busy, please wait…"
msgstr ""
#: src/component/navigation.vue:194
#: src/component/navigation.vue:202
#: src/routes.js:117
#: src/routes.js:124
msgid "Calendar"
@ -379,11 +379,11 @@ msgstr ""
msgid "Change private flag"
msgstr ""
#: src/dialog/photo/info.vue:165
#: src/dialog/photo/info.vue:173
msgid "Checked"
msgstr ""
#: src/dialog/photo/files.vue:114
#: src/dialog/photo/files.vue:120
msgid "Chroma"
msgstr ""
@ -493,7 +493,7 @@ msgstr ""
msgid "Create album"
msgstr ""
#: src/dialog/photo/info.vue:141
#: src/dialog/photo/info.vue:149
msgid "Created"
msgstr ""
@ -553,7 +553,7 @@ msgstr ""
msgid "Details"
msgstr ""
#: src/dialog/photo/files.vue:205
#: src/dialog/photo/files.vue:211
msgid "Dimensions"
msgstr ""
@ -608,7 +608,7 @@ msgstr ""
msgid "Downloading…"
msgstr ""
#: src/dialog/photo/files.vue:132
#: src/dialog/photo/files.vue:138
msgid "Duplicate"
msgstr ""
@ -639,7 +639,7 @@ msgstr ""
msgid "Edit Photo"
msgstr ""
#: src/dialog/photo/info.vue:157
#: src/dialog/photo/info.vue:165
msgid "Edited"
msgstr ""
@ -652,11 +652,11 @@ msgstr ""
msgid "English"
msgstr ""
#: src/dialog/photo/files.vue:120
#: src/dialog/photo/files.vue:126
msgid "Error"
msgstr ""
#: src/component/navigation.vue:300
#: src/component/navigation.vue:308
msgid "Errors"
msgstr ""
@ -706,12 +706,12 @@ msgstr ""
msgid "Favorite"
msgstr ""
#: src/component/navigation.vue:116
#: src/component/navigation.vue:124
#: src/routes.js:150
msgid "Favorites"
msgstr ""
#: src/model/file.js:233
#: src/model/file.js:234
msgid "File"
msgstr ""
@ -760,7 +760,7 @@ msgstr ""
msgid "Folder Name"
msgstr ""
#: src/component/navigation.vue:173
#: src/component/navigation.vue:181
#: src/routes.js:130
#: src/routes.js:137
msgid "Folders"
@ -804,13 +804,13 @@ msgstr ""
msgid "Hash"
msgstr ""
#: src/component/navigation.vue:331
#: src/component/navigation.vue:339
#: src/component/navigation.vue:25
#: src/pages/help.vue:4
msgid "Help"
msgstr ""
#: src/component/navigation.vue:291
#: src/component/navigation.vue:299
msgid "Hidden"
msgstr ""
@ -870,8 +870,8 @@ msgstr ""
msgid "Importing files to originals…"
msgstr ""
#: src/dialog/photo/files.vue:140
#: src/dialog/photo/files.vue:146
#: src/dialog/photo/files.vue:152
msgid "in"
msgstr ""
@ -921,7 +921,7 @@ msgstr ""
msgid "Label Name"
msgstr ""
#: src/component/navigation.vue:252
#: src/component/navigation.vue:260
#: src/dialog/photo/edit.vue:27
#: src/pages/settings/general.vue:543
#: src/routes.js:229
@ -942,7 +942,7 @@ msgid "Last Backup"
msgstr ""
#: src/dialog/photo/details.vue:451
#: src/dialog/photo/info.vue:109
#: src/dialog/photo/info.vue:117
msgid "Latitude"
msgstr ""
@ -959,8 +959,8 @@ msgstr ""
msgid "Let PhotoPrism create albums from past events."
msgstr ""
#: src/component/navigation.vue:265
#: src/component/navigation.vue:274
#: src/component/navigation.vue:273
#: src/component/navigation.vue:282
#: src/pages/settings/general.vue:6
#: src/pages/settings/general.vue:575
#: src/routes.js:248
@ -969,7 +969,7 @@ msgstr ""
msgid "Library"
msgstr ""
#: src/component/navigation.vue:339
#: src/component/navigation.vue:347
#: src/component/navigation.vue:33
#: src/dialog/photo/details.vue:460
#: src/pages/about/license.vue:4
@ -1015,11 +1015,11 @@ msgstr ""
msgid "Log messages appear here whenever PhotoPrism comes across broken files, or there are other potential issues."
msgstr ""
#: src/component/navigation.vue:365
#: src/component/navigation.vue:373
msgid "Login"
msgstr ""
#: src/component/navigation.vue:353
#: src/component/navigation.vue:361
msgid "Logout"
msgstr ""
@ -1029,7 +1029,7 @@ msgid "Logs"
msgstr ""
#: src/dialog/photo/details.vue:452
#: src/dialog/photo/info.vue:117
#: src/dialog/photo/info.vue:125
msgid "Longitude"
msgstr ""
@ -1037,7 +1037,7 @@ msgstr ""
msgid "Magenta"
msgstr ""
#: src/dialog/photo/files.vue:108
#: src/dialog/photo/files.vue:114
msgid "Main Color"
msgstr ""
@ -1053,11 +1053,11 @@ msgstr ""
msgid "Medium"
msgstr ""
#: src/dialog/photo/files.vue:126
#: src/dialog/photo/files.vue:132
msgid "Missing"
msgstr ""
#: src/component/navigation.vue:207
#: src/component/navigation.vue:215
#: src/pages/settings/general.vue:511
#: src/routes.js:91
#: src/routes.js:98
@ -1129,7 +1129,7 @@ msgstr[1] ""
#: src/dialog/account/edit.vue:488
#: src/dialog/album/edit.vue:121
#: src/dialog/photo/files.vue:60
#: src/dialog/photo/files.vue:204
#: src/dialog/photo/files.vue:210
#: src/dialog/photo/info.vue:30
#: src/pages/login.vue:72
#: src/share/photo/list.vue:117
@ -1164,6 +1164,7 @@ msgstr ""
#: src/dialog/photo/info.vue:192
#: src/dialog/photo/info.vue:219
#: src/dialog/photo/info.vue:246
#: src/dialog/photo/info.vue:273
msgid "No"
msgstr ""
@ -1246,7 +1247,7 @@ msgstr ""
msgid "Nothing to see here yet. Be patient."
msgstr ""
#: src/component/navigation.vue:377
#: src/component/navigation.vue:385
#: src/options/options.js:156
msgid "Offline"
msgstr ""
@ -1304,12 +1305,20 @@ msgstr ""
msgid "Original Name"
msgstr ""
#: src/component/navigation.vue:282
#: src/component/navigation.vue:290
#: src/pages/library/files.vue:6
#: src/pages/settings/general.vue:479
msgid "Originals"
msgstr ""
#: src/dialog/photo/info.vue:101
msgid "Panorama"
msgstr ""
#: src/component/navigation.vue:86
msgid "Panoramas"
msgstr ""
#: src/dialog/account/add.vue:80
#: src/dialog/account/edit.vue:561
#: src/dialog/share.vue:154
@ -1329,7 +1338,7 @@ msgstr ""
msgid "People you share a link with will be able to view public contents."
msgstr ""
#: src/model/photo.js:647
#: src/model/photo.js:648
msgid "Photo"
msgstr ""
@ -1354,12 +1363,12 @@ msgstr ""
msgid "Pink"
msgstr ""
#: src/dialog/photo/info.vue:101
#: src/dialog/photo/info.vue:109
msgid "Place"
msgstr ""
#: src/component/navigation.vue:220
#: src/component/navigation.vue:229
#: src/component/navigation.vue:228
#: src/component/navigation.vue:237
#: src/pages/settings/general.vue:124
#: src/pages/settings/general.vue:671
#: src/routes.js:185
@ -1412,16 +1421,20 @@ msgstr ""
#: src/dialog/photo/files.vue:33
#: src/dialog/photo/files.vue:90
#: src/dialog/photo/files.vue:198
#: src/dialog/photo/files.vue:204
msgid "Primary"
msgstr ""
#: src/component/navigation.vue:129
#: src/component/navigation.vue:137
#: src/dialog/photo/info.vue:85
#: src/routes.js:171
msgid "Private"
msgstr ""
#: src/dialog/photo/files.vue:102
msgid "Projection"
msgstr ""
#: src/options/options.js:218
msgid "Purple"
msgstr ""
@ -1511,7 +1524,7 @@ msgstr ""
msgid "Retype Password"
msgstr ""
#: src/component/navigation.vue:94
#: src/component/navigation.vue:102
#: src/routes.js:164
msgid "Review"
msgstr ""
@ -1530,7 +1543,7 @@ msgstr ""
msgid "Scan"
msgstr ""
#: src/component/navigation.vue:86
#: src/component/navigation.vue:94
msgid "Scans"
msgstr ""
@ -1600,8 +1613,8 @@ msgstr ""
msgid "Service URL"
msgstr ""
#: src/component/navigation.vue:314
#: src/component/navigation.vue:323
#: src/component/navigation.vue:322
#: src/component/navigation.vue:331
#: src/component/navigation.vue:8
#: src/component/navigation.vue:17
#: src/routes.js:269
@ -1646,7 +1659,7 @@ msgstr ""
msgid "Showing all %{n} results"
msgstr ""
#: src/model/file.js:174
#: src/model/file.js:175
msgid "Sidecar"
msgstr ""
@ -1660,7 +1673,7 @@ msgstr ""
#: src/dialog/account/edit.vue:261
#: src/dialog/photo/files.vue:72
#: src/dialog/photo/files.vue:206
#: src/dialog/photo/files.vue:212
msgid "Size"
msgstr ""
@ -1697,11 +1710,11 @@ msgstr ""
msgid "Start"
msgstr ""
#: src/component/navigation.vue:238
#: src/component/navigation.vue:246
msgid "States"
msgstr ""
#: src/dialog/photo/files.vue:208
#: src/dialog/photo/files.vue:214
msgid "Status"
msgstr ""
@ -1824,7 +1837,7 @@ msgstr ""
#: src/dialog/account/edit.vue:620
#: src/dialog/photo/files.vue:78
#: src/dialog/photo/files.vue:207
#: src/dialog/photo/files.vue:213
#: src/dialog/photo/info.vue:15
msgid "Type"
msgstr ""
@ -1834,14 +1847,14 @@ msgid "UID"
msgstr ""
#: src/dialog/photo/details.vue:431
#: src/dialog/photo/info.vue:241
#: src/dialog/photo/info.vue:254
#: src/model/album.js:122
#: src/model/photo.js:400
#: src/model/photo.js:414
#: src/model/photo.js:437
#: src/model/photo.js:451
#: src/model/photo.js:528
#: src/model/photo.js:541
#: 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
#: src/model/photo.js:542
#: src/options/options.js:15
#: src/options/options.js:29
#: src/options/options.js:43
@ -1853,7 +1866,7 @@ msgstr ""
msgid "Unknown"
msgstr ""
#: src/component/navigation.vue:181
#: src/component/navigation.vue:189
#: src/routes.js:143
msgid "Unsorted"
msgstr ""
@ -1862,8 +1875,8 @@ msgstr ""
msgid "Unstack"
msgstr ""
#: src/dialog/photo/files.vue:144
#: src/dialog/photo/info.vue:149
#: src/dialog/photo/files.vue:150
#: src/dialog/photo/info.vue:157
msgid "Updated"
msgstr ""
@ -1928,14 +1941,14 @@ msgstr ""
msgid "Username"
msgstr ""
#: src/model/file.js:172
#: src/model/photo.js:488
#: src/model/photo.js:502
#: src/model/file.js:173
#: src/model/photo.js:489
#: src/model/photo.js:503
#: src/options/options.js:191
msgid "Video"
msgstr ""
#: src/component/navigation.vue:142
#: src/component/navigation.vue:150
#: src/routes.js:157
msgid "Videos"
msgstr ""
@ -1985,11 +1998,12 @@ msgstr ""
#: src/dialog/photo/archive.vue:15
#: src/dialog/photo/files.vue:92
#: src/dialog/photo/files.vue:98
#: src/dialog/photo/files.vue:128
#: src/dialog/photo/files.vue:134
#: src/dialog/photo/files.vue:140
#: src/dialog/photo/info.vue:191
#: src/dialog/photo/info.vue:218
#: src/dialog/photo/info.vue:245
#: src/dialog/photo/info.vue:272
msgid "Yes"
msgstr ""

View file

@ -60,6 +60,7 @@ export class File extends RestModel {
Width: 0,
Height: 0,
Orientation: 0,
Projection: "",
AspectRatio: 1.0,
MainColor: "",
Colors: "",

View file

@ -56,7 +56,7 @@ export class Photo extends RestModel {
Favorite: false,
Private: false,
Scan: false,
P360: false,
Panorama: false,
TakenAt: "",
TakenAtLocal: "",
TakenSrc: "",

View file

@ -44,6 +44,12 @@ const (
RootImport = "import"
RootPath = "/"
// Panorama projections.
ProjectionDefault = ""
ProjectionEquirectangular = "equirectangular"
ProjectionCubestrip = "cubestrip"
ProjectionCylindrical = "cylindrical"
// Event names.
Updated = "updated"
Created = "created"

View file

@ -41,6 +41,7 @@ type File struct {
FileWidth int `json:"Width" yaml:"Width,omitempty"`
FileHeight int `json:"Height" yaml:"Height,omitempty"`
FileOrientation int `json:"Orientation" yaml:"Orientation,omitempty"`
FileProjection string `gorm:"type:varbinary(16);" json:"Projection,omitempty" yaml:"Projection,omitempty"`
FileAspectRatio float32 `gorm:"type:FLOAT;" json:"AspectRatio" yaml:"AspectRatio,omitempty"`
FileMainColor string `gorm:"type:varbinary(16);index;" json:"MainColor" yaml:"MainColor,omitempty"`
FileColors string `gorm:"type:varbinary(9);" json:"Colors" yaml:"Colors,omitempty"`
@ -218,3 +219,12 @@ func (m *File) NoJPEG() bool {
func (m *File) Links() Links {
return FindLinks("", m.FileUID)
}
// Panorama tests if the file seems to be a panorama image.
func (m *File) Panorama() bool {
if m.FileSidecar || m.FileWidth <= 1000 || m.FileHeight <= 500 {
return false
}
return m.FileProjection != ProjectionDefault || m.FileWidth / m.FileHeight > 2
}

View file

@ -215,7 +215,7 @@ func TestFile_Update(t *testing.T) {
}
func TestFile_Links(t *testing.T) {
t.Run("1 result", func(t *testing.T) {
t.Run("result", func(t *testing.T) {
file := FileFixturesExampleBridge
links := file.Links()
assert.Equal(t, "5jxf3jfn2k", links[0].LinkToken)
@ -223,12 +223,31 @@ func TestFile_Links(t *testing.T) {
}
func TestFile_NoJPEG(t *testing.T) {
t.Run("true", func(t *testing.T) {
file := &File{Photo: nil, FileType: "xmp", FileSize: 500}
assert.True(t, file.NoJPEG())
})
t.Run("false", func(t *testing.T) {
file := &File{Photo: nil, FileType: "jpg", FileSize: 500}
assert.False(t, file.NoJPEG())
})
t.Run("tre", func(t *testing.T) {
file := &File{Photo: nil, FileType: "xmp", FileSize: 500}
assert.True(t, file.NoJPEG())
}
func TestFile_Panorama(t *testing.T) {
t.Run("true", func(t *testing.T) {
file := &File{Photo: nil, FileType: "jpg", FileSidecar: false, FileWidth: 3000, FileHeight: 1000}
assert.True(t, file.Panorama())
})
t.Run("false", func(t *testing.T) {
file := &File{Photo: nil, FileType: "jpg", FileSidecar: false, FileWidth: 1500, FileHeight: 1000}
assert.False(t, file.Panorama())
})
t.Run("equirectangular", func(t *testing.T) {
file := &File{Photo: nil, FileType: "jpg", FileSidecar: false, FileWidth: 1500, FileHeight: 1000, FileProjection: ProjectionEquirectangular}
assert.True(t, file.Panorama())
})
t.Run("sidecar", func(t *testing.T) {
file := &File{Photo: nil, FileType: "xmp", FileSidecar: true, FileWidth: 3000, FileHeight: 1000}
assert.False(t, file.Panorama())
})
}

View file

@ -50,7 +50,7 @@ type Photo struct {
PhotoFavorite bool `json:"Favorite" yaml:"Favorite,omitempty"`
PhotoPrivate bool `json:"Private" yaml:"Private,omitempty"`
PhotoScan bool `json:"Scan" yaml:"Scan,omitempty"`
PhotoP360 bool `gorm:"column:photo_P360" json:"P360" yaml:"P360,omitempty"`
PhotoPanorama bool `json:"Panorama" yaml:"Panorama,omitempty"`
TimeZone string `gorm:"type:varbinary(64);" json:"TimeZone" yaml:"-"`
PlaceID string `gorm:"type:varbinary(42);index;default:'zz'" json:"PlaceID" yaml:"-"`
PlaceSrc string `gorm:"type:varbinary(8);" json:"PlaceSrc" yaml:"PlaceSrc,omitempty"`

View file

@ -36,7 +36,7 @@ type Photo struct {
PhotoPrivate bool `json:"Private"`
PhotoReview bool `json:"Review"`
PhotoScan bool `json:"Scan"`
PhotoP360 bool `json:"P360"`
PhotoPanorama bool `json:"Panorama"`
PhotoAltitude int `json:"Altitude"`
PhotoLat float32 `json:"Lat"`
PhotoLng float32 `json:"Lng"`

View file

@ -20,6 +20,7 @@ type PhotoSearch struct {
Video bool `form:"video"`
Photo bool `form:"photo"`
Scan bool `form:"scan"`
Panorama bool `form:"panorama"`
Duplicate bool `form:"duplicate"`
Error bool `form:"error"`
Hidden bool `form:"hidden"`

View file

@ -21,6 +21,7 @@ type Data struct {
Artist string `meta:"Artist,Creator"`
Description string `meta:"Description"`
Copyright string `meta:"Rights,Copyright"`
Projection string `meta:"ProjectionType"`
CameraMake string `meta:"CameraMake,Make"`
CameraModel string `meta:"CameraModel,Model"`
CameraOwner string `meta:"OwnerName"`

View file

@ -354,6 +354,11 @@ func (data *Data) Exif(fileName string) (err error) {
data.Description = SanitizeDescription(value)
}
if value, ok := tags["ProjectionType"]; ok {
data.AddKeyword(KeywordPanorama)
data.Projection = SanitizeString(value)
}
data.All = tags
return nil

View file

@ -287,4 +287,35 @@ func TestExif(t *testing.T) {
assert.Equal(t, "", data.LensMake)
assert.Equal(t, "", data.LensModel)
})
t.Run("panorama360.jpg", func(t *testing.T) {
data, err := Exif("testdata/panorama360.jpg")
if err != nil {
t.Fatal(err)
}
// t.Logf("all: %+v", data.All)
assert.Equal(t, "", data.Artist)
assert.Equal(t, "2020-05-24T11:55:21Z", data.TakenAt.Format("2006-01-02T15:04:05Z"))
assert.Equal(t, "2020-05-24T11:55:21Z", data.TakenAtLocal.Format("2006-01-02T15:04:05Z"))
assert.Equal(t, "", data.Title)
assert.Equal(t, "", data.Keywords)
assert.Equal(t, "", data.Description)
assert.Equal(t, "", data.Copyright)
assert.Equal(t, 3600, data.Height)
assert.Equal(t, 7200, data.Width)
assert.Equal(t, float32(0), data.Lat)
assert.Equal(t, float32(0), data.Lng)
assert.Equal(t, 0, data.Altitude)
assert.Equal(t, "1/1250", data.Exposure)
assert.Equal(t, "SAMSUNG", data.CameraMake)
assert.Equal(t, "SM-C200", data.CameraModel)
assert.Equal(t, "", data.CameraOwner)
assert.Equal(t, "", data.CameraSerial)
assert.Equal(t, 6, data.FocalLength)
assert.Equal(t, 0, int(data.Orientation))
assert.Equal(t, "", data.Projection)
})
}

View file

@ -174,6 +174,10 @@ func (data *Data) Exiftool(jsonData []byte, originalName string) (err error) {
data.InstanceID = SanitizeUID(data.InstanceID)
}
if data.Projection == "equirectangular" {
data.AddKeyword(KeywordPanorama)
}
data.Title = SanitizeTitle(data.Title)
data.Description = SanitizeDescription(data.Description)

View file

@ -430,4 +430,35 @@ func TestJSON(t *testing.T) {
assert.Len(t, data.Albums, 1)
}
})
t.Run("panorama360.json", func(t *testing.T) {
data, err := JSON("testdata/panorama360.json", "panorama360.jpg")
if err != nil {
t.Fatal(err)
}
// t.Logf("all: %+v", data.All)
assert.Equal(t, "", data.Artist)
assert.Equal(t, "2020-05-24T08:55:21Z", data.TakenAt.Format("2006-01-02T15:04:05Z"))
assert.Equal(t, "2020-05-24T11:55:21Z", data.TakenAtLocal.Format("2006-01-02T15:04:05Z"))
assert.Equal(t, "", data.Title)
assert.Equal(t, "panorama", data.Keywords)
assert.Equal(t, "", data.Description)
assert.Equal(t, "", data.Copyright)
assert.Equal(t, 3600, data.Height)
assert.Equal(t, 7200, data.Width)
assert.Equal(t, float32(59.84083), data.Lat)
assert.Equal(t, float32(30.51), data.Lng)
assert.Equal(t, 0, data.Altitude)
assert.Equal(t, "1/1250", data.Exposure)
assert.Equal(t, "SAMSUNG", data.CameraMake)
assert.Equal(t, "SM-C200", data.CameraModel)
assert.Equal(t, "", data.CameraOwner)
assert.Equal(t, "", data.CameraSerial)
assert.Equal(t, 0, data.FocalLength)
assert.Equal(t, 1, data.Orientation)
assert.Equal(t, "equirectangular", data.Projection)
})
}

View file

@ -6,9 +6,11 @@ const (
KeywordFlash = "flash"
KeywordHdr = "hdr"
KeywordBurst = "burst"
KeywordPanorama = "panorama"
KeywordEquirectangular = "equirectangular"
)
var AutoKeywords = []string{KeywordHdr, KeywordBurst}
var AutoKeywords = []string{KeywordHdr, KeywordBurst, KeywordPanorama, KeywordEquirectangular}
// AddKeyword appends a keyword if not exists.
func (data *Data) AddKeyword(w string) {

View file

@ -10,6 +10,7 @@ import (
var UnwantedDescriptions = map[string]bool{
"OLYMPUS DIGITAL CAMERA": true, // Olympus
"SAMSUNG": true, // Samsung
"rhdr": true, // Huawei
"hdrpl": true,
"fbt": true,

14
internal/meta/testdata/README.md vendored Normal file
View file

@ -0,0 +1,14 @@
# Metadata Test Files
You may use [ImageMagick](http://www.imagemagick.org/Usage/resize/#resample)
to create smaller versions of images to be stored in this repository:
```
convert original.JPG -resize 500 small.JPG
```
JSON sidecar files for testing can be created with [exiftool](https://exiftool.org/):
```
exiftool -j example.jpg > example.json
```

BIN
internal/meta/testdata/panorama360.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

116
internal/meta/testdata/panorama360.json vendored Normal file
View file

@ -0,0 +1,116 @@
[{
"SourceFile": "panorama360.jpg",
"ExifToolVersion": 12.00,
"FileName": "panorama360.jpg",
"Directory": ".",
"FileSize": "8.7 MB",
"FileModifyDate": "2020:07:16 11:19:57+02:00",
"FileAccessDate": "2020:07:16 11:20:35+02:00",
"FileInodeChangeDate": "2020:07:16 11:20:33+02:00",
"FilePermissions": "rw-r--r--",
"FileType": "JPEG",
"FileTypeExtension": "jpg",
"MIMEType": "image/jpeg",
"ExifByteOrder": "Little-endian (Intel, II)",
"Make": "SAMSUNG",
"Model": "SM-C200",
"Orientation": "Horizontal (normal)",
"XResolution": 350,
"YResolution": 350,
"ResolutionUnit": "inches",
"Software": 0.76,
"ImageDescription": "SAMSUNG ",
"ModifyDate": "2020:05:24 11:55:21",
"YCbCrPositioning": "Co-sited",
"ExposureTime": "1/1250",
"FNumber": 2.0,
"ISO": 100,
"ExifVersion": "0230",
"DateTimeOriginal": "2020:05:24 11:55:21",
"CreateDate": "2020:05:24 11:55:21",
"ComponentsConfiguration": "Y, Cb, Cr, -",
"ExposureCompensation": "+1",
"MeteringMode": "Multi-segment",
"LightSource": "Unknown",
"Flash": "No Flash",
"FocalLength": "1.2 mm",
"MakerNoteVersion": "0100",
"DeviceType": "High-end NX Camera",
"SamsungModelID": "Various Models (0x5001038)",
"SmartAlbumColor": "n/a",
"PictureWizardMode": "Standard",
"PictureWizardColor": 65535,
"PictureWizardSaturation": -4,
"PictureWizardSharpness": -4,
"PictureWizardContrast": -4,
"LocationName": "",
"WhiteBalanceSetup": "Auto",
"RawDataByteOrder": "Little-endian (Intel, II)",
"CameraTemperature": "undef",
"FaceDetect": "Off",
"FaceRecognition": "Off",
"FaceName": "",
"PitchAngle": 0,
"RollAngle": 0,
"FirmwareName": 0,
"FlashpixVersion": "0100",
"ExposureProgram": "Program AE",
"ColorSpace": "sRGB",
"SensingMethod": "One-chip color area",
"ExifImageWidth": 7200,
"ExifImageHeight": 3600,
"WhiteBalance": "Auto",
"DigitalZoomRatio": 1,
"Contrast": "Normal",
"Saturation": "Normal",
"Sharpness": "Normal",
"SceneCaptureType": "Standard",
"ExposureMode": "Auto",
"FocalLengthIn35mmFormat": "6 mm",
"InteropIndex": "R98 - DCF basic file (sRGB)",
"InteropVersion": "0100",
"GPSVersionID": "2.2.0.0",
"GPSLatitudeRef": "North",
"GPSLongitudeRef": "East",
"GPSAltitudeRef": "Above Sea Level",
"Compression": "JPEG (old-style)",
"ThumbnailOffset": 36385,
"ThumbnailLength": 19647,
"ProjectionType": "equirectangular",
"CaptureSoftware": "Samsung Gear 360 Manager Samsung Gear 360 Manager 1.2.00.8",
"StitchingSoftware": "Samsung Gear 360 Manager Samsung Gear 360 Manager 1.2.00.8",
"UsePanoramaViewer": true,
"CroppedAreaImageWidthPixels": 7200,
"CroppedAreaImageHeightPixels": 3600,
"FullPanoWidthPixels": 7200,
"FullPanoHeightPixels": 3600,
"CroppedAreaLeftPixels": 0,
"CroppedAreaTopPixels": 0,
"PoseHeadingDegrees": 0.000000,
"PosePitchDegrees": 0.000000,
"PoseRollDegrees": 0.000000,
"InitialViewHeadingDegrees": 0,
"InitialViewPitchDegrees": 0,
"InitialViewRollDegrees": 0,
"ImageWidth": 7200,
"ImageHeight": 3600,
"EncodingProcess": "Baseline DCT, Huffman coding",
"BitsPerSample": 8,
"ColorComponents": 3,
"YCbCrSubSampling": "YCbCr4:2:0 (2 2)",
"TimeStamp": "2020:05:24 10:55:22+02:00",
"Aperture": 2.0,
"ImageSize": "7200x3600",
"Megapixels": 25.9,
"ScaleFactor35efl": 5.0,
"ShutterSpeed": "1/1250",
"ThumbnailImage": "(Binary data 19647 bytes, use -b option to extract)",
"GPSLatitude": "59 deg 50' 27.00\" N",
"GPSLongitude": "30 deg 30' 36.00\" E",
"CircleOfConfusion": "0.006 mm",
"FOV": "143.1 deg",
"FocalLength35efl": "1.2 mm (35 mm equivalent: 6.0 mm)",
"GPSPosition": "59 deg 50' 27.00\" N, 30 deg 30' 36.00\" E",
"HyperfocalDistance": "0.12 m",
"LightValue": 12.3
}]

View file

@ -208,7 +208,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
return result
}
// Handle file types other than JPEG.
// Handle file types.
switch {
case m.IsJpeg():
// Color information
@ -234,6 +234,10 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
photo.PhotoResolution = megapixels
}
}
if metaData := m.MetaData(); metaData.Error == nil {
file.FileProjection = metaData.Projection
}
case m.IsXMP():
// TODO: Proof-of-concept for indexing XMP sidecar files
if data, err := meta.XMP(m.FileName()); err == nil {
@ -300,6 +304,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
file.FileHeight = metaData.ActualHeight()
file.FileAspectRatio = metaData.AspectRatio()
file.FilePortrait = metaData.Portrait()
file.FileProjection = metaData.Projection
if res := metaData.Megapixels(); res > photo.PhotoResolution {
photo.PhotoResolution = res
@ -361,6 +366,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
file.FileDuration = metaData.Duration
file.FileAspectRatio = metaData.AspectRatio()
file.FilePortrait = metaData.Portrait()
file.FileProjection = metaData.Projection
if res := metaData.Megapixels(); res > photo.PhotoResolution {
photo.PhotoResolution = res
@ -397,6 +403,10 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
file.FileMissing = false
file.FileError = ""
if file.Panorama() {
photo.PhotoPanorama = true
}
// primary files are used for rendering thumbnails and image classification (plus sidecar files if they exist)
if file.FilePrimary {
primaryFile = file

View file

@ -15,7 +15,9 @@ func TestMediaFile_Exif_JPEG(t *testing.T) {
t.Run("elephants.jpg", func(t *testing.T) {
img, err := NewMediaFile(conf.ExamplesPath() + "/elephants.jpg")
assert.Nil(t, err)
if err != nil {
t.Fatal(err)
}
data := img.MetaData()
@ -52,7 +54,9 @@ func TestMediaFile_Exif_JPEG(t *testing.T) {
t.Run("fern_green.jpg", func(t *testing.T) {
img, err := NewMediaFile(conf.ExamplesPath() + "/fern_green.jpg")
assert.Nil(t, err)
if err != nil {
t.Fatal(err)
}
info := img.MetaData()
@ -81,10 +85,13 @@ func TestMediaFile_Exif_JPEG(t *testing.T) {
t.Logf("UTC: %s", info.TakenAt.String())
t.Logf("Local: %s", info.TakenAtLocal.String())
})
t.Run("blue-go-video.mp4", func(t *testing.T) {
img, err := NewMediaFile(conf.ExamplesPath() + "/blue-go-video.mp4")
assert.Nil(t, err)
if err != nil {
t.Fatal(err)
}
info := img.MetaData()
@ -92,6 +99,41 @@ func TestMediaFile_Exif_JPEG(t *testing.T) {
assert.IsType(t, meta.Data{}, info)
})
t.Run("panorama360.jpg", func(t *testing.T) {
img, err := NewMediaFile("testdata/panorama360.jpg")
if err != nil {
t.Fatal(err)
}
data := img.MetaData()
assert.Empty(t, err)
assert.IsType(t, meta.Data{}, data)
assert.Equal(t, "", data.Artist)
assert.Equal(t, "2020-05-24T08:55:21Z", data.TakenAt.Format("2006-01-02T15:04:05Z"))
assert.Equal(t, "2020-05-24T11:55:21Z", data.TakenAtLocal.Format("2006-01-02T15:04:05Z"))
assert.Equal(t, "", data.Title)
assert.Equal(t, "panorama", data.Keywords)
assert.Equal(t, "", data.Description)
assert.Equal(t, "", data.Copyright)
assert.Equal(t, 3600, data.Height)
assert.Equal(t, 7200, data.Width)
assert.Equal(t, float32(59.84083), data.Lat)
assert.Equal(t, float32(30.51), data.Lng)
assert.Equal(t, 0, data.Altitude)
assert.Equal(t, "1/1250", data.Exposure)
assert.Equal(t, "SAMSUNG", data.CameraMake)
assert.Equal(t, "SM-C200", data.CameraModel)
assert.Equal(t, "", data.CameraOwner)
assert.Equal(t, "", data.CameraSerial)
assert.Equal(t, 6, data.FocalLength)
assert.Equal(t, 1, data.Orientation)
assert.Equal(t, "equirectangular", data.Projection)
})
}
func TestMediaFile_Exif_DNG(t *testing.T) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

View file

@ -0,0 +1,116 @@
[{
"SourceFile": "panorama360.jpg",
"ExifToolVersion": 12.00,
"FileName": "panorama360.jpg",
"Directory": ".",
"FileSize": "8.7 MB",
"FileModifyDate": "2020:07:16 11:19:57+02:00",
"FileAccessDate": "2020:07:16 11:20:35+02:00",
"FileInodeChangeDate": "2020:07:16 11:20:33+02:00",
"FilePermissions": "rw-r--r--",
"FileType": "JPEG",
"FileTypeExtension": "jpg",
"MIMEType": "image/jpeg",
"ExifByteOrder": "Little-endian (Intel, II)",
"Make": "SAMSUNG",
"Model": "SM-C200",
"Orientation": "Horizontal (normal)",
"XResolution": 350,
"YResolution": 350,
"ResolutionUnit": "inches",
"Software": 0.76,
"ImageDescription": "SAMSUNG ",
"ModifyDate": "2020:05:24 11:55:21",
"YCbCrPositioning": "Co-sited",
"ExposureTime": "1/1250",
"FNumber": 2.0,
"ISO": 100,
"ExifVersion": "0230",
"DateTimeOriginal": "2020:05:24 11:55:21",
"CreateDate": "2020:05:24 11:55:21",
"ComponentsConfiguration": "Y, Cb, Cr, -",
"ExposureCompensation": "+1",
"MeteringMode": "Multi-segment",
"LightSource": "Unknown",
"Flash": "No Flash",
"FocalLength": "1.2 mm",
"MakerNoteVersion": "0100",
"DeviceType": "High-end NX Camera",
"SamsungModelID": "Various Models (0x5001038)",
"SmartAlbumColor": "n/a",
"PictureWizardMode": "Standard",
"PictureWizardColor": 65535,
"PictureWizardSaturation": -4,
"PictureWizardSharpness": -4,
"PictureWizardContrast": -4,
"LocationName": "",
"WhiteBalanceSetup": "Auto",
"RawDataByteOrder": "Little-endian (Intel, II)",
"CameraTemperature": "undef",
"FaceDetect": "Off",
"FaceRecognition": "Off",
"FaceName": "",
"PitchAngle": 0,
"RollAngle": 0,
"FirmwareName": 0,
"FlashpixVersion": "0100",
"ExposureProgram": "Program AE",
"ColorSpace": "sRGB",
"SensingMethod": "One-chip color area",
"ExifImageWidth": 7200,
"ExifImageHeight": 3600,
"WhiteBalance": "Auto",
"DigitalZoomRatio": 1,
"Contrast": "Normal",
"Saturation": "Normal",
"Sharpness": "Normal",
"SceneCaptureType": "Standard",
"ExposureMode": "Auto",
"FocalLengthIn35mmFormat": "6 mm",
"InteropIndex": "R98 - DCF basic file (sRGB)",
"InteropVersion": "0100",
"GPSVersionID": "2.2.0.0",
"GPSLatitudeRef": "North",
"GPSLongitudeRef": "East",
"GPSAltitudeRef": "Above Sea Level",
"Compression": "JPEG (old-style)",
"ThumbnailOffset": 36385,
"ThumbnailLength": 19647,
"ProjectionType": "equirectangular",
"CaptureSoftware": "Samsung Gear 360 Manager Samsung Gear 360 Manager 1.2.00.8",
"StitchingSoftware": "Samsung Gear 360 Manager Samsung Gear 360 Manager 1.2.00.8",
"UsePanoramaViewer": true,
"CroppedAreaImageWidthPixels": 7200,
"CroppedAreaImageHeightPixels": 3600,
"FullPanoWidthPixels": 7200,
"FullPanoHeightPixels": 3600,
"CroppedAreaLeftPixels": 0,
"CroppedAreaTopPixels": 0,
"PoseHeadingDegrees": 0.000000,
"PosePitchDegrees": 0.000000,
"PoseRollDegrees": 0.000000,
"InitialViewHeadingDegrees": 0,
"InitialViewPitchDegrees": 0,
"InitialViewRollDegrees": 0,
"ImageWidth": 7200,
"ImageHeight": 3600,
"EncodingProcess": "Baseline DCT, Huffman coding",
"BitsPerSample": 8,
"ColorComponents": 3,
"YCbCrSubSampling": "YCbCr4:2:0 (2 2)",
"TimeStamp": "2020:05:24 10:55:22+02:00",
"Aperture": 2.0,
"ImageSize": "7200x3600",
"Megapixels": 25.9,
"ScaleFactor35efl": 5.0,
"ShutterSpeed": "1/1250",
"ThumbnailImage": "(Binary data 19647 bytes, use -b option to extract)",
"GPSLatitude": "59 deg 50' 27.00\" N",
"GPSLongitude": "30 deg 30' 36.00\" E",
"CircleOfConfusion": "0.006 mm",
"FOV": "143.1 deg",
"FocalLength35efl": "1.2 mm (35 mm equivalent: 6.0 mm)",
"GPSPosition": "59 deg 50' 27.00\" N, 30 deg 30' 36.00\" E",
"HyperfocalDistance": "0.12 m",
"LightValue": 12.3
}]

View file

@ -40,7 +40,7 @@ type PhotoResult struct {
PhotoQuality int `json:"Quality"`
PhotoResolution int `json:"Resolution"`
PhotoScan bool `json:"Scan"`
PhotoP360 bool `json:"P360"`
PhotoPanorama bool `json:"Panorama"`
CameraID uint `json:"CameraID"` // Camera
CameraSerial string `json:"CameraSerial,omitempty"`
CameraSrc string `json:"CameraSrc,omitempty"`
@ -76,6 +76,7 @@ type PhotoResult struct {
FileMime string `json:"-"`
FileSize int64 `json:"-"`
FileOrientation int `json:"-"`
FileProjection string `json:"-"`
FileAspectRatio float32 `json:"-"`
FileColors string `json:"-"`
FileChroma uint8 `json:"-"`

View file

@ -29,7 +29,7 @@ func PhotoSearch(f form.PhotoSearch) (results PhotoResults, count int, err error
files.id AS file_id, files.file_uid, files.file_primary, files.file_missing, files.file_name,
files.file_root, files.file_hash, files.file_codec, files.file_type, files.file_mime, files.file_width,
files.file_height, files.file_aspect_ratio, files.file_orientation, files.file_main_color,
files.file_colors, files.file_luminance, files.file_chroma,
files.file_colors, files.file_luminance, files.file_chroma, files.file_projection,
files.file_diff, files.file_video, files.file_duration, files.file_size,
cameras.camera_make, cameras.camera_model,
lenses.lens_make, lenses.lens_model,
@ -194,6 +194,10 @@ func PhotoSearch(f form.PhotoSearch) (results PhotoResults, count int, err error
s = s.Where("photos.photo_scan = 1")
}
if f.Panorama {
s = s.Where("photos.photo_panorama = 1")
}
if f.Country != "" {
s = s.Where("photos.photo_country IN (?)", strings.Split(strings.ToLower(f.Country), ","))
}