Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
parent
e580a117d6
commit
ca154f3bb3
|
@ -95,10 +95,10 @@
|
||||||
:label="labels.year"
|
:label="labels.year"
|
||||||
flat solo hide-details
|
flat solo hide-details
|
||||||
color="secondary-dark"
|
color="secondary-dark"
|
||||||
item-value="Year"
|
item-value="value"
|
||||||
item-text="Name"
|
item-text="text"
|
||||||
v-model="filter.year"
|
v-model="filter.year"
|
||||||
:items="yearOptions">
|
:items="yearOptions()">
|
||||||
</v-select>
|
</v-select>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
<v-flex xs12 sm6 md3 pa-2 class="p-month-select">
|
<v-flex xs12 sm6 md3 pa-2 class="p-month-select">
|
||||||
|
@ -106,8 +106,8 @@
|
||||||
:label="labels.month"
|
:label="labels.month"
|
||||||
flat solo hide-details
|
flat solo hide-details
|
||||||
color="secondary-dark"
|
color="secondary-dark"
|
||||||
item-value="Month"
|
item-value="value"
|
||||||
item-text="Name"
|
item-text="text"
|
||||||
v-model="filter.month"
|
v-model="filter.month"
|
||||||
:items="monthOptions()">
|
:items="monthOptions()">
|
||||||
</v-select>
|
</v-select>
|
||||||
|
@ -175,7 +175,8 @@
|
||||||
lenses: [{ID: 0, Name: this.$gettext("All Lenses")}],
|
lenses: [{ID: 0, Name: this.$gettext("All Lenses")}],
|
||||||
colors: [{Slug: "", Name: this.$gettext("All Colors")}],
|
colors: [{Slug: "", Name: this.$gettext("All Colors")}],
|
||||||
categories: [{Slug: "", Name: this.$gettext("All Categories")}],
|
categories: [{Slug: "", Name: this.$gettext("All Categories")}],
|
||||||
months: [{Month: 0, Name: this.$gettext("All Months")}],
|
months: [{value: 0, text: this.$gettext("All Months")}],
|
||||||
|
years: [{value: 0, text: this.$gettext("All Years")}],
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
'views': [
|
'views': [
|
||||||
|
@ -221,21 +222,6 @@
|
||||||
categoryOptions() {
|
categoryOptions() {
|
||||||
return this.all.categories.concat(this.config.categories);
|
return this.all.categories.concat(this.config.categories);
|
||||||
},
|
},
|
||||||
yearOptions() {
|
|
||||||
let result = [
|
|
||||||
{"Year": 0, "Name": this.$gettext("All Years")},
|
|
||||||
];
|
|
||||||
|
|
||||||
if (this.config.years) {
|
|
||||||
for (let i = 0; i < this.config.years.length; i++) {
|
|
||||||
result.push({"Year": this.config.years[i], "Name": this.config.years[i].toString()});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.push({"Year": -1, "Name": this.$gettext("Unknown")});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
colorOptions() {
|
colorOptions() {
|
||||||
|
@ -244,6 +230,9 @@
|
||||||
monthOptions() {
|
monthOptions() {
|
||||||
return this.all.months.concat(options.Months());
|
return this.all.months.concat(options.Months());
|
||||||
},
|
},
|
||||||
|
yearOptions() {
|
||||||
|
return this.all.years.concat(options.IndexedYears());
|
||||||
|
},
|
||||||
dropdownChange() {
|
dropdownChange() {
|
||||||
this.filterChange();
|
this.filterChange();
|
||||||
|
|
||||||
|
|
|
@ -48,61 +48,70 @@
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
|
|
||||||
<v-flex xs12 sm6 md3 pa-2 class="p-date-select">
|
<v-flex xs12 sm6 md2 pa-2>
|
||||||
|
<v-autocomplete
|
||||||
|
@change="updateTime"
|
||||||
|
:disabled="disabled"
|
||||||
|
:label="$gettext('Day')"
|
||||||
|
hide-details
|
||||||
|
color="secondary-dark"
|
||||||
|
v-model="model.Day"
|
||||||
|
:items="options.Days()"
|
||||||
|
class="input-day">
|
||||||
|
</v-autocomplete>
|
||||||
|
</v-flex>
|
||||||
|
<v-flex xs12 sm6 md2 pa-2>
|
||||||
|
<v-autocomplete
|
||||||
|
@change="updateTime"
|
||||||
|
:disabled="disabled"
|
||||||
|
:label="$gettext('Month')"
|
||||||
|
hide-details
|
||||||
|
color="secondary-dark"
|
||||||
|
v-model="model.Month"
|
||||||
|
:items="options.Months()"
|
||||||
|
class="input-month">
|
||||||
|
</v-autocomplete>
|
||||||
|
</v-flex>
|
||||||
|
<v-flex xs12 sm6 md2 pa-2>
|
||||||
|
<v-autocomplete
|
||||||
|
@change="updateTime"
|
||||||
|
:disabled="disabled"
|
||||||
|
:label="$gettext('Year')"
|
||||||
|
hide-details
|
||||||
|
color="secondary-dark"
|
||||||
|
v-model="model.Year"
|
||||||
|
:items="options.Years()"
|
||||||
|
class="input-year">
|
||||||
|
</v-autocomplete>
|
||||||
|
</v-flex>
|
||||||
|
|
||||||
|
<v-flex xs12 sm6 md2 class="pa-2">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:value="timeLocalFormatted"
|
:value="localTime"
|
||||||
browser-autocomplete="off"
|
browser-autocomplete="off"
|
||||||
:label="labels.localtime"
|
:label="$gettext('Local Time')"
|
||||||
readonly
|
readonly
|
||||||
hide-details
|
hide-details
|
||||||
color="secondary-dark"
|
color="secondary-dark"
|
||||||
class="input-local-time"
|
class="input-local-time"
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
|
|
||||||
</v-flex>
|
</v-flex>
|
||||||
<v-flex xs12 sm6 md3 pa-2 class="p-date-select">
|
|
||||||
|
<v-flex xs12 sm6 md2 pa-2>
|
||||||
<v-text-field
|
<v-text-field
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
@change="updateTime"
|
@change="updateTime"
|
||||||
v-model="time"
|
v-model="time"
|
||||||
:label="labels.utctime"
|
:label="$gettext('Time UTC')"
|
||||||
hide-details return-masked-value
|
hide-details return-masked-value
|
||||||
mask="##:##:##"
|
mask="##:##:##"
|
||||||
color="secondary-dark"
|
color="secondary-dark"
|
||||||
class="input-utc-time"
|
class="input-utc-time"
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
<v-flex xs12 sm6 md3 class="pa-2 p-date-select">
|
|
||||||
<v-menu
|
|
||||||
:disabled="disabled"
|
|
||||||
:close-on-content-click="false"
|
|
||||||
full-width
|
|
||||||
max-width="290"
|
|
||||||
>
|
|
||||||
<template v-slot:activator="{ on }">
|
|
||||||
<v-text-field
|
|
||||||
:disabled="disabled"
|
|
||||||
:value="dateFormatted"
|
|
||||||
browser-autocomplete="off"
|
|
||||||
:label="labels.utcdate"
|
|
||||||
readonly
|
|
||||||
hide-details
|
|
||||||
v-on="on"
|
|
||||||
color="secondary-dark"
|
|
||||||
class="input-utc-date"
|
|
||||||
></v-text-field>
|
|
||||||
</template>
|
|
||||||
<v-date-picker
|
|
||||||
color="secondary-dark"
|
|
||||||
v-model="date"
|
|
||||||
@change="showDatePicker = false"
|
|
||||||
></v-date-picker>
|
|
||||||
</v-menu>
|
|
||||||
</v-flex>
|
|
||||||
|
|
||||||
<v-flex xs12 sm6 md3 class="pa-2 p-timezone-select">
|
<v-flex xs12 sm6 md2 class="pa-2">
|
||||||
<v-autocomplete
|
<v-autocomplete
|
||||||
@change="updateTime"
|
@change="updateTime"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
|
@ -117,6 +126,34 @@
|
||||||
</v-autocomplete>
|
</v-autocomplete>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
|
|
||||||
|
<v-flex xs12 sm6 md4 class="pa-2">
|
||||||
|
<v-autocomplete
|
||||||
|
:disabled="disabled"
|
||||||
|
:label="labels.country"
|
||||||
|
hide-details
|
||||||
|
browser-autocomplete="off"
|
||||||
|
color="secondary-dark"
|
||||||
|
item-value="Code"
|
||||||
|
item-text="Name"
|
||||||
|
v-model="model.Country"
|
||||||
|
:items="countries"
|
||||||
|
class="input-country">
|
||||||
|
</v-autocomplete>
|
||||||
|
</v-flex>
|
||||||
|
|
||||||
|
<v-flex xs12 sm6 md2 class="pa-2">
|
||||||
|
<v-text-field
|
||||||
|
:disabled="disabled"
|
||||||
|
hide-details
|
||||||
|
browser-autocomplete="off"
|
||||||
|
:label="labels.altitude"
|
||||||
|
placeholder=""
|
||||||
|
color="secondary-dark"
|
||||||
|
v-model="model.Altitude"
|
||||||
|
class="input-altitude"
|
||||||
|
></v-text-field>
|
||||||
|
</v-flex>
|
||||||
|
|
||||||
<v-flex xs12 sm6 md3 class="pa-2">
|
<v-flex xs12 sm6 md3 class="pa-2">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
|
@ -143,34 +180,6 @@
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
|
|
||||||
<v-flex xs12 sm6 md3 class="pa-2">
|
|
||||||
<v-text-field
|
|
||||||
:disabled="disabled"
|
|
||||||
hide-details
|
|
||||||
browser-autocomplete="off"
|
|
||||||
:label="labels.altitude"
|
|
||||||
placeholder=""
|
|
||||||
color="secondary-dark"
|
|
||||||
v-model="model.Altitude"
|
|
||||||
class="input-altitude"
|
|
||||||
></v-text-field>
|
|
||||||
</v-flex>
|
|
||||||
|
|
||||||
<v-flex xs12 sm6 md3 class="pa-2 p-countries-select">
|
|
||||||
<v-autocomplete
|
|
||||||
:disabled="disabled"
|
|
||||||
:label="labels.country"
|
|
||||||
hide-details
|
|
||||||
browser-autocomplete="off"
|
|
||||||
color="secondary-dark"
|
|
||||||
item-value="Code"
|
|
||||||
item-text="Name"
|
|
||||||
v-model="model.Country"
|
|
||||||
:items="countries"
|
|
||||||
class="input-country">
|
|
||||||
</v-autocomplete>
|
|
||||||
</v-flex>
|
|
||||||
|
|
||||||
<v-flex xs12 md6 pa-2 class="p-camera-select">
|
<v-flex xs12 md6 pa-2 class="p-camera-select">
|
||||||
<v-select
|
<v-select
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
|
@ -420,9 +429,6 @@
|
||||||
language: this.$gettext("Language"),
|
language: this.$gettext("Language"),
|
||||||
timezone: this.$gettext("Time Zone"),
|
timezone: this.$gettext("Time Zone"),
|
||||||
title: this.$gettext("Title"),
|
title: this.$gettext("Title"),
|
||||||
localtime: this.$gettext("Local Time"),
|
|
||||||
utctime: this.$gettext("UTC Time"),
|
|
||||||
utcdate: this.$gettext("UTC Date"),
|
|
||||||
latitude: this.$gettext("Latitude"),
|
latitude: this.$gettext("Latitude"),
|
||||||
longitude: this.$gettext("Longitude"),
|
longitude: this.$gettext("Longitude"),
|
||||||
altitude: this.$gettext("Altitude (m)"),
|
altitude: this.$gettext("Altitude (m)"),
|
||||||
|
@ -439,23 +445,18 @@
|
||||||
},
|
},
|
||||||
showDatePicker: false,
|
showDatePicker: false,
|
||||||
showTimePicker: false,
|
showTimePicker: false,
|
||||||
date: "",
|
|
||||||
time: "",
|
time: "",
|
||||||
dateFormatted: "",
|
localTime: "",
|
||||||
timeFormatted: "",
|
|
||||||
timeLocalFormatted: "",
|
|
||||||
textRule: v => v.length <= this.$config.get('clip') || this.$gettext("Text too long"),
|
textRule: v => v.length <= this.$config.get('clip') || this.$gettext("Text too long"),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
date() {
|
model() {
|
||||||
if (!this.date) {
|
if (!this.model.hasId()) {
|
||||||
this.dateFormatted = "";
|
return;
|
||||||
this.timeLocalFormatted = "";
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dateFormatted = DateTime.fromISO(this.date).toLocaleString(DateTime.DATE_FULL);
|
this.updateTime();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -468,29 +469,22 @@
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updateTime() {
|
updateTime() {
|
||||||
if (!this.time) {
|
const isoDate = this.model.isoDate(this.time);
|
||||||
this.time = DateTime.fromISO(this.model.TakenAt).toUTC().toFormat("HH:mm:ss");
|
|
||||||
return;
|
if(!isoDate) {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.date) {
|
this.model.TakenAt = isoDate;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.timeFormatted = DateTime.fromISO(this.time).toLocaleString(DateTime.TIME_24_WITH_SECONDS);
|
const utcDate = this.model.utcDate();
|
||||||
|
|
||||||
const utcDate = this.date + "T" + this.time + "Z";
|
this.time = utcDate.toFormat("HH:mm:ss");
|
||||||
|
|
||||||
this.model.TakenAt = utcDate;
|
let localDate = utcDate;
|
||||||
|
|
||||||
this.time = DateTime.fromISO(this.model.TakenAt).toUTC().toFormat("HH:mm:ss");
|
|
||||||
|
|
||||||
let localDate = DateTime.fromISO(utcDate);
|
|
||||||
|
|
||||||
if (this.model.TimeZone) {
|
if (this.model.TimeZone) {
|
||||||
localDate = localDate.setZone(this.model.TimeZone);
|
localDate = localDate.setZone(this.model.TimeZone);
|
||||||
} else {
|
|
||||||
localDate = localDate.toUTC(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.model.TakenAtLocal = localDate.toISO({
|
this.model.TakenAtLocal = localDate.toISO({
|
||||||
|
@ -498,7 +492,19 @@
|
||||||
includeOffset: false,
|
includeOffset: false,
|
||||||
}) + "Z";
|
}) + "Z";
|
||||||
|
|
||||||
this.timeLocalFormatted = localDate.toLocaleString(DateTime.TIME_24_WITH_SECONDS);
|
if(this.model.Day === 0) {
|
||||||
|
this.model.Day = parseInt(localDate.toFormat("d"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.model.Month === 0) {
|
||||||
|
this.model.Month = parseInt(localDate.toFormat("L"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.model.Year === 0) {
|
||||||
|
this.model.Year = parseInt(localDate.toFormat("y"));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.localTime = localDate.toLocaleString(DateTime.TIME_24_WITH_SECONDS);
|
||||||
},
|
},
|
||||||
left() {
|
left() {
|
||||||
this.$emit('next');
|
this.$emit('next');
|
||||||
|
@ -509,21 +515,8 @@
|
||||||
openPhoto() {
|
openPhoto() {
|
||||||
this.$viewer.show(Thumb.fromFiles([this.model]), 0)
|
this.$viewer.show(Thumb.fromFiles([this.model]), 0)
|
||||||
},
|
},
|
||||||
refresh(model) {
|
|
||||||
if (!model.hasId()) return;
|
|
||||||
|
|
||||||
if (model.TakenAt) {
|
|
||||||
const date = DateTime.fromISO(model.TakenAt).toUTC();
|
|
||||||
this.date = date.toISODate();
|
|
||||||
this.time = date.toFormat("HH:mm:ss");
|
|
||||||
|
|
||||||
this.updateTime();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
save(close) {
|
save(close) {
|
||||||
if (this.time && this.date) {
|
this.model.TakenAt = this.model.isoDate(this.time);
|
||||||
this.model.TakenAt = this.date + "T" + this.time + "Z";
|
|
||||||
}
|
|
||||||
|
|
||||||
this.model.update().then(() => {
|
this.model.update().then(() => {
|
||||||
if (close) {
|
if (close) {
|
||||||
|
|
|
@ -54,35 +54,6 @@
|
||||||
</td>
|
</td>
|
||||||
<td>{{ model.TitleSrc | capitalize }}</td>
|
<td>{{ model.TitleSrc | capitalize }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-if="model.TakenAcc">
|
|
||||||
<td>
|
|
||||||
<translate key="Year">Year</translate>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<v-text-field
|
|
||||||
flat solo dense hide-details v-model="model.Year"
|
|
||||||
color="secondary-dark"
|
|
||||||
style="font-weight: 400; font-size: 13px;"
|
|
||||||
></v-text-field>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr v-if="model.TakenAcc">
|
|
||||||
<td>
|
|
||||||
<translate key="Month">Month</translate>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<v-select
|
|
||||||
label="Month"
|
|
||||||
flat solo dense hide-details
|
|
||||||
color="secondary-dark"
|
|
||||||
style="font-weight: 400; font-size: 13px;"
|
|
||||||
item-value="Month"
|
|
||||||
item-text="Name"
|
|
||||||
v-model="model.Month"
|
|
||||||
:items="monthOptions">
|
|
||||||
</v-select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<translate key="Quality Score">Quality Score</translate>
|
<translate key="Quality Score">Quality Score</translate>
|
||||||
|
|
|
@ -44,6 +44,7 @@ export const TypeJpeg = "jpg";
|
||||||
export const TypeImage = "image";
|
export const TypeImage = "image";
|
||||||
export const YearUnknown = -1;
|
export const YearUnknown = -1;
|
||||||
export const MonthUnknown = -1;
|
export const MonthUnknown = -1;
|
||||||
|
export const DayUnknown = -1;
|
||||||
|
|
||||||
export class Photo extends RestModel {
|
export class Photo extends RestModel {
|
||||||
getDefaults() {
|
getDefaults() {
|
||||||
|
@ -57,7 +58,6 @@ export class Photo extends RestModel {
|
||||||
TakenAt: "",
|
TakenAt: "",
|
||||||
TakenAtLocal: "",
|
TakenAtLocal: "",
|
||||||
TakenSrc: "",
|
TakenSrc: "",
|
||||||
TakenAcc: 0,
|
|
||||||
TimeZone: "",
|
TimeZone: "",
|
||||||
Path: "",
|
Path: "",
|
||||||
Color: "",
|
Color: "",
|
||||||
|
@ -86,6 +86,7 @@ export class Photo extends RestModel {
|
||||||
Country: "",
|
Country: "",
|
||||||
Year: YearUnknown,
|
Year: YearUnknown,
|
||||||
Month: MonthUnknown,
|
Month: MonthUnknown,
|
||||||
|
Day: DayUnknown,
|
||||||
Details: {
|
Details: {
|
||||||
Keywords: "",
|
Keywords: "",
|
||||||
Notes: "",
|
Notes: "",
|
||||||
|
@ -124,6 +125,48 @@ export class Photo extends RestModel {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isoDay() {
|
||||||
|
if(!this.Day || this.Day <= 0) {
|
||||||
|
return this.TakenAt.substr(8, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.Day.toString().padStart(2, "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
isoMonth() {
|
||||||
|
if(!this.Month || this.Month <= 0) {
|
||||||
|
return this.TakenAt.substr(5, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.Month.toString().padStart(2, "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
isoYear() {
|
||||||
|
if(!this.Year || this.Year <= 1000) {
|
||||||
|
return this.TakenAt.substr(0, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.Year.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
isoDate(time) {
|
||||||
|
if(!this.isoYear()) {
|
||||||
|
return this.TakenAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
let date = this.isoYear() + "-" + this.isoMonth() + "-" + this.isoDay();
|
||||||
|
|
||||||
|
if(!time) {
|
||||||
|
time = this.TakenAt.substr(11, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${date}T${time}Z`;
|
||||||
|
}
|
||||||
|
|
||||||
|
utcDate() {
|
||||||
|
return DateTime.fromISO(this.TakenAt).toUTC();
|
||||||
|
}
|
||||||
|
|
||||||
baseName(truncate) {
|
baseName(truncate) {
|
||||||
let result = this.fileBase(this.FileName ? this.FileName : this.mainFile().Name);
|
let result = this.fileBase(this.FileName ? this.FileName : this.mainFile().Name);
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,60 @@
|
||||||
import {$gettext} from "common/vm";
|
import {$gettext} from "common/vm";
|
||||||
import moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
import {Info} from "luxon";
|
import {Info} from "luxon";
|
||||||
|
import {config} from "../session";
|
||||||
|
|
||||||
export const TimeZones = () => moment.tz.names();
|
export const TimeZones = () => moment.tz.names();
|
||||||
|
|
||||||
|
export const Days = () => {
|
||||||
|
let result = [];
|
||||||
|
|
||||||
|
for (let i = 1; i <= 31; i++) {
|
||||||
|
result.push({"value": i, "text": i.toString().padStart(2, "0")});
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push({"value": -1, "text": $gettext("Unknown")});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Years = () => {
|
||||||
|
let result = [];
|
||||||
|
|
||||||
|
const currentYear = new Date().getUTCFullYear();
|
||||||
|
|
||||||
|
for (let i = currentYear; i >= 1750; i--) {
|
||||||
|
result.push({"value": i, "text": i.toString().padStart(4, "0")});
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push({"value": -1, "text": $gettext("Unknown")});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const IndexedYears = () => {
|
||||||
|
let result = [];
|
||||||
|
|
||||||
|
if (config.values.years) {
|
||||||
|
for (let i = 0; i < config.values.years.length; i++) {
|
||||||
|
result.push({"value": parseInt(config.values.years[i]), "text": config.values.years[i].toString()});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push({"value": -1, "text": $gettext("Unknown")});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
export const Months = () => {
|
export const Months = () => {
|
||||||
let result = [];
|
let result = [];
|
||||||
|
|
||||||
const months = Info.months("long");
|
const months = Info.months("long");
|
||||||
|
|
||||||
for (let i = 0; i < months.length; i++) {
|
for (let i = 0; i < months.length; i++) {
|
||||||
result.push({"Month": i + 1, "Name": months[i]});
|
result.push({"value": i + 1, "text": months[i]});
|
||||||
}
|
}
|
||||||
|
|
||||||
result.push({"Month": -1, "Name": $gettext("Unknown")});
|
result.push({"value": -1, "text": $gettext("Unknown")});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
|
@ -43,6 +43,7 @@ type Album struct {
|
||||||
AlbumCountry string `gorm:"type:varbinary(2);index:idx_albums_country_year_month;default:'zz'" json:"Country" yaml:"Country,omitempty"`
|
AlbumCountry string `gorm:"type:varbinary(2);index:idx_albums_country_year_month;default:'zz'" json:"Country" yaml:"Country,omitempty"`
|
||||||
AlbumYear int `gorm:"index:idx_albums_country_year_month;" json:"Year" yaml:"Year,omitempty"`
|
AlbumYear int `gorm:"index:idx_albums_country_year_month;" json:"Year" yaml:"Year,omitempty"`
|
||||||
AlbumMonth int `gorm:"index:idx_albums_country_year_month;" json:"Month" yaml:"Month,omitempty"`
|
AlbumMonth int `gorm:"index:idx_albums_country_year_month;" json:"Month" yaml:"Month,omitempty"`
|
||||||
|
AlbumDay int `json:"Day" yaml:"Day,omitempty"`
|
||||||
AlbumFavorite bool `json:"Favorite" yaml:"Favorite,omitempty"`
|
AlbumFavorite bool `json:"Favorite" yaml:"Favorite,omitempty"`
|
||||||
AlbumPrivate bool `json:"Private" yaml:"Private,omitempty"`
|
AlbumPrivate bool `json:"Private" yaml:"Private,omitempty"`
|
||||||
CreatedAt time.Time `json:"CreatedAt" yaml:"-"`
|
CreatedAt time.Time `json:"CreatedAt" yaml:"-"`
|
||||||
|
|
|
@ -25,6 +25,7 @@ const (
|
||||||
// Unknown values.
|
// Unknown values.
|
||||||
YearUnknown = -1
|
YearUnknown = -1
|
||||||
MonthUnknown = -1
|
MonthUnknown = -1
|
||||||
|
DayUnknown = -1
|
||||||
TitleUnknown = "Unknown"
|
TitleUnknown = "Unknown"
|
||||||
|
|
||||||
// Content types.
|
// Content types.
|
||||||
|
|
|
@ -38,6 +38,9 @@ type Person struct {
|
||||||
CanDownload bool `json:"CanDownload" yaml:"CanDownload,omitempty"`
|
CanDownload bool `json:"CanDownload" yaml:"CanDownload,omitempty"`
|
||||||
WebDAV bool `gorm:"column:webdav" json:"WebDAV" yaml:"WebDAV,omitempty"`
|
WebDAV bool `gorm:"column:webdav" json:"WebDAV" yaml:"WebDAV,omitempty"`
|
||||||
ApiToken string `json:"ApiToken" yaml:"ApiToken,omitempty"`
|
ApiToken string `json:"ApiToken" yaml:"ApiToken,omitempty"`
|
||||||
|
BirthYear int `json:"BirthYear" yaml:"BirthYear,omitempty"`
|
||||||
|
BirthMonth int `json:"BirthMonth" yaml:"BirthMonth,omitempty"`
|
||||||
|
BirthDay int `json:"BirthDay" yaml:"BirthDay,omitempty"`
|
||||||
LoginAttempts int `json:"-" yaml:"-,omitempty"`
|
LoginAttempts int `json:"-" yaml:"-,omitempty"`
|
||||||
LoginAt *time.Time `json:"-" yaml:"-"`
|
LoginAt *time.Time `json:"-" yaml:"-"`
|
||||||
CreatedAt time.Time `json:"CreatedAt" yaml:"-"`
|
CreatedAt time.Time `json:"CreatedAt" yaml:"-"`
|
||||||
|
|
|
@ -37,7 +37,6 @@ type Photo struct {
|
||||||
TakenAt time.Time `gorm:"type:datetime;index:idx_photos_taken_uid;" json:"TakenAt" yaml:"TakenAt"`
|
TakenAt time.Time `gorm:"type:datetime;index:idx_photos_taken_uid;" json:"TakenAt" yaml:"TakenAt"`
|
||||||
TakenAtLocal time.Time `gorm:"type:datetime;" yaml:"-"`
|
TakenAtLocal time.Time `gorm:"type:datetime;" yaml:"-"`
|
||||||
TakenSrc string `gorm:"type:varbinary(8);" json:"TakenSrc" yaml:"TakenSrc,omitempty"`
|
TakenSrc string `gorm:"type:varbinary(8);" json:"TakenSrc" yaml:"TakenSrc,omitempty"`
|
||||||
TakenAcc int `json:"TakenAcc" yaml:"TakenAcc,omitempty"`
|
|
||||||
PhotoUID string `gorm:"type:varbinary(42);unique_index;index:idx_photos_taken_uid;" json:"UID" yaml:"UID"`
|
PhotoUID string `gorm:"type:varbinary(42);unique_index;index:idx_photos_taken_uid;" json:"UID" yaml:"UID"`
|
||||||
PhotoType string `gorm:"type:varbinary(8);default:'image';" json:"Type" yaml:"Type"`
|
PhotoType string `gorm:"type:varbinary(8);default:'image';" json:"Type" yaml:"Type"`
|
||||||
PhotoTitle string `gorm:"type:varchar(255);" json:"Title" yaml:"Title"`
|
PhotoTitle string `gorm:"type:varchar(255);" json:"Title" yaml:"Title"`
|
||||||
|
@ -60,8 +59,9 @@ type Photo struct {
|
||||||
PhotoLng float32 `gorm:"type:FLOAT;index;" json:"Lng" yaml:"Lng,omitempty"`
|
PhotoLng float32 `gorm:"type:FLOAT;index;" json:"Lng" yaml:"Lng,omitempty"`
|
||||||
PhotoAltitude int `json:"Altitude" yaml:"Altitude,omitempty"`
|
PhotoAltitude int `json:"Altitude" yaml:"Altitude,omitempty"`
|
||||||
PhotoCountry string `gorm:"type:varbinary(2);index:idx_photos_country_year_month;default:'zz'" json:"Country" yaml:"-"`
|
PhotoCountry string `gorm:"type:varbinary(2);index:idx_photos_country_year_month;default:'zz'" json:"Country" yaml:"-"`
|
||||||
PhotoYear int `gorm:"index:idx_photos_country_year_month;" json:"Year" yaml:"-"`
|
PhotoYear int `gorm:"index:idx_photos_country_year_month;" json:"Year" yaml:"Year"`
|
||||||
PhotoMonth int `gorm:"index:idx_photos_country_year_month;" json:"Month" yaml:"-"`
|
PhotoMonth int `gorm:"index:idx_photos_country_year_month;" json:"Month" yaml:"Month"`
|
||||||
|
PhotoDay int `json:"Day" yaml:"Day"`
|
||||||
PhotoIso int `json:"Iso" yaml:"ISO,omitempty"`
|
PhotoIso int `json:"Iso" yaml:"ISO,omitempty"`
|
||||||
PhotoExposure string `gorm:"type:varbinary(64);" json:"Exposure" yaml:"Exposure,omitempty"`
|
PhotoExposure string `gorm:"type:varbinary(64);" json:"Exposure" yaml:"Exposure,omitempty"`
|
||||||
PhotoFNumber float32 `gorm:"type:FLOAT;" json:"FNumber" yaml:"FNumber,omitempty"`
|
PhotoFNumber float32 `gorm:"type:FLOAT;" json:"FNumber" yaml:"FNumber,omitempty"`
|
||||||
|
@ -111,7 +111,7 @@ func SavePhotoForm(model Photo, form form.Photo, geoApi string) error {
|
||||||
return errors.New("photo: can't save form, id is empty")
|
return errors.New("photo: can't save form, id is empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
model.UpdateYearMonth()
|
model.UpdateDateFields()
|
||||||
|
|
||||||
if form.Details.PhotoID == model.ID {
|
if form.Details.PhotoID == model.ID {
|
||||||
if err := deepcopier.Copy(&model.Details).From(form.Details); err != nil {
|
if err := deepcopier.Copy(&model.Details).From(form.Details); err != nil {
|
||||||
|
@ -182,7 +182,7 @@ func (m *Photo) Save() error {
|
||||||
|
|
||||||
labels := m.ClassifyLabels()
|
labels := m.ClassifyLabels()
|
||||||
|
|
||||||
m.UpdateYearMonth()
|
m.UpdateDateFields()
|
||||||
|
|
||||||
if err := m.UpdateTitle(labels); err != nil {
|
if err := m.UpdateTitle(labels); err != nil {
|
||||||
log.Info(err)
|
log.Info(err)
|
||||||
|
@ -735,11 +735,11 @@ func (m *Photo) SetTakenAt(taken, local time.Time, zone, source string) {
|
||||||
m.TimeZone = zone
|
m.TimeZone = zone
|
||||||
}
|
}
|
||||||
|
|
||||||
m.UpdateYearMonth()
|
m.UpdateDateFields()
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateYearMonth updates internal date fields.
|
// UpdateDateFields updates internal date fields.
|
||||||
func (m *Photo) UpdateYearMonth() {
|
func (m *Photo) UpdateDateFields() {
|
||||||
if m.TakenAt.IsZero() || m.TakenAt.Year() < 1000 {
|
if m.TakenAt.IsZero() || m.TakenAt.Year() < 1000 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -751,9 +751,11 @@ func (m *Photo) UpdateYearMonth() {
|
||||||
if m.TakenSrc == SrcAuto {
|
if m.TakenSrc == SrcAuto {
|
||||||
m.PhotoYear = YearUnknown
|
m.PhotoYear = YearUnknown
|
||||||
m.PhotoMonth = MonthUnknown
|
m.PhotoMonth = MonthUnknown
|
||||||
} else {
|
m.PhotoDay = DayUnknown
|
||||||
|
} else if m.TakenSrc != SrcManual {
|
||||||
m.PhotoYear = m.TakenAtLocal.Year()
|
m.PhotoYear = m.TakenAtLocal.Year()
|
||||||
m.PhotoMonth = int(m.TakenAtLocal.Month())
|
m.PhotoMonth = int(m.TakenAtLocal.Month())
|
||||||
|
m.PhotoDay = m.TakenAtLocal.Day()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ func (m *Photo) Optimize() (updated bool, err error) {
|
||||||
|
|
||||||
labels := m.ClassifyLabels()
|
labels := m.ClassifyLabels()
|
||||||
|
|
||||||
m.UpdateYearMonth()
|
m.UpdateDateFields()
|
||||||
|
|
||||||
if err := m.UpdateTitle(labels); err != nil {
|
if err := m.UpdateTitle(labels); err != nil {
|
||||||
log.Info(err)
|
log.Info(err)
|
||||||
|
|
|
@ -11,6 +11,7 @@ type AlbumSearch struct {
|
||||||
Country string `json:"country"`
|
Country string `json:"country"`
|
||||||
Year int `json:"year"`
|
Year int `json:"year"`
|
||||||
Month int `json:"month"`
|
Month int `json:"month"`
|
||||||
|
Day int `json:"day"`
|
||||||
Favorite bool `form:"favorite"`
|
Favorite bool `form:"favorite"`
|
||||||
Private bool `form:"private"`
|
Private bool `form:"private"`
|
||||||
Count int `form:"count" binding:"required" serialize:"-"`
|
Count int `form:"count" binding:"required" serialize:"-"`
|
||||||
|
|
|
@ -22,8 +22,10 @@ type Photo struct {
|
||||||
TakenAt time.Time `json:"TakenAt"`
|
TakenAt time.Time `json:"TakenAt"`
|
||||||
TakenAtLocal time.Time `json:"TakenAtLocal"`
|
TakenAtLocal time.Time `json:"TakenAtLocal"`
|
||||||
TakenSrc string `json:"TakenSrc"`
|
TakenSrc string `json:"TakenSrc"`
|
||||||
TakenAcc int `json:"TakenAcc"`
|
|
||||||
TimeZone string `json:"TimeZone"`
|
TimeZone string `json:"TimeZone"`
|
||||||
|
PhotoYear int `json:"Year"`
|
||||||
|
PhotoMonth int `json:"Month"`
|
||||||
|
PhotoDay int `json:"Day"`
|
||||||
PhotoTitle string `json:"Title"`
|
PhotoTitle string `json:"Title"`
|
||||||
TitleSrc string `json:"TitleSrc"`
|
TitleSrc string `json:"TitleSrc"`
|
||||||
PhotoDescription string `json:"Description"`
|
PhotoDescription string `json:"Description"`
|
||||||
|
|
|
@ -46,6 +46,7 @@ type PhotoSearch struct {
|
||||||
State string `form:"state"` // Moments
|
State string `form:"state"` // Moments
|
||||||
Year int `form:"year"` // Moments
|
Year int `form:"year"` // Moments
|
||||||
Month int `form:"month"` // Moments
|
Month int `form:"month"` // Moments
|
||||||
|
Day int `form:"day"` // Moments
|
||||||
Color string `form:"color"`
|
Color string `form:"color"`
|
||||||
Quality int `form:"quality"`
|
Quality int `form:"quality"`
|
||||||
Review bool `form:"review"`
|
Review bool `form:"review"`
|
||||||
|
|
|
@ -466,7 +466,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
|
||||||
photo.PlaceID = entity.UnknownPlace.ID
|
photo.PlaceID = entity.UnknownPlace.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
photo.UpdateYearMonth()
|
photo.UpdateDateFields()
|
||||||
|
|
||||||
file.FileSidecar = m.IsSidecar()
|
file.FileSidecar = m.IsSidecar()
|
||||||
file.FileVideo = m.IsVideo()
|
file.FileVideo = m.IsVideo()
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/photoprism/photoprism/internal/entity"
|
"github.com/photoprism/photoprism/internal/entity"
|
||||||
"github.com/photoprism/photoprism/internal/form"
|
"github.com/photoprism/photoprism/internal/form"
|
||||||
"github.com/photoprism/photoprism/pkg/capture"
|
"github.com/photoprism/photoprism/pkg/capture"
|
||||||
|
"github.com/photoprism/photoprism/pkg/txt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AlbumResult contains found albums
|
// AlbumResult contains found albums
|
||||||
|
@ -29,6 +30,7 @@ type AlbumResult struct {
|
||||||
AlbumCountry string `json:"Country"`
|
AlbumCountry string `json:"Country"`
|
||||||
AlbumYear int `json:"Year"`
|
AlbumYear int `json:"Year"`
|
||||||
AlbumMonth int `json:"Month"`
|
AlbumMonth int `json:"Month"`
|
||||||
|
AlbumDay int `json:"Day"`
|
||||||
AlbumFavorite bool `json:"Favorite"`
|
AlbumFavorite bool `json:"Favorite"`
|
||||||
AlbumPrivate bool `json:"Private"`
|
AlbumPrivate bool `json:"Private"`
|
||||||
PhotoCount int `json:"PhotoCount"`
|
PhotoCount int `json:"PhotoCount"`
|
||||||
|
@ -138,11 +140,23 @@ func AlbumSearch(f form.AlbumSearch) (results AlbumResults, err error) {
|
||||||
s = s.Where("albums.album_favorite = 1")
|
s = s.Where("albums.album_favorite = 1")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (f.Year > 0 && f.Year <= txt.YearMax) || f.Year == entity.YearUnknown {
|
||||||
|
s = s.Where("albums.album_year = ?", f.Year)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f.Month >= txt.MonthMin && f.Month <= txt.MonthMax) || f.Month == entity.MonthUnknown {
|
||||||
|
s = s.Where("albums.album_month = ?", f.Month)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f.Day >= txt.DayMin && f.Month <= txt.DayMax) || f.Day == entity.DayUnknown {
|
||||||
|
s = s.Where("albums.album_day = ?", f.Day)
|
||||||
|
}
|
||||||
|
|
||||||
switch f.Order {
|
switch f.Order {
|
||||||
case "slug":
|
case "slug":
|
||||||
s = s.Order("albums.album_favorite DESC, album_slug ASC")
|
s = s.Order("albums.album_favorite DESC, album_slug ASC")
|
||||||
default:
|
default:
|
||||||
s = s.Order("albums.album_favorite DESC, albums.album_year DESC, albums.album_month DESC, albums.album_title, albums.created_at DESC")
|
s = s.Order("albums.album_favorite DESC, albums.album_year DESC, albums.album_month DESC, albums.album_day DESC, albums.album_title, albums.created_at DESC")
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.Count > 0 && f.Count <= MaxResults {
|
if f.Count > 0 && f.Count <= MaxResults {
|
||||||
|
|
|
@ -20,7 +20,6 @@ type PhotoResult struct {
|
||||||
TakenAt time.Time `json:"TakenAt"`
|
TakenAt time.Time `json:"TakenAt"`
|
||||||
TakenAtLocal time.Time `json:"TakenAtLocal"`
|
TakenAtLocal time.Time `json:"TakenAtLocal"`
|
||||||
TakenSrc string `json:"TakenSrc"`
|
TakenSrc string `json:"TakenSrc"`
|
||||||
TakenAcc int `json:"TakenAcc"`
|
|
||||||
TimeZone string `json:"TimeZone"`
|
TimeZone string `json:"TimeZone"`
|
||||||
PhotoPath string `json:"Path"`
|
PhotoPath string `json:"Path"`
|
||||||
PhotoName string `json:"Name"`
|
PhotoName string `json:"Name"`
|
||||||
|
@ -29,6 +28,7 @@ type PhotoResult struct {
|
||||||
PhotoDescription string `json:"Description"`
|
PhotoDescription string `json:"Description"`
|
||||||
PhotoYear int `json:"Year"`
|
PhotoYear int `json:"Year"`
|
||||||
PhotoMonth int `json:"Month"`
|
PhotoMonth int `json:"Month"`
|
||||||
|
PhotoDay int `json:"Day"`
|
||||||
PhotoCountry string `json:"Country"`
|
PhotoCountry string `json:"Country"`
|
||||||
PhotoFavorite bool `json:"Favorite"`
|
PhotoFavorite bool `json:"Favorite"`
|
||||||
PhotoPrivate bool `json:"Private"`
|
PhotoPrivate bool `json:"Private"`
|
||||||
|
|
|
@ -178,6 +178,10 @@ func PhotoSearch(f form.PhotoSearch) (results PhotoResults, count int, err error
|
||||||
s = s.Where("photos.photo_month = ?", f.Month)
|
s = s.Where("photos.photo_month = ?", f.Month)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (f.Day >= txt.DayMin && f.Month <= txt.DayMax) || f.Day == entity.DayUnknown {
|
||||||
|
s = s.Where("photos.photo_day = ?", f.Day)
|
||||||
|
}
|
||||||
|
|
||||||
if f.Color != "" {
|
if f.Color != "" {
|
||||||
s = s.Where("files.file_main_color IN (?)", strings.Split(strings.ToLower(f.Color), ","))
|
s = s.Where("files.file_main_color IN (?)", strings.Split(strings.ToLower(f.Color), ","))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue