Add search filter for content that has no album #377

Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
Michael Mayer 2020-07-05 14:48:49 +02:00
parent 7787988e25
commit 581404648c
44 changed files with 704 additions and 581 deletions

View file

@ -1,8 +1,9 @@
{
"name": "photoprism",
"version": "1.0.0",
"description": "Single-page Web app frontend for PhotoPrism",
"description": "PhotoPrism Progressive Web App (PWA)",
"author": "Michael Mayer",
"license": "AGPL-3.0",
"private": true,
"scripts": {
"watch": "webpack --watch",

View file

@ -69,7 +69,7 @@ const Notify = {
}
},
wait: function () {
this.warn($gettext("Busy, please wait..."));
this.warn($gettext("Busy, please wait"));
},
};

View file

@ -118,7 +118,7 @@ export default class Util {
length = 100;
}
if (ending == null) {
ending = "...";
ending = "";
}
if (str.length > length) {
return str.substring(0, length - ending.length) + ending;

View file

@ -122,7 +122,7 @@
methods: {
editDialog() {
if (this.selection.length !== 1) {
this.$notify.error("select one album to edit");
this.$notify.error(this.$gettext("You may only select one item"));
return;
}
@ -135,7 +135,7 @@
},
shareDialog() {
if (this.selection.length !== 1) {
this.$notify.error("select one album to share");
this.$notify.error(this.$gettext("You may only select one item"));
return;
}
@ -178,7 +178,7 @@
this.expanded = false;
},
onDownload(path) {
Notify.success(this.$gettext("Downloading..."));
Notify.success(this.$gettext("Downloading"));
const link = document.createElement('a')
link.href = path;
link.download = "album.zip";

View file

@ -103,7 +103,7 @@
this.expanded = false;
},
onDownload(path) {
Notify.success(this.$gettext("Downloading..."));
Notify.success(this.$gettext("Downloading"));
const link = document.createElement('a')
link.href = path;
link.download = "photos.zip";

View file

@ -129,7 +129,7 @@
this.expanded = false;
},
onDownload(path) {
Notify.success(this.$gettext("Downloading..."));
Notify.success(this.$gettext("Downloading"));
const link = document.createElement('a')
link.href = path;
link.download = "label.zip";

View file

@ -90,6 +90,14 @@
</v-list-tile-content>
</v-list-tile>
<v-list-tile :to="{name: 'photos', query: { q: 'analog:true' }}" :exact="true" @click="">
<v-list-tile-content>
<v-list-tile-title>
<translate>Film Scans</translate>
</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
<v-list-tile to="/review" @click="" v-if="$config.feature('review')"
class="nav-review">
<v-list-tile-content>
@ -178,6 +186,14 @@
class="nav-count">{{ config.count.folders }}</span></v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
<v-list-tile to="/unsorted" class="nav-unsorted">
<v-list-tile-content>
<v-list-tile-title>
<translate key="Unsorted">Unsorted</translate>
</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</v-list-group>
<v-list-tile :to="{ name: 'calendar' }" @click="" class="nav-calendar">
@ -376,7 +392,7 @@
<v-list-tile-content>
<v-list-tile-title class="text--warning">
<translate key="Connecting">Connecting...</translate>
<translate key="Offline">Offline</translate>
</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>

View file

@ -3,15 +3,15 @@
<v-card v-if="photos.length === 0" class="p-photos-empty secondary-light lighten-1 ma-1" flat>
<v-card-title primary-title>
<div>
<h3 class="title mb-3">
<h3 class="title ma-0 pa-0">
<translate>No photos or videos found</translate>
</h3>
<div>
<p class="mt-4 mb-0 pa-0">
<translate>Try using other terms and search options such as category, country and camera.</translate>
<span v-show="$config.feature('review')">
</p>
<p v-if="$config.feature('review')" class="mt-2 mb-0 pa-0">
<translate>Non-photographic and low-quality images require a review before they appear in search results.</translate>
</span>
</div>
</p>
</div>
</v-card-title>
</v-card>

View file

@ -220,7 +220,7 @@
this.expanded = false;
},
onDownload(path) {
Notify.success(this.$gettext("Downloading..."));
Notify.success(this.$gettext("Downloading"));
const link = document.createElement('a')
link.href = path;
link.download = "photos.zip";

View file

@ -3,15 +3,15 @@
<v-card v-if="photos.length === 0" class="p-photos-empty secondary-light lighten-1 ma-1" flat>
<v-card-title primary-title>
<div>
<h3 class="title mb-3">
<h3 class="title ma-0 pa-0">
<translate>No photos or videos found</translate>
</h3>
<div>
<p class="mt-4 mb-0 pa-0">
<translate>Try using other terms and search options such as category, country and camera.</translate>
<span v-show="$config.feature('review')">
</p>
<p v-if="$config.feature('review')" class="mt-2 mb-0 pa-0">
<translate>Non-photographic and low-quality images require a review before they appear in search results.</translate>
</span>
</div>
</p>
</div>
</v-card-title>
</v-card>

View file

@ -143,7 +143,7 @@
return;
}
Notify.success(this.$gettext("Downloading..."));
Notify.success(this.$gettext("Downloading"));
let photo = new Photo();
photo.find(this.item.uid).then((p) => {
p.downloadAll();

View file

@ -48,15 +48,6 @@
color="secondary-dark"
class="input-category"
>
<template v-slot:no-data>
<v-list-tile>
<v-list-tile-content>
<v-list-tile-title>
<translate>Press enter to create a new category.</translate>
</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</template>
</v-combobox>
</v-flex>
<v-flex xs12 md6 pa-2>

View file

@ -130,6 +130,19 @@
></v-switch>
</td>
</tr>
<tr>
<td>
<translate key="Analog">Analog</translate>
</td>
<td>
<v-switch
@change="save"
hide-details
v-model="model.Analog"
:label="model.Analog ? $gettext('Yes') : $gettext('No')"
></v-switch>
</td>
</tr>
<tr>
<td>
<translate key="Created">Created</translate>

View file

@ -20,8 +20,7 @@
<template v-slot:header>
<button class="text-xs-left action-url ml-0 mt-0 mb-0 pa-0 mr-2" @click.stop="copyUrl(link)" style="user-select: none;">
<v-icon size="16" class="pr-1">link</v-icon>
/s/<strong style="font-weight: 500;" v-if="link.Token">{{ link.Token.toLowerCase()
}}</strong><span v-else>...</span>
/s/<strong style="font-weight: 500;" v-if="link.Token">{{ link.getToken() }}</strong><span v-else></span>
</button>
</template>
<v-card>
@ -62,6 +61,7 @@
color="secondary-dark"
v-model="link.Token"
class="input-secret"
mask="n"
></v-text-field>
</v-flex>
<!-- v-flex xs12 sm6 class="pa-2">
@ -97,9 +97,10 @@
</v-expansion-panel>
<v-container fluid text-xs-left class="pb-0 pt-3 pr-0 pl-0 caption">
People you share a link with will be able to view the {{model.modelName().toLowerCase()}}.<!-- TODO: translate-->
<translate>A click will copy it to your clipboard. Any private photos remain private.
Alternatively, you can upload files directly to WebDAV servers like Nextcloud.</translate>
<translate :translate-params="{name: model.modelName()}">People you share a link with will be able to view public contents.</translate>
<translate>A click will copy it to your clipboard.</translate>
<translate>Any private photos and videos remain private and won't be shared.</translate>
<translate>Alternatively, you can upload files directly to WebDAV servers like Nextcloud.</translate>
</v-container>
</v-card-text>
<v-card-actions class="pt-0">

View file

@ -21,7 +21,7 @@
<v-select
color="secondary-dark"
hide-details hide-no-data flat
:label="labels.account"
:label="$gettext('Account')"
item-text="AccName"
item-value="ID"
@change="onChange"
@ -44,21 +44,21 @@
:disabled="loading || noAccounts"
item-text="abs"
item-value="abs"
:label="labels.path"
:label="$gettext('Folder')"
>
</v-autocomplete>
</v-flex>
<v-flex xs12 text-xs-right class="pt-4">
<v-btn @click.stop="cancel" depressed color="secondary-light" class="action-cancel ml-0 mt-0 mb-0 mr-2">
<translate key="Cancel">Cancel</translate>
<translate>Cancel</translate>
</v-btn>
<v-btn color="secondary-dark" depressed dark @click.stop="setup"
class="action-setup ma-0" v-if="noAccounts">
<span>{{ labels.setup }}</span>
<translate>Setup</translate>
</v-btn>
<v-btn color="secondary-dark" depressed dark @click.stop="confirm"
class="action-upload ma-0" v-else>
<span>{{ labels.upload }}</span>
<translate>Upload</translate>
</v-btn>
</v-flex>
</v-layout>
@ -88,12 +88,6 @@
],
pathItems: [],
newPath: "",
labels: {
account: this.$gettext("Account"),
path: this.$gettext("Folder"),
upload: this.$gettext("Upload"),
setup: this.$gettext("Setup"),
}
}
},
methods: {
@ -115,9 +109,9 @@
this.loading = false;
if (files.length === 1) {
this.$notify.success("One photo shared");
this.$notify.success("One file uploaded");
} else {
this.$notify.success(this.$gettextInterpolate(this.$gettext("%{n} photos shared"), {n: files.length}));
this.$notify.success(this.$gettextInterpolate(this.$gettext("%{n} files uploaded"), {n: files.length}));
}
this.$emit('confirm', this.account);

View file

@ -50,20 +50,18 @@
</v-combobox>
<span v-else-if="failed"><translate key="Upload failed">Upload failed</translate></span>
<span v-else-if="total > 0 && completed < 100">
<translate key="Uploading">Uploading</translate> {{current}} <translate key="of">of</translate> {{total}}...
<translate :translate-params="{n: current, t: total}">Uploading %{n} of %{t}</translate>
</span>
<span v-else-if="indexing"><translate key="Upload complete">Upload complete. Indexing...</translate></span>
<span v-else-if="indexing"><translate key="Upload complete">Upload complete. Indexing</translate></span>
<span v-else-if="completed === 100"><translate key="Done">Done.</translate></span>
</p>
<v-progress-linear color="secondary-dark" v-model="completed"
:indeterminate="indexing"></v-progress-linear>
<p class="body-1" v-if="safe">
<translate>Please don't upload photos containing offensive content. Uploads
that may contain such images will be rejected automatically.</translate>
<translate>Please don't upload photos containing offensive content.</translate>
<translate>Uploads that may contain such images will be rejected automatically.</translate>
</p>
<p class="body-1" v-if="review">
@ -140,7 +138,7 @@
},
cancel() {
if (this.busy) {
Notify.info(this.$gettext("Uploading photos..."));
Notify.info(this.$gettext("Uploading photos"));
return;
}
@ -148,7 +146,7 @@
},
confirm() {
if (this.busy) {
Notify.info(this.$gettext("Uploading photos..."));
Notify.info(this.$gettext("Uploading photos"));
return;
}
@ -189,7 +187,7 @@
this.completed = 0;
this.uploads = [];
Notify.info(this.$gettext("Uploading photos..."));
Notify.info(this.$gettext("Uploading photos"));
let addToAlbums = [];

View file

@ -85,7 +85,7 @@ export class File extends RestModel {
}
if (truncate) {
result = Util.truncate(result, truncate, "...");
result = Util.truncate(result, truncate, "");
}
return result;

View file

@ -72,7 +72,7 @@ export class Folder extends RestModel {
}
if(truncate) {
result = Util.truncate(result, truncate, "...");
result = Util.truncate(result, truncate, "");
}
return result;

View file

@ -51,11 +51,15 @@ export default class Link extends Model {
};
}
getToken() {
return this.Token.toLowerCase().trim();
}
url() {
let token = this.Token.toLowerCase();
let token = this.getToken();
if(!token) {
token = "...";
token = "";
}
if(this.hasSlug()) {
@ -66,7 +70,7 @@ export default class Link extends Model {
}
caption() {
return `/s/${this.Token.toLowerCase()}`;
return `/s/${this.getToken()}`;
}
getId() {

View file

@ -53,6 +53,7 @@ export class Photo extends RestModel {
Type: TypeImage,
Favorite: false,
Private: false,
Analog: false,
TakenAt: "",
TakenAtLocal: "",
TakenSrc: "",
@ -127,7 +128,7 @@ export class Photo extends RestModel {
let result = this.fileBase(this.FileName ? this.FileName : this.mainFile().Name);
if (truncate) {
result = Util.truncate(result, truncate, "...");
result = Util.truncate(result, truncate, "");
}
return result;

View file

@ -102,6 +102,10 @@ export class Rest extends Model {
updateLink(link) {
let values = link.getValues(false);
if(link.Token) {
values["Token"] = link.getToken();
}
if(link.Password) {
values["Password"] = link.Password;
}

View file

@ -78,9 +78,10 @@
<h2 class="py-2 subheading"><translate>Trademarks</translate></h2>
<p class="body-1"><translate>PhotoPrism is a registered trademark of Michael Mayer. You may use it as required to
describe our software, run your own server, for educational purposes, but not for offering commercial
goods, products, or services without prior written permission. In other words, please ask.</translate></p>
<p class="body-1">
<translate>PhotoPrism is a registered trademark of Michael Mayer.</translate>
<translate>You may use it as required to describe our software, run your own server, for educational purposes, but not for offering commercial goods, products, or services without prior written permission. In other words, please ask.</translate>
</p>
</v-container>
<p-about-footer></p-about-footer>

View file

@ -155,13 +155,13 @@
<v-card-text class="pl-3 pr-3 pt-0 pb-3 p-album-desc"
v-else-if="album.Type === 'album'">
<div v-if="album.PhotoCount === 1" class="caption">
<translate>Contains one photo.</translate>
<translate>Contains one entry.</translate>
</div>
<div v-else-if="album.PhotoCount > 0" class="caption">
<translate :translate-params="{n: album.PhotoCount}">Contains %{n} photos.</translate>
<translate :translate-params="{n: album.PhotoCount}">Contains %{n} entries.</translate>
</div>
<button v-else @click.stop.prevent="$router.push({name: 'photos'})" class="caption">
<translate>Add photos from search results by selecting them.</translate>
<translate>Add photos or videos from search results by selecting them.</translate>
</button>
</v-card-text>
</v-card>

View file

@ -35,12 +35,10 @@
<v-card-title primary-title>
<div>
<h3 class="title mb-3">
<translate key="nofolders">No files matched your search</translate>
<translate>No files matched your search</translate>
</h3>
<div>
<translate key="tryagain">Please re-index your originals if a file you expect is
missing.
</translate>
<translate>Please re-index your originals if a file you expect is missing.</translate>
</div>
</div>
</v-card-title>
@ -405,13 +403,17 @@
this.breadcrumbs = this.getBreadcrumbs();
if (response.count === 0) {
this.$notify.warn(this.$gettext('Directory is empty'));
} else if (response.count === 1) {
this.$notify.info(this.$gettext('One entry found'));
this.$notify.warn(this.$gettext('Folder is empty'));
} else if (response.files === 1) {
this.$notify.info(this.$gettext('One file found'));
} else if (response.files === 0 && response.folders === 1) {
this.$notify.info(this.$gettext('One folder found'));
} else if (response.files === 0 && response.folders > 1) {
this.$notify.info(this.$gettextInterpolate(this.$gettext("%{n} folders found"), {n: response.folders}));
} else if (response.files < this.files.limit) {
this.$notify.info(response.count + this.$gettext(' entries found'));
this.$notify.info(this.$gettextInterpolate(this.$gettext("Folder contains %{n} files"), {n: response.files}));
} else {
this.$notify.warn(this.$gettext('Too many files in folder, showing first') + ` ${response.files}`);
this.$notify.warn(this.$gettextInterpolate(this.$gettext("Limit reached, showing first %{n} files"), {n: response.files}));
}
}).finally(() => {
this.dirty = false;

View file

@ -3,11 +3,10 @@
<v-form ref="form" class="p-photo-import" lazy-validation @submit.prevent="submit" dense>
<v-container fluid>
<p class="subheading">
<span v-if="fileName">{{ $gettext('Importing') }} {{fileName}}...</span>
<span v-else-if="busy">{{ $gettext('Importing files to originals...') }}</span>
<span v-else-if="completed">{{ $gettext('Done.') }}</span>
<span v-else-if="settings.import.move">{{ $gettext('Press button to start moving...') }}</span>
<span v-else>{{ $gettext('Press button to start copying...') }}</span>
<span v-if="fileName"><translate :translate-params="{name: fileName}">Importing %{name}</translate></span>
<span v-else-if="busy"><translate>Importing files to originals</translate></span>
<span v-else-if="completed"><translate>Done.</translate></span>
<span v-else><translate>Press button to start importing</translate></span>
</p>
<v-autocomplete
@ -209,7 +208,7 @@
found = true;
}
this.dirs.push({path: folders[i].Path, name: "/" + Util.truncate(folders[i].Path, 100, "...")});
this.dirs.push({path: folders[i].Path, name: "/" + Util.truncate(folders[i].Path, 100, "")});
}
if (!found) {

View file

@ -3,10 +3,10 @@
<v-form ref="form" class="p-photo-index" lazy-validation @submit.prevent="submit" dense>
<v-container fluid>
<p class="subheading">
<span v-if="fileName">{{ action }} {{ fileName }}...</span>
<span v-else-if="busy">{{ $gettext('Indexing photos and sidecar files...') }}</span>
<span v-else-if="completed">{{ $gettext('Done.') }}</span>
<span v-else>{{ $gettext('Press button to start indexing...') }}</span>
<span v-if="fileName">{{ action }} {{ fileName }}</span>
<span v-else-if="busy"><translate>Indexing media and sidecar files</translate></span>
<span v-else-if="completed"><translate>Done.</translate></span>
<span v-else><translate>Press button to start indexing</translate></span>
</p>
<v-autocomplete
@ -173,19 +173,19 @@
switch (type) {
case "indexing":
this.action = "Indexing";
this.action = this.$gettext("Indexing");
this.busy = true;
this.completed = 0;
this.fileName = data.fileName;
break;
case "converting":
this.action = "Converting";
this.action = this.$gettext("Converting");
this.busy = true;
this.completed = 0;
this.fileName = data.fileName;
break;
case "thumbnails":
this.action = "Creating thumbnails for";
this.action = this.$gettext("Creating thumbnails for");
this.busy = true;
this.completed = 0;
this.fileName = data.fileName;
@ -217,7 +217,7 @@
found = true;
}
this.dirs.push({path: folders[i].Path, name: "/" + Util.truncate(folders[i].Path, 100, "...")});
this.dirs.push({path: folders[i].Path, name: "/" + Util.truncate(folders[i].Path, 100, "")});
}
if (!found) {

View file

@ -20,7 +20,7 @@
v-model="settings.features.private"
color="secondary-dark"
:label="$gettext('Hide Private')"
:hint="$gettext('Exclude photos marked as private from search results, shared albums, labels and places.')"
:hint="$gettext('Exclude content marked as private from search results, shared albums, labels and places.')"
prepend-icon="lock"
persistent-hint
>
@ -374,7 +374,7 @@
this.settings.save().then((s) => {
if (reload) {
this.$notify.info(this.$gettext("Reloading..."));
this.$notify.info(this.$gettext("Reloading"));
this.$notify.blockUI();
setTimeout(() => window.location.reload(), 100);
} else {

Binary file not shown.

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -136,6 +136,13 @@ export default [
component: AlbumPhotos,
meta: {title: $gettext("Folders"), auth: true},
},
{
name: "unsorted",
path: "/unsorted",
component: Photos,
meta: {title: $gettext("Unsorted"), auth: true},
props: {staticFilter: {unsorted: true}},
},
{
name: "favorites",
path: "/favorites",

View file

@ -90,7 +90,7 @@
this.expanded = false;
},
onDownload(path) {
Notify.success(this.$gettext("Downloading..."));
Notify.success(this.$gettext("Downloading"));
const link = document.createElement('a')
link.href = path;
link.download = "album.zip";

View file

@ -3,15 +3,12 @@
<v-card v-if="photos.length === 0" class="p-photos-empty secondary-light lighten-1 ma-1" flat>
<v-card-title primary-title>
<div>
<h3 class="title mb-3">
<h3 class="title ma-0 pa-0">
<translate>No photos or videos found</translate>
</h3>
<div>
<p class="mt-4 mb-0 pa-0">
<translate>Try using other terms and search options such as category, country and camera.</translate>
<span v-show="$config.feature('review')">
<translate>Non-photographic and low-quality images require a review before they appear in search results.</translate>
</span>
</div>
</p>
</div>
</v-card-title>
</v-card>

View file

@ -94,7 +94,7 @@
this.expanded = false;
},
onDownload(path) {
Notify.success(this.$gettext("Downloading..."));
Notify.success(this.$gettext("Downloading"));
const link = document.createElement('a')
link.href = path;
link.download = "photos.zip";

View file

@ -3,15 +3,12 @@
<v-card v-if="photos.length === 0" class="p-photos-empty secondary-light lighten-1 ma-1" flat>
<v-card-title primary-title>
<div>
<h3 class="title mb-3">
<h3 class="title ma-0 pa-0">
<translate>No photos or videos found</translate>
</h3>
<div>
<p class="mt-4 mb-0 pa-0">
<translate>Try using other terms and search options such as category, country and camera.</translate>
<span v-show="$config.feature('review')">
<translate>Non-photographic and low-quality images require a review before they appear in search results.</translate>
</span>
</div>
</p>
</div>
</v-card-title>
</v-card>

View file

@ -26,7 +26,7 @@ describe("model/file", () => {
const result = file.baseName();
assert.equal(result, "IMG123.jpg");
const result2 = file.baseName(8);
assert.equal(result2, "IMG12...");
assert.equal(result2, "IMG123.…");
});
it("should return true", () => {

View file

@ -70,7 +70,7 @@ describe("model/folder", () => {
const result = folder.baseName();
assert.equal(result, "10-Halloween");
const result2 = folder.baseName(8);
assert.equal(result2, "10-Ha...");
assert.equal(result2, "10-Hall…");
});
it("should return false", () => {

View file

@ -31,7 +31,7 @@ describe("model/link", () => {
const values2 = {UID: 5, Token: "", Share: "family"};
const link2 = new Link(values2);
const result2 = link2.url();
assert.equal(result2, "http://localhost:9876/s/.../family");
assert.equal(result2, "http://localhost:9876/s//family");
});
it("should get link caption", () => {

View file

@ -285,7 +285,7 @@ describe("model/photo", () => {
const result = photo.baseName();
assert.equal(result, "superCuteKitten.jpg");
const result2 = photo.baseName(5);
assert.equal(result2, "su...");
assert.equal(result2, "supe…");
});
it("should refresh file attributes", () => {

View file

@ -50,6 +50,7 @@ type Photo struct {
OriginalName string `gorm:"type:varbinary(768);" json:"OriginalName" yaml:"OriginalName,omitempty"`
PhotoFavorite bool `json:"Favorite" yaml:"Favorite,omitempty"`
PhotoPrivate bool `json:"Private" yaml:"Private,omitempty"`
PhotoAnalog bool `json:"Analog" yaml:"Analog,omitempty"`
TimeZone string `gorm:"type:varbinary(64);" json:"TimeZone" yaml:"-"`
PlaceID string `gorm:"type:varbinary(42);index;" json:"PlaceID" yaml:"-"`
LocationID string `gorm:"type:varbinary(42);index;" json:"LocationID" yaml:"-"`

View file

@ -19,6 +19,7 @@ type PhotoSearch struct {
Primary bool `form:"primary"`
Video bool `form:"video"`
Photo bool `form:"photo"`
Analog bool `form:"analog"`
Duplicate bool `form:"duplicate"`
Error bool `form:"error"`
Hidden bool `form:"hidden"`
@ -26,6 +27,7 @@ type PhotoSearch struct {
Public bool `form:"public"`
Private bool `form:"private"`
Favorite bool `form:"favorite"`
Unsorted bool `form:"unsorted"`
Lat float32 `form:"lat"`
Lng float32 `form:"lng"`
Dist uint `form:"dist"`

View file

@ -41,6 +41,7 @@ type PhotoResult struct {
PhotoExposure string `json:"Exposure"`
PhotoQuality int `json:"Quality"`
PhotoResolution int `json:"Resolution"`
PhotoAnalog bool `json:"Analog"`
CameraID uint `json:"CameraID"` // Camera
CameraSerial string `json:"CameraSerial"`
CameraSrc string `json:"CameraSrc"`

View file

@ -186,6 +186,10 @@ func PhotoSearch(f form.PhotoSearch) (results PhotoResults, count int, err error
s = s.Where("photos.photo_favorite = 1")
}
if f.Analog {
s = s.Where("photos.photo_analog = 1")
}
if f.Country != "" {
s = s.Where("photos.photo_country IN (?)", strings.Split(strings.ToLower(f.Country), ","))
}
@ -303,6 +307,8 @@ func PhotoSearch(f form.PhotoSearch) (results PhotoResults, count int, err error
} else {
s = s.Joins("JOIN photos_albums ON photos_albums.photo_uid = photos.photo_uid").Where("photos_albums.hidden = 0 AND photos_albums.album_uid = ?", f.Album)
}
} else if f.Unsorted && f.Filter == "" {
s = s.Where("photos.photo_uid NOT IN (SELECT photo_uid FROM photos_albums pa WHERE pa.hidden = 0)")
}
// Set sort order for results.