Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
commit
b8416f3d04
|
@ -15,14 +15,14 @@ assets-path: ~/.local/share/photoprism
|
||||||
resources-path: ~/.local/share/photoprism/resources
|
resources-path: ~/.local/share/photoprism/resources
|
||||||
originals-path: ~/Pictures/Originals
|
originals-path: ~/Pictures/Originals
|
||||||
import-path: ~/Pictures/Import
|
import-path: ~/Pictures/Import
|
||||||
sql-host: localhost
|
|
||||||
sql-port: 4000
|
|
||||||
sql-password: photoprism
|
|
||||||
http-host:
|
http-host:
|
||||||
http-mode: release
|
http-mode: release
|
||||||
http-port: 2342
|
http-port: 2342
|
||||||
database-driver: internal
|
tidb-host: localhost
|
||||||
database-dsn: root:photoprism@tcp(localhost:4000)/photoprism?parseTime=true
|
tidb-port: 2343
|
||||||
|
tidb-password: photoprism
|
||||||
|
database-driver: tidb
|
||||||
|
database-dsn: root:photoprism@tcp(localhost:2343)/photoprism?parseTime=true
|
||||||
pid-filename: ~/.local/share/photoprism/photoprism.pid
|
pid-filename: ~/.local/share/photoprism/photoprism.pid
|
||||||
log-filename: ~/.local/share/photoprism/photoprism.log
|
log-filename: ~/.local/share/photoprism/photoprism.log
|
||||||
detach-server: false
|
detach-server: false
|
||||||
|
|
|
@ -12,23 +12,23 @@ services:
|
||||||
- "~/.cache/go-mod:/go/pkg/mod"
|
- "~/.cache/go-mod:/go/pkg/mod"
|
||||||
environment:
|
environment:
|
||||||
PHOTOPRISM_URL: "http://localhost:2342/"
|
PHOTOPRISM_URL: "http://localhost:2342/"
|
||||||
|
PHOTOPRISM_UPLOAD_NSFW: "false"
|
||||||
|
PHOTOPRISM_DETECT_NSFW: "true"
|
||||||
|
PHOTOPRISM_PID_FILENAME: "photoprism.pid"
|
||||||
|
PHOTOPRISM_LOG_FILENAME: "photoprism.log"
|
||||||
|
PHOTOPRISM_DETACH_SERVER: "true"
|
||||||
|
PHOTOPRISM_HTTP_HOST: "0.0.0.0"
|
||||||
|
PHOTOPRISM_HTTP_PORT: 2342
|
||||||
|
PHOTOPRISM_TIDB_HOST: "0.0.0.0"
|
||||||
|
PHOTOPRISM_TIDB_PORT: 2343
|
||||||
|
PHOTOPRISM_TIDB_PASSWORD: "photoprism"
|
||||||
|
PHOTOPRISM_DATABASE_DRIVER: "tidb"
|
||||||
|
PHOTOPRISM_DATABASE_DSN: "root:photoprism@tcp(localhost:2343)/photoprism?parseTime=true"
|
||||||
PHOTOPRISM_TITLE: "PhotoPrism"
|
PHOTOPRISM_TITLE: "PhotoPrism"
|
||||||
PHOTOPRISM_SUBTITLE: "Browse your life"
|
PHOTOPRISM_SUBTITLE: "Browse your life"
|
||||||
PHOTOPRISM_DESCRIPTION: "Personal Photo Management tested by Travis CI."
|
PHOTOPRISM_DESCRIPTION: "Personal Photo Management tested by Travis CI."
|
||||||
PHOTOPRISM_AUTHOR: "PhotoPrism.org"
|
PHOTOPRISM_AUTHOR: "PhotoPrism.org"
|
||||||
PHOTOPRISM_TWITTER: "@browseyourlife"
|
PHOTOPRISM_TWITTER: "@browseyourlife"
|
||||||
PHOTOPRISM_HTTP_HOST: "0.0.0.0"
|
|
||||||
PHOTOPRISM_HTTP_PORT: 2342
|
|
||||||
PHOTOPRISM_SQL_HOST: "0.0.0.0"
|
|
||||||
PHOTOPRISM_SQL_PORT: 4000
|
|
||||||
PHOTOPRISM_SQL_PASSWORD: "photoprism"
|
|
||||||
PHOTOPRISM_DATABASE_DSN: "root:photoprism@tcp(localhost:4000)/photoprism?parseTime=true"
|
|
||||||
PHOTOPRISM_DATABASE_DRIVER: "internal"
|
|
||||||
PHOTOPRISM_PID_FILENAME: "photoprism.pid"
|
|
||||||
PHOTOPRISM_LOG_FILENAME: "photoprism.log"
|
|
||||||
PHOTOPRISM_DETACH_SERVER: "true"
|
|
||||||
PHOTOPRISM_UPLOAD_NSFW: "false"
|
|
||||||
PHOTOPRISM_DETECT_NSFW: "true"
|
|
||||||
CODECOV_TOKEN:
|
CODECOV_TOKEN:
|
||||||
CODECOV_ENV:
|
CODECOV_ENV:
|
||||||
CODECOV_URL:
|
CODECOV_URL:
|
||||||
|
|
|
@ -8,16 +8,12 @@ services:
|
||||||
- photoprism-db
|
- photoprism-db
|
||||||
ports:
|
ports:
|
||||||
- "2342:2342" # Web Server (PhotoPrism)
|
- "2342:2342" # Web Server (PhotoPrism)
|
||||||
- "4000:4000" # Database (MySQL compatible)
|
- "2343:2343" # Database (built-in TiDB)
|
||||||
volumes:
|
volumes:
|
||||||
- ".:/go/src/github.com/photoprism/photoprism"
|
- ".:/go/src/github.com/photoprism/photoprism"
|
||||||
shm_size: "2gb"
|
shm_size: "2gb"
|
||||||
environment:
|
environment:
|
||||||
PHOTOPRISM_URL: "http://localhost:2342/"
|
PHOTOPRISM_URL: "http://localhost:2342/"
|
||||||
PHOTOPRISM_TITLE: "PhotoPrism"
|
|
||||||
PHOTOPRISM_SUBTITLE: "Browse your life"
|
|
||||||
PHOTOPRISM_AUTHOR: "PhotoPrism.org"
|
|
||||||
PHOTOPRISM_TWITTER: "@browseyourlife"
|
|
||||||
PHOTOPRISM_DEBUG: "true"
|
PHOTOPRISM_DEBUG: "true"
|
||||||
PHOTOPRISM_READONLY: "false"
|
PHOTOPRISM_READONLY: "false"
|
||||||
PHOTOPRISM_PUBLIC: "false"
|
PHOTOPRISM_PUBLIC: "false"
|
||||||
|
@ -25,6 +21,13 @@ services:
|
||||||
PHOTOPRISM_UPLOAD_NSFW: "false"
|
PHOTOPRISM_UPLOAD_NSFW: "false"
|
||||||
PHOTOPRISM_DETECT_NSFW: "true"
|
PHOTOPRISM_DETECT_NSFW: "true"
|
||||||
PHOTOPRISM_SERVER_MODE: "debug"
|
PHOTOPRISM_SERVER_MODE: "debug"
|
||||||
|
PHOTOPRISM_HTTP_HOST: "0.0.0.0"
|
||||||
|
PHOTOPRISM_HTTP_PORT: 2342
|
||||||
|
PHOTOPRISM_TIDB_HOST: "0.0.0.0"
|
||||||
|
PHOTOPRISM_TIDB_PORT: 2343
|
||||||
|
PHOTOPRISM_TIDB_PASSWORD: "photoprism"
|
||||||
|
PHOTOPRISM_DATABASE_DRIVER: "tidb"
|
||||||
|
PHOTOPRISM_DATABASE_DSN: "root:photoprism@tcp(localhost:2343)/photoprism?parseTime=true"
|
||||||
PHOTOPRISM_ASSETS_PATH: "/go/src/github.com/photoprism/photoprism/assets"
|
PHOTOPRISM_ASSETS_PATH: "/go/src/github.com/photoprism/photoprism/assets"
|
||||||
PHOTOPRISM_CACHE_PATH: "/go/src/github.com/photoprism/photoprism/assets/cache"
|
PHOTOPRISM_CACHE_PATH: "/go/src/github.com/photoprism/photoprism/assets/cache"
|
||||||
PHOTOPRISM_RESOURCES_PATH: "/go/src/github.com/photoprism/photoprism/assets/resources"
|
PHOTOPRISM_RESOURCES_PATH: "/go/src/github.com/photoprism/photoprism/assets/resources"
|
||||||
|
@ -32,13 +35,10 @@ services:
|
||||||
PHOTOPRISM_IMPORT_PATH: "/go/src/github.com/photoprism/photoprism/assets/photos/import"
|
PHOTOPRISM_IMPORT_PATH: "/go/src/github.com/photoprism/photoprism/assets/photos/import"
|
||||||
PHOTOPRISM_TEMP_PATH: "/go/src/github.com/photoprism/photoprism/assets/photos/temp"
|
PHOTOPRISM_TEMP_PATH: "/go/src/github.com/photoprism/photoprism/assets/photos/temp"
|
||||||
PHOTOPRISM_ORIGINALS_PATH: "/go/src/github.com/photoprism/photoprism/assets/photos/originals"
|
PHOTOPRISM_ORIGINALS_PATH: "/go/src/github.com/photoprism/photoprism/assets/photos/originals"
|
||||||
PHOTOPRISM_DATABASE_DRIVER: "internal"
|
PHOTOPRISM_TITLE: "PhotoPrism"
|
||||||
PHOTOPRISM_DATABASE_DSN: "root:photoprism@tcp(localhost:4000)/photoprism?parseTime=true"
|
PHOTOPRISM_SUBTITLE: "Browse your life"
|
||||||
PHOTOPRISM_HTTP_HOST: "0.0.0.0"
|
PHOTOPRISM_AUTHOR: "PhotoPrism.org"
|
||||||
PHOTOPRISM_HTTP_PORT: 2342
|
PHOTOPRISM_TWITTER: "@browseyourlife"
|
||||||
PHOTOPRISM_SQL_HOST: "0.0.0.0"
|
|
||||||
PHOTOPRISM_SQL_PORT: 4000
|
|
||||||
PHOTOPRISM_SQL_PASSWORD: "photoprism"
|
|
||||||
TF_CPP_MIN_LOG_LEVEL: 0
|
TF_CPP_MIN_LOG_LEVEL: 0
|
||||||
|
|
||||||
photoprism-db:
|
photoprism-db:
|
||||||
|
|
|
@ -119,8 +119,8 @@ RUN echo "alias go=richgo" > /root/.bash_aliases
|
||||||
# Set up project directory
|
# Set up project directory
|
||||||
WORKDIR "/go/src/github.com/photoprism/photoprism"
|
WORKDIR "/go/src/github.com/photoprism/photoprism"
|
||||||
|
|
||||||
# Expose HTTP port 2342 plus 4000 for TiDB and 9515 for chromedriver
|
# Expose HTTP port 2342 plus 2343 for TiDB and 9515 for chromedriver
|
||||||
EXPOSE 2342 4000 9515
|
EXPOSE 2342 2343 9515
|
||||||
|
|
||||||
# Keep container running (services can be started manually using a terminal)
|
# Keep container running (services can be started manually using a terminal)
|
||||||
CMD tail -f /dev/null
|
CMD tail -f /dev/null
|
||||||
|
|
|
@ -56,7 +56,7 @@ ENV PATH /photoprism/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin
|
||||||
|
|
||||||
ENV PHOTOPRISM_ORIGINALS_PATH /photoprism/originals
|
ENV PHOTOPRISM_ORIGINALS_PATH /photoprism/originals
|
||||||
ENV PHOTOPRISM_IMPORT_PATH /photoprism/import
|
ENV PHOTOPRISM_IMPORT_PATH /photoprism/import
|
||||||
ENV PHOTOPRISM_DATABASE_PATH /photoprism/database
|
ENV PHOTOPRISM_TIDB_PATH /photoprism/database
|
||||||
ENV PHOTOPRISM_TEMP_PATH /photoprism/temp
|
ENV PHOTOPRISM_TEMP_PATH /photoprism/temp
|
||||||
ENV PHOTOPRISM_CACHE_PATH /photoprism/cache
|
ENV PHOTOPRISM_CACHE_PATH /photoprism/cache
|
||||||
ENV PHOTOPRISM_CONFIG_PATH /photoprism/config
|
ENV PHOTOPRISM_CONFIG_PATH /photoprism/config
|
||||||
|
@ -85,7 +85,7 @@ RUN chmod -R 777 /photoprism
|
||||||
RUN photoprism -v
|
RUN photoprism -v
|
||||||
|
|
||||||
# Expose http and database ports
|
# Expose http and database ports
|
||||||
EXPOSE 2342 4000
|
EXPOSE 2342 2343
|
||||||
|
|
||||||
# Run server
|
# Run server
|
||||||
CMD photoprism start
|
CMD photoprism start
|
||||||
|
|
|
@ -52,7 +52,7 @@ RUN apt-get update && apt-get upgrade && \
|
||||||
ENV LD_LIBRARY_PATH /root/.local/lib:/usr/local/lib:/usr/lib:/lib
|
ENV LD_LIBRARY_PATH /root/.local/lib:/usr/local/lib:/usr/lib:/lib
|
||||||
ENV TF_CPP_MIN_LOG_LEVEL 0
|
ENV TF_CPP_MIN_LOG_LEVEL 0
|
||||||
RUN curl -L \
|
RUN curl -L \
|
||||||
"https://dl.photoprism.org/tensorflow/arm64/libtensorflow-arm64-1.14.0.tar.gz" | \
|
"https://dl.photoprism.org/tensorflow/arm64/libtensorflow-arm64-1.15.2.tar.gz" | \
|
||||||
tar -C "/usr" -xz
|
tar -C "/usr" -xz
|
||||||
RUN ldconfig
|
RUN ldconfig
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ RUN wget "https://dl.photoprism.org/tensorflow/nasnet.zip?${BUILD_TAG}" -O /tmp/
|
||||||
|
|
||||||
# Set up project directory
|
# Set up project directory
|
||||||
WORKDIR "/go/src/github.com/photoprism/photoprism"
|
WORKDIR "/go/src/github.com/photoprism/photoprism"
|
||||||
COPY ../../photoprism-arm64 .
|
COPY . .
|
||||||
|
|
||||||
# Build PhotoPrism
|
# Build PhotoPrism
|
||||||
RUN make dep build-js install
|
RUN make dep build-js install
|
||||||
|
@ -140,7 +140,7 @@ ENV PATH /photoprism/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin
|
||||||
|
|
||||||
ENV PHOTOPRISM_ORIGINALS_PATH /photoprism/originals
|
ENV PHOTOPRISM_ORIGINALS_PATH /photoprism/originals
|
||||||
ENV PHOTOPRISM_IMPORT_PATH /photoprism/import
|
ENV PHOTOPRISM_IMPORT_PATH /photoprism/import
|
||||||
ENV PHOTOPRISM_DATABASE_PATH /photoprism/database
|
ENV PHOTOPRISM_TIDB_PATH /photoprism/database
|
||||||
ENV PHOTOPRISM_TEMP_PATH /photoprism/temp
|
ENV PHOTOPRISM_TEMP_PATH /photoprism/temp
|
||||||
ENV PHOTOPRISM_CACHE_PATH /photoprism/cache
|
ENV PHOTOPRISM_CACHE_PATH /photoprism/cache
|
||||||
ENV PHOTOPRISM_CONFIG_PATH /photoprism/config
|
ENV PHOTOPRISM_CONFIG_PATH /photoprism/config
|
||||||
|
@ -169,7 +169,7 @@ RUN chmod -R 777 /photoprism
|
||||||
RUN photoprism -v
|
RUN photoprism -v
|
||||||
|
|
||||||
# Expose http and database ports
|
# Expose http and database ports
|
||||||
EXPOSE 2342 4000
|
EXPOSE 2342 2343
|
||||||
|
|
||||||
# Run server
|
# Run server
|
||||||
CMD photoprism start
|
CMD photoprism start
|
||||||
|
|
|
@ -13,7 +13,7 @@ services:
|
||||||
- seccomp:unconfined
|
- seccomp:unconfined
|
||||||
ports:
|
ports:
|
||||||
- 2342:2342 # [local port]:[container port]
|
- 2342:2342 # [local port]:[container port]
|
||||||
# - 4000:4000 # Internal database (MySQL compatible)
|
# - 2343:2343 # Database (built-in TiDB)
|
||||||
healthcheck: # Optional
|
healthcheck: # Optional
|
||||||
test: "photoprism status"
|
test: "photoprism status"
|
||||||
interval: 60s
|
interval: 60s
|
||||||
|
@ -35,13 +35,13 @@ services:
|
||||||
PHOTOPRISM_DISABLE_SETTINGS: "false"
|
PHOTOPRISM_DISABLE_SETTINGS: "false"
|
||||||
PHOTOPRISM_HTTP_HOST: "0.0.0.0"
|
PHOTOPRISM_HTTP_HOST: "0.0.0.0"
|
||||||
PHOTOPRISM_HTTP_PORT: 2342
|
PHOTOPRISM_HTTP_PORT: 2342
|
||||||
PHOTOPRISM_SQL_HOST: "0.0.0.0"
|
PHOTOPRISM_TIDB_HOST: "0.0.0.0"
|
||||||
PHOTOPRISM_SQL_PORT: 4000 # Port for internal TiDB SQL server (driver "internal")
|
PHOTOPRISM_TIDB_PORT: 2343 # Port for built-in TiDB SQL server (driver "tidb")
|
||||||
PHOTOPRISM_SQL_PASSWORD: "photoprism" # Plain text only (username "root")
|
PHOTOPRISM_TIDB_PASSWORD: "photoprism" # Plain text only (username "root")
|
||||||
PHOTOPRISM_ADMIN_PASSWORD: "photoprism" # Plain text or bcrypt hash (escape "$" with "$$")
|
PHOTOPRISM_ADMIN_PASSWORD: "photoprism" # Plain text or bcrypt hash (escape "$" with "$$")
|
||||||
PHOTOPRISM_WEBDAV_PASSWORD: "photoprism" # Plain text only (username "photoprism")
|
PHOTOPRISM_WEBDAV_PASSWORD: "photoprism" # Plain text only (username "photoprism")
|
||||||
PHOTOPRISM_DATABASE_DRIVER: "internal" # Change to "mysql" for external MySQL or MariaDB
|
PHOTOPRISM_DATABASE_DRIVER: "tidb" # Change to "mysql" for external MySQL or MariaDB
|
||||||
PHOTOPRISM_DATABASE_DSN: "root:photoprism@tcp(localhost:4000)/photoprism?parseTime=true"
|
PHOTOPRISM_DATABASE_DSN: "root:photoprism@tcp(localhost:2343)/photoprism?parseTime=true"
|
||||||
# PHOTOPRISM_THUMB_QUALITY: 95 # High-quality thumbnails (optional)
|
# PHOTOPRISM_THUMB_QUALITY: 95 # High-quality thumbnails (optional)
|
||||||
# PHOTOPRISM_THUMB_SIZE: 3840
|
# PHOTOPRISM_THUMB_SIZE: 3840
|
||||||
# PHOTOPRISM_THUMB_LIMIT: 3840
|
# PHOTOPRISM_THUMB_LIMIT: 3840
|
||||||
|
|
|
@ -12,7 +12,7 @@ services:
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- 2342:2342 # [local port]:[container port]
|
- 2342:2342 # [local port]:[container port]
|
||||||
# - 4000:4000 # Internal database (MySQL compatible)
|
# - 2343:2343 # Database (built-in TiDB)
|
||||||
healthcheck: # Optional
|
healthcheck: # Optional
|
||||||
test: "photoprism status"
|
test: "photoprism status"
|
||||||
interval: 60s
|
interval: 60s
|
||||||
|
@ -34,13 +34,13 @@ services:
|
||||||
PHOTOPRISM_DISABLE_SETTINGS: "false"
|
PHOTOPRISM_DISABLE_SETTINGS: "false"
|
||||||
PHOTOPRISM_HTTP_HOST: "0.0.0.0"
|
PHOTOPRISM_HTTP_HOST: "0.0.0.0"
|
||||||
PHOTOPRISM_HTTP_PORT: 2342
|
PHOTOPRISM_HTTP_PORT: 2342
|
||||||
PHOTOPRISM_SQL_HOST: "0.0.0.0"
|
PHOTOPRISM_TIDB_HOST: "0.0.0.0"
|
||||||
PHOTOPRISM_SQL_PORT: 4000 # Port for internal TiDB SQL server (driver "internal")
|
PHOTOPRISM_TIDB_PORT: 2343 # Port for built-in TiDB SQL server (driver "tidb")
|
||||||
PHOTOPRISM_SQL_PASSWORD: "photoprism" # Plain text only (username "root")
|
PHOTOPRISM_TIDB_PASSWORD: "photoprism" # Plain text only (username "root")
|
||||||
PHOTOPRISM_ADMIN_PASSWORD: "photoprism" # Plain text or bcrypt hash (escape "$" with "$$")
|
PHOTOPRISM_ADMIN_PASSWORD: "photoprism" # Plain text or bcrypt hash (escape "$" with "$$")
|
||||||
PHOTOPRISM_WEBDAV_PASSWORD: "photoprism" # Plain text only (username "photoprism")
|
PHOTOPRISM_WEBDAV_PASSWORD: "photoprism" # Plain text only (username "photoprism")
|
||||||
PHOTOPRISM_DATABASE_DRIVER: "internal" # Change to "mysql" for external MySQL or MariaDB
|
PHOTOPRISM_DATABASE_DRIVER: "tidb" # Change to "mysql" for external MySQL or MariaDB
|
||||||
PHOTOPRISM_DATABASE_DSN: "root:photoprism@tcp(localhost:4000)/photoprism?parseTime=true"
|
PHOTOPRISM_DATABASE_DSN: "root:photoprism@tcp(localhost:2343)/photoprism?parseTime=true"
|
||||||
# PHOTOPRISM_DATABASE_DRIVER: "mysql" # Using MariaDB or MySQL instead of the internal TiDB is optional
|
# PHOTOPRISM_DATABASE_DRIVER: "mysql" # Using MariaDB or MySQL instead of the internal TiDB is optional
|
||||||
# PHOTOPRISM_DATABASE_DSN: "photoprism:photoprism@tcp(photoprism-db:3306)/photoprism?parseTime=true"
|
# PHOTOPRISM_DATABASE_DSN: "photoprism:photoprism@tcp(photoprism-db:3306)/photoprism?parseTime=true"
|
||||||
# PHOTOPRISM_THUMB_QUALITY: 95 # High-quality thumbnails (optional, default JPEG quality is 90)
|
# PHOTOPRISM_THUMB_QUALITY: 95 # High-quality thumbnails (optional, default JPEG quality is 90)
|
||||||
|
|
|
@ -85,7 +85,7 @@
|
||||||
<div class="caption">
|
<div class="caption">
|
||||||
<button @click.exact="editPhoto(index)">
|
<button @click.exact="editPhoto(index)">
|
||||||
<v-icon size="14" title="Taken" v-if="photo.TakenSrc">date_range</v-icon>
|
<v-icon size="14" title="Taken" v-if="photo.TakenSrc">date_range</v-icon>
|
||||||
<v-icon size="14" title="Imported" v-else>save</v-icon>
|
<v-icon size="14" title="Modified" v-else>save</v-icon>
|
||||||
{{ photo.getDateString() }}
|
{{ photo.getDateString() }}
|
||||||
</button>
|
</button>
|
||||||
<br/>
|
<br/>
|
||||||
|
|
|
@ -102,7 +102,7 @@ class Photo extends RestModel {
|
||||||
refreshFileAttr() {
|
refreshFileAttr() {
|
||||||
const file = this.mainFile();
|
const file = this.mainFile();
|
||||||
|
|
||||||
if (!file) {
|
if (!file || !file.FileHash) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,11 @@ class Thumb extends Model {
|
||||||
let result = [];
|
let result = [];
|
||||||
|
|
||||||
photos.forEach((p) => {
|
photos.forEach((p) => {
|
||||||
result.push(this.fromPhoto(p));
|
let thumb = this.fromPhoto(p);
|
||||||
|
|
||||||
|
if(thumb) {
|
||||||
|
result.push(thumb);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -40,11 +44,15 @@ class Thumb extends Model {
|
||||||
return this.fromFile(photo, photo.Files.find(f => !!f.FilePrimary));
|
return this.fromFile(photo, photo.Files.find(f => !!f.FilePrimary));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!photo || !photo.FileHash) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
uuid: photo.PhotoUUID,
|
uuid: photo.PhotoUUID,
|
||||||
title: photo.PhotoTitle,
|
title: photo.PhotoTitle,
|
||||||
favorite: photo.PhotoFavorite,
|
favorite: photo.PhotoFavorite,
|
||||||
download_url: "/api/v1/download/" + photo.FileHash,
|
download_url: this.downloadUrl(photo),
|
||||||
original_w: photo.FileWidth,
|
original_w: photo.FileWidth,
|
||||||
original_h: photo.FileHeight,
|
original_h: photo.FileHeight,
|
||||||
};
|
};
|
||||||
|
@ -63,11 +71,15 @@ class Thumb extends Model {
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromFile(photo, file) {
|
static fromFile(photo, file) {
|
||||||
|
if(!photo || !file || !file.FileHash) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
uuid: photo.PhotoUUID,
|
uuid: photo.PhotoUUID,
|
||||||
title: photo.PhotoTitle,
|
title: photo.PhotoTitle,
|
||||||
favorite: photo.PhotoFavorite,
|
favorite: photo.PhotoFavorite,
|
||||||
download_url: "/api/v1/download/" + file.FileHash,
|
download_url: this.downloadUrl(file),
|
||||||
original_w: file.FileWidth,
|
original_w: file.FileWidth,
|
||||||
original_h: file.FileHeight,
|
original_h: file.FileHeight,
|
||||||
};
|
};
|
||||||
|
@ -92,8 +104,12 @@ class Thumb extends Model {
|
||||||
if (!p.Files) return;
|
if (!p.Files) return;
|
||||||
|
|
||||||
p.Files.forEach((f) => {
|
p.Files.forEach((f) => {
|
||||||
if (f.FileType === "jpg") {
|
if (f && f.FileType === "jpg") {
|
||||||
result.push(this.fromFile(p, f));
|
let thumb = this.fromFile(p, f);
|
||||||
|
|
||||||
|
if(thumb) {
|
||||||
|
result.push(thumb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -132,6 +148,15 @@ class Thumb extends Model {
|
||||||
|
|
||||||
return "/api/v1/thumbnails/" + file.FileHash + "/" + type;
|
return "/api/v1/thumbnails/" + file.FileHash + "/" + type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static downloadUrl(file) {
|
||||||
|
if (!file || !file.FileHash) {
|
||||||
|
return "";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return "/api/v1/download/" + file.FileHash;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Thumb;
|
export default Thumb;
|
||||||
|
|
|
@ -57,18 +57,18 @@ func configAction(ctx *cli.Context) error {
|
||||||
fmt.Printf("static-path %s\n", conf.HttpStaticPath())
|
fmt.Printf("static-path %s\n", conf.HttpStaticPath())
|
||||||
fmt.Printf("static-build-path %s\n", conf.HttpStaticBuildPath())
|
fmt.Printf("static-build-path %s\n", conf.HttpStaticBuildPath())
|
||||||
|
|
||||||
fmt.Printf("database-path %s\n", conf.DatabasePath())
|
|
||||||
fmt.Printf("database-driver %s\n", conf.DatabaseDriver())
|
|
||||||
fmt.Printf("database-dsn %s\n", conf.DatabaseDsn())
|
|
||||||
|
|
||||||
fmt.Printf("sql-host %s\n", conf.SqlServerHost())
|
|
||||||
fmt.Printf("sql-port %d\n", conf.SqlServerPort())
|
|
||||||
fmt.Printf("sql-password %s\n", conf.SqlServerPassword())
|
|
||||||
|
|
||||||
fmt.Printf("http-host %s\n", conf.HttpServerHost())
|
fmt.Printf("http-host %s\n", conf.HttpServerHost())
|
||||||
fmt.Printf("http-port %d\n", conf.HttpServerPort())
|
fmt.Printf("http-port %d\n", conf.HttpServerPort())
|
||||||
fmt.Printf("http-mode %s\n", conf.HttpServerMode())
|
fmt.Printf("http-mode %s\n", conf.HttpServerMode())
|
||||||
|
|
||||||
|
fmt.Printf("tidb-host %s\n", conf.TidbServerHost())
|
||||||
|
fmt.Printf("tidb-port %d\n", conf.TidbServerPort())
|
||||||
|
fmt.Printf("tidb-password %s\n", conf.TidbServerPassword())
|
||||||
|
fmt.Printf("tidb-path %s\n", conf.TidbServerPath())
|
||||||
|
|
||||||
|
fmt.Printf("database-driver %s\n", conf.DatabaseDriver())
|
||||||
|
fmt.Printf("database-dsn %s\n", conf.DatabaseDsn())
|
||||||
|
|
||||||
fmt.Printf("sips-bin %s\n", conf.SipsBin())
|
fmt.Printf("sips-bin %s\n", conf.SipsBin())
|
||||||
fmt.Printf("darktable-bin %s\n", conf.DarktableBin())
|
fmt.Printf("darktable-bin %s\n", conf.DarktableBin())
|
||||||
fmt.Printf("exiftool-bin %s\n", conf.ExifToolBin())
|
fmt.Printf("exiftool-bin %s\n", conf.ExifToolBin())
|
||||||
|
|
|
@ -51,11 +51,11 @@ func startAction(ctx *cli.Context) error {
|
||||||
if ctx.IsSet("config") {
|
if ctx.IsSet("config") {
|
||||||
fmt.Printf("NAME VALUE\n")
|
fmt.Printf("NAME VALUE\n")
|
||||||
fmt.Printf("detach-server %t\n", conf.DetachServer())
|
fmt.Printf("detach-server %t\n", conf.DetachServer())
|
||||||
fmt.Printf("database-path %s\n", conf.DatabasePath())
|
|
||||||
|
|
||||||
fmt.Printf("sql-host %s\n", conf.SqlServerHost())
|
fmt.Printf("tidb-host %s\n", conf.TidbServerHost())
|
||||||
fmt.Printf("sql-port %d\n", conf.SqlServerPort())
|
fmt.Printf("tidb-port %d\n", conf.TidbServerPort())
|
||||||
fmt.Printf("sql-password %s\n", conf.SqlServerPassword())
|
fmt.Printf("tidb-password %s\n", conf.TidbServerPassword())
|
||||||
|
fmt.Printf("tidb-path %s\n", conf.TidbServerPath())
|
||||||
|
|
||||||
fmt.Printf("http-host %s\n", conf.HttpServerHost())
|
fmt.Printf("http-host %s\n", conf.HttpServerHost())
|
||||||
fmt.Printf("http-port %d\n", conf.HttpServerPort())
|
fmt.Printf("http-port %d\n", conf.HttpServerPort())
|
||||||
|
|
|
@ -36,7 +36,7 @@ func TestConfig_Version(t *testing.T) {
|
||||||
c := NewConfig(ctx)
|
c := NewConfig(ctx)
|
||||||
|
|
||||||
version := c.Version()
|
version := c.Version()
|
||||||
assert.Equal(t, "1.0.0", version)
|
assert.Equal(t, "test", version)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfig_TensorFlowVersion(t *testing.T) {
|
func TestConfig_TensorFlowVersion(t *testing.T) {
|
||||||
|
@ -103,35 +103,35 @@ func TestConfig_DetachServer(t *testing.T) {
|
||||||
assert.Equal(t, false, detachServer)
|
assert.Equal(t, false, detachServer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfig_SqlServerHost(t *testing.T) {
|
func TestConfig_TidbServerHost(t *testing.T) {
|
||||||
ctx := CliTestContext()
|
ctx := CliTestContext()
|
||||||
c := NewConfig(ctx)
|
c := NewConfig(ctx)
|
||||||
|
|
||||||
host := c.SqlServerHost()
|
host := c.TidbServerHost()
|
||||||
assert.Equal(t, "127.0.0.1", host)
|
assert.Equal(t, "127.0.0.1", host)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfig_SqlServerPort(t *testing.T) {
|
func TestConfig_TidbServerPort(t *testing.T) {
|
||||||
ctx := CliTestContext()
|
ctx := CliTestContext()
|
||||||
c := NewConfig(ctx)
|
c := NewConfig(ctx)
|
||||||
|
|
||||||
port := c.SqlServerPort()
|
port := c.TidbServerPort()
|
||||||
assert.Equal(t, uint(4000), port)
|
assert.Equal(t, uint(2343), port)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfig_SqlServerPath(t *testing.T) {
|
func TestConfig_TidbServerPath(t *testing.T) {
|
||||||
ctx := CliTestContext()
|
ctx := CliTestContext()
|
||||||
c := NewConfig(ctx)
|
c := NewConfig(ctx)
|
||||||
|
|
||||||
path := c.DatabasePath()
|
path := c.TidbServerPath()
|
||||||
assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/resources/database", path)
|
assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/resources/database", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfig_SqlServerPassword(t *testing.T) {
|
func TestConfig_TidbServerPassword(t *testing.T) {
|
||||||
ctx := CliTestContext()
|
ctx := CliTestContext()
|
||||||
c := NewConfig(ctx)
|
c := NewConfig(ctx)
|
||||||
|
|
||||||
password := c.SqlServerPassword()
|
password := c.TidbServerPassword()
|
||||||
assert.Equal(t, "", password)
|
assert.Equal(t, "", password)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,7 +222,7 @@ func TestConfig_DatabaseDriver(t *testing.T) {
|
||||||
c := NewConfig(ctx)
|
c := NewConfig(ctx)
|
||||||
|
|
||||||
driver := c.DatabaseDriver()
|
driver := c.DatabaseDriver()
|
||||||
assert.Equal(t, "internal", driver)
|
assert.Equal(t, DriverTidb, driver)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfig_DatabaseDsn(t *testing.T) {
|
func TestConfig_DatabaseDsn(t *testing.T) {
|
||||||
|
@ -230,7 +230,7 @@ func TestConfig_DatabaseDsn(t *testing.T) {
|
||||||
c := NewConfig(ctx)
|
c := NewConfig(ctx)
|
||||||
|
|
||||||
dsn := c.DatabaseDriver()
|
dsn := c.DatabaseDriver()
|
||||||
assert.Equal(t, "internal", dsn)
|
assert.Equal(t, DriverTidb, dsn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfig_CachePath(t *testing.T) {
|
func TestConfig_CachePath(t *testing.T) {
|
||||||
|
|
|
@ -18,17 +18,17 @@ import (
|
||||||
|
|
||||||
// DatabaseDriver returns the database driver name.
|
// DatabaseDriver returns the database driver name.
|
||||||
func (c *Config) DatabaseDriver() string {
|
func (c *Config) DatabaseDriver() string {
|
||||||
if c.params.DatabaseDriver == "" {
|
if strings.ToLower(c.params.DatabaseDriver) == "mysql" {
|
||||||
return DbTiDB
|
return DriverMysql
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.params.DatabaseDriver
|
return DriverTidb
|
||||||
}
|
}
|
||||||
|
|
||||||
// DatabaseDsn returns the database data source name (DSN).
|
// DatabaseDsn returns the database data source name (DSN).
|
||||||
func (c *Config) DatabaseDsn() string {
|
func (c *Config) DatabaseDsn() string {
|
||||||
if c.params.DatabaseDsn == "" {
|
if c.params.DatabaseDsn == "" {
|
||||||
return "root:photoprism@tcp(localhost:4000)/photoprism?parseTime=true"
|
return "root:photoprism@tcp(localhost:2343)/photoprism?parseTime=true"
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.params.DatabaseDsn
|
return c.params.DatabaseDsn
|
||||||
|
@ -67,7 +67,6 @@ func (c *Config) MigrateDb() {
|
||||||
&entity.FileSync{},
|
&entity.FileSync{},
|
||||||
&entity.Photo{},
|
&entity.Photo{},
|
||||||
&entity.Description{},
|
&entity.Description{},
|
||||||
&entity.Event{},
|
|
||||||
&entity.Place{},
|
&entity.Place{},
|
||||||
&entity.Location{},
|
&entity.Location{},
|
||||||
&entity.Camera{},
|
&entity.Camera{},
|
||||||
|
@ -106,7 +105,6 @@ func (c *Config) DropTables() {
|
||||||
&entity.FileSync{},
|
&entity.FileSync{},
|
||||||
&entity.Photo{},
|
&entity.Photo{},
|
||||||
&entity.Description{},
|
&entity.Description{},
|
||||||
&entity.Event{},
|
|
||||||
&entity.Place{},
|
&entity.Place{},
|
||||||
&entity.Location{},
|
&entity.Location{},
|
||||||
&entity.Camera{},
|
&entity.Camera{},
|
||||||
|
@ -146,17 +144,17 @@ func (c *Config) connectToDatabase(ctx context.Context) error {
|
||||||
isTiDB := false
|
isTiDB := false
|
||||||
initSuccess := false
|
initSuccess := false
|
||||||
|
|
||||||
if dbDriver == DbTiDB {
|
if dbDriver == DriverTidb {
|
||||||
isTiDB = true
|
isTiDB = true
|
||||||
dbDriver = DbMySQL
|
dbDriver = DriverMysql
|
||||||
}
|
}
|
||||||
|
|
||||||
db, err := gorm.Open(dbDriver, dbDsn)
|
db, err := gorm.Open(dbDriver, dbDsn)
|
||||||
if err != nil || db == nil {
|
if err != nil || db == nil {
|
||||||
if isTiDB {
|
if isTiDB {
|
||||||
log.Infof("starting database server at %s:%d\n", c.SqlServerHost(), c.SqlServerPort())
|
log.Infof("starting database server at %s:%d\n", c.TidbServerHost(), c.TidbServerPort())
|
||||||
|
|
||||||
go tidb.Start(ctx, c.DatabasePath(), c.SqlServerPort(), c.SqlServerHost(), c.Debug())
|
go tidb.Start(ctx, c.TidbServerPath(), c.TidbServerPort(), c.TidbServerHost(), c.Debug())
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 1; i <= 12; i++ {
|
for i := 1; i <= 12; i++ {
|
||||||
|
@ -169,7 +167,7 @@ func (c *Config) connectToDatabase(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if isTiDB && !initSuccess {
|
if isTiDB && !initSuccess {
|
||||||
err = tidb.InitDatabase(c.SqlServerPort(), c.SqlServerPassword())
|
err = tidb.InitDatabase(c.TidbServerPort(), c.TidbServerPassword())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug(err)
|
log.Debug(err)
|
||||||
|
|
|
@ -61,8 +61,8 @@ func (c *Config) CreateDirectories() error {
|
||||||
return createError(c.ResourcesPath(), err)
|
return createError(c.ResourcesPath(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.MkdirAll(c.DatabasePath(), os.ModePerm); err != nil {
|
if err := os.MkdirAll(c.TidbServerPath(), os.ModePerm); err != nil {
|
||||||
return createError(c.DatabasePath(), err)
|
return createError(c.TidbServerPath(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.MkdirAll(c.TensorFlowModelPath(), os.ModePerm); err != nil {
|
if err := os.MkdirAll(c.TensorFlowModelPath(), os.ModePerm); err != nil {
|
||||||
|
|
|
@ -149,23 +149,6 @@ var GlobalFlags = []cli.Flag{
|
||||||
Value: "~/.local/share/photoprism",
|
Value: "~/.local/share/photoprism",
|
||||||
EnvVar: "PHOTOPRISM_ASSETS_PATH",
|
EnvVar: "PHOTOPRISM_ASSETS_PATH",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
|
||||||
Name: "database-path",
|
|
||||||
Usage: "built-in database server storage path",
|
|
||||||
EnvVar: "PHOTOPRISM_DATABASE_PATH",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "database-driver",
|
|
||||||
Usage: "database `DRIVER` (internal or mysql)",
|
|
||||||
Value: "internal",
|
|
||||||
EnvVar: "PHOTOPRISM_DATABASE_DRIVER",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "database-dsn",
|
|
||||||
Usage: "database data source name (`DSN`)",
|
|
||||||
Value: "root:@tcp(localhost:4000)/photoprism?parseTime=true",
|
|
||||||
EnvVar: "PHOTOPRISM_DATABASE_DSN",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "sips-bin",
|
Name: "sips-bin",
|
||||||
Usage: "sips cli binary `FILENAME`",
|
Usage: "sips cli binary `FILENAME`",
|
||||||
|
@ -192,6 +175,7 @@ var GlobalFlags = []cli.Flag{
|
||||||
},
|
},
|
||||||
cli.IntFlag{
|
cli.IntFlag{
|
||||||
Name: "http-port",
|
Name: "http-port",
|
||||||
|
Value: 2342,
|
||||||
Usage: "HTTP server port",
|
Usage: "HTTP server port",
|
||||||
EnvVar: "PHOTOPRISM_HTTP_PORT",
|
EnvVar: "PHOTOPRISM_HTTP_PORT",
|
||||||
},
|
},
|
||||||
|
@ -206,19 +190,37 @@ var GlobalFlags = []cli.Flag{
|
||||||
EnvVar: "PHOTOPRISM_HTTP_MODE",
|
EnvVar: "PHOTOPRISM_HTTP_MODE",
|
||||||
},
|
},
|
||||||
cli.IntFlag{
|
cli.IntFlag{
|
||||||
Name: "sql-port",
|
Name: "tidb-port",
|
||||||
Usage: "built-in SQL server port",
|
Value: 2343,
|
||||||
EnvVar: "PHOTOPRISM_SQL_PORT",
|
Usage: "built-in TiDB server port",
|
||||||
|
EnvVar: "PHOTOPRISM_TIDB_PORT",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "sql-host",
|
Name: "tidb-host",
|
||||||
Usage: "built-in SQL server host",
|
Usage: "built-in TiDB server host",
|
||||||
EnvVar: "PHOTOPRISM_SQL_HOST",
|
EnvVar: "PHOTOPRISM_TIDB_HOST",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "sql-password",
|
Name: "tidb-password",
|
||||||
Usage: "built-in SQL server password",
|
Usage: "built-in TiDB server password",
|
||||||
EnvVar: "PHOTOPRISM_SQL_PASSWORD",
|
EnvVar: "PHOTOPRISM_TIDB_PASSWORD",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "tidb-path",
|
||||||
|
Usage: "built-in TiDB server storage path",
|
||||||
|
EnvVar: "PHOTOPRISM_TIDB_PATH",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "database-driver",
|
||||||
|
Usage: "database `DRIVER` (tidb or mysql)",
|
||||||
|
Value: "tidb",
|
||||||
|
EnvVar: "PHOTOPRISM_DATABASE_DRIVER",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "database-dsn",
|
||||||
|
Usage: "database data source name (`DSN`)",
|
||||||
|
Value: "root:@tcp(localhost:2343)/photoprism?parseTime=true",
|
||||||
|
EnvVar: "PHOTOPRISM_DATABASE_DSN",
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "detect-nsfw",
|
Name: "detect-nsfw",
|
||||||
|
|
|
@ -15,8 +15,8 @@ import (
|
||||||
|
|
||||||
// define database drivers const
|
// define database drivers const
|
||||||
const (
|
const (
|
||||||
DbTiDB = "internal"
|
DriverTidb = "tidb"
|
||||||
DbMySQL = "mysql"
|
DriverMysql = "mysql"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Params provides a struct in which application configuration is stored.
|
// Params provides a struct in which application configuration is stored.
|
||||||
|
@ -54,12 +54,12 @@ type Params struct {
|
||||||
ImportPath string `yaml:"import-path" flag:"import-path"`
|
ImportPath string `yaml:"import-path" flag:"import-path"`
|
||||||
AssetsPath string `yaml:"assets-path" flag:"assets-path"`
|
AssetsPath string `yaml:"assets-path" flag:"assets-path"`
|
||||||
ResourcesPath string `yaml:"resources-path" flag:"resources-path"`
|
ResourcesPath string `yaml:"resources-path" flag:"resources-path"`
|
||||||
DatabasePath string `yaml:"database-path" flag:"database-path"`
|
|
||||||
DatabaseDriver string `yaml:"database-driver" flag:"database-driver"`
|
DatabaseDriver string `yaml:"database-driver" flag:"database-driver"`
|
||||||
DatabaseDsn string `yaml:"database-dsn" flag:"database-dsn"`
|
DatabaseDsn string `yaml:"database-dsn" flag:"database-dsn"`
|
||||||
SqlServerHost string `yaml:"sql-host" flag:"sql-host"`
|
TidbServerHost string `yaml:"tidb-host" flag:"tidb-host"`
|
||||||
SqlServerPort uint `yaml:"sql-port" flag:"sql-port"`
|
TidbServerPort uint `yaml:"tidb-port" flag:"tidb-port"`
|
||||||
SqlServerPassword string `yaml:"sql-password" flag:"sql-password"`
|
TidbServerPassword string `yaml:"tidb-password" flag:"tidb-password"`
|
||||||
|
TidbServerPath string `yaml:"tidb-path" flag:"tidb-path"`
|
||||||
HttpServerHost string `yaml:"http-host" flag:"http-host"`
|
HttpServerHost string `yaml:"http-host" flag:"http-host"`
|
||||||
HttpServerPort int `yaml:"http-port" flag:"http-port"`
|
HttpServerPort int `yaml:"http-port" flag:"http-port"`
|
||||||
HttpServerMode string `yaml:"http-mode" flag:"http-mode"`
|
HttpServerMode string `yaml:"http-mode" flag:"http-mode"`
|
||||||
|
@ -116,7 +116,7 @@ func (c *Params) expandFilenames() {
|
||||||
c.OriginalsPath = fs.Abs(c.OriginalsPath)
|
c.OriginalsPath = fs.Abs(c.OriginalsPath)
|
||||||
c.ImportPath = fs.Abs(c.ImportPath)
|
c.ImportPath = fs.Abs(c.ImportPath)
|
||||||
c.TempPath = fs.Abs(c.TempPath)
|
c.TempPath = fs.Abs(c.TempPath)
|
||||||
c.DatabasePath = fs.Abs(c.DatabasePath)
|
c.TidbServerPath = fs.Abs(c.TidbServerPath)
|
||||||
c.PIDFilename = fs.Abs(c.PIDFilename)
|
c.PIDFilename = fs.Abs(c.PIDFilename)
|
||||||
c.LogFilename = fs.Abs(c.LogFilename)
|
c.LogFilename = fs.Abs(c.LogFilename)
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ func TestParams_SetValuesFromFile(t *testing.T) {
|
||||||
assert.Equal(t, "/srv/photoprism/photos/originals", c.OriginalsPath)
|
assert.Equal(t, "/srv/photoprism/photos/originals", c.OriginalsPath)
|
||||||
assert.Equal(t, "/srv/photoprism/photos/import", c.ImportPath)
|
assert.Equal(t, "/srv/photoprism/photos/import", c.ImportPath)
|
||||||
assert.Equal(t, "/srv/photoprism/temp", c.TempPath)
|
assert.Equal(t, "/srv/photoprism/temp", c.TempPath)
|
||||||
assert.Equal(t, "internal", c.DatabaseDriver)
|
assert.Equal(t, DriverTidb, c.DatabaseDriver)
|
||||||
assert.Equal(t, "root:photoprism@tcp(localhost:4000)/photoprism?parseTime=true", c.DatabaseDsn)
|
assert.Equal(t, "root:photoprism@tcp(localhost:2343)/photoprism?parseTime=true", c.DatabaseDsn)
|
||||||
assert.Equal(t, 81, c.HttpServerPort)
|
assert.Equal(t, 81, c.HttpServerPort)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,15 +2,6 @@ package config
|
||||||
|
|
||||||
import "github.com/photoprism/photoprism/pkg/fs"
|
import "github.com/photoprism/photoprism/pkg/fs"
|
||||||
|
|
||||||
// DatabasePath returns the database storage path for TiDB.
|
|
||||||
func (c *Config) DatabasePath() string {
|
|
||||||
if c.params.DatabasePath == "" {
|
|
||||||
return c.ResourcesPath() + "/database"
|
|
||||||
}
|
|
||||||
|
|
||||||
return fs.Abs(c.params.DatabasePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DetachServer returns true if server should detach from console (daemon mode).
|
// DetachServer returns true if server should detach from console (daemon mode).
|
||||||
func (c *Config) DetachServer() bool {
|
func (c *Config) DetachServer() bool {
|
||||||
return c.params.DetachServer
|
return c.params.DetachServer
|
||||||
|
@ -72,25 +63,34 @@ func (c *Config) HttpStaticBuildPath() string {
|
||||||
return c.HttpStaticPath() + "/build"
|
return c.HttpStaticPath() + "/build"
|
||||||
}
|
}
|
||||||
|
|
||||||
// SqlServerHost returns the built-in SQL server host name or IP address (empty for all interfaces).
|
// TidbServerHost returns the host for the built-in TiDB server. (empty for all interfaces).
|
||||||
func (c *Config) SqlServerHost() string {
|
func (c *Config) TidbServerHost() string {
|
||||||
if c.params.SqlServerHost == "" {
|
if c.params.TidbServerHost == "" {
|
||||||
return "127.0.0.1"
|
return "127.0.0.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.params.SqlServerHost
|
return c.params.TidbServerHost
|
||||||
}
|
}
|
||||||
|
|
||||||
// SqlServerPort returns the built-in SQL server port.
|
// TidbServerPort returns the port for the built-in TiDB server.
|
||||||
func (c *Config) SqlServerPort() uint {
|
func (c *Config) TidbServerPort() uint {
|
||||||
if c.params.SqlServerPort == 0 {
|
if c.params.TidbServerPort == 0 {
|
||||||
return 4000
|
return 2343
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.params.SqlServerPort
|
return c.params.TidbServerPort
|
||||||
}
|
}
|
||||||
|
|
||||||
// SqlServerPassword returns the password for the built-in database server.
|
// TidbServerPassword returns the password for the built-in TiDB server.
|
||||||
func (c *Config) SqlServerPassword() string {
|
func (c *Config) TidbServerPassword() string {
|
||||||
return c.params.SqlServerPassword
|
return c.params.TidbServerPassword
|
||||||
|
}
|
||||||
|
|
||||||
|
// TidbServerPath returns the database storage path for the built-in TiDB server.
|
||||||
|
func (c *Config) TidbServerPath() string {
|
||||||
|
if c.params.TidbServerPath == "" {
|
||||||
|
return c.ResourcesPath() + "/database"
|
||||||
|
}
|
||||||
|
|
||||||
|
return fs.Abs(c.params.TidbServerPath)
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,7 +144,7 @@ func CliTestContext() *cli.Context {
|
||||||
globalSet.Bool("detect-nsfw", config.DetectNSFW, "doc")
|
globalSet.Bool("detect-nsfw", config.DetectNSFW, "doc")
|
||||||
|
|
||||||
app := cli.NewApp()
|
app := cli.NewApp()
|
||||||
app.Version = "1.0.0"
|
app.Version = "test"
|
||||||
|
|
||||||
c := cli.NewContext(app, globalSet, nil)
|
c := cli.NewContext(app, globalSet, nil)
|
||||||
|
|
||||||
|
|
10
internal/config/testdata/config.yml
vendored
10
internal/config/testdata/config.yml
vendored
|
@ -5,14 +5,14 @@ cache-path: /srv/photoprism/cache
|
||||||
originals-path: /srv/photoprism/photos/originals
|
originals-path: /srv/photoprism/photos/originals
|
||||||
import-path: /srv/photoprism/photos/import
|
import-path: /srv/photoprism/photos/import
|
||||||
temp-path: /srv/photoprism/temp
|
temp-path: /srv/photoprism/temp
|
||||||
sql-host: localhost
|
|
||||||
sql-port: 4000
|
|
||||||
sql-password: photoprism
|
|
||||||
http-host:
|
http-host:
|
||||||
http-mode: release
|
http-mode: release
|
||||||
http-port: 81
|
http-port: 81
|
||||||
http-password:
|
http-password:
|
||||||
database-driver: internal
|
tidb-host: localhost
|
||||||
database-dsn: root:photoprism@tcp(localhost:4000)/photoprism?parseTime=true
|
tidb-port: 2343
|
||||||
|
tidb-password: photoprism
|
||||||
|
database-driver: tidb
|
||||||
|
database-dsn: root:photoprism@tcp(localhost:2343)/photoprism?parseTime=true
|
||||||
theme: lavendel
|
theme: lavendel
|
||||||
language: english
|
language: english
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
package entity
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/jinzhu/gorm"
|
|
||||||
"github.com/photoprism/photoprism/pkg/rnd"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Event defines temporal event that can be used to link photos together
|
|
||||||
type Event struct {
|
|
||||||
EventUUID string `gorm:"type:varbinary(36);unique_index;"`
|
|
||||||
EventSlug string `gorm:"type:varbinary(255);unique_index;"`
|
|
||||||
EventName string
|
|
||||||
EventType string
|
|
||||||
EventDescription string `gorm:"type:text;"`
|
|
||||||
EventNotes string `gorm:"type:text;"`
|
|
||||||
EventBegin time.Time `gorm:"type:datetime;"`
|
|
||||||
EventEnd time.Time `gorm:"type:datetime;"`
|
|
||||||
EventLat float32 `gorm:"type:FLOAT;"`
|
|
||||||
EventLng float32 `gorm:"type:FLOAT;"`
|
|
||||||
EventDist float32 `gorm:"type:FLOAT;"`
|
|
||||||
CreatedAt time.Time
|
|
||||||
UpdatedAt time.Time
|
|
||||||
DeletedAt *time.Time `sql:"index"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TableName returns Event table identifier "events"
|
|
||||||
func (Event) TableName() string {
|
|
||||||
return "events"
|
|
||||||
}
|
|
||||||
|
|
||||||
// BeforeCreate computes a random UUID when a new event is created in database
|
|
||||||
func (e *Event) BeforeCreate(scope *gorm.Scope) error {
|
|
||||||
return scope.SetColumn("EventUUID", rnd.PPID('e'))
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
package entity
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestEvent_TableName(t *testing.T) {
|
|
||||||
event := &Event{EventSlug: "christmas-2000"}
|
|
||||||
tableName := event.TableName()
|
|
||||||
|
|
||||||
assert.Equal(t, "events", tableName)
|
|
||||||
}
|
|
|
@ -45,21 +45,22 @@ func (m *Location) Find(db *gorm.DB, api string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if place := FindPlaceByLabel(l.ID, l.LocLabel, db); place != nil {
|
if place := FindPlaceByLabel(l.S2Token(), l.Label(), db); place != nil {
|
||||||
m.Place = place
|
m.Place = place
|
||||||
} else {
|
} else {
|
||||||
m.Place = &Place{
|
m.Place = &Place{
|
||||||
ID: l.ID,
|
ID: l.S2Token(),
|
||||||
LocLabel: l.LocLabel,
|
LocLabel: l.Label(),
|
||||||
LocCity: l.LocCity,
|
LocCity: l.City(),
|
||||||
LocState: l.LocState,
|
LocState: l.State(),
|
||||||
LocCountry: l.LocCountry,
|
LocCountry: l.CountryCode(),
|
||||||
|
LocKeywords: l.KeywordString(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m.LocName = l.LocName
|
m.LocName = l.Name()
|
||||||
m.LocCategory = l.LocCategory
|
m.LocCategory = l.Category()
|
||||||
m.LocSource = l.LocSource
|
m.LocSource = l.Source()
|
||||||
|
|
||||||
if err := db.Create(m).Error; err == nil {
|
if err := db.Create(m).Error; err == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -72,11 +73,17 @@ func (m *Location) Find(db *gorm.DB, api string) error {
|
||||||
|
|
||||||
// Keywords computes keyword based on a Location
|
// Keywords computes keyword based on a Location
|
||||||
func (m *Location) Keywords() (result []string) {
|
func (m *Location) Keywords() (result []string) {
|
||||||
|
if m.Place == nil {
|
||||||
|
log.Errorf("location: place for %s is nil - you might have found a bug", m.ID)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
result = append(result, txt.Keywords(txt.ReplaceSpaces(m.City(), "-"))...)
|
result = append(result, txt.Keywords(txt.ReplaceSpaces(m.City(), "-"))...)
|
||||||
result = append(result, txt.Keywords(txt.ReplaceSpaces(m.State(), "-"))...)
|
result = append(result, txt.Keywords(txt.ReplaceSpaces(m.State(), "-"))...)
|
||||||
result = append(result, txt.Keywords(txt.ReplaceSpaces(m.CountryName(), "-"))...)
|
result = append(result, txt.Keywords(txt.ReplaceSpaces(m.CountryName(), "-"))...)
|
||||||
result = append(result, txt.Keywords(m.Category())...)
|
result = append(result, txt.Keywords(m.Category())...)
|
||||||
result = append(result, txt.Keywords(m.Name())...)
|
result = append(result, txt.Keywords(m.Name())...)
|
||||||
|
result = append(result, txt.Keywords(m.Place.LocKeywords)...)
|
||||||
|
|
||||||
result = txt.UniqueWords(result)
|
result = txt.UniqueWords(result)
|
||||||
|
|
||||||
|
|
|
@ -455,8 +455,6 @@ func (m *Photo) SetTakenAt(taken, local time.Time, zone, source string) {
|
||||||
|
|
||||||
if zone != "" {
|
if zone != "" {
|
||||||
m.TimeZone = zone
|
m.TimeZone = zone
|
||||||
} else {
|
|
||||||
m.TimeZone = time.UTC.String()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,11 @@ import (
|
||||||
// Place used to associate photos to places
|
// Place used to associate photos to places
|
||||||
type Place struct {
|
type Place struct {
|
||||||
ID string `gorm:"type:varbinary(16);primary_key;auto_increment:false;"`
|
ID string `gorm:"type:varbinary(16);primary_key;auto_increment:false;"`
|
||||||
LocLabel string `gorm:"type:varbinary(512);unique_index;"`
|
LocLabel string `gorm:"type:varbinary(768);unique_index;"`
|
||||||
LocCity string `gorm:"type:varchar(128);"`
|
LocCity string `gorm:"type:varchar(255);"`
|
||||||
LocState string `gorm:"type:varchar(128);"`
|
LocState string `gorm:"type:varchar(255);"`
|
||||||
LocCountry string `gorm:"type:varbinary(2);"`
|
LocCountry string `gorm:"type:varbinary(2);"`
|
||||||
|
LocKeywords string `gorm:"type:varchar(255);"`
|
||||||
LocNotes string `gorm:"type:text;"`
|
LocNotes string `gorm:"type:text;"`
|
||||||
LocFavorite bool
|
LocFavorite bool
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
|
@ -24,11 +25,14 @@ type Place struct {
|
||||||
|
|
||||||
// UnknownPlace is defined here to use it as a default
|
// UnknownPlace is defined here to use it as a default
|
||||||
var UnknownPlace = Place{
|
var UnknownPlace = Place{
|
||||||
ID: "zz",
|
ID: "zz",
|
||||||
LocLabel: "Unknown",
|
LocLabel: "Unknown",
|
||||||
LocCity: "Unknown",
|
LocCity: "Unknown",
|
||||||
LocState: "Unknown",
|
LocState: "Unknown",
|
||||||
LocCountry: "zz",
|
LocCountry: "zz",
|
||||||
|
LocKeywords: "",
|
||||||
|
LocNotes: "",
|
||||||
|
LocFavorite: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateUnknownPlace initializes default place in the database
|
// CreateUnknownPlace initializes default place in the database
|
||||||
|
|
|
@ -32,6 +32,7 @@ type Location struct {
|
||||||
LocState string
|
LocState string
|
||||||
LocCountry string
|
LocCountry string
|
||||||
LocSource string
|
LocSource string
|
||||||
|
LocKeywords []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type LocationSource interface {
|
type LocationSource interface {
|
||||||
|
@ -42,9 +43,10 @@ type LocationSource interface {
|
||||||
City() string
|
City() string
|
||||||
State() string
|
State() string
|
||||||
Source() string
|
Source() string
|
||||||
|
Keywords() []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLocation(id string, name string, category string, label string, city string, state string, country string, source string) *Location {
|
func NewLocation(id, name, category, label, city, state, country, source string, keywords []string) *Location {
|
||||||
result := &Location{
|
result := &Location{
|
||||||
ID: id,
|
ID: id,
|
||||||
LocName: name,
|
LocName: name,
|
||||||
|
@ -54,6 +56,7 @@ func NewLocation(id string, name string, category string, label string, city str
|
||||||
LocState: state,
|
LocState: state,
|
||||||
LocCountry: country,
|
LocCountry: country,
|
||||||
LocSource: source,
|
LocSource: source,
|
||||||
|
LocKeywords: keywords,
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
@ -84,6 +87,7 @@ func (l *Location) QueryPlaces() error {
|
||||||
l.LocCountry = s.CountryCode()
|
l.LocCountry = s.CountryCode()
|
||||||
l.LocCategory = s.Category()
|
l.LocCategory = s.Category()
|
||||||
l.LocLabel = s.Label()
|
l.LocLabel = s.Label()
|
||||||
|
l.LocKeywords = s.Keywords()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -114,6 +118,7 @@ func (l *Location) Assign(s LocationSource) error {
|
||||||
l.LocCountry = s.CountryCode()
|
l.LocCountry = s.CountryCode()
|
||||||
l.LocCategory = s.Category()
|
l.LocCategory = s.Category()
|
||||||
l.LocLabel = l.label()
|
l.LocLabel = l.label()
|
||||||
|
l.LocKeywords = s.Keywords()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -147,6 +152,10 @@ func (l *Location) label() string {
|
||||||
return strings.Join(loc[:], ", ")
|
return strings.Join(loc[:], ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l Location) S2Token() string {
|
||||||
|
return l.ID
|
||||||
|
}
|
||||||
|
|
||||||
func (l Location) Name() string {
|
func (l Location) Name() string {
|
||||||
return l.LocName
|
return l.LocName
|
||||||
}
|
}
|
||||||
|
@ -178,3 +187,11 @@ func (l Location) CountryName() string {
|
||||||
func (l Location) Source() string {
|
func (l Location) Source() string {
|
||||||
return l.LocSource
|
return l.LocSource
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l Location) Keywords() []string {
|
||||||
|
return l.LocKeywords
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l Location) KeywordString() string {
|
||||||
|
return strings.Join(l.LocKeywords, ", ")
|
||||||
|
}
|
||||||
|
|
|
@ -15,18 +15,42 @@ func TestLocation_QueryPlaces(t *testing.T) {
|
||||||
lng := 13.40806264572578
|
lng := 13.40806264572578
|
||||||
id := s2.Token(lat, lng)
|
id := s2.Token(lat, lng)
|
||||||
|
|
||||||
l := NewLocation(id, "", "", "", "", "", "", "")
|
l := NewLocation(id, "", "", "", "", "", "", "", []string{})
|
||||||
|
|
||||||
if err := l.QueryPlaces(); err != nil {
|
if err := l.QueryPlaces(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, "Alt-Berlin", l.LocName)
|
|
||||||
assert.Equal(t, "Berlin, Germany", l.LocLabel)
|
assert.Equal(t, "Berlin, Germany", l.LocLabel)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLocation_Assign(t *testing.T) {
|
func TestLocation_Assign(t *testing.T) {
|
||||||
|
t.Run("Italy", func(t *testing.T) {
|
||||||
|
id := "47786b2bed37"
|
||||||
|
|
||||||
|
o, err := places.FindLocation(id)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, "Comici I", o.Name())
|
||||||
|
assert.Equal(t, "Trentino-Alto Adige", o.State())
|
||||||
|
assert.Equal(t, "it", o.CountryCode())
|
||||||
|
|
||||||
|
var l Location
|
||||||
|
|
||||||
|
if err := l.Assign(o); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, "Comici I", l.LocName)
|
||||||
|
assert.Equal(t, "Plan de Gralba, Trentino-Alto Adige, Italy", l.LocLabel)
|
||||||
|
assert.IsType(t, []string{}, l.Keywords())
|
||||||
|
assert.Equal(t, "südtirol", l.KeywordString())
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("BerlinFernsehturm", func(t *testing.T) {
|
t.Run("BerlinFernsehturm", func(t *testing.T) {
|
||||||
lat := 52.5208
|
lat := 52.5208
|
||||||
lng := 13.40953
|
lng := 13.40953
|
||||||
|
@ -50,6 +74,8 @@ func TestLocation_Assign(t *testing.T) {
|
||||||
|
|
||||||
assert.Equal(t, "Fernsehturm Berlin", l.LocName)
|
assert.Equal(t, "Fernsehturm Berlin", l.LocName)
|
||||||
assert.Equal(t, "Berlin, Germany", l.LocLabel)
|
assert.Equal(t, "Berlin, Germany", l.LocLabel)
|
||||||
|
assert.IsType(t, []string{}, l.Keywords())
|
||||||
|
assert.Equal(t, "", l.KeywordString())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("SantaMonica", func(t *testing.T) {
|
t.Run("SantaMonica", func(t *testing.T) {
|
||||||
|
@ -167,7 +193,7 @@ func TestLocation_Assign(t *testing.T) {
|
||||||
|
|
||||||
assert.Equal(t, "Indian Ocean", l.LocName)
|
assert.Equal(t, "Indian Ocean", l.LocName)
|
||||||
assert.Equal(t, "", l.LocCategory)
|
assert.Equal(t, "", l.LocCategory)
|
||||||
assert.Equal(t, "", l.LocCity)
|
assert.Equal(t, "Unknown", l.LocCity)
|
||||||
// TODO: Should be zz for international waters, fixed in places server
|
// TODO: Should be zz for international waters, fixed in places server
|
||||||
// assert.Equal(t, "", l.LocCountry)
|
// assert.Equal(t, "", l.LocCountry)
|
||||||
})
|
})
|
||||||
|
@ -179,7 +205,7 @@ func TestLocation_Unknown(t *testing.T) {
|
||||||
lng := 0.0
|
lng := 0.0
|
||||||
id := s2.Token(lat, lng)
|
id := s2.Token(lat, lng)
|
||||||
|
|
||||||
l := NewLocation(id, "", "", "", "", "", "", "")
|
l := NewLocation(id, "", "", "", "", "", "", "", []string{})
|
||||||
|
|
||||||
assert.Equal(t, true, l.Unknown())
|
assert.Equal(t, true, l.Unknown())
|
||||||
})
|
})
|
||||||
|
@ -188,7 +214,7 @@ func TestLocation_Unknown(t *testing.T) {
|
||||||
lng := 29.148046666666666
|
lng := 29.148046666666666
|
||||||
id := s2.Token(lat, lng)
|
id := s2.Token(lat, lng)
|
||||||
|
|
||||||
l := NewLocation(id, "", "", "", "", "", "", "")
|
l := NewLocation(id, "", "", "", "", "", "", "", []string{})
|
||||||
|
|
||||||
assert.Equal(t, false, l.Unknown())
|
assert.Equal(t, false, l.Unknown())
|
||||||
})
|
})
|
||||||
|
@ -200,12 +226,12 @@ func TestLocation_place(t *testing.T) {
|
||||||
lng := 0.0
|
lng := 0.0
|
||||||
id := s2.Token(lat, lng)
|
id := s2.Token(lat, lng)
|
||||||
|
|
||||||
l := NewLocation(id, "", "", "", "", "", "", "")
|
l := NewLocation(id, "", "", "", "", "", "", "", []string{})
|
||||||
|
|
||||||
assert.Equal(t, "Unknown", l.label())
|
assert.Equal(t, "Unknown", l.label())
|
||||||
})
|
})
|
||||||
t.Run("Nürnberg, Bayern, Germany", func(t *testing.T) {
|
t.Run("Nürnberg, Bayern, Germany", func(t *testing.T) {
|
||||||
l := NewLocation("", "", "", "", "Nürnberg", "Bayern", "de", "")
|
l := NewLocation("", "", "", "", "Nürnberg", "Bayern", "de", "", []string{})
|
||||||
|
|
||||||
assert.Equal(t, "Unknown", l.label())
|
assert.Equal(t, "Unknown", l.label())
|
||||||
})
|
})
|
||||||
|
@ -213,7 +239,7 @@ func TestLocation_place(t *testing.T) {
|
||||||
|
|
||||||
func TestLocation_Name(t *testing.T) {
|
func TestLocation_Name(t *testing.T) {
|
||||||
t.Run("Christkindlesmarkt", func(t *testing.T) {
|
t.Run("Christkindlesmarkt", func(t *testing.T) {
|
||||||
l := NewLocation("", "Christkindlesmarkt", "", "", "Nürnberg", "Bayern", "de", "")
|
l := NewLocation("", "Christkindlesmarkt", "", "", "Nürnberg", "Bayern", "de", "", []string{})
|
||||||
|
|
||||||
assert.Equal(t, "Christkindlesmarkt", l.Name())
|
assert.Equal(t, "Christkindlesmarkt", l.Name())
|
||||||
})
|
})
|
||||||
|
@ -221,7 +247,7 @@ func TestLocation_Name(t *testing.T) {
|
||||||
|
|
||||||
func TestLocation_City(t *testing.T) {
|
func TestLocation_City(t *testing.T) {
|
||||||
t.Run("Nürnberg", func(t *testing.T) {
|
t.Run("Nürnberg", func(t *testing.T) {
|
||||||
l := NewLocation("", "Christkindlesmarkt", "", "", "Nürnberg", "Bayern", "de", "")
|
l := NewLocation("", "Christkindlesmarkt", "", "", "Nürnberg", "Bayern", "de", "", []string{})
|
||||||
|
|
||||||
assert.Equal(t, "Nürnberg", l.City())
|
assert.Equal(t, "Nürnberg", l.City())
|
||||||
})
|
})
|
||||||
|
@ -229,7 +255,7 @@ func TestLocation_City(t *testing.T) {
|
||||||
|
|
||||||
func TestLocation_State(t *testing.T) {
|
func TestLocation_State(t *testing.T) {
|
||||||
t.Run("Bayern", func(t *testing.T) {
|
t.Run("Bayern", func(t *testing.T) {
|
||||||
l := NewLocation("", "Christkindlesmarkt", "", "", "Nürnberg", "Bayern", "de", "")
|
l := NewLocation("", "Christkindlesmarkt", "", "", "Nürnberg", "Bayern", "de", "", []string{})
|
||||||
|
|
||||||
assert.Equal(t, "Bayern", l.State())
|
assert.Equal(t, "Bayern", l.State())
|
||||||
})
|
})
|
||||||
|
@ -237,7 +263,7 @@ func TestLocation_State(t *testing.T) {
|
||||||
|
|
||||||
func TestLocation_Category(t *testing.T) {
|
func TestLocation_Category(t *testing.T) {
|
||||||
t.Run("test", func(t *testing.T) {
|
t.Run("test", func(t *testing.T) {
|
||||||
l := NewLocation("", "Christkindlesmarkt", "test", "", "Nürnberg", "Bayern", "de", "")
|
l := NewLocation("", "Christkindlesmarkt", "test", "", "Nürnberg", "Bayern", "de", "", []string{})
|
||||||
|
|
||||||
assert.Equal(t, "test", l.Category())
|
assert.Equal(t, "test", l.Category())
|
||||||
})
|
})
|
||||||
|
@ -245,7 +271,7 @@ func TestLocation_Category(t *testing.T) {
|
||||||
|
|
||||||
func TestLocation_Source(t *testing.T) {
|
func TestLocation_Source(t *testing.T) {
|
||||||
t.Run("source", func(t *testing.T) {
|
t.Run("source", func(t *testing.T) {
|
||||||
l := NewLocation("", "Christkindlesmarkt", "", "", "Nürnberg", "Bayern", "de", "source")
|
l := NewLocation("", "Christkindlesmarkt", "", "", "Nürnberg", "Bayern", "de", "source", []string{})
|
||||||
|
|
||||||
assert.Equal(t, "source", l.Source())
|
assert.Equal(t, "source", l.Source())
|
||||||
})
|
})
|
||||||
|
@ -253,7 +279,7 @@ func TestLocation_Source(t *testing.T) {
|
||||||
|
|
||||||
func TestLocation_Place(t *testing.T) {
|
func TestLocation_Place(t *testing.T) {
|
||||||
t.Run("test-label", func(t *testing.T) {
|
t.Run("test-label", func(t *testing.T) {
|
||||||
l := NewLocation("", "Christkindlesmarkt", "", "test-label", "Nürnberg", "Bayern", "de", "")
|
l := NewLocation("", "Christkindlesmarkt", "", "test-label", "Nürnberg", "Bayern", "de", "", []string{})
|
||||||
|
|
||||||
assert.Equal(t, "test-label", l.Label())
|
assert.Equal(t, "test-label", l.Label())
|
||||||
})
|
})
|
||||||
|
@ -261,7 +287,7 @@ func TestLocation_Place(t *testing.T) {
|
||||||
|
|
||||||
func TestLocation_CountryCode(t *testing.T) {
|
func TestLocation_CountryCode(t *testing.T) {
|
||||||
t.Run("de", func(t *testing.T) {
|
t.Run("de", func(t *testing.T) {
|
||||||
l := NewLocation("", "Christkindlesmarkt", "test", "test-label", "Nürnberg", "Bayern", "de", "")
|
l := NewLocation("", "Christkindlesmarkt", "test", "test-label", "Nürnberg", "Bayern", "de", "", []string{})
|
||||||
|
|
||||||
assert.Equal(t, "de", l.CountryCode())
|
assert.Equal(t, "de", l.CountryCode())
|
||||||
})
|
})
|
||||||
|
@ -269,14 +295,14 @@ func TestLocation_CountryCode(t *testing.T) {
|
||||||
|
|
||||||
func TestLocation_CountryName(t *testing.T) {
|
func TestLocation_CountryName(t *testing.T) {
|
||||||
t.Run("Germany", func(t *testing.T) {
|
t.Run("Germany", func(t *testing.T) {
|
||||||
l := NewLocation("", "Christkindlesmarkt", "test", "test-label", "Nürnberg", "Bayern", "de", "")
|
l := NewLocation("", "Christkindlesmarkt", "test", "test-label", "Nürnberg", "Bayern", "de", "", []string{})
|
||||||
|
|
||||||
assert.Equal(t, "Germany", l.CountryName())
|
assert.Equal(t, "Germany", l.CountryName())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLocation_QueryApi(t *testing.T) {
|
func TestLocation_QueryApi(t *testing.T) {
|
||||||
l := NewLocation("3", "Christkindlesmarkt", "test", "test-label", "Nürnberg", "Bayern", "de", "")
|
l := NewLocation("3", "Christkindlesmarkt", "test", "test-label", "Nürnberg", "Bayern", "de", "", []string{})
|
||||||
t.Run("xxx", func(t *testing.T) {
|
t.Run("xxx", func(t *testing.T) {
|
||||||
api := l.QueryApi("xxx")
|
api := l.QueryApi("xxx")
|
||||||
assert.Error(t, api, "maps: reverse lookup disabled")
|
assert.Error(t, api, "maps: reverse lookup disabled")
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
|
|
||||||
"github.com/melihmucuk/geocache"
|
"github.com/melihmucuk/geocache"
|
||||||
"github.com/photoprism/photoprism/pkg/s2"
|
"github.com/photoprism/photoprism/pkg/s2"
|
||||||
"github.com/photoprism/photoprism/pkg/txt"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Location struct {
|
type Location struct {
|
||||||
|
@ -123,7 +122,7 @@ func (l Location) CountryCode() (result string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l Location) Keywords() (result []string) {
|
func (l Location) Keywords() (result []string) {
|
||||||
return txt.Keywords(l.LocDisplayName)
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l Location) Source() string {
|
func (l Location) Source() string {
|
||||||
|
|
|
@ -26,7 +26,7 @@ type Location struct {
|
||||||
var ReverseLookupURL = "https://places.photoprism.org/v1/location/%s"
|
var ReverseLookupURL = "https://places.photoprism.org/v1/location/%s"
|
||||||
var client = &http.Client{Timeout: 30 * time.Second} // TODO: Change timeout if needed
|
var client = &http.Client{Timeout: 30 * time.Second} // TODO: Change timeout if needed
|
||||||
|
|
||||||
func NewLocation(id string, lat float64, lng float64, name string, category string, place Place, cached bool) *Location {
|
func NewLocation(id string, lat, lng float64, name, category string, place Place, cached bool) *Location {
|
||||||
result := &Location{
|
result := &Location{
|
||||||
ID: id,
|
ID: id,
|
||||||
LocLat: lat,
|
LocLat: lat,
|
||||||
|
@ -68,7 +68,15 @@ func FindLocation(id string) (result Location, err error) {
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := client.Do(req)
|
var r *http.Response
|
||||||
|
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
r, err = client.Do(req)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("places: %s", err.Error())
|
log.Errorf("places: %s", err.Error())
|
||||||
|
@ -135,7 +143,7 @@ func (l Location) Longitude() (result float64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l Location) Keywords() (result []string) {
|
func (l Location) Keywords() (result []string) {
|
||||||
return txt.Keywords(l.Label())
|
return txt.UniqueKeywords(l.Place.LocKeywords)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l Location) Source() string {
|
func (l Location) Source() string {
|
||||||
|
|
|
@ -20,7 +20,6 @@ func TestFindLocation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.False(t, l.Cached)
|
assert.False(t, l.Cached)
|
||||||
assert.Equal(t, "Alt-Berlin", l.Name())
|
|
||||||
assert.Equal(t, "Berlin", l.City())
|
assert.Equal(t, "Berlin", l.City())
|
||||||
assert.Equal(t, "de", l.CountryCode())
|
assert.Equal(t, "de", l.CountryCode())
|
||||||
})
|
})
|
||||||
|
@ -35,7 +34,7 @@ func TestFindLocation(t *testing.T) {
|
||||||
t.Log(l)
|
t.Log(l)
|
||||||
})
|
})
|
||||||
t.Run("cached true", func(t *testing.T) {
|
t.Run("cached true", func(t *testing.T) {
|
||||||
var p = NewPlace("1", "", "", "", "de")
|
var p = NewPlace("1", "", "", "", "de", "")
|
||||||
location := NewLocation("54", 52.51961810676184, 13.40806264572578, "TestLocation", "test", p, true)
|
location := NewLocation("54", 52.51961810676184, 13.40806264572578, "TestLocation", "test", p, true)
|
||||||
l, err := FindLocation(location.ID)
|
l, err := FindLocation(location.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -52,7 +51,7 @@ func TestFindLocation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLocationGetters(t *testing.T) {
|
func TestLocationGetters(t *testing.T) {
|
||||||
var p = NewPlace("1", "testLabel", "berlin", "berlin", "de")
|
var p = NewPlace("1", "testLabel", "berlin", "berlin", "de", "foobar")
|
||||||
location := NewLocation("54", 52.51961810676184, 13.40806264572578, "TestLocation", "test", p, true)
|
location := NewLocation("54", 52.51961810676184, 13.40806264572578, "TestLocation", "test", p, true)
|
||||||
t.Run("wrong id", func(t *testing.T) {
|
t.Run("wrong id", func(t *testing.T) {
|
||||||
assert.Equal(t, "54", location.CellID())
|
assert.Equal(t, "54", location.CellID())
|
||||||
|
@ -65,7 +64,7 @@ func TestLocationGetters(t *testing.T) {
|
||||||
assert.Equal(t, 52.51961810676184, location.Latitude())
|
assert.Equal(t, 52.51961810676184, location.Latitude())
|
||||||
assert.Equal(t, 13.40806264572578, location.Longitude())
|
assert.Equal(t, 13.40806264572578, location.Longitude())
|
||||||
assert.Equal(t, "places", location.Source())
|
assert.Equal(t, "places", location.Source())
|
||||||
assert.Equal(t, []string{"testlabel"}, location.Keywords())
|
assert.Equal(t, []string{"foobar"}, location.Keywords())
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,20 +2,22 @@ package places
|
||||||
|
|
||||||
// Place
|
// Place
|
||||||
type Place struct {
|
type Place struct {
|
||||||
PlaceID string `json:"id"`
|
PlaceID string `json:"id"`
|
||||||
LocLabel string `json:"label"`
|
LocLabel string `json:"label"`
|
||||||
LocCity string `json:"city"`
|
LocCity string `json:"city"`
|
||||||
LocState string `json:"state"`
|
LocState string `json:"state"`
|
||||||
LocCountry string `json:"country"`
|
LocCountry string `json:"country"`
|
||||||
|
LocKeywords string `json:"keywords"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPlace(id string, label string, city string, state string, country string) Place {
|
func NewPlace(id, label, city, state, country, keywords string) Place {
|
||||||
result := Place{
|
result := Place{
|
||||||
PlaceID: id,
|
PlaceID: id,
|
||||||
LocLabel: label,
|
LocLabel: label,
|
||||||
LocCity: city,
|
LocCity: city,
|
||||||
LocState: state,
|
LocState: state,
|
||||||
LocCountry: country,
|
LocCountry: country,
|
||||||
|
LocKeywords: keywords,
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -201,7 +201,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
|
||||||
}
|
}
|
||||||
|
|
||||||
if photo.TakenAt.IsZero() || photo.TakenAtLocal.IsZero() {
|
if photo.TakenAt.IsZero() || photo.TakenAtLocal.IsZero() {
|
||||||
photo.SetTakenAt(m.DateCreated(), m.DateCreated(), time.UTC.String(), entity.SrcAuto)
|
photo.SetTakenAt(m.DateCreated(), m.DateCreated(), "", entity.SrcAuto)
|
||||||
}
|
}
|
||||||
|
|
||||||
if fileChanged || o.UpdateKeywords || o.UpdateLocation || o.UpdateTitle || photo.NoTitle() {
|
if fileChanged || o.UpdateKeywords || o.UpdateLocation || o.UpdateTitle || photo.NoTitle() {
|
||||||
|
|
|
@ -43,12 +43,12 @@ func TestFileType_Find(t *testing.T) {
|
||||||
result := TypeJpeg.Find("testdata/test (2).xmp", true)
|
result := TypeJpeg.Find("testdata/test (2).xmp", true)
|
||||||
assert.Equal(t, "testdata/test.jpg", result)
|
assert.Equal(t, "testdata/test.jpg", result)
|
||||||
})
|
})
|
||||||
t.Run("prefixUpper", func(t *testing.T) {
|
t.Run("name upper", func(t *testing.T) {
|
||||||
result := TypeJpeg.Find("testdata/catyellow.xmp", true)
|
result := TypeJpeg.Find("testdata/CATYELLOW.xmp", true)
|
||||||
assert.Equal(t, "testdata/CATYELLOW.jpg", result)
|
assert.Equal(t, "testdata/CATYELLOW.jpg", result)
|
||||||
})
|
})
|
||||||
t.Run("prefixLower", func(t *testing.T) {
|
t.Run("name lower", func(t *testing.T) {
|
||||||
result := TypeJpeg.Find("testdata/CHAMELEON_LIME.xmp", true)
|
result := TypeJpeg.Find("testdata/chameleon_lime.xmp", true)
|
||||||
assert.Equal(t, "testdata/chameleon_lime.jpg", result)
|
assert.Equal(t, "testdata/chameleon_lime.jpg", result)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@ photos
|
||||||
import
|
import
|
||||||
export
|
export
|
||||||
abc
|
abc
|
||||||
|
val
|
||||||
|
tmp
|
||||||
xyz
|
xyz
|
||||||
jpg
|
jpg
|
||||||
jpeg
|
jpeg
|
||||||
|
|
|
@ -17,6 +17,8 @@ var Stopwords = map[string]bool{
|
||||||
"import": true,
|
"import": true,
|
||||||
"export": true,
|
"export": true,
|
||||||
"abc": true,
|
"abc": true,
|
||||||
|
"val": true,
|
||||||
|
"tmp": true,
|
||||||
"xyz": true,
|
"xyz": true,
|
||||||
"jpg": true,
|
"jpg": true,
|
||||||
"jpeg": true,
|
"jpeg": true,
|
||||||
|
|
Loading…
Reference in a new issue