Add PHOTOPRISM_SIDECAR_HIDDEN and PHOTOPRISM_JPEG_HIDDEN flags #311

Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
Michael Mayer 2020-05-19 11:00:17 +02:00
parent 7777fa95a5
commit e61dda495a
31 changed files with 125 additions and 53 deletions

View file

@ -26,3 +26,8 @@ database-dsn: root:photoprism@tcp(localhost:2343)/photoprism?parseTime=true
pid-filename: ~/.local/share/photoprism/photoprism.pid
log-filename: ~/.local/share/photoprism/photoprism.log
detach-server: false
sidecar-json: false
sidecar-yaml: false
sidecar-hidden: true
jpeg-quality: 90
jpeg-hidden: true

View file

@ -29,6 +29,15 @@ services:
PHOTOPRISM_SUBTITLE: "Browse your life"
PHOTOPRISM_DESCRIPTION: "Personal Photo Management tested by Travis CI."
PHOTOPRISM_AUTHOR: "PhotoPrism.org"
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)
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_SIDECAR_JSON: "true" # Read metadata from JSON sidecar files created by exiftool
PHOTOPRISM_SIDECAR_YAML: "true" # Backup photo metadata to YAML sidecar files
PHOTOPRISM_SIDECAR_HIDDEN: "true" # Create JSON and YAML sidecar files in .photoprism (if enabled)
CODECOV_TOKEN:
CODECOV_ENV:
CODECOV_URL:

View file

@ -13,6 +13,7 @@ services:
- ".:/go/src/github.com/photoprism/photoprism"
shm_size: "2gb"
environment:
TF_CPP_MIN_LOG_LEVEL: 0 # Show TensorFlow log messages for development
PHOTOPRISM_URL: "http://localhost:2342/"
PHOTOPRISM_DEBUG: "true"
PHOTOPRISM_READONLY: "false"
@ -39,7 +40,15 @@ services:
PHOTOPRISM_TITLE: "PhotoPrism"
PHOTOPRISM_SUBTITLE: "Browse your life"
PHOTOPRISM_AUTHOR: "PhotoPrism.org"
TF_CPP_MIN_LOG_LEVEL: 0
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)
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_SIDECAR_JSON: "true" # Read metadata from JSON sidecar files created by exiftool
PHOTOPRISM_SIDECAR_YAML: "true" # Backup photo metadata to YAML sidecar files
PHOTOPRISM_SIDECAR_HIDDEN: "true" # Create JSON and YAML sidecar files in .photoprism (if enabled)
photoprism-db:
image: mariadb:10.5

View file

@ -16,14 +16,16 @@ ENV PHOTOPRISM_PUBLIC true
ENV PHOTOPRISM_EXPERIMENTAL true
ENV PHOTOPRISM_UPLOAD_NSFW false
ENV PHOTOPRISM_DETECT_NSFW true
ENV PHOTOPRISM_JPEG_QUALITY 95
ENV PHOTOPRISM_SIDECAR_JSON true
ENV PHOTOPRISM_SIDECAR_YAML false
ENV PHOTOPRISM_SIDECAR_HIDDEN true
ENV PHOTOPRISM_GEOCODING_API places
ENV PHOTOPRISM_THUMB_FILTER lanczos
ENV PHOTOPRISM_THUMB_UNCACHED true
ENV PHOTOPRISM_THUMB_SIZE 3840
ENV PHOTOPRISM_THUMB_LIMIT 3840
ENV PHOTOPRISM_GEOCODING_API places
ENV PHOTOPRISM_SIDECAR_JSON true
ENV PHOTOPRISM_SIDECAR_YAML false
ENV PHOTOPRISM_JPEG_QUALITY 95
ENV PHOTOPRISM_JPEG_HIDDEN false
# Import example photos
RUN photoprism import

View file

@ -43,14 +43,16 @@ services:
PHOTOPRISM_DATABASE_DSN: "root:photoprism@tcp(localhost:2343)/photoprism?parseTime=true"
# PHOTOPRISM_DATABASE_DRIVER: "mysql" # Using MariaDB or MySQL instead of the internal TiDB is optional
# PHOTOPRISM_DATABASE_DSN: "photoprism:photoprism@tcp(photoprism-db:3306)/photoprism?parseTime=true"
PHOTOPRISM_JPEG_QUALITY: 90 # Use 95 for high-quality thumbnails (requires more storage)
# PHOTOPRISM_SIDECAR_JSON: "true" # Read metadata from JSON sidecar files created by exiftool
# PHOTOPRISM_SIDECAR_YAML: "true" # Backup photo metadata to YAML sidecar files
PHOTOPRISM_SIDECAR_HIDDEN: "true" # Create JSON and YAML sidecar files in .photoprism (if enabled)
PHOTOPRISM_THUMB_FILTER: "lanczos" # Resample filter, best to worst: blackman, lanczos, cubic, linear
PHOTOPRISM_THUMB_UNCACHED: "false" # 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)
# PHOTOPRISM_THUMB_SIZE: 3840 # For retina screens (requires more storage)
PHOTOPRISM_THUMB_LIMIT: 3840 # On-demand thumbnail size limit (default 2048, min 720, max 3840)
# PHOTOPRISM_SIDECAR_JSON: "true" # read metadata from json sidecar files created by exiftool
# PHOTOPRISM_SIDECAR_YAML: "true" # backup photo metadata to yaml sidecar files
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)
volumes:
- "~/Pictures/Originals:/photoprism/originals" # [local path]:[container path]
- "~/Pictures/Import:/photoprism/import" # [local path]:[container path] (optional)

View file

@ -42,14 +42,16 @@ services:
PHOTOPRISM_DATABASE_DSN: "root:photoprism@tcp(localhost:2343)/photoprism?parseTime=true"
# PHOTOPRISM_DATABASE_DRIVER: "mysql" # Using MariaDB or MySQL instead of the internal TiDB is optional
# PHOTOPRISM_DATABASE_DSN: "photoprism:photoprism@tcp(photoprism-db:3306)/photoprism?parseTime=true"
PHOTOPRISM_JPEG_QUALITY: 90 # Use 95 for high-quality thumbnails (requires more storage)
# PHOTOPRISM_SIDECAR_JSON: "true" # Read metadata from JSON sidecar files created by exiftool
# PHOTOPRISM_SIDECAR_YAML: "true" # Backup photo metadata to YAML sidecar files
PHOTOPRISM_SIDECAR_HIDDEN: "true" # Create JSON and YAML sidecar files in .photoprism (if enabled)
PHOTOPRISM_THUMB_FILTER: "lanczos" # Resample filter, best to worst: blackman, lanczos, cubic, linear
PHOTOPRISM_THUMB_UNCACHED: "false" # 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)
# PHOTOPRISM_THUMB_SIZE: 3840 # For retina screens (requires more storage)
PHOTOPRISM_THUMB_LIMIT: 3840 # On-demand thumbnail size limit (default 2048, min 720, max 3840)
# PHOTOPRISM_SIDECAR_JSON: "true" # read metadata from json sidecar files created by exiftool
# PHOTOPRISM_SIDECAR_YAML: "true" # backup photo metadata to yaml sidecar files
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)
volumes:
- "~/Pictures/Originals:/photoprism/originals" # [local path]:[container path]
- "~/Pictures/Import:/photoprism/import" # [local path]:[container path] (optional)

View file

