From d0199598d03cd7a864358e1af14d893c15832ac5 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sun, 31 May 2020 02:09:52 +0200 Subject: [PATCH] Simplify configuration #66 Signed-off-by: Michael Mayer --- .dockerignore | 13 +- .gitignore | 14 +- Dockerfile | 2 +- Makefile | 29 +- .../examples/.photoprism/example.jpg | Bin assets/{resources => }/examples/.ppignore | 0 .../{resources => }/examples/6720px_white.jpg | Bin .../{resources => }/examples/IMG_4120 (1).JPG | Bin .../examples/IMG_4120 copy.JPG | Bin assets/{resources => }/examples/IMG_4120.AAE | 0 assets/{resources => }/examples/IMG_4120.JPG | Bin assets/{resources => }/examples/IMG_E4120.JPG | Bin assets/{resources => }/examples/Random.docx | Bin .../Screenshot 2019-05-21 at 10.45.52.png | Bin assets/{resources => }/examples/airport_grey | Bin .../examples/beach_colorfilter.jpg | Bin .../{resources => }/examples/beach_sand.jpg | Bin .../{resources => }/examples/beach_wood.jpg | Bin .../{resources => }/examples/bitcoin_gold.jpg | Bin .../examples/building_mono.jpg | Bin .../{resources => }/examples/canon_eos_6d.dng | Bin .../examples/canon_eos_6d.json | 0 .../{resources => }/examples/canon_eos_6d.xmp | 0 .../examples/cash_register_gold.jpg | Bin assets/{resources => }/examples/cat_black.jpg | Bin assets/{resources => }/examples/cat_brown.jpg | Bin .../examples/cat_yellow_grey.jpg | Bin .../examples/chameleon_lime.jpg | Bin assets/{resources => }/examples/christmas.mp4 | Bin .../{resources => }/examples/clock_purple.jpg | Bin .../examples/clowns_colorful.jpg | Bin assets/{resources => }/examples/coin_gold.jpg | Bin .../examples/dog_created_1919.jpg | Bin .../{resources => }/examples/dog_orange.jpg | Bin .../examples/dog_toshi_red.jpg | Bin .../examples/dog_toshi_yellow.jpg | Bin assets/{resources => }/examples/door_cyan.jpg | Bin .../examples/elephant_mono.jpg | Bin assets/{resources => }/examples/elephants.jpg | Bin .../examples/ephedra_green_lime.jpg | Bin assets/{resources => }/examples/example.bmp | Bin assets/{resources => }/examples/example.gif | Bin assets/{resources => }/examples/example.png | Bin assets/{resources => }/examples/example.tif | Bin .../{resources => }/examples/fern_green.jpg | Bin .../examples/ferriswheel_colorful.jpg | Bin .../examples/fish_anthias_magenta.jpg | Bin .../examples/giraffe_green_brown.jpg | Bin .../{resources => }/examples/gopher-video.mp4 | Bin .../examples/instrument_gold.jpg | Bin assets/{resources => }/examples/iphone_7.heic | Bin assets/{resources => }/examples/iphone_7.json | 0 assets/{resources => }/examples/iphone_7.xmp | 0 .../examples/jellyfish_blue.jpg | Bin .../{resources => }/examples/leaves_gold.jpg | Bin assets/{resources => }/examples/limes.jpg | Bin .../{resources => }/examples/ocean_cyan.jpg | Bin .../examples/orange_square.jpg | Bin .../{resources => }/examples/palms_beach.jpg | Bin .../{resources => }/examples/peacock_blue.jpg | Bin .../{resources => }/examples/pillar_cyan.jpg | Bin .../examples/pineapple_white.jpg | Bin assets/{resources => }/examples/preloader.gif | Bin assets/{resources => }/examples/purple.tiff | Bin .../examples/robot_magenta.jpg | Bin .../examples/rope_blue_cyan.jpg | Bin .../{resources => }/examples/sharks_blue.jpg | Bin .../examples/snake_lime_brown.jpg | Bin .../{resources => }/examples/table_white.jpg | Bin .../examples/telegram_2020-01-30_09-57-18.jpg | Bin assets/{resources => }/examples/test.md | 0 assets/{resources => }/examples/test.txt | 0 assets/{resources => }/examples/test.xml | 0 assets/{resources => }/examples/test.yml | 0 .../{resources => }/examples/tree_white.jpg | Bin .../examples/turtle_brown_blue.jpg | Bin assets/{resources => }/examples/tweethog.png | Bin .../examples/yellow_rose-small.bmp | Bin .../examples/zebra_green_brown.jpg | Bin assets/{cache => static/build}/.gitignore | 0 .../static/favicons/favicon.ico | Bin .../static/favicons/favicon.png | Bin assets/{resources => }/static/img/logo.png | Bin assets/{resources => }/static/manifest.json | 0 assets/{resources => }/templates/index.tmpl | 20 +- assets/{resources => }/templates/minimal.tmpl | 2 +- docker-compose.travis.yml | 17 +- docker-compose.yml | 21 +- docker/demo/Dockerfile | 5 +- docker/demo/index.tmpl | 20 +- docker/photoprism/Dockerfile | 17 +- docker/photoprism/arm64/Dockerfile | 15 +- docker/photoprism/arm64/docker-compose.yml | 32 +- docker/photoprism/docker-compose.yml | 32 +- frontend/package-lock.json | 12 +- frontend/src/app.js | 4 +- frontend/src/common/config.js | 6 +- frontend/src/component/p-navigation.vue | 2 +- frontend/src/pages/album/photos.vue | 4 +- frontend/src/routes.js | 2 +- frontend/webpack.config.js | 2 +- internal/api/settings.go | 2 +- internal/classify/tensorflow_test.go | 25 +- internal/commands/commands.go | 2 +- internal/commands/config.go | 83 +++--- internal/config/client.go | 72 ++--- internal/config/config.go | 68 ++--- internal/config/config_test.go | 58 ++-- internal/config/db.go | 9 +- internal/config/filenames.go | 92 +++--- internal/config/flags.go | 279 +++++++++--------- internal/config/params.go | 32 +- internal/config/server.go | 36 +-- internal/config/settings.go | 8 +- internal/config/tensorflow.go | 22 +- internal/config/test.go | 25 +- internal/config/test_test.go | 2 +- internal/nsfw/nsfw_test.go | 2 +- internal/photoprism/import_test.go | 10 +- internal/photoprism/index_mediafile.go | 2 +- internal/photoprism/index_test.go | 2 +- internal/photoprism/mediafile_test.go | 18 +- internal/photoprism/resample_test.go | 4 +- internal/server/routes.go | 6 +- internal/server/server.go | 2 +- internal/service/classify.go | 2 +- {assets/photos => photos}/.gitignore | 0 scripts/download-nasnet.sh | 4 +- scripts/download-nsfw.sh | 4 +- .../static/build => storage/cache}/.gitignore | 0 .../settings}/photoprism.yml | 10 +- .../config => storage/settings}/settings.yml | 0 132 files changed, 588 insertions(+), 564 deletions(-) rename assets/{resources => }/examples/.photoprism/example.jpg (100%) rename assets/{resources => }/examples/.ppignore (100%) rename assets/{resources => }/examples/6720px_white.jpg (100%) rename assets/{resources => }/examples/IMG_4120 (1).JPG (100%) rename assets/{resources => }/examples/IMG_4120 copy.JPG (100%) rename assets/{resources => }/examples/IMG_4120.AAE (100%) rename assets/{resources => }/examples/IMG_4120.JPG (100%) rename assets/{resources => }/examples/IMG_E4120.JPG (100%) rename assets/{resources => }/examples/Random.docx (100%) rename assets/{resources => }/examples/Screenshot 2019-05-21 at 10.45.52.png (100%) rename assets/{resources => }/examples/airport_grey (100%) rename assets/{resources => }/examples/beach_colorfilter.jpg (100%) rename assets/{resources => }/examples/beach_sand.jpg (100%) rename assets/{resources => }/examples/beach_wood.jpg (100%) rename assets/{resources => }/examples/bitcoin_gold.jpg (100%) rename assets/{resources => }/examples/building_mono.jpg (100%) rename assets/{resources => }/examples/canon_eos_6d.dng (100%) rename assets/{resources => }/examples/canon_eos_6d.json (100%) rename assets/{resources => }/examples/canon_eos_6d.xmp (100%) rename assets/{resources => }/examples/cash_register_gold.jpg (100%) rename assets/{resources => }/examples/cat_black.jpg (100%) rename assets/{resources => }/examples/cat_brown.jpg (100%) rename assets/{resources => }/examples/cat_yellow_grey.jpg (100%) rename assets/{resources => }/examples/chameleon_lime.jpg (100%) rename assets/{resources => }/examples/christmas.mp4 (100%) rename assets/{resources => }/examples/clock_purple.jpg (100%) rename assets/{resources => }/examples/clowns_colorful.jpg (100%) rename assets/{resources => }/examples/coin_gold.jpg (100%) rename assets/{resources => }/examples/dog_created_1919.jpg (100%) rename assets/{resources => }/examples/dog_orange.jpg (100%) rename assets/{resources => }/examples/dog_toshi_red.jpg (100%) rename assets/{resources => }/examples/dog_toshi_yellow.jpg (100%) rename assets/{resources => }/examples/door_cyan.jpg (100%) rename assets/{resources => }/examples/elephant_mono.jpg (100%) rename assets/{resources => }/examples/elephants.jpg (100%) rename assets/{resources => }/examples/ephedra_green_lime.jpg (100%) rename assets/{resources => }/examples/example.bmp (100%) rename assets/{resources => }/examples/example.gif (100%) rename assets/{resources => }/examples/example.png (100%) rename assets/{resources => }/examples/example.tif (100%) rename assets/{resources => }/examples/fern_green.jpg (100%) rename assets/{resources => }/examples/ferriswheel_colorful.jpg (100%) rename assets/{resources => }/examples/fish_anthias_magenta.jpg (100%) rename assets/{resources => }/examples/giraffe_green_brown.jpg (100%) rename assets/{resources => }/examples/gopher-video.mp4 (100%) rename assets/{resources => }/examples/instrument_gold.jpg (100%) rename assets/{resources => }/examples/iphone_7.heic (100%) rename assets/{resources => }/examples/iphone_7.json (100%) rename assets/{resources => }/examples/iphone_7.xmp (100%) rename assets/{resources => }/examples/jellyfish_blue.jpg (100%) rename assets/{resources => }/examples/leaves_gold.jpg (100%) rename assets/{resources => }/examples/limes.jpg (100%) rename assets/{resources => }/examples/ocean_cyan.jpg (100%) rename assets/{resources => }/examples/orange_square.jpg (100%) rename assets/{resources => }/examples/palms_beach.jpg (100%) rename assets/{resources => }/examples/peacock_blue.jpg (100%) rename assets/{resources => }/examples/pillar_cyan.jpg (100%) rename assets/{resources => }/examples/pineapple_white.jpg (100%) rename assets/{resources => }/examples/preloader.gif (100%) rename assets/{resources => }/examples/purple.tiff (100%) rename assets/{resources => }/examples/robot_magenta.jpg (100%) rename assets/{resources => }/examples/rope_blue_cyan.jpg (100%) rename assets/{resources => }/examples/sharks_blue.jpg (100%) rename assets/{resources => }/examples/snake_lime_brown.jpg (100%) rename assets/{resources => }/examples/table_white.jpg (100%) rename assets/{resources => }/examples/telegram_2020-01-30_09-57-18.jpg (100%) rename assets/{resources => }/examples/test.md (100%) rename assets/{resources => }/examples/test.txt (100%) rename assets/{resources => }/examples/test.xml (100%) rename assets/{resources => }/examples/test.yml (100%) rename assets/{resources => }/examples/tree_white.jpg (100%) rename assets/{resources => }/examples/turtle_brown_blue.jpg (100%) rename assets/{resources => }/examples/tweethog.png (100%) rename assets/{resources => }/examples/yellow_rose-small.bmp (100%) rename assets/{resources => }/examples/zebra_green_brown.jpg (100%) rename assets/{cache => static/build}/.gitignore (100%) rename assets/{resources => }/static/favicons/favicon.ico (100%) rename assets/{resources => }/static/favicons/favicon.png (100%) rename assets/{resources => }/static/img/logo.png (100%) rename assets/{resources => }/static/manifest.json (100%) rename assets/{resources => }/templates/index.tmpl (60%) rename assets/{resources => }/templates/minimal.tmpl (96%) rename {assets/photos => photos}/.gitignore (100%) rename {assets/resources/static/build => storage/cache}/.gitignore (100%) rename {assets/config => storage/settings}/photoprism.yml (68%) rename {assets/config => storage/settings}/settings.yml (100%) diff --git a/.dockerignore b/.dockerignore index 25a005ec2..587006c82 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,13 +1,10 @@ -/assets/photos/* -/assets/cache/* +/photos/* /frontend/node_modules/* /node_modules -/assets/resources/database/* -/assets/resources/static/build/* -/assets/resources/nasnet -/assets/resources/nsfw -/assets/testdata -/assets/backups +/assets/static/build/* +/assets/nasnet +/assets/nsfw +/storage /photoprism /coverage.* /frontend/tests/acceptance/screenshots diff --git a/.gitignore b/.gitignore index bd8144fe4..7d5c0f095 100644 --- a/.gitignore +++ b/.gitignore @@ -1,20 +1,16 @@ # Application files and directories /photoprism -/assets/photos/originals/* -/assets/photos/import/* -/assets/photos/export/* -/assets/resources/database/* -!/assets/resources/database/.gitignore +/photos/originals/* +/photos/import/* +/storage/* /node_modules /frontend/.eslintcache /frontend/node_modules/* /frontend/tests/*.html /frontend/tests/*.log /frontend/tests/screenshots -/assets/testdata -/assets/backups -/assets/resources/nasnet -/assets/resources/nsfw +/assets/nasnet +/assets/nsfw /package-lock.json *.log *.db diff --git a/Dockerfile b/Dockerfile index 77e6cfdc8..318254b60 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM photoprism/development:20200519 +FROM photoprism/development:20200530 # Set up project directory WORKDIR "/go/src/github.com/photoprism/photoprism" diff --git a/Makefile b/Makefile index d0b8089ed..88aae3a7c 100644 --- a/Makefile +++ b/Makefile @@ -38,20 +38,20 @@ install-bin: scripts/build.sh prod ~/.local/bin/$(BINARY_NAME) install-assets: $(info Installing assets) - mkdir -p ~/.config/photoprism - mkdir -p ~/.cache/photoprism + mkdir -p ~/.photoprism/storage/settings + mkdir -p ~/.photoprism/storage/cache + mkdir -p ~/.photoprism/storage + mkdir -p ~/.photoprism/assets mkdir -p ~/Pictures/Originals mkdir -p ~/Pictures/Import - mkdir -p ~/.local/share/photoprism/resources/database - cp -r assets/resources/static assets/resources/templates assets/resources/nasnet assets/resources/nsfw ~/.local/share/photoprism/resources - rsync -a -v --ignore-existing assets/config/*.yml ~/.config/photoprism - find ~/.local/share/photoprism -name '.*' -type f -delete -clean-local-share: - rm -rf ~/.local/share/photoprism + cp -r assets/static assets/templates assets/nasnet assets/nsfw ~/.photoprism/assets + find ~/.photoprism/assets -name '.*' -type f -delete +clean-local-assets: + rm -rf ~/.photoprism/assets/* clean-local-cache: - rm -rf ~/.cache/photoprism + rm -rf ~/.photoprism/storage/cache/* clean-local-config: - rm -f ~/.config/photoprism/* + rm -f ~/.photoprism/storage/settings/* dep-js: (cd frontend && npm install --silent) dep-go: @@ -64,9 +64,9 @@ dep-tensorflow: scripts/download-nasnet.sh scripts/download-nsfw.sh zip-nasnet: - (cd assets/resources && zip -r nasnet.zip nasnet -x "*/.*" -x "*/version.txt") + (cd assets && zip -r nasnet.zip nasnet -x "*/.*" -x "*/version.txt") zip-nsfw: - (cd assets/resources && zip -r nsfw.zip nsfw -x "*/.*" -x "*/version.txt") + (cd assets && zip -r nsfw.zip nsfw -x "*/.*" -x "*/version.txt") build-js: (cd frontend && env NODE_ENV=production npm run build) build-go: @@ -126,8 +126,9 @@ clean: rm -f $(BINARY_NAME) rm -f *.log rm -rf node_modules - rm -rf assets/testdata - rm -rf assets/backups + rm -rf storage/testdata + rm -rf storage/backups + rm -rf storage/cache rm -rf frontend/node_modules docker-development: scripts/docker-build.sh development $(DOCKER_TAG) diff --git a/assets/resources/examples/.photoprism/example.jpg b/assets/examples/.photoprism/example.jpg similarity index 100% rename from assets/resources/examples/.photoprism/example.jpg rename to assets/examples/.photoprism/example.jpg diff --git a/assets/resources/examples/.ppignore b/assets/examples/.ppignore similarity index 100% rename from assets/resources/examples/.ppignore rename to assets/examples/.ppignore diff --git a/assets/resources/examples/6720px_white.jpg b/assets/examples/6720px_white.jpg similarity index 100% rename from assets/resources/examples/6720px_white.jpg rename to assets/examples/6720px_white.jpg diff --git a/assets/resources/examples/IMG_4120 (1).JPG b/assets/examples/IMG_4120 (1).JPG similarity index 100% rename from assets/resources/examples/IMG_4120 (1).JPG rename to assets/examples/IMG_4120 (1).JPG diff --git a/assets/resources/examples/IMG_4120 copy.JPG b/assets/examples/IMG_4120 copy.JPG similarity index 100% rename from assets/resources/examples/IMG_4120 copy.JPG rename to assets/examples/IMG_4120 copy.JPG diff --git a/assets/resources/examples/IMG_4120.AAE b/assets/examples/IMG_4120.AAE similarity index 100% rename from assets/resources/examples/IMG_4120.AAE rename to assets/examples/IMG_4120.AAE diff --git a/assets/resources/examples/IMG_4120.JPG b/assets/examples/IMG_4120.JPG similarity index 100% rename from assets/resources/examples/IMG_4120.JPG rename to assets/examples/IMG_4120.JPG diff --git a/assets/resources/examples/IMG_E4120.JPG b/assets/examples/IMG_E4120.JPG similarity index 100% rename from assets/resources/examples/IMG_E4120.JPG rename to assets/examples/IMG_E4120.JPG diff --git a/assets/resources/examples/Random.docx b/assets/examples/Random.docx similarity index 100% rename from assets/resources/examples/Random.docx rename to assets/examples/Random.docx diff --git a/assets/resources/examples/Screenshot 2019-05-21 at 10.45.52.png b/assets/examples/Screenshot 2019-05-21 at 10.45.52.png similarity index 100% rename from assets/resources/examples/Screenshot 2019-05-21 at 10.45.52.png rename to assets/examples/Screenshot 2019-05-21 at 10.45.52.png diff --git a/assets/resources/examples/airport_grey b/assets/examples/airport_grey similarity index 100% rename from assets/resources/examples/airport_grey rename to assets/examples/airport_grey diff --git a/assets/resources/examples/beach_colorfilter.jpg b/assets/examples/beach_colorfilter.jpg similarity index 100% rename from assets/resources/examples/beach_colorfilter.jpg rename to assets/examples/beach_colorfilter.jpg diff --git a/assets/resources/examples/beach_sand.jpg b/assets/examples/beach_sand.jpg similarity index 100% rename from assets/resources/examples/beach_sand.jpg rename to assets/examples/beach_sand.jpg diff --git a/assets/resources/examples/beach_wood.jpg b/assets/examples/beach_wood.jpg similarity index 100% rename from assets/resources/examples/beach_wood.jpg rename to assets/examples/beach_wood.jpg diff --git a/assets/resources/examples/bitcoin_gold.jpg b/assets/examples/bitcoin_gold.jpg similarity index 100% rename from assets/resources/examples/bitcoin_gold.jpg rename to assets/examples/bitcoin_gold.jpg diff --git a/assets/resources/examples/building_mono.jpg b/assets/examples/building_mono.jpg similarity index 100% rename from assets/resources/examples/building_mono.jpg rename to assets/examples/building_mono.jpg diff --git a/assets/resources/examples/canon_eos_6d.dng b/assets/examples/canon_eos_6d.dng similarity index 100% rename from assets/resources/examples/canon_eos_6d.dng rename to assets/examples/canon_eos_6d.dng diff --git a/assets/resources/examples/canon_eos_6d.json b/assets/examples/canon_eos_6d.json similarity index 100% rename from assets/resources/examples/canon_eos_6d.json rename to assets/examples/canon_eos_6d.json diff --git a/assets/resources/examples/canon_eos_6d.xmp b/assets/examples/canon_eos_6d.xmp similarity index 100% rename from assets/resources/examples/canon_eos_6d.xmp rename to assets/examples/canon_eos_6d.xmp diff --git a/assets/resources/examples/cash_register_gold.jpg b/assets/examples/cash_register_gold.jpg similarity index 100% rename from assets/resources/examples/cash_register_gold.jpg rename to assets/examples/cash_register_gold.jpg diff --git a/assets/resources/examples/cat_black.jpg b/assets/examples/cat_black.jpg similarity index 100% rename from assets/resources/examples/cat_black.jpg rename to assets/examples/cat_black.jpg diff --git a/assets/resources/examples/cat_brown.jpg b/assets/examples/cat_brown.jpg similarity index 100% rename from assets/resources/examples/cat_brown.jpg rename to assets/examples/cat_brown.jpg diff --git a/assets/resources/examples/cat_yellow_grey.jpg b/assets/examples/cat_yellow_grey.jpg similarity index 100% rename from assets/resources/examples/cat_yellow_grey.jpg rename to assets/examples/cat_yellow_grey.jpg diff --git a/assets/resources/examples/chameleon_lime.jpg b/assets/examples/chameleon_lime.jpg similarity index 100% rename from assets/resources/examples/chameleon_lime.jpg rename to assets/examples/chameleon_lime.jpg diff --git a/assets/resources/examples/christmas.mp4 b/assets/examples/christmas.mp4 similarity index 100% rename from assets/resources/examples/christmas.mp4 rename to assets/examples/christmas.mp4 diff --git a/assets/resources/examples/clock_purple.jpg b/assets/examples/clock_purple.jpg similarity index 100% rename from assets/resources/examples/clock_purple.jpg rename to assets/examples/clock_purple.jpg diff --git a/assets/resources/examples/clowns_colorful.jpg b/assets/examples/clowns_colorful.jpg similarity index 100% rename from assets/resources/examples/clowns_colorful.jpg rename to assets/examples/clowns_colorful.jpg diff --git a/assets/resources/examples/coin_gold.jpg b/assets/examples/coin_gold.jpg similarity index 100% rename from assets/resources/examples/coin_gold.jpg rename to assets/examples/coin_gold.jpg diff --git a/assets/resources/examples/dog_created_1919.jpg b/assets/examples/dog_created_1919.jpg similarity index 100% rename from assets/resources/examples/dog_created_1919.jpg rename to assets/examples/dog_created_1919.jpg diff --git a/assets/resources/examples/dog_orange.jpg b/assets/examples/dog_orange.jpg similarity index 100% rename from assets/resources/examples/dog_orange.jpg rename to assets/examples/dog_orange.jpg diff --git a/assets/resources/examples/dog_toshi_red.jpg b/assets/examples/dog_toshi_red.jpg similarity index 100% rename from assets/resources/examples/dog_toshi_red.jpg rename to assets/examples/dog_toshi_red.jpg diff --git a/assets/resources/examples/dog_toshi_yellow.jpg b/assets/examples/dog_toshi_yellow.jpg similarity index 100% rename from assets/resources/examples/dog_toshi_yellow.jpg rename to assets/examples/dog_toshi_yellow.jpg diff --git a/assets/resources/examples/door_cyan.jpg b/assets/examples/door_cyan.jpg similarity index 100% rename from assets/resources/examples/door_cyan.jpg rename to assets/examples/door_cyan.jpg diff --git a/assets/resources/examples/elephant_mono.jpg b/assets/examples/elephant_mono.jpg similarity index 100% rename from assets/resources/examples/elephant_mono.jpg rename to assets/examples/elephant_mono.jpg diff --git a/assets/resources/examples/elephants.jpg b/assets/examples/elephants.jpg similarity index 100% rename from assets/resources/examples/elephants.jpg rename to assets/examples/elephants.jpg diff --git a/assets/resources/examples/ephedra_green_lime.jpg b/assets/examples/ephedra_green_lime.jpg similarity index 100% rename from assets/resources/examples/ephedra_green_lime.jpg rename to assets/examples/ephedra_green_lime.jpg diff --git a/assets/resources/examples/example.bmp b/assets/examples/example.bmp similarity index 100% rename from assets/resources/examples/example.bmp rename to assets/examples/example.bmp diff --git a/assets/resources/examples/example.gif b/assets/examples/example.gif similarity index 100% rename from assets/resources/examples/example.gif rename to assets/examples/example.gif diff --git a/assets/resources/examples/example.png b/assets/examples/example.png similarity index 100% rename from assets/resources/examples/example.png rename to assets/examples/example.png diff --git a/assets/resources/examples/example.tif b/assets/examples/example.tif similarity index 100% rename from assets/resources/examples/example.tif rename to assets/examples/example.tif diff --git a/assets/resources/examples/fern_green.jpg b/assets/examples/fern_green.jpg similarity index 100% rename from assets/resources/examples/fern_green.jpg rename to assets/examples/fern_green.jpg diff --git a/assets/resources/examples/ferriswheel_colorful.jpg b/assets/examples/ferriswheel_colorful.jpg similarity index 100% rename from assets/resources/examples/ferriswheel_colorful.jpg rename to assets/examples/ferriswheel_colorful.jpg diff --git a/assets/resources/examples/fish_anthias_magenta.jpg b/assets/examples/fish_anthias_magenta.jpg similarity index 100% rename from assets/resources/examples/fish_anthias_magenta.jpg rename to assets/examples/fish_anthias_magenta.jpg diff --git a/assets/resources/examples/giraffe_green_brown.jpg b/assets/examples/giraffe_green_brown.jpg similarity index 100% rename from assets/resources/examples/giraffe_green_brown.jpg rename to assets/examples/giraffe_green_brown.jpg diff --git a/assets/resources/examples/gopher-video.mp4 b/assets/examples/gopher-video.mp4 similarity index 100% rename from assets/resources/examples/gopher-video.mp4 rename to assets/examples/gopher-video.mp4 diff --git a/assets/resources/examples/instrument_gold.jpg b/assets/examples/instrument_gold.jpg similarity index 100% rename from assets/resources/examples/instrument_gold.jpg rename to assets/examples/instrument_gold.jpg diff --git a/assets/resources/examples/iphone_7.heic b/assets/examples/iphone_7.heic similarity index 100% rename from assets/resources/examples/iphone_7.heic rename to assets/examples/iphone_7.heic diff --git a/assets/resources/examples/iphone_7.json b/assets/examples/iphone_7.json similarity index 100% rename from assets/resources/examples/iphone_7.json rename to assets/examples/iphone_7.json diff --git a/assets/resources/examples/iphone_7.xmp b/assets/examples/iphone_7.xmp similarity index 100% rename from assets/resources/examples/iphone_7.xmp rename to assets/examples/iphone_7.xmp diff --git a/assets/resources/examples/jellyfish_blue.jpg b/assets/examples/jellyfish_blue.jpg similarity index 100% rename from assets/resources/examples/jellyfish_blue.jpg rename to assets/examples/jellyfish_blue.jpg diff --git a/assets/resources/examples/leaves_gold.jpg b/assets/examples/leaves_gold.jpg similarity index 100% rename from assets/resources/examples/leaves_gold.jpg rename to assets/examples/leaves_gold.jpg diff --git a/assets/resources/examples/limes.jpg b/assets/examples/limes.jpg similarity index 100% rename from assets/resources/examples/limes.jpg rename to assets/examples/limes.jpg diff --git a/assets/resources/examples/ocean_cyan.jpg b/assets/examples/ocean_cyan.jpg similarity index 100% rename from assets/resources/examples/ocean_cyan.jpg rename to assets/examples/ocean_cyan.jpg diff --git a/assets/resources/examples/orange_square.jpg b/assets/examples/orange_square.jpg similarity index 100% rename from assets/resources/examples/orange_square.jpg rename to assets/examples/orange_square.jpg diff --git a/assets/resources/examples/palms_beach.jpg b/assets/examples/palms_beach.jpg similarity index 100% rename from assets/resources/examples/palms_beach.jpg rename to assets/examples/palms_beach.jpg diff --git a/assets/resources/examples/peacock_blue.jpg b/assets/examples/peacock_blue.jpg similarity index 100% rename from assets/resources/examples/peacock_blue.jpg rename to assets/examples/peacock_blue.jpg diff --git a/assets/resources/examples/pillar_cyan.jpg b/assets/examples/pillar_cyan.jpg similarity index 100% rename from assets/resources/examples/pillar_cyan.jpg rename to assets/examples/pillar_cyan.jpg diff --git a/assets/resources/examples/pineapple_white.jpg b/assets/examples/pineapple_white.jpg similarity index 100% rename from assets/resources/examples/pineapple_white.jpg rename to assets/examples/pineapple_white.jpg diff --git a/assets/resources/examples/preloader.gif b/assets/examples/preloader.gif similarity index 100% rename from assets/resources/examples/preloader.gif rename to assets/examples/preloader.gif diff --git a/assets/resources/examples/purple.tiff b/assets/examples/purple.tiff similarity index 100% rename from assets/resources/examples/purple.tiff rename to assets/examples/purple.tiff diff --git a/assets/resources/examples/robot_magenta.jpg b/assets/examples/robot_magenta.jpg similarity index 100% rename from assets/resources/examples/robot_magenta.jpg rename to assets/examples/robot_magenta.jpg diff --git a/assets/resources/examples/rope_blue_cyan.jpg b/assets/examples/rope_blue_cyan.jpg similarity index 100% rename from assets/resources/examples/rope_blue_cyan.jpg rename to assets/examples/rope_blue_cyan.jpg diff --git a/assets/resources/examples/sharks_blue.jpg b/assets/examples/sharks_blue.jpg similarity index 100% rename from assets/resources/examples/sharks_blue.jpg rename to assets/examples/sharks_blue.jpg diff --git a/assets/resources/examples/snake_lime_brown.jpg b/assets/examples/snake_lime_brown.jpg similarity index 100% rename from assets/resources/examples/snake_lime_brown.jpg rename to assets/examples/snake_lime_brown.jpg diff --git a/assets/resources/examples/table_white.jpg b/assets/examples/table_white.jpg similarity index 100% rename from assets/resources/examples/table_white.jpg rename to assets/examples/table_white.jpg diff --git a/assets/resources/examples/telegram_2020-01-30_09-57-18.jpg b/assets/examples/telegram_2020-01-30_09-57-18.jpg similarity index 100% rename from assets/resources/examples/telegram_2020-01-30_09-57-18.jpg rename to assets/examples/telegram_2020-01-30_09-57-18.jpg diff --git a/assets/resources/examples/test.md b/assets/examples/test.md similarity index 100% rename from assets/resources/examples/test.md rename to assets/examples/test.md diff --git a/assets/resources/examples/test.txt b/assets/examples/test.txt similarity index 100% rename from assets/resources/examples/test.txt rename to assets/examples/test.txt diff --git a/assets/resources/examples/test.xml b/assets/examples/test.xml similarity index 100% rename from assets/resources/examples/test.xml rename to assets/examples/test.xml diff --git a/assets/resources/examples/test.yml b/assets/examples/test.yml similarity index 100% rename from assets/resources/examples/test.yml rename to assets/examples/test.yml diff --git a/assets/resources/examples/tree_white.jpg b/assets/examples/tree_white.jpg similarity index 100% rename from assets/resources/examples/tree_white.jpg rename to assets/examples/tree_white.jpg diff --git a/assets/resources/examples/turtle_brown_blue.jpg b/assets/examples/turtle_brown_blue.jpg similarity index 100% rename from assets/resources/examples/turtle_brown_blue.jpg rename to assets/examples/turtle_brown_blue.jpg diff --git a/assets/resources/examples/tweethog.png b/assets/examples/tweethog.png similarity index 100% rename from assets/resources/examples/tweethog.png rename to assets/examples/tweethog.png diff --git a/assets/resources/examples/yellow_rose-small.bmp b/assets/examples/yellow_rose-small.bmp similarity index 100% rename from assets/resources/examples/yellow_rose-small.bmp rename to assets/examples/yellow_rose-small.bmp diff --git a/assets/resources/examples/zebra_green_brown.jpg b/assets/examples/zebra_green_brown.jpg similarity index 100% rename from assets/resources/examples/zebra_green_brown.jpg rename to assets/examples/zebra_green_brown.jpg diff --git a/assets/cache/.gitignore b/assets/static/build/.gitignore similarity index 100% rename from assets/cache/.gitignore rename to assets/static/build/.gitignore diff --git a/assets/resources/static/favicons/favicon.ico b/assets/static/favicons/favicon.ico similarity index 100% rename from assets/resources/static/favicons/favicon.ico rename to assets/static/favicons/favicon.ico diff --git a/assets/resources/static/favicons/favicon.png b/assets/static/favicons/favicon.png similarity index 100% rename from assets/resources/static/favicons/favicon.png rename to assets/static/favicons/favicon.png diff --git a/assets/resources/static/img/logo.png b/assets/static/img/logo.png similarity index 100% rename from assets/resources/static/img/logo.png rename to assets/static/img/logo.png diff --git a/assets/resources/static/manifest.json b/assets/static/manifest.json similarity index 100% rename from assets/resources/static/manifest.json rename to assets/static/manifest.json diff --git a/assets/resources/templates/index.tmpl b/assets/templates/index.tmpl similarity index 60% rename from assets/resources/templates/index.tmpl rename to assets/templates/index.tmpl index 5c3d7a146..423a59c79 100644 --- a/assets/resources/templates/index.tmpl +++ b/assets/templates/index.tmpl @@ -5,20 +5,20 @@ - {{ .config.Title }} + {{ .config.SiteTitle }} - - - - + + + + - - - + + + - - + + diff --git a/assets/resources/templates/minimal.tmpl b/assets/templates/minimal.tmpl similarity index 96% rename from assets/resources/templates/minimal.tmpl rename to assets/templates/minimal.tmpl index 5cf284cea..7c75f5db8 100644 --- a/assets/resources/templates/minimal.tmpl +++ b/assets/templates/minimal.tmpl @@ -5,7 +5,7 @@ - {{ .config.Title }} + {{ .config.SiteTitle }} diff --git a/docker-compose.travis.yml b/docker-compose.travis.yml index c9e493428..6033e308b 100644 --- a/docker-compose.travis.yml +++ b/docker-compose.travis.yml @@ -11,7 +11,11 @@ services: - "~/.cache/npm:/root/.cache/npm" - "~/.cache/go-mod:/go/pkg/mod" environment: - PHOTOPRISM_URL: "http://localhost:2342/" + PHOTOPRISM_SITE_URL: "http://localhost:2342/" + PHOTOPRISM_SITE_TITLE: "PhotoPrism" + PHOTOPRISM_SITE_CAPTION: "Browse your life" + PHOTOPRISM_SITE_DESCRIPTION: "Personal Photo Management powered by Go and Google TensorFlow. Free and open-source." + PHOTOPRISM_SITE_AUTHOR: "Anonymous" PHOTOPRISM_DEBUG: "false" PHOTOPRISM_READONLY: "false" PHOTOPRISM_PUBLIC: "true" @@ -24,11 +28,12 @@ services: PHOTOPRISM_HTTP_PORT: 2342 PHOTOPRISM_DATABASE_DRIVER: "mysql" PHOTOPRISM_DATABASE_DSN: "root:photoprism@tcp(photoprism-db:4001)/photoprism?parseTime=true" - PHOTOPRISM_TEST_DRIVER: "test" - PHOTOPRISM_TITLE: "PhotoPrism" - PHOTOPRISM_SUBTITLE: "Browse your life" - PHOTOPRISM_DESCRIPTION: "Personal Photo Management tested by Travis CI." - PHOTOPRISM_AUTHOR: "PhotoPrism.org" + PHOTOPRISM_TEST_DRIVER: "sqlite" + PHOTOPRISM_TEST_DSN: ".test.db" + PHOTOPRISM_ASSETS_PATH: "/go/src/github.com/photoprism/photoprism/assets" + PHOTOPRISM_STORAGE_PATH: "/go/src/github.com/photoprism/photoprism/storage" + PHOTOPRISM_ORIGINALS_PATH: "/go/src/github.com/photoprism/photoprism/photos/originals" + PHOTOPRISM_IMPORT_PATH: "/go/src/github.com/photoprism/photoprism/photos/import" PHOTOPRISM_THUMB_FILTER: "lanczos" # Resample filter, best to worst: blackman, lanczos, cubic, linear PHOTOPRISM_THUMB_UNCACHED: "true" # On-demand rendering of default thumbnails (high memory and cpu usage) PHOTOPRISM_THUMB_SIZE: 2048 # Default thumbnail size limit (default 2048, min 720, max 3840) diff --git a/docker-compose.yml b/docker-compose.yml index 5d8d0550d..54645b011 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,7 +14,11 @@ services: shm_size: "2gb" environment: TF_CPP_MIN_LOG_LEVEL: 0 # Show TensorFlow log messages for development - PHOTOPRISM_URL: "http://localhost:2342/" + PHOTOPRISM_SITE_URL: "http://localhost:2342/" + PHOTOPRISM_SITE_TITLE: "PhotoPrism" + PHOTOPRISM_SITE_CAPTION: "Browse your life" + PHOTOPRISM_SITE_DESCRIPTION: "Personal Photo Management powered by Go and Google TensorFlow. Free and open-source." + PHOTOPRISM_SITE_AUTHOR: "Anonymous" PHOTOPRISM_DEBUG: "true" PHOTOPRISM_READONLY: "false" PHOTOPRISM_PUBLIC: "true" @@ -26,17 +30,12 @@ services: PHOTOPRISM_HTTP_PORT: 2342 PHOTOPRISM_DATABASE_DRIVER: "mysql" PHOTOPRISM_DATABASE_DSN: "root:photoprism@tcp(photoprism-db:4001)/photoprism?parseTime=true" - PHOTOPRISM_TEST_DRIVER: "test" + PHOTOPRISM_TEST_DRIVER: "sqlite" + PHOTOPRISM_TEST_DSN: ".test.db" PHOTOPRISM_ASSETS_PATH: "/go/src/github.com/photoprism/photoprism/assets" - PHOTOPRISM_CACHE_PATH: "/go/src/github.com/photoprism/photoprism/assets/cache" - PHOTOPRISM_RESOURCES_PATH: "/go/src/github.com/photoprism/photoprism/assets/resources" - PHOTOPRISM_CONFIG_PATH: "/go/src/github.com/photoprism/photoprism/assets/config" - 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_ORIGINALS_PATH: "/go/src/github.com/photoprism/photoprism/assets/photos/originals" - PHOTOPRISM_TITLE: "PhotoPrism" - PHOTOPRISM_SUBTITLE: "Browse your life" - PHOTOPRISM_AUTHOR: "PhotoPrism.org" + PHOTOPRISM_STORAGE_PATH: "/go/src/github.com/photoprism/photoprism/storage" + PHOTOPRISM_ORIGINALS_PATH: "/go/src/github.com/photoprism/photoprism/photos/originals" + PHOTOPRISM_IMPORT_PATH: "/go/src/github.com/photoprism/photoprism/photos/import" PHOTOPRISM_THUMB_FILTER: "lanczos" # Resample filter, best to worst: blackman, lanczos, cubic, linear PHOTOPRISM_THUMB_UNCACHED: "true" # On-demand rendering of default thumbnails (high memory and cpu usage) PHOTOPRISM_THUMB_SIZE: 2048 # Default thumbnail size limit (default 2048, min 720, max 3840) diff --git a/docker/demo/Dockerfile b/docker/demo/Dockerfile index efb66e950..0366d5604 100644 --- a/docker/demo/Dockerfile +++ b/docker/demo/Dockerfile @@ -4,18 +4,19 @@ FROM photoprism/photoprism:latest as build ENV TF_CPP_MIN_LOG_LEVEL 2 # Copy assets -COPY /docker/demo/index.tmpl /photoprism/assets/resources/templates +COPY /docker/demo/index.tmpl /photoprism/assets/templates # Download example photos RUN wget -qO- https://dl.photoprism.org/fixtures/demo.tar.gz | tar xvz -C /photoprism/import # Configure PhotoPrism +ENV PHOTOPRISM_STORAGE_PATH /photoprism/storage ENV PHOTOPRISM_DEBUG false ENV PHOTOPRISM_READONLY false ENV PHOTOPRISM_PUBLIC true ENV PHOTOPRISM_EXPERIMENTAL true ENV PHOTOPRISM_UPLOAD_NSFW false -ENV PHOTOPRISM_DETECT_NSFW true +ENV PHOTOPRISM_DETECT_NSFW false ENV PHOTOPRISM_SIDECAR_JSON true ENV PHOTOPRISM_SIDECAR_YAML false ENV PHOTOPRISM_SIDECAR_HIDDEN true diff --git a/docker/demo/index.tmpl b/docker/demo/index.tmpl index 423c8660e..6700c6235 100644 --- a/docker/demo/index.tmpl +++ b/docker/demo/index.tmpl @@ -5,21 +5,21 @@ - {{ .config.Title }} + {{ .config.SiteTitle }} - - - - + + + + - - - + + + - - + + diff --git a/docker/photoprism/Dockerfile b/docker/photoprism/Dockerfile index 5e779e953..f9f080fdc 100644 --- a/docker/photoprism/Dockerfile +++ b/docker/photoprism/Dockerfile @@ -1,4 +1,4 @@ -FROM photoprism/development:20200519 as build +FROM photoprism/development:20200530 as build # Set up project directory WORKDIR "/go/src/github.com/photoprism/photoprism" @@ -54,29 +54,22 @@ ENV TF_CPP_MIN_LOG_LEVEL 2 ENV PATH /photoprism/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +ENV PHOTOPRISM_ASSETS_PATH /photoprism/assets ENV PHOTOPRISM_ORIGINALS_PATH /photoprism/originals ENV PHOTOPRISM_IMPORT_PATH /photoprism/import -ENV PHOTOPRISM_TEMP_PATH /photoprism/temp -ENV PHOTOPRISM_CACHE_PATH /photoprism/cache -ENV PHOTOPRISM_CONFIG_PATH /photoprism/config -ENV PHOTOPRISM_CONFIG_FILE /photoprism/config/photoprism.yml -ENV PHOTOPRISM_ASSETS_PATH /photoprism/assets -ENV PHOTOPRISM_RESOURCES_PATH /photoprism/assets/resources ENV PHOTOPRISM_LOG_FILENAME /photoprism/photoprism.log ENV PHOTOPRISM_PID_FILENAME /photoprism/photoprism.pid # Copy files to /photoprism COPY --from=build /root/.local/bin/photoprism /photoprism/bin/photoprism -COPY --from=build /root/.local/share/photoprism /photoprism/assets -COPY --from=build /root/.config/photoprism /photoprism/config +COPY --from=build /root/.photoprism/assets /photoprism/assets # Create directories RUN mkdir -p \ /photoprism/originals \ /photoprism/import \ - /photoprism/database \ - /photoprism/temp \ - /photoprism/cache + /photoprism/storage/settings \ + /photoprism/storage/cache RUN chmod -R 777 /photoprism diff --git a/docker/photoprism/arm64/Dockerfile b/docker/photoprism/arm64/Dockerfile index ebdc6168d..5d68c98c2 100644 --- a/docker/photoprism/arm64/Dockerfile +++ b/docker/photoprism/arm64/Dockerfile @@ -138,29 +138,22 @@ ENV TF_CPP_MIN_LOG_LEVEL 2 ENV PATH /photoprism/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +ENV PHOTOPRISM_ASSETS_PATH /photoprism/assets ENV PHOTOPRISM_ORIGINALS_PATH /photoprism/originals ENV PHOTOPRISM_IMPORT_PATH /photoprism/import -ENV PHOTOPRISM_TEMP_PATH /photoprism/temp -ENV PHOTOPRISM_CACHE_PATH /photoprism/cache -ENV PHOTOPRISM_CONFIG_PATH /photoprism/config -ENV PHOTOPRISM_CONFIG_FILE /photoprism/config/photoprism.yml -ENV PHOTOPRISM_ASSETS_PATH /photoprism/assets -ENV PHOTOPRISM_RESOURCES_PATH /photoprism/assets/resources ENV PHOTOPRISM_LOG_FILENAME /photoprism/photoprism.log ENV PHOTOPRISM_PID_FILENAME /photoprism/photoprism.pid # Copy files to /photoprism COPY --from=build /root/.local/bin/photoprism /photoprism/bin/photoprism -COPY --from=build /root/.local/share/photoprism /photoprism/assets -COPY --from=build /root/.config/photoprism /photoprism/config +COPY --from=build /root/.photoprism/assets /photoprism/assets # Create directories RUN mkdir -p \ /photoprism/originals \ /photoprism/import \ - /photoprism/database \ - /photoprism/temp \ - /photoprism/cache + /photoprism/storage/settings \ + /photoprism/storage/cache RUN chmod -R 777 /photoprism diff --git a/docker/photoprism/arm64/docker-compose.yml b/docker/photoprism/arm64/docker-compose.yml index 59dee5a61..3311bed3f 100644 --- a/docker/photoprism/arm64/docker-compose.yml +++ b/docker/photoprism/arm64/docker-compose.yml @@ -19,20 +19,20 @@ services: timeout: 15s retries: 5 environment: # Run "photoprism help" and "photoprism config" too see all config options and current values - PHOTOPRISM_URL: "https://demo.photoprism.org/" - PHOTOPRISM_TITLE: "PhotoPrism" - PHOTOPRISM_SUBTITLE: "Browse your life" - PHOTOPRISM_DESCRIPTION: "Personal Photo Management powered by Go and Google TensorFlow. Free and open-source." - PHOTOPRISM_AUTHOR: "Anonymous" - PHOTOPRISM_UPLOAD_NSFW: "true" - PHOTOPRISM_DETECT_NSFW: "false" - PHOTOPRISM_EXPERIMENTAL: "false" - PHOTOPRISM_DEBUG: "false" - PHOTOPRISM_READONLY: "false" - PHOTOPRISM_PUBLIC: "false" - PHOTOPRISM_DISABLE_SETTINGS: "false" + PHOTOPRISM_DEBUG: "false" # Run in debug mode + PHOTOPRISM_PUBLIC: "false" # No authentication / password required + PHOTOPRISM_READONLY: "false" # Don't add files or modify originals directory in any way + PHOTOPRISM_UPLOAD_NSFW: "true" # Allow uploads that may be offensive + PHOTOPRISM_DETECT_NSFW: "false" # Flag photos as private that MAY be offensive + PHOTOPRISM_EXPERIMENTAL: "false" # Enable experimental features + PHOTOPRISM_SITE_URL: "http://localhost:2342/" # Canonical / public site URL + PHOTOPRISM_SITE_TITLE: "PhotoPrism" + PHOTOPRISM_SITE_CAPTION: "Browse your life" + PHOTOPRISM_SITE_DESCRIPTION: "Personal Photo Management powered by Go and Google TensorFlow. Free and open-source." + PHOTOPRISM_SITE_AUTHOR: "Anonymous" PHOTOPRISM_HTTP_HOST: "0.0.0.0" PHOTOPRISM_HTTP_PORT: 2342 + PHOTOPRISM_SETTINGS_HIDDEN: "false" # Users can not view or change settings PHOTOPRISM_ADMIN_PASSWORD: "photoprism" # Plain text or bcrypt hash (escape "$" with "$$") PHOTOPRISM_WEBDAV_PASSWORD: "photoprism" # Plain text only (username "photoprism") PHOTOPRISM_DATABASE_DRIVER: "sqlite" # Change to "mysql" for external MySQL or MariaDB @@ -48,14 +48,12 @@ services: PHOTOPRISM_THUMB_LIMIT: 3840 # On-demand thumbnail size limit (default 2048, min 720, max 3840) PHOTOPRISM_JPEG_QUALITY: 90 # Use 95 for high-quality thumbnails (requires more storage) PHOTOPRISM_JPEG_HIDDEN: "true" # Create JPEG files in .photoprism (when converting other file types) + PHOTOPRISM_STORAGE_PATH: "/photoprism/storage" volumes: - "~/Pictures/Originals:/photoprism/originals" # [local path]:[container path] - "~/Pictures/Import:/photoprism/import" # [local path]:[container path] (optional) - - "photoprism-config:/photoprism/config" # keep settings - - "photoprism-cache:/photoprism/cache" # keep thumbnail cache + - "photoprism-storage:/photoprism/storage" # Keep cache, settings and database volumes: # keep this - photoprism-config: - driver: local - photoprism-cache: + photoprism-storage: driver: local diff --git a/docker/photoprism/docker-compose.yml b/docker/photoprism/docker-compose.yml index 4ebffbcca..fccf2e090 100644 --- a/docker/photoprism/docker-compose.yml +++ b/docker/photoprism/docker-compose.yml @@ -18,20 +18,20 @@ services: timeout: 15s retries: 5 environment: # Run "photoprism help" and "photoprism config" too see all config options and current values - PHOTOPRISM_URL: "http://localhost:2342/" - PHOTOPRISM_TITLE: "PhotoPrism" - PHOTOPRISM_SUBTITLE: "Browse your life" - PHOTOPRISM_DESCRIPTION: "Personal Photo Management powered by Go and Google TensorFlow. Free and open-source." - PHOTOPRISM_AUTHOR: "Anonymous" - PHOTOPRISM_UPLOAD_NSFW: "true" - PHOTOPRISM_DETECT_NSFW: "false" - PHOTOPRISM_EXPERIMENTAL: "false" - PHOTOPRISM_DEBUG: "false" - PHOTOPRISM_READONLY: "false" - PHOTOPRISM_PUBLIC: "false" - PHOTOPRISM_DISABLE_SETTINGS: "false" + PHOTOPRISM_DEBUG: "false" # Run in debug mode + PHOTOPRISM_PUBLIC: "false" # No authentication / password required + PHOTOPRISM_READONLY: "false" # Don't add files or modify originals directory in any way + PHOTOPRISM_UPLOAD_NSFW: "true" # Allow uploads that may be offensive + PHOTOPRISM_DETECT_NSFW: "false" # Flag photos as private that MAY be offensive + PHOTOPRISM_EXPERIMENTAL: "false" # Enable experimental features + PHOTOPRISM_SITE_URL: "http://localhost:2342/" # Canonical / public site URL + PHOTOPRISM_SITE_TITLE: "PhotoPrism" + PHOTOPRISM_SITE_CAPTION: "Browse your life" + PHOTOPRISM_SITE_DESCRIPTION: "Personal Photo Management powered by Go and Google TensorFlow. Free and open-source." + PHOTOPRISM_SITE_AUTHOR: "Anonymous" PHOTOPRISM_HTTP_HOST: "0.0.0.0" PHOTOPRISM_HTTP_PORT: 2342 + PHOTOPRISM_SETTINGS_HIDDEN: "false" # Users can not view or change settings PHOTOPRISM_ADMIN_PASSWORD: "photoprism" # Plain text or bcrypt hash (escape "$" with "$$") PHOTOPRISM_WEBDAV_PASSWORD: "photoprism" # Plain text only (username "photoprism") PHOTOPRISM_DATABASE_DRIVER: "sqlite" # Change to "mysql" for external MySQL or MariaDB @@ -47,11 +47,11 @@ services: PHOTOPRISM_THUMB_LIMIT: 3840 # On-demand thumbnail size limit (default 2048, min 720, max 3840) PHOTOPRISM_JPEG_QUALITY: 90 # Use 95 for high-quality thumbnails (requires more storage) PHOTOPRISM_JPEG_HIDDEN: "true" # Create JPEG files in .photoprism (when converting other file types) + PHOTOPRISM_STORAGE_PATH: "/photoprism/storage" volumes: - "~/Pictures/Originals:/photoprism/originals" # [local path]:[container path] - "~/Pictures/Import:/photoprism/import" # [local path]:[container path] (optional) - - "photoprism-config:/photoprism/config" # keep settings - - "photoprism-cache:/photoprism/cache" # keep thumbnail cache + - "photoprism-storage:/photoprism/storage" # Keep cache, settings and database # photoprism-db: # Uncomment, if you want to use MariaDB instead of SQLite # image: mariadb:10.5 # Alternatively mysql:8.0 @@ -66,9 +66,7 @@ services: # MYSQL_DATABASE: photoprism volumes: # keep this - photoprism-config: - driver: local - photoprism-cache: + photoprism-storage: driver: local # photoprism-database: # driver: local diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 77d99b702..8f1e8db3b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1798,7 +1798,8 @@ }, "minimist": { "version": "1.2.0", - "resolved": "" + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" } } }, @@ -11237,7 +11238,8 @@ }, "minimist": { "version": "1.2.0", - "resolved": "" + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, "postcss": { "version": "7.0.21", @@ -13030,7 +13032,8 @@ }, "minimist": { "version": "1.2.0", - "resolved": "" + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, "parse-json": { "version": "2.2.0", @@ -13441,7 +13444,8 @@ }, "minimist": { "version": "1.2.0", - "resolved": "" + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, "string-width": { "version": "3.1.0", diff --git a/frontend/src/app.js b/frontend/src/app.js index 45116c543..2dde4ab22 100644 --- a/frontend/src/app.js +++ b/frontend/src/app.js @@ -100,10 +100,10 @@ router.beforeEach((to, from, next) => { router.afterEach((to) => { if (to.meta.title) { config.page.title = to.meta.title; - window.document.title = "PhotoPrism: " + to.meta.title; + window.document.title = config.values.siteTitle + ": " + to.meta.title; } else { config.page.title = ""; - window.document.title = "PhotoPrism"; + window.document.title = config.values.siteTitle; } }); diff --git a/frontend/src/common/config.js b/frontend/src/common/config.js index f8a6b4c37..2784b201b 100644 --- a/frontend/src/common/config.js +++ b/frontend/src/common/config.js @@ -2,6 +2,7 @@ import Event from "pubsub-js"; import themes from "../resources/themes.json"; import translations from "../resources/translations.json"; import Api from "./api"; +import {config} from "../session"; class Config { /** @@ -14,9 +15,7 @@ class Config { this.$vuetify = null; this.translations = translations; - this.page = { - title: "PhotoPrism", - }; + this.page = {}; if (!values) { console.warn("config: values are empty"); @@ -25,6 +24,7 @@ class Config { return; } + this.page.title = values.siteTitle; this.values = values; this.debug = !!values.debug; diff --git a/frontend/src/component/p-navigation.vue b/frontend/src/component/p-navigation.vue index 1ad20964b..387f335e1 100644 --- a/frontend/src/component/p-navigation.vue +++ b/frontend/src/component/p-navigation.vue @@ -4,7 +4,7 @@ @click.stop="showNavigation()"> - {{ page.title }} + {{ config.siteTitle }} diff --git a/frontend/src/pages/album/photos.vue b/frontend/src/pages/album/photos.vue index f32a2c02f..5c370d20d 100644 --- a/frontend/src/pages/album/photos.vue +++ b/frontend/src/pages/album/photos.vue @@ -297,7 +297,7 @@ this.model = m; this.filter.order = m.Order; - window.document.title = `PhotoPrism: ${this.model.Title}`; + window.document.title = `${this.$config.get("siteTitle")}: ${this.model.Title}`; return Promise.resolve(this.model) }); @@ -319,7 +319,7 @@ } } - window.document.title = `PhotoPrism: ${this.model.Title}` + window.document.title = `${this.$config.get("siteTitle")}: ${this.model.Title}` this.dirty = true; this.scrollDisabled = false; diff --git a/frontend/src/routes.js b/frontend/src/routes.js index 06f594d3b..ddee8c381 100644 --- a/frontend/src/routes.js +++ b/frontend/src/routes.js @@ -30,7 +30,7 @@ export default [ name: "photos", path: "/photos", component: Photos, - meta: {title: c.subtitle, auth: true}, + meta: {title: c.siteCaption, auth: true}, props: {staticFilter: {photo: "true"}}, }, { diff --git a/frontend/webpack.config.js b/frontend/webpack.config.js index 5ebaae953..49220b4ec 100644 --- a/frontend/webpack.config.js +++ b/frontend/webpack.config.js @@ -16,7 +16,7 @@ const PATHS = { app: path.join(__dirname, "src/app.js"), js: path.join(__dirname, "src"), css: path.join(__dirname, "src/css"), - build: path.join(__dirname, "../assets/resources/static/build"), + build: path.join(__dirname, "../assets/static/build"), }; const config = { diff --git a/internal/api/settings.go b/internal/api/settings.go index bbd840bbb..a412fcb7a 100644 --- a/internal/api/settings.go +++ b/internal/api/settings.go @@ -25,7 +25,7 @@ func GetSettings(router *gin.RouterGroup, conf *config.Config) { // POST /api/v1/settings func SaveSettings(router *gin.RouterGroup, conf *config.Config) { router.POST("/settings", func(c *gin.Context) { - if conf.DisableSettings() || Unauthorized(c, conf) { + if conf.SettingsHidden() || Unauthorized(c, conf) { c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized) return } diff --git a/internal/classify/tensorflow_test.go b/internal/classify/tensorflow_test.go index f1735f0c3..2a49bc50a 100644 --- a/internal/classify/tensorflow_test.go +++ b/internal/classify/tensorflow_test.go @@ -5,21 +5,22 @@ import ( "sync" "testing" + "github.com/photoprism/photoprism/pkg/fs" tensorflow "github.com/tensorflow/tensorflow/tensorflow/go" "github.com/stretchr/testify/assert" ) -var resourcesPath = "../../assets/resources" -var modelPath = resourcesPath + "/nasnet" -var examplesPath = resourcesPath + "/examples" +var assetsPath = fs.Abs("../../assets") +var modelPath = assetsPath + "/nasnet" +var examplesPath = assetsPath + "/examples" var once sync.Once var testInstance *TensorFlow // NewTest returns a new TensorFlow test instance. func NewTest(t *testing.T) *TensorFlow { once.Do(func() { - testInstance = New(resourcesPath, false) + testInstance = New(assetsPath, false) if err := testInstance.loadModel(); err != nil { t.Fatal(err) } @@ -58,7 +59,7 @@ func TestTensorFlow_LabelsFromFile(t *testing.T) { assert.Empty(t, result) }) t.Run("disabled true", func(t *testing.T) { - tensorFlow := New(resourcesPath, true) + tensorFlow := New(assetsPath, true) result, err := tensorFlow.File(examplesPath + "/chameleon_lime.jpg") assert.Nil(t, err) @@ -160,7 +161,7 @@ func TestTensorFlow_Labels(t *testing.T) { } }) t.Run("disabled true", func(t *testing.T) { - tensorFlow := New(resourcesPath, true) + tensorFlow := New(assetsPath, true) if imageBuffer, err := ioutil.ReadFile(examplesPath + "/dog_orange.jpg"); err != nil { t.Error(err) @@ -184,7 +185,7 @@ func TestTensorFlow_LoadModel(t *testing.T) { assert.True(t, tf.ModelLoaded()) }) t.Run("model path does not exist", func(t *testing.T) { - tensorFlow := New(resourcesPath+"foo", false) + tensorFlow := New(assetsPath+"foo", false) if err := tensorFlow.loadModel(); err != nil { assert.Contains(t, err.Error(), "Could not find SavedModel") } else { @@ -195,7 +196,7 @@ func TestTensorFlow_LoadModel(t *testing.T) { func TestTensorFlow_BestLabels(t *testing.T) { t.Run("labels not loaded", func(t *testing.T) { - tensorFlow := New(resourcesPath, false) + tensorFlow := New(assetsPath, false) p := make([]float32, 1000) @@ -205,7 +206,7 @@ func TestTensorFlow_BestLabels(t *testing.T) { assert.Empty(t, result) }) t.Run("labels loaded", func(t *testing.T) { - tensorFlow := New(resourcesPath, false) + tensorFlow := New(assetsPath, false) if err := tensorFlow.loadLabels(modelPath); err != nil { t.Fatal(err) @@ -229,7 +230,11 @@ func TestTensorFlow_MakeTensor(t *testing.T) { tensorFlow := NewTest(t) imageBuffer, err := ioutil.ReadFile(examplesPath + "/cat_brown.jpg") - assert.Nil(t, err) + + if err != nil { + t.Fatal(err) + } + result, err := tensorFlow.makeTensor(imageBuffer, "jpeg") assert.Equal(t, tensorflow.DataType(0x1), result.DataType()) assert.Equal(t, int64(1), result.Shape()[0]) diff --git a/internal/commands/commands.go b/internal/commands/commands.go index 166412010..5bffc0b49 100644 --- a/internal/commands/commands.go +++ b/internal/commands/commands.go @@ -18,7 +18,7 @@ import ( var log = event.Log -// chilAlreadyRunning test if a .pid file at filePath is a running proccess. +// childAlreadyRunning tests if a .pid file at filePath is a running proccess. // it returns the pid value and the running status (true or false). func childAlreadyRunning(filePath string) (pid int, running bool) { if !fs.FileExists(filePath) { diff --git a/internal/commands/config.go b/internal/commands/config.go index 5dbc48faf..3d2d4ed1e 100644 --- a/internal/commands/config.go +++ b/internal/commands/config.go @@ -24,68 +24,67 @@ func configAction(ctx *cli.Context) error { fmt.Printf("%-25s VALUE\n", "NAME") - // Database config - fmt.Printf("%-25s %s\n", "database-driver", dbDriver) - fmt.Printf("%-25s %s\n", "database-dsn", dbDsn) - fmt.Printf("%-25s %d\n", "database-conns", conf.DatabaseConns()) - - // Description - fmt.Printf("%-25s %s\n", "name", conf.Name()) - fmt.Printf("%-25s %s\n", "url", conf.Url()) - fmt.Printf("%-25s %s\n", "title", conf.Title()) - fmt.Printf("%-25s %s\n", "subtitle", conf.Subtitle()) - fmt.Printf("%-25s %s\n", "description", conf.Description()) - fmt.Printf("%-25s %s\n", "author", conf.Author()) - fmt.Printf("%-25s %s\n", "version", conf.Version()) - fmt.Printf("%-25s %s\n", "copyright", conf.Copyright()) - - // Flags + // Feature flags. fmt.Printf("%-25s %t\n", "debug", conf.Debug()) - fmt.Printf("%-25s %t\n", "read-only", conf.ReadOnly()) fmt.Printf("%-25s %t\n", "public", conf.Public()) + fmt.Printf("%-25s %t\n", "read-only", conf.ReadOnly()) fmt.Printf("%-25s %t\n", "experimental", conf.Experimental()) - fmt.Printf("%-25s %t\n", "disable-settings", conf.DisableSettings()) - // TensorFlow - fmt.Printf("%-25s %t\n", "disable-tf", conf.DisableTensorFlow()) + // Site information. + fmt.Printf("%-25s %s\n", "site-url", conf.SiteUrl()) + fmt.Printf("%-25s %s\n", "site-title", conf.SiteTitle()) + fmt.Printf("%-25s %s\n", "site-caption", conf.SiteCaption()) + fmt.Printf("%-25s %s\n", "site-description", conf.SiteDescription()) + fmt.Printf("%-25s %s\n", "site-author", conf.SiteAuthor()) + + // Everything related to TensorFlow. + fmt.Printf("%-25s %t\n", "tf-off", conf.TensorFlowOff()) fmt.Printf("%-25s %s\n", "tf-version", conf.TensorFlowVersion()) fmt.Printf("%-25s %s\n", "tf-model-path", conf.TensorFlowModelPath()) fmt.Printf("%-25s %t\n", "detect-nsfw", conf.DetectNSFW()) fmt.Printf("%-25s %t\n", "upload-nsfw", conf.UploadNSFW()) - // Passwords + // Passwords. fmt.Printf("%-25s %s\n", "admin-password", conf.AdminPassword()) fmt.Printf("%-25s %s\n", "webdav-password", conf.WebDAVPassword()) - // Background workers and logging + // Background workers and logging. fmt.Printf("%-25s %d\n", "workers", conf.Workers()) fmt.Printf("%-25s %d\n", "wakeup-interval", conf.WakeupInterval()/time.Second) fmt.Printf("%-25s %s\n", "log-level", conf.LogLevel()) - - // Path and file names fmt.Printf("%-25s %s\n", "log-filename", conf.LogFilename()) fmt.Printf("%-25s %s\n", "pid-filename", conf.PIDFilename()) - fmt.Printf("%-25s %s\n", "config-file", conf.ConfigFile()) - fmt.Printf("%-25s %s\n", "config-path", conf.ConfigPath()) - fmt.Printf("%-25s %s\n", "assets-path", conf.AssetsPath()) - fmt.Printf("%-25s %s\n", "originals-path", conf.OriginalsPath()) - fmt.Printf("%-25s %d\n", "originals-limit", conf.OriginalsLimit()) - fmt.Printf("%-25s %s\n", "import-path", conf.ImportPath()) - fmt.Printf("%-25s %s\n", "temp-path", conf.TempPath()) - fmt.Printf("%-25s %s\n", "cache-path", conf.CachePath()) - fmt.Printf("%-25s %s\n", "resources-path", conf.ResourcesPath()) - // HTTP server config - fmt.Printf("%-25s %s\n", "favicons-path", conf.HttpFaviconsPath()) - fmt.Printf("%-25s %s\n", "static-path", conf.HttpStaticPath()) - fmt.Printf("%-25s %s\n", "static-build-path", conf.HttpStaticBuildPath()) - fmt.Printf("%-25s %s\n", "templates-path", conf.HttpTemplatesPath()) - fmt.Printf("%-25s %s\n", "http-template", conf.HttpDefaultTemplate()) + // HTTP server configuration. fmt.Printf("%-25s %s\n", "http-host", conf.HttpServerHost()) fmt.Printf("%-25s %d\n", "http-port", conf.HttpServerPort()) fmt.Printf("%-25s %s\n", "http-mode", conf.HttpServerMode()) - // External binaries + // Database configuration. + fmt.Printf("%-25s %s\n", "database-driver", dbDriver) + fmt.Printf("%-25s %s\n", "database-dsn", dbDsn) + fmt.Printf("%-25s %d\n", "database-conns", conf.DatabaseConns()) + + // Main directories. + fmt.Printf("%-25s %s\n", "assets-path", conf.AssetsPath()) + fmt.Printf("%-25s %s\n", "storage-path", conf.StoragePath()) + fmt.Printf("%-25s %s\n", "import-path", conf.ImportPath()) + fmt.Printf("%-25s %s\n", "originals-path", conf.OriginalsPath()) + fmt.Printf("%-25s %d\n", "originals-limit", conf.OriginalsLimit()) + + // Additional path and file names. + fmt.Printf("%-25s %s\n", "static-path", conf.StaticPath()) + fmt.Printf("%-25s %s\n", "static-build-path", conf.StaticBuildPath()) + fmt.Printf("%-25s %s\n", "favicons-path", conf.FaviconsPath()) + fmt.Printf("%-25s %s\n", "templates-path", conf.TemplatesPath()) + fmt.Printf("%-25s %s\n", "default-template", conf.DefaultTemplate()) + fmt.Printf("%-25s %s\n", "cache-path", conf.CachePath()) + fmt.Printf("%-25s %s\n", "temp-path", conf.TempPath()) + fmt.Printf("%-25s %s\n", "config-file", conf.ConfigFile()) + fmt.Printf("%-25s %s\n", "settings-path", conf.SettingsPath()) + fmt.Printf("%-25s %t\n", "settings-hidden", conf.SettingsHidden()) + + // External binaries and sidecar configuration. fmt.Printf("%-25s %s\n", "sips-bin", conf.SipsBin()) fmt.Printf("%-25s %s\n", "darktable-bin", conf.DarktableBin()) fmt.Printf("%-25s %s\n", "heifconvert-bin", conf.HeifConvertBin()) @@ -95,10 +94,10 @@ func configAction(ctx *cli.Context) error { fmt.Printf("%-25s %t\n", "sidecar-yaml", conf.SidecarYaml()) fmt.Printf("%-25s %t\n", "sidecar-hidden", conf.SidecarHidden()) - // Places / Geocoding API + // Places / Geocoding API configuration. fmt.Printf("%-25s %s\n", "geocoding-api", conf.GeoCodingApi()) - // Thumbnails + // Thumbnails, resampling and download security token. fmt.Printf("%-25s %s\n", "download-token", conf.DownloadToken()) fmt.Printf("%-25s %s\n", "thumb-token", conf.PreviewToken()) fmt.Printf("%-25s %s\n", "thumb-filter", conf.ThumbFilter()) diff --git a/internal/config/client.go b/internal/config/client.go index 938e0b1fd..6947823c6 100644 --- a/internal/config/client.go +++ b/internal/config/client.go @@ -13,15 +13,15 @@ import ( // ClientConfig contains HTTP client / Web UI config values type ClientConfig struct { - Flags string `json:"flags"` Name string `json:"name"` - URL string `json:"url"` - Title string `json:"title"` - Subtitle string `json:"subtitle"` - Description string `json:"description"` - Author string `json:"author"` Version string `json:"version"` Copyright string `json:"copyright"` + Flags string `json:"flags"` + SiteUrl string `json:"siteUrl"` + SiteTitle string `json:"siteTitle"` + SiteCaption string `json:"siteCaption"` + SiteDescription string `json:"siteDescription"` + SiteAuthor string `json:"siteAuthor"` Debug bool `json:"debug"` ReadOnly bool `json:"readonly"` UploadNSFW bool `json:"uploadNSFW"` @@ -101,7 +101,7 @@ func (c *Config) Flags() (flags []string) { flags = append(flags, "readonly") } - if !c.DisableSettings() { + if !c.SettingsHidden() { flags = append(flags, "settings") } @@ -117,27 +117,27 @@ func (c *Config) PublicClientConfig() ClientConfig { settings := c.Settings() result := ClientConfig{ - Settings: Settings{Language: settings.Language, Theme: settings.Theme}, - Flags: strings.Join(c.Flags(), " "), - Name: c.Name(), - URL: c.Url(), - Title: c.Title(), - Subtitle: c.Subtitle(), - Description: c.Description(), - Author: c.Author(), - Version: c.Version(), - Copyright: c.Copyright(), - Debug: c.Debug(), - ReadOnly: c.ReadOnly(), - Public: c.Public(), - Experimental: c.Experimental(), - Thumbnails: Thumbnails, - Colors: colors.All.List(), - JSHash: fs.Checksum(c.HttpStaticBuildPath() + "/app.js"), - CSSHash: fs.Checksum(c.HttpStaticBuildPath() + "/app.css"), - Clip: txt.ClipDefault, - PreviewToken: "public", - DownloadToken: "public", + Settings: Settings{Language: settings.Language, Theme: settings.Theme}, + Flags: strings.Join(c.Flags(), " "), + Name: c.Name(), + SiteUrl: c.SiteUrl(), + SiteTitle: c.SiteTitle(), + SiteCaption: c.SiteCaption(), + SiteDescription: c.SiteDescription(), + SiteAuthor: c.SiteAuthor(), + Version: c.Version(), + Copyright: c.Copyright(), + Debug: c.Debug(), + ReadOnly: c.ReadOnly(), + Public: c.Public(), + Experimental: c.Experimental(), + Thumbnails: Thumbnails, + Colors: colors.All.List(), + JSHash: fs.Checksum(c.StaticBuildPath() + "/app.js"), + CSSHash: fs.Checksum(c.StaticBuildPath() + "/app.css"), + Clip: txt.ClipDefault, + PreviewToken: "public", + DownloadToken: "public", } return result @@ -151,25 +151,25 @@ func (c *Config) ClientConfig() ClientConfig { Settings: *c.Settings(), Flags: strings.Join(c.Flags(), " "), Name: c.Name(), - URL: c.Url(), - Title: c.Title(), - Subtitle: c.Subtitle(), - Description: c.Description(), - Author: c.Author(), + SiteUrl: c.SiteUrl(), + SiteTitle: c.SiteTitle(), + SiteCaption: c.SiteCaption(), + SiteDescription: c.SiteDescription(), + SiteAuthor: c.SiteAuthor(), Version: c.Version(), Copyright: c.Copyright(), Debug: c.Debug(), ReadOnly: c.ReadOnly(), UploadNSFW: c.UploadNSFW(), - DisableSettings: c.DisableSettings(), + DisableSettings: c.SettingsHidden(), Public: c.Public(), Experimental: c.Experimental(), Colors: colors.All.List(), Thumbnails: Thumbnails, DownloadToken: c.DownloadToken(), PreviewToken: c.PreviewToken(), - JSHash: fs.Checksum(c.HttpStaticBuildPath() + "/app.js"), - CSSHash: fs.Checksum(c.HttpStaticBuildPath() + "/app.css"), + JSHash: fs.Checksum(c.StaticBuildPath() + "/app.js"), + CSSHash: fs.Checksum(c.StaticBuildPath() + "/app.css"), Clip: txt.ClipDefault, Server: NewRuntimeInfo(), } diff --git a/internal/config/config.go b/internal/config/config.go index 400102033..71a5ed98c 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -86,44 +86,11 @@ func (c *Config) Init(ctx context.Context) error { return c.connectToDatabase(ctx) } -// Name returns the application name. +// Name returns the application name ("PhotoPrism"). func (c *Config) Name() string { return c.params.Name } -// Url returns the public server URL (default is "http://localhost:2342/"). -func (c *Config) Url() string { - if c.params.Url == "" { - return "http://localhost:2342/" - } - - return c.params.Url -} - -// Title returns the site title (default is application name). -func (c *Config) Title() string { - if c.params.Title == "" { - return c.Name() - } - - return c.params.Title -} - -// Subtitle returns the site title. -func (c *Config) Subtitle() string { - return c.params.Subtitle -} - -// Description returns the site title. -func (c *Config) Description() string { - return c.params.Description -} - -// Author returns the site author / copyright. -func (c *Config) Author() string { - return c.params.Author -} - // Version returns the application version. func (c *Config) Version() string { return c.params.Version @@ -134,6 +101,39 @@ func (c *Config) Copyright() string { return c.params.Copyright } +// SiteUrl returns the public server URL (default is "http://localhost:2342/"). +func (c *Config) SiteUrl() string { + if c.params.SiteUrl == "" { + return "http://localhost:2342/" + } + + return c.params.SiteUrl +} + +// SiteTitle returns the main site title (default is application name). +func (c *Config) SiteTitle() string { + if c.params.SiteTitle == "" { + return c.Name() + } + + return c.params.SiteTitle +} + +// SiteCaption returns a short site caption. +func (c *Config) SiteCaption() string { + return c.params.SiteCaption +} + +// SiteDescription returns a long site description. +func (c *Config) SiteDescription() string { + return c.params.SiteDescription +} + +// SiteAuthor returns the site author / copyright. +func (c *Config) SiteAuthor() string { + return c.params.SiteAuthor +} + // Debug returns true if Debug mode is on. func (c *Config) Debug() bool { return c.params.Debug diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 06ef2bc95..343e35b12 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -66,7 +66,7 @@ func TestConfig_TensorFlowDisabled(t *testing.T) { ctx := CliTestContext() c := NewConfig(ctx) - version := c.DisableTensorFlow() + version := c.TensorFlowOff() assert.Equal(t, false, version) } @@ -82,32 +82,28 @@ func TestConfig_ConfigFile(t *testing.T) { ctx := CliTestContext() c := NewConfig(ctx) - configFile := c.ConfigFile() - assert.Equal(t, "", configFile) + assert.Contains(t, c.ConfigFile(), "/storage/testdata/settings/photoprism.yml") } -func TestConfig_ConfigPath(t *testing.T) { +func TestConfig_SettingsPath(t *testing.T) { ctx := CliTestContext() c := NewConfig(ctx) - configPath := c.ConfigPath() - assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/config", configPath) + assert.Contains(t, c.SettingsPath(), "/storage/testdata/settings") } func TestConfig_PIDFilename(t *testing.T) { ctx := CliTestContext() c := NewConfig(ctx) - filename := c.PIDFilename() - assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/photoprism.pid", filename) + assert.Contains(t, c.PIDFilename(), "/storage/testdata/photoprism.pid") } func TestConfig_LogFilename(t *testing.T) { ctx := CliTestContext() c := NewConfig(ctx) - filename := c.LogFilename() - assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/photoprism.log", filename) + assert.Contains(t, c.LogFilename(), "/storage/testdata/photoprism.log") } func TestConfig_DetachServer(t *testing.T) { @@ -156,7 +152,7 @@ func TestConfig_OriginalsPath(t *testing.T) { result := c.OriginalsPath() assert.True(t, strings.HasPrefix(result, "/")) - assert.True(t, strings.HasSuffix(result, "assets/testdata/originals")) + assert.True(t, strings.HasSuffix(result, "/storage/testdata/originals")) } func TestConfig_ImportPath(t *testing.T) { @@ -165,7 +161,7 @@ func TestConfig_ImportPath(t *testing.T) { result := c.ImportPath() assert.True(t, strings.HasPrefix(result, "/")) - assert.True(t, strings.HasSuffix(result, "assets/testdata/import")) + assert.True(t, strings.HasSuffix(result, "/storage/testdata/import")) } func TestConfig_SipsBin(t *testing.T) { @@ -220,7 +216,7 @@ func TestConfig_CachePath(t *testing.T) { ctx := CliTestContext() c := NewConfig(ctx) - assert.True(t, strings.HasSuffix(c.CachePath(), "assets/testdata/cache")) + assert.True(t, strings.HasSuffix(c.CachePath(), "storage/testdata/cache")) } func TestConfig_ThumbnailsPath(t *testing.T) { @@ -228,23 +224,14 @@ func TestConfig_ThumbnailsPath(t *testing.T) { c := NewConfig(ctx) assert.True(t, strings.HasPrefix(c.ThumbPath(), "/")) - assert.True(t, strings.HasSuffix(c.ThumbPath(), "assets/testdata/cache/thumbnails")) + assert.True(t, strings.HasSuffix(c.ThumbPath(), "storage/testdata/cache/thumbnails")) } func TestConfig_AssetsPath(t *testing.T) { ctx := CliTestContext() c := NewConfig(ctx) - path := c.AssetsPath() - assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets", path) -} - -func TestConfig_ResourcesPath(t *testing.T) { - ctx := CliTestContext() - c := NewConfig(ctx) - - path := c.ResourcesPath() - assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/resources", path) + assert.True(t, strings.HasSuffix(c.AssetsPath(), "/assets")) } func TestConfig_DetectNSFW(t *testing.T) { @@ -267,8 +254,7 @@ func TestConfig_NSFWModelPath(t *testing.T) { ctx := CliTestContext() c := NewConfig(ctx) - result := c.NSFWModelPath() - assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/resources/nsfw", result) + assert.Contains(t, c.NSFWModelPath(), "/assets/nsfw") } func TestConfig_ExamplesPath(t *testing.T) { @@ -276,7 +262,7 @@ func TestConfig_ExamplesPath(t *testing.T) { c := NewConfig(ctx) path := c.ExamplesPath() - assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/resources/examples", path) + assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/examples", path) } func TestConfig_TensorFlowModelPath(t *testing.T) { @@ -284,39 +270,39 @@ func TestConfig_TensorFlowModelPath(t *testing.T) { c := NewConfig(ctx) path := c.TensorFlowModelPath() - assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/resources/nasnet", path) + assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/nasnet", path) } func TestConfig_HttpTemplatesPath(t *testing.T) { ctx := CliTestContext() c := NewConfig(ctx) - path := c.HttpTemplatesPath() - assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/resources/templates", path) + path := c.TemplatesPath() + assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/templates", path) } func TestConfig_HttpFaviconsPath(t *testing.T) { ctx := CliTestContext() c := NewConfig(ctx) - path := c.HttpFaviconsPath() - assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/resources/static/favicons", path) + path := c.FaviconsPath() + assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/static/favicons", path) } func TestConfig_HttpStaticPath(t *testing.T) { ctx := CliTestContext() c := NewConfig(ctx) - path := c.HttpStaticPath() - assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/resources/static", path) + path := c.StaticPath() + assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/static", path) } func TestConfig_HttpStaticBuildPath(t *testing.T) { ctx := CliTestContext() c := NewConfig(ctx) - path := c.HttpStaticBuildPath() - assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/resources/static/build", path) + path := c.StaticBuildPath() + assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/static/build", path) } func TestConfig_ClientConfig(t *testing.T) { diff --git a/internal/config/db.go b/internal/config/db.go index 885f674b8..973cb157e 100644 --- a/internal/config/db.go +++ b/internal/config/db.go @@ -4,7 +4,6 @@ import ( "context" "errors" "io/ioutil" - "os" "path/filepath" "runtime" "strings" @@ -44,13 +43,7 @@ func (c *Config) DatabaseDsn() string { case MySQL: return "photoprism:photoprism@tcp(photoprism-db:3306)/photoprism?parseTime=true" case SQLite: - storagePath := filepath.Join(c.ConfigPath()) - - if err := os.MkdirAll(storagePath, os.ModePerm); err != nil { - log.Errorf("config: %s (database storage path)", err.Error()) - } else { - return filepath.Join(storagePath, "index.db") - } + return filepath.Join(c.StoragePath(), "index.db") default: log.Errorf("config: empty database dsn") return "" diff --git a/internal/config/filenames.go b/internal/config/filenames.go index dd824ee36..2bd5a8da0 100644 --- a/internal/config/filenames.go +++ b/internal/config/filenames.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "os/exec" + "os/user" "path/filepath" "github.com/photoprism/photoprism/pkg/fs" @@ -42,6 +43,14 @@ func (c *Config) CreateDirectories() error { return result } + if err := os.MkdirAll(c.AssetsPath(), os.ModePerm); err != nil { + return createError(c.AssetsPath(), err) + } + + if err := os.MkdirAll(c.StoragePath(), os.ModePerm); err != nil { + return createError(c.StoragePath(), err) + } + if err := os.MkdirAll(c.OriginalsPath(), os.ModePerm); err != nil { return createError(c.OriginalsPath(), err) } @@ -50,24 +59,28 @@ func (c *Config) CreateDirectories() error { return createError(c.ImportPath(), err) } - if err := os.MkdirAll(c.TempPath(), os.ModePerm); err != nil { - return createError(c.TempPath(), err) + if err := os.MkdirAll(c.CachePath(), os.ModePerm); err != nil { + return createError(c.CachePath(), err) } if err := os.MkdirAll(c.ThumbPath(), os.ModePerm); err != nil { return createError(c.ThumbPath(), err) } - if err := os.MkdirAll(c.ResourcesPath(), os.ModePerm); err != nil { - return createError(c.ResourcesPath(), err) + if err := os.MkdirAll(c.SettingsPath(), os.ModePerm); err != nil { + return createError(c.SettingsPath(), err) + } + + if err := os.MkdirAll(c.TempPath(), os.ModePerm); err != nil { + return createError(c.TempPath(), err) } if err := os.MkdirAll(c.TensorFlowModelPath(), os.ModePerm); err != nil { return createError(c.TensorFlowModelPath(), err) } - if err := os.MkdirAll(c.HttpStaticBuildPath(), os.ModePerm); err != nil { - return createError(c.HttpStaticBuildPath(), err) + if err := os.MkdirAll(c.StaticBuildPath(), os.ModePerm); err != nil { + return createError(c.StaticBuildPath(), err) } if err := os.MkdirAll(filepath.Dir(c.PIDFilename()), os.ModePerm); err != nil { @@ -83,27 +96,31 @@ func (c *Config) CreateDirectories() error { // ConfigFile returns the config file name. func (c *Config) ConfigFile() string { + if c.params.ConfigFile == "" || !fs.FileExists(c.params.ConfigFile) { + return filepath.Join(c.SettingsPath(), "photoprism.yml") + } + return c.params.ConfigFile } // SettingsFile returns the user settings file name. func (c *Config) SettingsFile() string { - return filepath.Join(c.ConfigPath(), "settings.yml") + return filepath.Join(c.SettingsPath(), "settings.yml") } -// ConfigPath returns the config path. -func (c *Config) ConfigPath() string { - if c.params.ConfigPath == "" { - return filepath.Join(c.AssetsPath(), "config") +// SettingsPath returns the config path. +func (c *Config) SettingsPath() string { + if c.params.SettingsPath == "" { + return filepath.Join(c.StoragePath(), "settings") } - return fs.Abs(c.params.ConfigPath) + return fs.Abs(c.params.SettingsPath) } // PIDFilename returns the filename for storing the server process id (pid). func (c *Config) PIDFilename() string { if c.params.PIDFilename == "" { - return filepath.Join(c.AssetsPath(), "photoprism.pid") + return filepath.Join(c.StoragePath(), "photoprism.pid") } return fs.Abs(c.params.PIDFilename) @@ -112,7 +129,7 @@ func (c *Config) PIDFilename() string { // LogFilename returns the filename for storing server logs. func (c *Config) LogFilename() string { if c.params.LogFilename == "" { - return filepath.Join(c.AssetsPath(), "photoprism.log") + return filepath.Join(c.StoragePath(), "photoprism.log") } return fs.Abs(c.params.LogFilename) @@ -187,34 +204,43 @@ func (c *Config) TempPath() string { // CachePath returns the path to the cache. func (c *Config) CachePath() string { + if c.params.CachePath == "" { + return filepath.Join(c.StoragePath(), "cache") + } + return fs.Abs(c.params.CachePath) } -// AssetsPath returns the path to the assets. +// StoragePath returns the path for generated files. +func (c *Config) StoragePath() string { + if c.params.StoragePath == "" { + if usr, _ := user.Current(); usr.HomeDir != "" { + p := filepath.Join(usr.HomeDir, fs.HiddenPath, "storage") + + if fs.PathExists(p) { + return p + } + } + + if !c.ReadOnly() { + return filepath.Join(c.OriginalsPath(), fs.HiddenPath, "storage") + } + } + + return fs.Abs(c.params.StoragePath) +} + +// AssetsPath returns the path to static assets. func (c *Config) AssetsPath() string { return fs.Abs(c.params.AssetsPath) } -// ResourcesPath returns the path to the app resources like static files. -func (c *Config) ResourcesPath() string { - if c.params.ResourcesPath == "" { - return filepath.Join(c.AssetsPath(), "resources") - } - - return fs.Abs(c.params.ResourcesPath) -} - // ExamplesPath returns the example files path. func (c *Config) ExamplesPath() string { - return filepath.Join(c.ResourcesPath(), "examples") + return filepath.Join(c.AssetsPath(), "examples") } -// TensorFlowModelPath returns the tensorflow model path. -func (c *Config) TensorFlowModelPath() string { - return filepath.Join(c.ResourcesPath(), "nasnet") -} - -// NSFWModelPath returns the NSFW tensorflow model path. -func (c *Config) NSFWModelPath() string { - return filepath.Join(c.ResourcesPath(), "nsfw") +// TestdataPath returns the test files path. +func (c *Config) TestdataPath() string { + return filepath.Join(c.StoragePath(), "testdata") } diff --git a/internal/config/flags.go b/internal/config/flags.go index 16d6b559d..ee23e9071 100644 --- a/internal/config/flags.go +++ b/internal/config/flags.go @@ -4,8 +4,101 @@ import ( "github.com/urfave/cli" ) -// GlobalFlags lists all CLI flags +// PhotoPrism command-line parameters and flags. var GlobalFlags = []cli.Flag{ + cli.BoolFlag{ + Name: "debug", + Usage: "run in debug mode", + EnvVar: "PHOTOPRISM_DEBUG", + }, + cli.BoolFlag{ + Name: "public, p", + Usage: "no authentication / password required", + EnvVar: "PHOTOPRISM_PUBLIC", + }, + cli.BoolFlag{ + Name: "read-only, r", + Usage: "don't add files or modify originals directory in any way", + EnvVar: "PHOTOPRISM_READONLY", + }, + cli.BoolFlag{ + Name: "tf-off", + Usage: "don't use TensorFlow for image classification (or anything else)", + EnvVar: "PHOTOPRISM_TENSORFLOW_OFF", + }, + cli.BoolFlag{ + Name: "experimental, e", + Usage: "enable experimental features", + EnvVar: "PHOTOPRISM_EXPERIMENTAL", + }, + cli.StringFlag{ + Name: "admin-password", + Usage: "admin password", + Value: "photoprism", + EnvVar: "PHOTOPRISM_ADMIN_PASSWORD", + }, + cli.StringFlag{ + Name: "webdav-password", + Usage: "WebDAV password (none to disable)", + Value: "", + EnvVar: "PHOTOPRISM_WEBDAV_PASSWORD", + }, + cli.IntFlag{ + Name: "workers, w", + Usage: "number of workers for indexing", + EnvVar: "PHOTOPRISM_WORKERS", + }, + cli.IntFlag{ + Name: "wakeup-interval", + Usage: "background worker wakeup interval in seconds", + EnvVar: "PHOTOPRISM_WAKEUP_INTERVAL", + }, + cli.StringFlag{ + Name: "site-url", + Usage: "canonical / public site URL", + Value: "http://localhost:2342/", + EnvVar: "PHOTOPRISM_SITE_URL", + }, + cli.StringFlag{ + Name: "site-title", + Usage: "site title", + Value: "PhotoPrism", + EnvVar: "PHOTOPRISM_SITE_TITLE", + }, + cli.StringFlag{ + Name: "site-caption", + Usage: "short caption / tagline", + Value: "Browse your life", + EnvVar: "PHOTOPRISM_SITE_CAPTION", + }, + cli.StringFlag{ + Name: "site-description", + Usage: "long site description", + Value: "Personal Photo Management", + EnvVar: "PHOTOPRISM_SITE_DESCRIPTION", + }, + cli.StringFlag{ + Name: "site-author", + Usage: "site owner / copyright", + Value: "Anonymous", + EnvVar: "PHOTOPRISM_SITE_AUTHOR", + }, + cli.IntFlag{ + Name: "http-port", + Value: 2342, + Usage: "HTTP server port", + EnvVar: "PHOTOPRISM_HTTP_PORT", + }, + cli.StringFlag{ + Name: "http-host", + Usage: "HTTP server host", + EnvVar: "PHOTOPRISM_HTTP_HOST", + }, + cli.StringFlag{ + Name: "http-mode, m", + Usage: "debug, release or test", + EnvVar: "PHOTOPRISM_HTTP_MODE", + }, cli.StringFlag{ Name: "database-driver", Usage: "database `DRIVER` (sqlite or mysql)", @@ -25,111 +118,22 @@ var GlobalFlags = []cli.Flag{ EnvVar: "PHOTOPRISM_DATABASE_CONNS", }, cli.StringFlag{ - Name: "admin-password", - Usage: "admin password", - Value: "photoprism", - EnvVar: "PHOTOPRISM_ADMIN_PASSWORD", - }, - cli.StringFlag{ - Name: "webdav-password", - Usage: "WebDAV password (none to disable)", + Name: "assets-path", + Usage: "assets `PATH` for static files", Value: "", - EnvVar: "PHOTOPRISM_WEBDAV_PASSWORD", - }, - cli.BoolFlag{ - Name: "debug", - Usage: "run in debug mode", - EnvVar: "PHOTOPRISM_DEBUG", - }, - cli.BoolFlag{ - Name: "read-only, r", - Usage: "run in read-only mode", - EnvVar: "PHOTOPRISM_READONLY", - }, - cli.BoolFlag{ - Name: "public, p", - Usage: "no authentication required", - EnvVar: "PHOTOPRISM_PUBLIC", - }, - cli.BoolFlag{ - Name: "experimental, e", - Usage: "enable experimental features", - EnvVar: "PHOTOPRISM_EXPERIMENTAL", - }, - cli.IntFlag{ - Name: "workers, w", - Usage: "number of workers for indexing", - EnvVar: "PHOTOPRISM_WORKERS", - }, - cli.IntFlag{ - Name: "wakeup-interval", - Usage: "background worker wakeup interval in seconds", - EnvVar: "PHOTOPRISM_WAKEUP_INTERVAL", + EnvVar: "PHOTOPRISM_ASSETS_PATH", }, cli.StringFlag{ - Name: "url", - Usage: "canonical site URL", - Value: "http://localhost:2342/", - EnvVar: "PHOTOPRISM_URL", + Name: "storage-path", + Usage: "storage `PATH` for generated files", + Value: "", + EnvVar: "PHOTOPRISM_STORAGE_PATH", }, cli.StringFlag{ - Name: "title", - Usage: "site title", - Value: "PhotoPrism", - EnvVar: "PHOTOPRISM_TITLE", - }, - cli.StringFlag{ - Name: "subtitle", - Usage: "site subtitle", - Value: "Browse your life", - EnvVar: "PHOTOPRISM_SUBTITLE", - }, - cli.StringFlag{ - Name: "description", - Usage: "site description", - Value: "Personal Photo Management", - EnvVar: "PHOTOPRISM_DESCRIPTION", - }, - cli.StringFlag{ - Name: "author", - Usage: "site owner / copyright", - Value: "Anonymous", - EnvVar: "PHOTOPRISM_AUTHOR", - }, - cli.StringFlag{ - Name: "log-level, l", - Usage: "trace, debug, info, warning, error, fatal or panic", - Value: "info", - EnvVar: "PHOTOPRISM_LOG_LEVEL", - }, - cli.StringFlag{ - Name: "log-filename", - Usage: "filename for storing server logs", - EnvVar: "PHOTOPRISM_LOG_FILENAME", - Value: "~/.local/share/photoprism/photoprism.log", - }, - cli.StringFlag{ - Name: "pid-filename", - Usage: "filename for the server process id (pid)", - EnvVar: "PHOTOPRISM_PID_FILENAME", - Value: "~/.local/share/photoprism/photoprism.pid", - }, - cli.StringFlag{ - Name: "config-file, c", - Usage: "load configuration from `FILENAME`", - Value: "~/.config/photoprism/photoprism.yml", - EnvVar: "PHOTOPRISM_CONFIG_FILE", - }, - cli.StringFlag{ - Name: "config-path", - Usage: "config `PATH`", - Value: "~/.config/photoprism", - EnvVar: "PHOTOPRISM_CONFIG_PATH", - }, - cli.StringFlag{ - Name: "resources-path", - Usage: "resources `PATH`", - EnvVar: "PHOTOPRISM_RESOURCES_PATH", + Name: "import-path", + Usage: "import `PATH`", + Value: "~/Pictures/Import", + EnvVar: "PHOTOPRISM_IMPORT_PATH", }, cli.StringFlag{ Name: "originals-path", @@ -144,10 +148,28 @@ var GlobalFlags = []cli.Flag{ EnvVar: "PHOTOPRISM_ORIGINALS_LIMIT", }, cli.StringFlag{ - Name: "import-path", - Usage: "import `PATH`", - Value: "~/Pictures/Import", - EnvVar: "PHOTOPRISM_IMPORT_PATH", + Name: "log-level, l", + Usage: "trace, debug, info, warning, error, fatal or panic", + Value: "info", + EnvVar: "PHOTOPRISM_LOG_LEVEL", + }, + cli.StringFlag{ + Name: "log-filename", + Usage: "filename for storing server logs", + EnvVar: "PHOTOPRISM_LOG_FILENAME", + Value: "", + }, + cli.StringFlag{ + Name: "pid-filename", + Usage: "filename for the server process id (pid)", + EnvVar: "PHOTOPRISM_PID_FILENAME", + Value: "", + }, + cli.StringFlag{ + Name: "cache-path", + Usage: "cache `PATH`", + Value: "", + EnvVar: "PHOTOPRISM_CACHE_PATH", }, cli.StringFlag{ Name: "temp-path", @@ -156,16 +178,21 @@ var GlobalFlags = []cli.Flag{ EnvVar: "PHOTOPRISM_TEMP_PATH", }, cli.StringFlag{ - Name: "cache-path", - Usage: "cache `PATH`", - Value: "~/.cache/photoprism", - EnvVar: "PHOTOPRISM_CACHE_PATH", + Name: "config-file, c", + Usage: "load configuration from `FILENAME`", + Value: "", + EnvVar: "PHOTOPRISM_CONFIG_FILE", }, cli.StringFlag{ - Name: "assets-path", - Usage: "assets `PATH`", - Value: "~/.local/share/photoprism", - EnvVar: "PHOTOPRISM_ASSETS_PATH", + Name: "settings-path", + Usage: "settings `PATH`", + Value: "", + EnvVar: "PHOTOPRISM_SETTINGS_PATH", + }, + cli.BoolFlag{ + Name: "settings-hidden", + Usage: "users can not view or change settings", + EnvVar: "PHOTOPRISM_SETTINGS_HIDDEN", }, cli.StringFlag{ Name: "sips-bin", @@ -212,22 +239,6 @@ var GlobalFlags = []cli.Flag{ Usage: "create JSON and YAML sidecar files in .photoprism if enabled", EnvVar: "PHOTOPRISM_SIDECAR_HIDDEN", }, - cli.IntFlag{ - Name: "http-port", - Value: 2342, - Usage: "HTTP server port", - EnvVar: "PHOTOPRISM_HTTP_PORT", - }, - cli.StringFlag{ - Name: "http-host", - Usage: "HTTP server host", - EnvVar: "PHOTOPRISM_HTTP_HOST", - }, - cli.StringFlag{ - Name: "http-mode, m", - Usage: "debug, release or test", - EnvVar: "PHOTOPRISM_HTTP_MODE", - }, cli.BoolFlag{ Name: "detect-nsfw", Usage: "flag photos as private that may be offensive", @@ -290,14 +301,4 @@ var GlobalFlags = []cli.Flag{ Usage: "create JPEG files in .photoprism when converting other file types", EnvVar: "PHOTOPRISM_JPEG_HIDDEN", }, - cli.BoolFlag{ - Name: "disable-tf", - Usage: "don't use TensorFlow for image classification", - EnvVar: "PHOTOPRISM_DISABLE_TF", - }, - cli.BoolFlag{ - Name: "disable-settings", - Usage: "user can not change settings", - EnvVar: "PHOTOPRISM_DISABLE_SETTINGS", - }, } diff --git a/internal/config/params.go b/internal/config/params.go index ff5a2b147..97e55686c 100644 --- a/internal/config/params.go +++ b/internal/config/params.go @@ -29,31 +29,33 @@ const ( // See https://github.com/photoprism/photoprism/issues/50#issuecomment-433856358 type Params struct { Name string - Url string `yaml:"url" flag:"url"` - Title string `yaml:"title" flag:"title"` - Subtitle string `yaml:"subtitle" flag:"subtitle"` - Description string `yaml:"description" flag:"description"` - Author string `yaml:"author" flag:"author"` Version string Copyright string + SiteUrl string `yaml:"site-url" flag:"site-url"` + SiteTitle string `yaml:"site-title" flag:"site-title"` + SiteCaption string `yaml:"site-caption" flag:"site-caption"` + SiteDescription string `yaml:"site-description" flag:"site-description"` + SiteAuthor string `yaml:"site-author" flag:"site-author"` Public bool `yaml:"public" flag:"public"` Debug bool `yaml:"debug" flag:"debug"` ReadOnly bool `yaml:"read-only" flag:"read-only"` Experimental bool `yaml:"experimental" flag:"experimental"` + TensorFlowOff bool `yaml:"tf-off" flag:"tf-off"` Workers int `yaml:"workers" flag:"workers"` WakeupInterval int `yaml:"wakeup-interval" flag:"wakeup-interval"` AdminPassword string `yaml:"admin-password" flag:"admin-password"` WebDAVPassword string `yaml:"webdav-password" flag:"webdav-password"` LogLevel string `yaml:"log-level" flag:"log-level"` - ConfigFile string - ConfigPath string `yaml:"config-path" flag:"config-path"` - TempPath string `yaml:"temp-path" flag:"temp-path"` - CachePath string `yaml:"cache-path" flag:"cache-path"` + AssetsPath string `yaml:"assets-path" flag:"assets-path"` + StoragePath string `yaml:"storage-path" flag:"storage-path"` + ImportPath string `yaml:"import-path" flag:"import-path"` OriginalsPath string `yaml:"originals-path" flag:"originals-path"` OriginalsLimit int64 `yaml:"originals-limit" flag:"originals-limit"` - ImportPath string `yaml:"import-path" flag:"import-path"` - AssetsPath string `yaml:"assets-path" flag:"assets-path"` - ResourcesPath string `yaml:"resources-path" flag:"resources-path"` + ConfigFile string + SettingsPath string `yaml:"settings-path" flag:"settings-path"` + SettingsHidden bool `yaml:"settings-hidden" flag:"settings-hidden"` + TempPath string `yaml:"temp-path" flag:"temp-path"` + CachePath string `yaml:"cache-path" flag:"cache-path"` DatabaseDriver string `yaml:"database-driver" flag:"database-driver"` DatabaseDsn string `yaml:"database-dsn" flag:"database-dsn"` DatabaseConns int `yaml:"database-conns" flag:"database-conns"` @@ -83,8 +85,6 @@ type Params struct { ThumbLimit int `yaml:"thumb-limit" flag:"thumb-limit"` JpegHidden bool `yaml:"jpeg-hidden" flag:"jpeg-hidden"` JpegQuality int `yaml:"jpeg-quality" flag:"jpeg-quality"` - DisableTensorFlow bool `yaml:"disable-tf" flag:"disable-tf"` - DisableSettings bool `yaml:"disable-settings" flag:"disable-settings"` } // NewParams creates a new configuration entity by using two methods: @@ -114,8 +114,8 @@ func NewParams(ctx *cli.Context) *Params { // expandFilenames converts path in config to absolute path func (c *Params) expandFilenames() { - c.ConfigPath = fs.Abs(c.ConfigPath) - c.ResourcesPath = fs.Abs(c.ResourcesPath) + c.SettingsPath = fs.Abs(c.SettingsPath) + c.StoragePath = fs.Abs(c.StoragePath) c.AssetsPath = fs.Abs(c.AssetsPath) c.CachePath = fs.Abs(c.CachePath) c.OriginalsPath = fs.Abs(c.OriginalsPath) diff --git a/internal/config/server.go b/internal/config/server.go index ef88f680e..6bbad9401 100644 --- a/internal/config/server.go +++ b/internal/config/server.go @@ -47,36 +47,36 @@ func (c *Config) HttpServerPassword() string { return c.params.HttpServerPassword } -// HttpTemplatesPath returns the server templates path. -func (c *Config) HttpTemplatesPath() string { - return filepath.Join(c.ResourcesPath(), "templates") +// TemplatesPath returns the server templates path. +func (c *Config) TemplatesPath() string { + return filepath.Join(c.AssetsPath(), "templates") } -// HttpTemplateExists returns true if a template with the given name exists (e.g. index.tmpl). -func (c *Config) HttpTemplateExists(name string) bool { - return fs.FileExists(filepath.Join(c.HttpTemplatesPath(), name)) +// TemplateExists returns true if a template with the given name exists (e.g. index.tmpl). +func (c *Config) TemplateExists(name string) bool { + return fs.FileExists(filepath.Join(c.TemplatesPath(), name)) } -// HttpDefaultTemplate returns the name of the default template (e.g. index.tmpl). -func (c *Config) HttpDefaultTemplate() string { - if c.HttpTemplateExists(c.Settings().Templates.Default) { +// DefaultTemplate returns the name of the default template (e.g. index.tmpl). +func (c *Config) DefaultTemplate() string { + if c.TemplateExists(c.Settings().Templates.Default) { return c.Settings().Templates.Default } return "index.tmpl" } -// HttpFaviconsPath returns the favicons path. -func (c *Config) HttpFaviconsPath() string { - return filepath.Join(c.HttpStaticPath(), "favicons") +// FaviconsPath returns the favicons path. +func (c *Config) FaviconsPath() string { + return filepath.Join(c.StaticPath(), "favicons") } -// HttpStaticPath returns the static server assets path (//server/static/*). -func (c *Config) HttpStaticPath() string { - return filepath.Join(c.ResourcesPath(), "static") +// StaticPath returns the static server assets path (//server/static/*). +func (c *Config) StaticPath() string { + return filepath.Join(c.AssetsPath(), "static") } -// HttpStaticBuildPath returns the static build path (//server/static/build/*). -func (c *Config) HttpStaticBuildPath() string { - return filepath.Join(c.HttpStaticPath(), "build") +// StaticBuildPath returns the static build path (//server/static/build/*). +func (c *Config) StaticBuildPath() string { + return filepath.Join(c.StaticPath(), "build") } diff --git a/internal/config/settings.go b/internal/config/settings.go index 002017d34..128a524b0 100644 --- a/internal/config/settings.go +++ b/internal/config/settings.go @@ -10,9 +10,9 @@ import ( "gopkg.in/yaml.v2" ) -// DisableSettings returns true if the user is not allowed to change settings. -func (c *Config) DisableSettings() bool { - return c.params.DisableSettings +// SettingsHidden returns true if the user is not allowed to change settings. +func (c *Config) SettingsHidden() bool { + return c.params.SettingsHidden } type TemplateSettings struct { @@ -154,7 +154,7 @@ func (c *Config) initSettings() { p := c.SettingsFile() if err := c.settings.Load(p); err != nil { - log.Error(err) + log.Info(err) } c.settings.Propagate() diff --git a/internal/config/tensorflow.go b/internal/config/tensorflow.go index 8b496ce7a..1149fa0eb 100644 --- a/internal/config/tensorflow.go +++ b/internal/config/tensorflow.go @@ -1,13 +1,27 @@ package config -import tf "github.com/tensorflow/tensorflow/tensorflow/go" +import ( + "path/filepath" + + tf "github.com/tensorflow/tensorflow/tensorflow/go" +) // TensorFlowVersion returns the TenorFlow framework version. func (c *Config) TensorFlowVersion() string { return tf.Version() } -// DisableTensorFlow returns true if TensorFlow should not be used for image classification. -func (c *Config) DisableTensorFlow() bool { - return c.params.DisableTensorFlow +// TensorFlowOff returns true if TensorFlow should NOT be used for image classification (or anything else). +func (c *Config) TensorFlowOff() bool { + return c.params.TensorFlowOff +} + +// TensorFlowModelPath returns the TensorFlow model path. +func (c *Config) TensorFlowModelPath() string { + return filepath.Join(c.AssetsPath(), "nasnet") +} + +// NSFWModelPath returns the "not safe for work" TensorFlow model path. +func (c *Config) NSFWModelPath() string { + return filepath.Join(c.AssetsPath(), "nsfw") } diff --git a/internal/config/test.go b/internal/config/test.go index 32c5049f8..114c09465 100644 --- a/internal/config/test.go +++ b/internal/config/test.go @@ -4,6 +4,7 @@ import ( "context" "flag" "os" + "path/filepath" "sync" "testing" "time" @@ -35,8 +36,8 @@ func testDataPath(assetsPath string) string { // NewTestParams inits valid params used for testing func NewTestParams() *Params { assetsPath := fs.Abs("../../assets") - - testDataPath := testDataPath(assetsPath) + storagePath := fs.Abs("../../storage") + testDataPath := filepath.Join(storagePath, "testdata") dbDriver := os.Getenv("PHOTOPRISM_TEST_DRIVER") dbDsn := os.Getenv("PHOTOPRISM_TEST_DSN") @@ -63,10 +64,12 @@ func NewTestParams() *Params { DarktableBin: "/usr/bin/darktable-cli", ExifToolBin: "/usr/bin/exiftool", AssetsPath: assetsPath, + StoragePath: testDataPath, CachePath: testDataPath + "/cache", OriginalsPath: testDataPath + "/originals", ImportPath: testDataPath + "/import", TempPath: testDataPath + "/temp", + SettingsPath: testDataPath + "/settings", DatabaseDriver: dbDriver, DatabaseDsn: dbDsn, } @@ -77,12 +80,12 @@ func NewTestParams() *Params { // NewTestParamsError inits invalid params used for testing func NewTestParamsError() *Params { assetsPath := fs.Abs("../..") - - testDataPath := testDataPath("../../assets") + testDataPath := fs.Abs("../../storage/testdata") c := &Params{ DarktableBin: "/usr/bin/darktable-cli", AssetsPath: assetsPath, + StoragePath: testDataPath, CachePath: testDataPath + "/cache", OriginalsPath: testDataPath + "/originals", ImportPath: testDataPath + "/import", @@ -117,6 +120,16 @@ func NewTestConfig() *Config { token: rnd.Token(8), } + s := NewSettings() + + if err := os.MkdirAll(c.SettingsPath(), os.ModePerm); err != nil { + log.Fatalf("config: %s", err.Error()) + } + + if err := s.Save(filepath.Join(c.SettingsPath(), "settings.yml")); err != nil { + log.Fatalf("config: %s", err.Error()) + } + c.initSettings() if err := c.Init(context.Background()); err != nil { @@ -151,6 +164,7 @@ func CliTestContext() *cli.Context { globalSet := flag.NewFlagSet("test", 0) globalSet.Bool("debug", false, "doc") + globalSet.String("storage-path", config.StoragePath, "doc") globalSet.String("config-file", config.ConfigFile, "doc") globalSet.String("assets-path", config.AssetsPath, "doc") globalSet.String("originals-path", config.OriginalsPath, "doc") @@ -165,6 +179,7 @@ func CliTestContext() *cli.Context { c := cli.NewContext(app, globalSet, nil) + LogError(c.Set("storage-path", config.StoragePath)) LogError(c.Set("config-file", config.ConfigFile)) LogError(c.Set("assets-path", config.AssetsPath)) LogError(c.Set("originals-path", config.OriginalsPath)) @@ -221,7 +236,7 @@ func (c *Config) DownloadTestData(t *testing.T) { // UnzipTestData in default test folder func (c *Config) UnzipTestData(t *testing.T) { - if _, err := fs.Unzip(TestDataZip, testDataPath(c.AssetsPath())); err != nil { + if _, err := fs.Unzip(TestDataZip, c.StoragePath()); err != nil { t.Fatalf("config: could not unzip test data: %s", err.Error()) } } diff --git a/internal/config/test_test.go b/internal/config/test_test.go index 7d50eecad..6ceeb0944 100644 --- a/internal/config/test_test.go +++ b/internal/config/test_test.go @@ -37,7 +37,7 @@ func TestNewTestParamsError(t *testing.T) { assert.IsType(t, new(Params), c) assert.Equal(t, fs.Abs("../.."), c.AssetsPath) - assert.Equal(t, "../../assets/testdata/cache", c.CachePath) + assert.Equal(t, fs.Abs("../../storage/testdata/cache"), c.CachePath) assert.False(t, c.Debug) } diff --git a/internal/nsfw/nsfw_test.go b/internal/nsfw/nsfw_test.go index 27ba1fd99..489b2f9f1 100644 --- a/internal/nsfw/nsfw_test.go +++ b/internal/nsfw/nsfw_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/assert" ) -var modelPath, _ = filepath.Abs("../../assets/resources/nsfw") +var modelPath, _ = filepath.Abs("../../assets/nsfw") var detector = New(modelPath) diff --git a/internal/photoprism/import_test.go b/internal/photoprism/import_test.go index fbc854f5b..7a245b350 100644 --- a/internal/photoprism/import_test.go +++ b/internal/photoprism/import_test.go @@ -12,7 +12,7 @@ import ( func TestNewImport(t *testing.T) { conf := config.TestConfig() - tf := classify.New(conf.ResourcesPath(), conf.DisableTensorFlow()) + tf := classify.New(conf.AssetsPath(), conf.TensorFlowOff()) nd := nsfw.New(conf.NSFWModelPath()) convert := NewConvert(conf) @@ -27,7 +27,7 @@ func TestImport_DestinationFilename(t *testing.T) { conf.InitializeTestData(t) - tf := classify.New(conf.ResourcesPath(), conf.DisableTensorFlow()) + tf := classify.New(conf.AssetsPath(), conf.TensorFlowOff()) nd := nsfw.New(conf.NSFWModelPath()) convert := NewConvert(conf) @@ -37,7 +37,9 @@ func TestImport_DestinationFilename(t *testing.T) { rawFile, err := NewMediaFile(conf.ImportPath() + "/raw/IMG_2567.CR2") - assert.Nil(t, err) + if err != nil { + t.Fatal(err) + } fileName, err := imp.DestinationFilename(rawFile, rawFile) @@ -57,7 +59,7 @@ func TestImport_Start(t *testing.T) { conf.InitializeTestData(t) - tf := classify.New(conf.ResourcesPath(), conf.DisableTensorFlow()) + tf := classify.New(conf.AssetsPath(), conf.TensorFlowOff()) nd := nsfw.New(conf.NSFWModelPath()) convert := NewConvert(conf) diff --git a/internal/photoprism/index_mediafile.go b/internal/photoprism/index_mediafile.go index 8060b92da..091e45e2b 100644 --- a/internal/photoprism/index_mediafile.go +++ b/internal/photoprism/index_mediafile.go @@ -259,7 +259,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) ( if file.FilePrimary { primaryFile = file - if !ind.conf.DisableTensorFlow() { + if !ind.conf.TensorFlowOff() { // Image classification via TensorFlow. labels = ind.classifyImage(m) diff --git a/internal/photoprism/index_test.go b/internal/photoprism/index_test.go index b01935e87..8923877b4 100644 --- a/internal/photoprism/index_test.go +++ b/internal/photoprism/index_test.go @@ -17,7 +17,7 @@ func TestIndex_Start(t *testing.T) { conf.InitializeTestData(t) - tf := classify.New(conf.ResourcesPath(), conf.DisableTensorFlow()) + tf := classify.New(conf.AssetsPath(), conf.TensorFlowOff()) nd := nsfw.New(conf.NSFWModelPath()) convert := NewConvert(conf) diff --git a/internal/photoprism/mediafile_test.go b/internal/photoprism/mediafile_test.go index 6baeab42e..e2f2b431e 100644 --- a/internal/photoprism/mediafile_test.go +++ b/internal/photoprism/mediafile_test.go @@ -475,12 +475,12 @@ func TestMediaFile_RelativeFilename(t *testing.T) { assert.Nil(t, err) t.Run("directory with end slash", func(t *testing.T) { - filename := mediaFile.RelativeName("/go/src/github.com/photoprism/photoprism/assets/resources/") + filename := mediaFile.RelativeName("/go/src/github.com/photoprism/photoprism/assets/") assert.Equal(t, "examples/tree_white.jpg", filename) }) t.Run("directory without end slash", func(t *testing.T) { - filename := mediaFile.RelativeName("/go/src/github.com/photoprism/photoprism/assets/resources") + filename := mediaFile.RelativeName("/go/src/github.com/photoprism/photoprism/assets") assert.Equal(t, "examples/tree_white.jpg", filename) }) t.Run("directory not part of filename", func(t *testing.T) { @@ -488,7 +488,7 @@ func TestMediaFile_RelativeFilename(t *testing.T) { assert.Equal(t, conf.ExamplesPath()+"/tree_white.jpg", filename) }) t.Run("directory equals example path", func(t *testing.T) { - filename := mediaFile.RelativeName("/go/src/github.com/photoprism/photoprism/assets/resources/examples") + filename := mediaFile.RelativeName("/go/src/github.com/photoprism/photoprism/assets/examples") assert.Equal(t, "tree_white.jpg", filename) }) } @@ -504,11 +504,11 @@ func TestMediaFile_RelativePath(t *testing.T) { } t.Run("directory with end slash", func(t *testing.T) { - path := mediaFile.RelativePath("/go/src/github.com/photoprism/photoprism/assets/resources/") + path := mediaFile.RelativePath("/go/src/github.com/photoprism/photoprism/assets/") assert.Equal(t, "examples", path) }) t.Run("directory without end slash", func(t *testing.T) { - path := mediaFile.RelativePath("/go/src/github.com/photoprism/photoprism/assets/resources") + path := mediaFile.RelativePath("/go/src/github.com/photoprism/photoprism/assets") assert.Equal(t, "examples", path) }) t.Run("directory equals filepath", func(t *testing.T) { @@ -517,7 +517,7 @@ func TestMediaFile_RelativePath(t *testing.T) { }) t.Run("directory does not match filepath", func(t *testing.T) { path := mediaFile.RelativePath("xxx") - assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/resources/examples", path) + assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/examples", path) }) mediaFile, err = NewMediaFile(conf.ExamplesPath() + "/.photoprism/example.jpg") @@ -547,15 +547,15 @@ func TestMediaFile_RelativeBasename(t *testing.T) { assert.Nil(t, err) t.Run("directory with end slash", func(t *testing.T) { - basename := mediaFile.RelativeBase("/go/src/github.com/photoprism/photoprism/assets/resources/", true) + basename := mediaFile.RelativeBase("/go/src/github.com/photoprism/photoprism/assets/", true) assert.Equal(t, "examples/tree_white", basename) }) t.Run("directory without end slash", func(t *testing.T) { - basename := mediaFile.RelativeBase("/go/src/github.com/photoprism/photoprism/assets/resources", true) + basename := mediaFile.RelativeBase("/go/src/github.com/photoprism/photoprism/assets", true) assert.Equal(t, "examples/tree_white", basename) }) t.Run("directory equals example path", func(t *testing.T) { - basename := mediaFile.RelativeBase("/go/src/github.com/photoprism/photoprism/assets/resources/examples/", true) + basename := mediaFile.RelativeBase("/go/src/github.com/photoprism/photoprism/assets/examples/", true) assert.Equal(t, "tree_white", basename) }) diff --git a/internal/photoprism/resample_test.go b/internal/photoprism/resample_test.go index a2ddbd03e..f59531cd4 100644 --- a/internal/photoprism/resample_test.go +++ b/internal/photoprism/resample_test.go @@ -27,7 +27,7 @@ func TestResample_Start(t *testing.T) { conf.InitializeTestData(t) - tf := classify.New(conf.ResourcesPath(), conf.DisableTensorFlow()) + tf := classify.New(conf.AssetsPath(), conf.TensorFlowOff()) nd := nsfw.New(conf.NSFWModelPath()) convert := NewConvert(conf) @@ -61,7 +61,7 @@ func TestThumb_Filename(t *testing.T) { t.Run("", func(t *testing.T) { filename, err := thumb.Filename("99988", thumbsPath, 150, 150, thumb.ResampleFit, thumb.ResampleNearestNeighbor) assert.Nil(t, err) - assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/testdata/cache/_tmp/9/9/9/99988_150x150_fit.jpg", filename) + assert.Equal(t, "/go/src/github.com/photoprism/photoprism/storage/testdata/cache/_tmp/9/9/9/99988_150x150_fit.jpg", filename) }) t.Run("hash too short", func(t *testing.T) { _, err := thumb.Filename("999", thumbsPath, 150, 150, thumb.ResampleFit, thumb.ResampleNearestNeighbor) diff --git a/internal/server/routes.go b/internal/server/routes.go index 009233e56..2956ac13f 100644 --- a/internal/server/routes.go +++ b/internal/server/routes.go @@ -10,10 +10,10 @@ import ( func registerRoutes(router *gin.Engine, conf *config.Config) { // Favicon - router.StaticFile("/favicon.ico", conf.HttpFaviconsPath()+"/favicon.ico") + router.StaticFile("/favicon.ico", conf.FaviconsPath()+"/favicon.ico") // Static assets like js and css files - router.Static("/static", conf.HttpStaticPath()) + router.Static("/static", conf.StaticPath()) // JSON-REST API Version 1 v1 := router.Group("/api/v1") @@ -125,6 +125,6 @@ func registerRoutes(router *gin.Engine, conf *config.Config) { // Default HTML page (client-side routing implemented via Vue.js) router.NoRoute(func(c *gin.Context) { clientConfig := conf.PublicClientConfig() - c.HTML(http.StatusOK, conf.HttpDefaultTemplate(), gin.H{"config": clientConfig}) + c.HTML(http.StatusOK, conf.DefaultTemplate(), gin.H{"config": clientConfig}) }) } diff --git a/internal/server/server.go b/internal/server/server.go index f443393f3..d5a9f9b3d 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -30,7 +30,7 @@ func Start(ctx context.Context, conf *config.Config) { router.Use(Logger(), Recovery()) // Set template directory - router.LoadHTMLGlob(conf.HttpTemplatesPath() + "/*") + router.LoadHTMLGlob(conf.TemplatesPath() + "/*") registerRoutes(router, conf) diff --git a/internal/service/classify.go b/internal/service/classify.go index 0fc825987..ace08e666 100644 --- a/internal/service/classify.go +++ b/internal/service/classify.go @@ -9,7 +9,7 @@ import ( var onceClassify sync.Once func initClassify() { - services.Classify = classify.New(Config().ResourcesPath(), Config().DisableTensorFlow()) + services.Classify = classify.New(Config().AssetsPath(), Config().TensorFlowOff()) } func Classify() *classify.TensorFlow { diff --git a/assets/photos/.gitignore b/photos/.gitignore similarity index 100% rename from assets/photos/.gitignore rename to photos/.gitignore diff --git a/scripts/download-nasnet.sh b/scripts/download-nasnet.sh index 8c3209ece..a08717f1b 100755 --- a/scripts/download-nasnet.sh +++ b/scripts/download-nasnet.sh @@ -4,7 +4,7 @@ TODAY=`date -u +%Y%m%d` MODEL_NAME="NASNet Mobile" MODEL_URL="https://dl.photoprism.org/tensorflow/nasnet.zip?$TODAY" -MODEL_PATH="assets/resources/nasnet" +MODEL_PATH="assets/nasnet" MODEL_ZIP="/tmp/photoprism/nasnet.zip" MODEL_HASH="f18b801354e95cade497b4f12e8d2537d04c04f6 $MODEL_ZIP" MODEL_VERSION="$MODEL_PATH/version.txt" @@ -40,7 +40,7 @@ if [[ -e ${MODEL_PATH} ]]; then fi # Unzip model -unzip ${MODEL_ZIP} -d assets/resources +unzip ${MODEL_ZIP} -d assets echo "$MODEL_NAME $TODAY $MODEL_HASH" > ${MODEL_VERSION} echo "Latest $MODEL_NAME installed." diff --git a/scripts/download-nsfw.sh b/scripts/download-nsfw.sh index 0b27f9977..325b3391d 100755 --- a/scripts/download-nsfw.sh +++ b/scripts/download-nsfw.sh @@ -4,7 +4,7 @@ TODAY=`date -u +%Y%m%d` MODEL_NAME="NSFW" MODEL_URL="https://dl.photoprism.org/tensorflow/nsfw.zip?$TODAY" -MODEL_PATH="assets/resources/nsfw" +MODEL_PATH="assets/nsfw" MODEL_ZIP="/tmp/photoprism/nsfw.zip" MODEL_HASH="2e03ad3c6aec27c270c650d0574ff2a6291d992b $MODEL_ZIP" MODEL_VERSION="$MODEL_PATH/version.txt" @@ -40,7 +40,7 @@ if [[ -e ${MODEL_PATH} ]]; then fi # Unzip model -unzip ${MODEL_ZIP} -d assets/resources +unzip ${MODEL_ZIP} -d assets echo "$MODEL_NAME $TODAY $MODEL_HASH" > ${MODEL_VERSION} echo "Latest $MODEL_NAME installed." diff --git a/assets/resources/static/build/.gitignore b/storage/cache/.gitignore similarity index 100% rename from assets/resources/static/build/.gitignore rename to storage/cache/.gitignore diff --git a/assets/config/photoprism.yml b/storage/settings/photoprism.yml similarity index 68% rename from assets/config/photoprism.yml rename to storage/settings/photoprism.yml index dff0ae59e..ef5442c8f 100644 --- a/assets/config/photoprism.yml +++ b/storage/settings/photoprism.yml @@ -9,18 +9,16 @@ read-only: false public: false experimental: false admin-password: photoprism -config-path: ~/.config/photoprism -cache-path: ~/.cache/photoprism -assets-path: ~/.local/share/photoprism -resources-path: ~/.local/share/photoprism/resources +assets-path: ~/.photoprism/assets +storage-path: ~/.photoprism/storage +pid-filename: ~/.photoprism/photoprism.pid +log-filename: ~/.photoprism/photoprism.log originals-path: ~/Pictures/Originals import-path: ~/Pictures/Import http-host: http-mode: release http-port: 2342 database-driver: sqlite -pid-filename: ~/.local/share/photoprism/photoprism.pid -log-filename: ~/.local/share/photoprism/photoprism.log detach-server: false sidecar-json: false sidecar-yaml: false diff --git a/assets/config/settings.yml b/storage/settings/settings.yml similarity index 100% rename from assets/config/settings.yml rename to storage/settings/settings.yml