@ -4,14 +4,12 @@ import (
"fmt"
"net/http"
"path"
"path/filepath"
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/photoprism"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
@ -21,7 +19,8 @@ import (
func SavePhotoAsYaml(p entity.Photo, conf *config.Config) {
// Write YAML sidecar file (optional).
if conf.SidecarYaml() {
yamlFile := filepath.Join(conf.OriginalsPath(), p.PhotoPath, photoprism.HiddenPath, p.PhotoName) + ".yml"
yamlFile := p.YamlFileName(conf.OriginalsPath(), conf.SidecarHidden())
if err := p.SaveAsYaml(yamlFile); err != nil {
log.Errorf("photo: %s (update yaml)", err)
} else {
@ -168,7 +167,7 @@ func GetPhotoYaml(router *gin.RouterGroup, conf *config.Config) {
}
if c.Query("download") != "" {
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", c.Param("uuid")+".yml"))
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", c.Param("uuid")+fs.YamlExt))
}
c.Data(http.StatusOK, "text/x-yaml; charset=utf-8", data)

View file

@ -94,17 +94,19 @@ func configAction(ctx *cli.Context) error {
fmt.Printf("%-25s %s\n", "exiftool-bin", conf.ExifToolBin())
fmt.Printf("%-25s %t\n", "sidecar-json", conf.SidecarJson())
fmt.Printf("%-25s %t\n", "sidecar-yaml", conf.SidecarYaml())
fmt.Printf("%-25s %t\n", "sidecar-hidden", conf.SidecarHidden())
// Places / Geocoding API
fmt.Printf("%-25s %s\n", "geocoding-api", conf.GeoCodingApi())
// Thumbnails
fmt.Printf("%-25s %d\n", "jpeg-quality", conf.JpegQuality())
fmt.Printf("%-25s %s\n", "thumb-filter", conf.ThumbFilter())
fmt.Printf("%-25s %t\n", "thumb-uncached", conf.ThumbUncached())
fmt.Printf("%-25s %d\n", "thumb-size", conf.ThumbSize())
fmt.Printf("%-25s %d\n", "thumb-limit", conf.ThumbLimit())
fmt.Printf("%-25s %s\n", "thumb-path", conf.ThumbPath())
fmt.Printf("%-25s %d\n", "jpeg-quality", conf.JpegQuality())
fmt.Printf("%-25s %t\n", "jpeg-hidden", conf.JpegHidden())
return nil
}

View file

@ -165,6 +165,11 @@ func (c *Config) SidecarYaml() bool {
return c.params.SidecarYaml
}
// SidecarHidden returns true if new sidecar files should be created in a .photoprism sub directory (hidden).
func (c *Config) SidecarHidden() bool {
return c.params.SidecarHidden
}
// HeifConvertBin returns the heif-convert executable file name.
func (c *Config) HeifConvertBin() string {
return findExecutable(c.params.HeifConvertBin, "heif-convert")

View file

@ -175,14 +175,19 @@ var GlobalFlags = []cli.Flag{
},
cli.BoolFlag{
Name: "sidecar-json, j",
Usage: "read metadata from json sidecar files created by exiftool",
Usage: "read metadata from JSON sidecar files created by exiftool",
EnvVar: "PHOTOPRISM_SIDECAR_JSON",
},
cli.BoolFlag{
Name: "sidecar-yaml, y",
Usage: "backup photo metadata to yaml sidecar files",
Usage: "backup photo metadata to YAML sidecar files",
EnvVar: "PHOTOPRISM_SIDECAR_YAML",
},
cli.BoolFlag{
Name: "sidecar-hidden",
Usage: "create JSON and YAML sidecar files in .photoprism if enabled",
EnvVar: "PHOTOPRISM_SIDECAR_HIDDEN",
},
cli.IntFlag{
Name: "http-port",
Value: 2342,
@ -248,12 +253,6 @@ var GlobalFlags = []cli.Flag{
Value: "places",
EnvVar: "PHOTOPRISM_GEOCODING_API",
},
cli.IntFlag{
Name: "jpeg-quality, q",
Usage: "set to 95 for high-quality thumbnails (25-100)",
Value: 90,
EnvVar: "PHOTOPRISM_JPEG_QUALITY",
},
cli.StringFlag{
Name: "thumb-filter, f",
Usage: "resample filter (best to worst: blackman, lanczos, cubic, linear)",
@ -277,6 +276,17 @@ var GlobalFlags = []cli.Flag{
Value: 3840,
EnvVar: "PHOTOPRISM_THUMB_LIMIT",
},
cli.IntFlag{
Name: "jpeg-quality, q",
Usage: "set to 95 for high-quality thumbnails (25-100)",
Value: 90,
EnvVar: "PHOTOPRISM_JPEG_QUALITY",
},
cli.BoolFlag{
Name: "jpeg-hidden",
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",

View file

@ -70,12 +70,14 @@ type Params struct {
ExifToolBin string `yaml:"exiftool-bin" flag:"exiftool-bin"`
SidecarJson bool `yaml:"sidecar-json" flag:"sidecar-json"`
SidecarYaml bool `yaml:"sidecar-yaml" flag:"sidecar-yaml"`
SidecarHidden bool `yaml:"sidecar-hidden" flag:"sidecar-hidden"`
PIDFilename string `yaml:"pid-filename" flag:"pid-filename"`
LogFilename string `yaml:"log-filename" flag:"log-filename"`
DetachServer bool `yaml:"detach-server" flag:"detach-server"`
DetectNSFW bool `yaml:"detect-nsfw" flag:"detect-nsfw"`
UploadNSFW bool `yaml:"upload-nsfw" flag:"upload-nsfw"`
GeoCodingApi string `yaml:"geocoding-api" flag:"geocoding-api"`
JpegHidden bool `yaml:"jpeg-hidden" flag:"jpeg-hidden"`
JpegQuality int `yaml:"jpeg-quality" flag:"jpeg-quality"`
ThumbFilter string `yaml:"thumb-filter" flag:"thumb-filter"`
ThumbUncached bool `yaml:"thumb-uncached" flag:"thumb-uncached"`

View file

@ -6,6 +6,11 @@ import (
"github.com/photoprism/photoprism/internal/thumb"
)
// JpegHidden returns true if JPEG files should be created in a .photoprism sub directory (hidden).
func (c *Config) JpegHidden() bool {
return c.params.JpegHidden
}
// JpegQuality returns the jpeg quality for resampling, use 95 for high-quality thumbs (25-100).
func (c *Config) JpegQuality() int {
if c.params.JpegQuality > 100 {

View file

@ -43,6 +43,7 @@ func NewTestParams() *Params {
ReadOnly: false,
DetectNSFW: true,
UploadNSFW: false,
SidecarHidden: true,
DarktableBin: "/usr/bin/darktable-cli",
ExifToolBin: "/usr/bin/exiftool",
AssetsPath: assetsPath,

View file

@ -4,6 +4,7 @@ import (
"testing"
"time"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/stretchr/testify/assert"
)
@ -30,7 +31,7 @@ func TestFile_DownloadFileName(t *testing.T) {
filename := file.ShareFileName()
assert.Contains(t, filename, "20190115-000000-Berlin-Morning-Mood")
assert.Contains(t, filename, ".jpg")
assert.Contains(t, filename, fs.JpegExt)
})
t.Run("photo without title", func(t *testing.T) {
photo := &Photo{TakenAtLocal: time.Date(2019, 01, 15, 0, 0, 0, 0, time.UTC), PhotoTitle: ""}
@ -39,7 +40,7 @@ func TestFile_DownloadFileName(t *testing.T) {
filename := file.ShareFileName()
assert.Contains(t, filename, "20190115-000000-123")
assert.Contains(t, filename, ".jpg")
assert.Contains(t, filename, fs.JpegExt)
})
t.Run("photo without photo", func(t *testing.T) {
file := &File{Photo: nil, FileType: "jpg", FileHash: "123Hash", FileUUID: "foobar345678765"}

View file

@ -3,7 +3,9 @@ package entity
import (
"io/ioutil"
"os"
"path/filepath"
"github.com/photoprism/photoprism/pkg/fs"
"gopkg.in/yaml.v2"
)
@ -53,3 +55,12 @@ func (m *Photo) LoadFromYaml(fileName string) error {
return nil
}
// YamlFileName returns the YAML backup file name.
func (m *Photo) YamlFileName(originalsPath string, hidden bool) string {
if hidden {
return filepath.Join(originalsPath, m.PhotoPath, fs.HiddenPath, m.PhotoName) + fs.YamlExt
}
return filepath.Join(originalsPath, m.PhotoPath, m.PhotoName) + fs.YamlExt
}

View file

@ -13,6 +13,7 @@ import (
"github.com/dsoprea/go-exif/v2/common"
"github.com/dsoprea/go-jpeg-image-structure"
"github.com/dsoprea/go-png-image-structure"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"gopkg.in/ugjka/go-tz.v2/tz"
)
@ -51,7 +52,7 @@ func (data *Data) Exif(fileName string) (err error) {
fileExtension := strings.ToLower(path.Ext(fileName))
if fileExtension == ".jpg" || fileExtension == ".jpeg" {
if fileExtension == fs.JpegExt || fileExtension == ".jpeg" {
jmp := jpegstructure.NewJpegMediaParser()
sl, err := jmp.ParseFile(fileName)

View file

@ -52,7 +52,7 @@ func (c *Convert) Start(path string) error {
}
done := make(map[string]bool)
ignore := fs.NewIgnoreList(IgnoreFile, true, false)
ignore := fs.NewIgnoreList(fs.IgnoreFile, true, false)
if err := ignore.Dir(path); err != nil {
log.Infof("convert: %s", err)
@ -136,7 +136,7 @@ func (c *Convert) ConvertCommand(mf *MediaFile, jpegName string, xmpName string)
// ToJson uses exiftool to export metadata to a json file.
func (c *Convert) ToJson(mf *MediaFile, hidden bool) (*MediaFile, error) {
jsonName := fs.TypeJson.FindSub(mf.FileName(), HiddenPath, c.conf.Settings().Index.Group)
jsonName := fs.TypeJson.FindSub(mf.FileName(), fs.HiddenPath, c.conf.Settings().Index.Group)
result, err := NewMediaFile(jsonName)
@ -202,7 +202,7 @@ func (c *Convert) ToJpeg(image *MediaFile, hidden bool) (*MediaFile, error) {
return image, nil
}
jpegName := fs.TypeJpeg.FindSub(image.FileName(), HiddenPath, c.conf.Settings().Index.Group)
jpegName := fs.TypeJpeg.FindSub(image.FileName(), fs.HiddenPath, c.conf.Settings().Index.Group)
mediaFile, err := NewMediaFile(jpegName)
@ -215,9 +215,9 @@ func (c *Convert) ToJpeg(image *MediaFile, hidden bool) (*MediaFile, error) {
}
if hidden {
jpegName = image.HiddenName(".jpg", c.conf.Settings().Index.Group)
jpegName = image.HiddenName(fs.JpegExt, c.conf.Settings().Index.Group)
} else {
jpegName = image.RelatedName(".jpg", c.conf.Settings().Index.Group)
jpegName = image.RelatedName(fs.JpegExt, c.conf.Settings().Index.Group)
}
fileName := image.RelativeName(c.conf.OriginalsPath())

View file

@ -84,7 +84,7 @@ func (imp *Import) Start(opt ImportOptions) map[string]bool {
}
indexOpt := IndexOptionsAll()
ignore := fs.NewIgnoreList(IgnoreFile, true, false)
ignore := fs.NewIgnoreList(fs.IgnoreFile, true, false)
if err := ignore.Dir(importPath); err != nil {
log.Infof("import: %s", err)

View file

@ -103,7 +103,7 @@ func ImportWorker(jobs <-chan ImportJob) {
}
if imp.conf.SidecarJson() && !f.HasJson() {
if jsonFile, err := imp.convert.ToJson(f, true); err != nil {
if jsonFile, err := imp.convert.ToJson(f, imp.conf.SidecarHidden()); err != nil {
log.Errorf("import: creating json sidecar file failed (%s)", err.Error())
} else {
log.Infof("import: %s created", fs.RelativeName(jsonFile.FileName(), imp.originalsPath()))

View file

@ -94,7 +94,7 @@ func (ind *Index) Start(opt IndexOptions) map[string]bool {
}()
}
ignore := fs.NewIgnoreList(IgnoreFile, true, false)
ignore := fs.NewIgnoreList(fs.IgnoreFile, true, false)
if err := ignore.Dir(originalsPath); err != nil {
log.Infof("index: %s", err)

View file

@ -350,7 +350,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
return result
}
} else {
if yamlName := fs.TypeYaml.FindSub(m.FileName(), HiddenPath, ind.conf.Settings().Index.Group); yamlName != "" {
if yamlName := fs.TypeYaml.FindSub(m.FileName(), fs.HiddenPath, ind.conf.Settings().Index.Group); yamlName != "" {
if err := photo.LoadFromYaml(yamlName); err != nil {
log.Errorf("index: %s (restore from yaml)", err.Error())
} else {
@ -491,7 +491,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
// Write YAML sidecar file (optional).
if file.FilePrimary && ind.conf.SidecarYaml() {
yamlFile := m.HiddenName(".yml", ind.conf.Settings().Index.Group)
yamlFile := photo.YamlFileName(ind.originalsPath(), ind.conf.SidecarHidden())
if err := photo.SaveAsYaml(yamlFile); err != nil {
log.Errorf("index: %s (update yaml)", err.Error())

View file

@ -44,7 +44,7 @@ func IndexWorker(jobs <-chan IndexJob) {
}
if ind.conf.SidecarJson() && !f.HasJson() {
if jsonFile, err := ind.convert.ToJson(f, true); err != nil {
if jsonFile, err := ind.convert.ToJson(f, ind.conf.SidecarHidden()); err != nil {
log.Errorf("index: creating json sidecar file failed (%s)", err.Error())
} else {
log.Infof("index: %s created", fs.RelativeName(jsonFile.FileName(), ind.originalsPath()))

View file

@ -311,7 +311,7 @@ func (m *MediaFile) RelatedFiles(stripSequence bool) (result RelatedFiles, err e
// Add hidden JPEG if exists.
if !result.ContainsJpeg() && result.Main != nil {
if jpegName := fs.TypeJpeg.FindSub(result.Main.FileName(), HiddenPath, stripSequence); jpegName != "" {
if jpegName := fs.TypeJpeg.FindSub(result.Main.FileName(), fs.HiddenPath, stripSequence); jpegName != "" {
if resultFile, err := NewMediaFile(jpegName); err == nil {
result.Files = append(result.Files, resultFile)
}
@ -357,7 +357,7 @@ func (m *MediaFile) RelativePath(directory string) string {
}
// Remove hidden sub directory if exists.
if path.Base(pathname) == HiddenPath {
if path.Base(pathname) == fs.HiddenPath {
pathname = path.Dir(pathname)
}
@ -395,7 +395,7 @@ func (m *MediaFile) AbsBase(stripSequence bool) string {
// HiddenName returns the a filename with the same base name and a given extension in a hidden sub directory.
func (m *MediaFile) HiddenName(fileExt string, stripSequence bool) string {
return fs.SubFileName(m.FileName(), HiddenPath, fileExt, stripSequence)
return fs.SubFileName(m.FileName(), fs.HiddenPath, fileExt, stripSequence)
}
// RelatedName returns the a filename with the same base name and a given extension in the same directory.
@ -611,7 +611,7 @@ func (m *MediaFile) Jpeg() (*MediaFile, error) {
return m, nil
}
jpegFilename := fs.TypeJpeg.FindSub(m.FileName(), HiddenPath, false)
jpegFilename := fs.TypeJpeg.FindSub(m.FileName(), fs.HiddenPath, false)
if jpegFilename == "" {
return nil, fmt.Errorf("no jpeg found for %s", m.FileName())
@ -626,7 +626,7 @@ func (m *MediaFile) HasJpeg() bool {
return true
}
return fs.TypeJpeg.FindSub(m.FileName(), HiddenPath, false) != ""
return fs.TypeJpeg.FindSub(m.FileName(), fs.HiddenPath, false) != ""
}
// HasJson returns true if this file has or is a json sidecar file.
@ -635,7 +635,7 @@ func (m *MediaFile) HasJson() bool {
return true
}
return fs.TypeJson.FindSub(m.FileName(), HiddenPath, false) != ""
return fs.TypeJson.FindSub(m.FileName(), fs.HiddenPath, false) != ""
}
func (m *MediaFile) decodeDimensions() error {

View file

@ -450,7 +450,7 @@ func TestMediaFile_RelativePath(t *testing.T) {
assert.Equal(t, conf.ExamplesPath(), path)
})
t.Run("hidden root", func(t *testing.T) {
path := mediaFile.RelativePath(filepath.Join(conf.ExamplesPath(), HiddenPath))
path := mediaFile.RelativePath(filepath.Join(conf.ExamplesPath(), fs.HiddenPath))
assert.Equal(t, "", path)
})
}
@ -639,7 +639,7 @@ func TestMediaFile_Extension(t *testing.T) {
mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/elephants.jpg")
assert.Nil(t, err)
assert.Equal(t, ".jpg", mediaFile.Extension())
assert.Equal(t, fs.JpegExt, mediaFile.Extension())
})
}

View file

@ -13,7 +13,7 @@ func (m *MediaFile) MetaData() (result meta.Data, err error) {
m.metaDataOnce.Do(func() {
err = m.metaData.Exif(m.FileName())
if jsonFile := fs.TypeJson.FindSub(m.FileName(), HiddenPath, false); jsonFile == "" {
if jsonFile := fs.TypeJson.FindSub(m.FileName(), fs.HiddenPath, false); jsonFile == "" {
log.Debugf("mediafile: no json sidecar file found for %s", txt.Quote(filepath.Base(m.FileName())))
} else if jsonErr := m.metaData.JSON(jsonFile); jsonErr != nil {
log.Warn(jsonErr)

View file

@ -12,6 +12,3 @@ import (
)
var log = event.Log
const IgnoreFile = ".ppignore"
const HiddenPath = ".photoprism"

View file

@ -47,7 +47,7 @@ func (rs *Resample) Start(force bool) error {
}
done := make(map[string]bool)
ignore := fs.NewIgnoreList(IgnoreFile, true, false)
ignore := fs.NewIgnoreList(fs.IgnoreFile, true, false)
if err := ignore.Dir(originalsPath); err != nil {
log.Infof("resample: %s", err)

View file

@ -139,7 +139,7 @@ func TestClient_UploadAndDelete(t *testing.T) {
assert.IsType(t, Client{}, c)
tempName := rnd.UUID() + ".jpg"
tempName := rnd.UUID() + fs.JpegExt
if err := c.Upload("testdata/example.jpg", tempName); err != nil {
t.Fatal(err)

View file

@ -4,6 +4,7 @@ import (
"os"
"testing"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/stretchr/testify/assert"
)
@ -13,7 +14,7 @@ func TestJpeg(t *testing.T) {
for _, ext := range formats {
t.Run(ext, func(t *testing.T) {
src := "testdata/example." + ext
dst := "testdata/example." + ext + ".jpg"
dst := "testdata/example." + ext + fs.JpegExt
assert.NoFileExists(t, dst)

View file

@ -36,6 +36,11 @@ const (
type FileExtensions map[string]FileType
type TypeExtensions map[FileType][]string
const (
YamlExt = ".yml"
JpegExt = ".jpg"
)
// FileExt contains the filename extensions of file formats known to PhotoPrism.
var FileExt = FileExtensions{
".bmp": TypeBitmap,

View file

@ -18,6 +18,9 @@ import (
"strings"
)
const IgnoreFile = ".ppignore"
const HiddenPath = ".photoprism"
// FileExists returns true if file exists and is not a directory.
func FileExists(fileName string) bool {
if fileName == "" {