2020-01-02 00:03:07 +01:00
|
|
|
package config
|
|
|
|
|
|
|
|
import (
|
2020-01-21 13:59:41 +01:00
|
|
|
"fmt"
|
2020-01-02 00:03:07 +01:00
|
|
|
"os"
|
|
|
|
"os/exec"
|
2020-05-31 02:09:52 +02:00
|
|
|
"os/user"
|
2020-01-02 00:03:07 +01:00
|
|
|
"path/filepath"
|
|
|
|
|
2020-01-12 14:00:56 +01:00
|
|
|
"github.com/photoprism/photoprism/pkg/fs"
|
2021-12-14 20:01:39 +01:00
|
|
|
"github.com/photoprism/photoprism/pkg/sanitize"
|
2020-01-02 00:03:07 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
func findExecutable(configBin, defaultBin string) (result string) {
|
|
|
|
if configBin == "" {
|
|
|
|
result = defaultBin
|
|
|
|
} else {
|
|
|
|
result = configBin
|
|
|
|
}
|
|
|
|
|
|
|
|
if path, err := exec.LookPath(result); err == nil {
|
|
|
|
result = path
|
|
|
|
}
|
|
|
|
|
2020-01-12 14:00:56 +01:00
|
|
|
if !fs.FileExists(result) {
|
2020-01-02 00:03:07 +01:00
|
|
|
result = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2020-01-21 13:59:41 +01:00
|
|
|
// CreateDirectories creates directories for storing photos, metadata and cache files.
|
2020-01-02 00:03:07 +01:00
|
|
|
func (c *Config) CreateDirectories() error {
|
2020-01-21 13:59:41 +01:00
|
|
|
createError := func(path string, err error) (result error) {
|
|
|
|
if fs.FileExists(path) {
|
2021-12-14 20:01:39 +01:00
|
|
|
result = fmt.Errorf("%s is a file, not a folder: please check your configuration", sanitize.Log(path))
|
2020-01-21 13:59:41 +01:00
|
|
|
} else {
|
2022-01-05 11:40:44 +01:00
|
|
|
result = fmt.Errorf("cannot create %s: please check configuration and permissions", sanitize.Log(path))
|
2020-01-21 13:59:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
log.Debug(err)
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2020-08-18 19:09:46 +02:00
|
|
|
notFoundError := func(name string) error {
|
2020-12-18 20:42:12 +01:00
|
|
|
return fmt.Errorf("%s path not found, run 'photoprism config' to check configuration options", name)
|
2020-08-18 19:09:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if c.AssetsPath() == "" {
|
|
|
|
return notFoundError("assets")
|
|
|
|
} else if err := os.MkdirAll(c.AssetsPath(), os.ModePerm); err != nil {
|
2020-05-31 02:09:52 +02:00
|
|
|
return createError(c.AssetsPath(), err)
|
|
|
|
}
|
|
|
|
|
2020-08-18 19:09:46 +02:00
|
|
|
if c.StoragePath() == "" {
|
|
|
|
return notFoundError("storage")
|
|
|
|
} else if err := os.MkdirAll(c.StoragePath(), os.ModePerm); err != nil {
|
2020-05-31 02:09:52 +02:00
|
|
|
return createError(c.StoragePath(), err)
|
|
|
|
}
|
|
|
|
|
2020-12-04 11:02:19 +01:00
|
|
|
if c.BackupPath() == "" {
|
|
|
|
return notFoundError("backup")
|
|
|
|
} else if err := os.MkdirAll(c.BackupPath(), os.ModePerm); err != nil {
|
|
|
|
return createError(c.BackupPath(), err)
|
|
|
|
}
|
|
|
|
|
2020-08-18 19:09:46 +02:00
|
|
|
if c.OriginalsPath() == "" {
|
|
|
|
return notFoundError("originals")
|
|
|
|
} else if err := os.MkdirAll(c.OriginalsPath(), os.ModePerm); err != nil {
|
2020-01-21 13:59:41 +01:00
|
|
|
return createError(c.OriginalsPath(), err)
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
2020-08-18 19:09:46 +02:00
|
|
|
if c.ImportPath() == "" {
|
|
|
|
return notFoundError("import")
|
|
|
|
} else if err := os.MkdirAll(c.ImportPath(), os.ModePerm); err != nil {
|
2020-01-21 13:59:41 +01:00
|
|
|
return createError(c.ImportPath(), err)
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
2020-06-07 10:09:35 +02:00
|
|
|
if filepath.IsAbs(c.SidecarPath()) {
|
|
|
|
if err := os.MkdirAll(c.SidecarPath(), os.ModePerm); err != nil {
|
|
|
|
return createError(c.SidecarPath(), err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-18 19:09:46 +02:00
|
|
|
if c.CachePath() == "" {
|
|
|
|
return notFoundError("cache")
|
|
|
|
} else if err := os.MkdirAll(c.CachePath(), os.ModePerm); err != nil {
|
2020-05-31 02:09:52 +02:00
|
|
|
return createError(c.CachePath(), err)
|
2020-04-06 16:34:29 +02:00
|
|
|
}
|
|
|
|
|
2020-08-18 19:09:46 +02:00
|
|
|
if c.ThumbPath() == "" {
|
|
|
|
return notFoundError("thumbs")
|
|
|
|
} else if err := os.MkdirAll(c.ThumbPath(), os.ModePerm); err != nil {
|
2020-05-05 17:04:13 +02:00
|
|
|
return createError(c.ThumbPath(), err)
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
2020-12-17 18:24:55 +01:00
|
|
|
if c.ConfigPath() == "" {
|
|
|
|
return notFoundError("config")
|
|
|
|
} else if err := os.MkdirAll(c.ConfigPath(), os.ModePerm); err != nil {
|
|
|
|
return createError(c.ConfigPath(), err)
|
2020-05-31 02:09:52 +02:00
|
|
|
}
|
|
|
|
|
2020-08-18 19:09:46 +02:00
|
|
|
if c.TempPath() == "" {
|
|
|
|
return notFoundError("temp")
|
|
|
|
} else if err := os.MkdirAll(c.TempPath(), os.ModePerm); err != nil {
|
2020-05-31 02:09:52 +02:00
|
|
|
return createError(c.TempPath(), err)
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
2020-12-17 18:24:55 +01:00
|
|
|
if c.AlbumsPath() == "" {
|
|
|
|
return notFoundError("albums")
|
|
|
|
} else if err := os.MkdirAll(c.AlbumsPath(), os.ModePerm); err != nil {
|
|
|
|
return createError(c.AlbumsPath(), err)
|
|
|
|
}
|
|
|
|
|
2020-08-18 19:09:46 +02:00
|
|
|
if c.TensorFlowModelPath() == "" {
|
|
|
|
return notFoundError("tensorflow model")
|
|
|
|
} else if err := os.MkdirAll(c.TensorFlowModelPath(), os.ModePerm); err != nil {
|
2020-01-21 13:59:41 +01:00
|
|
|
return createError(c.TensorFlowModelPath(), err)
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
2020-08-18 19:09:46 +02:00
|
|
|
if c.BuildPath() == "" {
|
|
|
|
return notFoundError("build")
|
|
|
|
} else if err := os.MkdirAll(c.BuildPath(), os.ModePerm); err != nil {
|
2020-06-26 16:11:56 +02:00
|
|
|
return createError(c.BuildPath(), err)
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
2020-08-18 19:09:46 +02:00
|
|
|
if filepath.Dir(c.PIDFilename()) == "" {
|
|
|
|
return notFoundError("pid file")
|
|
|
|
} else if err := os.MkdirAll(filepath.Dir(c.PIDFilename()), os.ModePerm); err != nil {
|
2020-01-21 13:59:41 +01:00
|
|
|
return createError(filepath.Dir(c.PIDFilename()), err)
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
2020-08-18 19:09:46 +02:00
|
|
|
if filepath.Dir(c.LogFilename()) == "" {
|
|
|
|
return notFoundError("log file")
|
|
|
|
} else if err := os.MkdirAll(filepath.Dir(c.LogFilename()), os.ModePerm); err != nil {
|
2020-01-21 13:59:41 +01:00
|
|
|
return createError(filepath.Dir(c.LogFilename()), err)
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ConfigFile returns the config file name.
|
|
|
|
func (c *Config) ConfigFile() string {
|
2020-12-18 20:42:12 +01:00
|
|
|
if c.options.ConfigFile == "" || !fs.FileExists(c.options.ConfigFile) {
|
|
|
|
return filepath.Join(c.ConfigPath(), "options.yml")
|
2020-05-31 02:09:52 +02:00
|
|
|
}
|
|
|
|
|
2020-12-18 20:42:12 +01:00
|
|
|
return c.options.ConfigFile
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
2020-12-17 18:24:55 +01:00
|
|
|
// ConfigPath returns the config path.
|
|
|
|
func (c *Config) ConfigPath() string {
|
2020-12-18 20:42:12 +01:00
|
|
|
if c.options.ConfigPath == "" {
|
2020-12-17 18:24:55 +01:00
|
|
|
if fs.PathExists(filepath.Join(c.StoragePath(), "settings")) {
|
|
|
|
return filepath.Join(c.StoragePath(), "settings")
|
|
|
|
}
|
|
|
|
|
|
|
|
return filepath.Join(c.StoragePath(), "config")
|
|
|
|
}
|
|
|
|
|
2020-12-18 20:42:12 +01:00
|
|
|
return fs.Abs(c.options.ConfigPath)
|
2020-12-17 18:24:55 +01:00
|
|
|
}
|
|
|
|
|
2020-12-04 13:10:32 +01:00
|
|
|
// HubConfigFile returns the backend api config file name.
|
|
|
|
func (c *Config) HubConfigFile() string {
|
2020-12-17 18:24:55 +01:00
|
|
|
return filepath.Join(c.ConfigPath(), "hub.yml")
|
2020-09-06 14:18:40 +02:00
|
|
|
}
|
|
|
|
|
2020-01-02 00:03:07 +01:00
|
|
|
// SettingsFile returns the user settings file name.
|
|
|
|
func (c *Config) SettingsFile() string {
|
2020-12-17 18:24:55 +01:00
|
|
|
return filepath.Join(c.ConfigPath(), "settings.yml")
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// PIDFilename returns the filename for storing the server process id (pid).
|
|
|
|
func (c *Config) PIDFilename() string {
|
2020-12-18 20:42:12 +01:00
|
|
|
if c.options.PIDFilename == "" {
|
2020-05-31 02:09:52 +02:00
|
|
|
return filepath.Join(c.StoragePath(), "photoprism.pid")
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
2020-12-18 20:42:12 +01:00
|
|
|
return fs.Abs(c.options.PIDFilename)
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// LogFilename returns the filename for storing server logs.
|
|
|
|
func (c *Config) LogFilename() string {
|
2020-12-18 20:42:12 +01:00
|
|
|
if c.options.LogFilename == "" {
|
2020-05-31 02:09:52 +02:00
|
|
|
return filepath.Join(c.StoragePath(), "photoprism.log")
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
2020-12-18 20:42:12 +01:00
|
|
|
return fs.Abs(c.options.LogFilename)
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
2020-12-26 18:06:54 +01:00
|
|
|
// CaseInsensitive tests if the storage path is case-insensitive.
|
|
|
|
func (c *Config) CaseInsensitive() (result bool, err error) {
|
|
|
|
storagePath := c.StoragePath()
|
|
|
|
return fs.CaseInsensitive(storagePath)
|
|
|
|
}
|
|
|
|
|
2020-01-02 00:03:07 +01:00
|
|
|
// OriginalsPath returns the originals.
|
|
|
|
func (c *Config) OriginalsPath() string {
|
2020-12-18 20:42:12 +01:00
|
|
|
if c.options.OriginalsPath == "" {
|
2020-05-31 12:20:32 +02:00
|
|
|
// Try to find the right directory by iterating through a list.
|
2020-12-18 20:42:12 +01:00
|
|
|
c.options.OriginalsPath = fs.FindDir(fs.OriginalPaths)
|
2020-05-31 12:20:32 +02:00
|
|
|
}
|
|
|
|
|
2020-12-18 20:42:12 +01:00
|
|
|
return fs.Abs(c.options.OriginalsPath)
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// ImportPath returns the import directory.
|
|
|
|
func (c *Config) ImportPath() string {
|
2020-12-18 20:42:12 +01:00
|
|
|
if c.options.ImportPath == "" {
|
2020-05-31 12:20:32 +02:00
|
|
|
// Try to find the right directory by iterating through a list.
|
2020-12-18 20:42:12 +01:00
|
|
|
c.options.ImportPath = fs.FindDir(fs.ImportPaths)
|
2020-05-31 12:20:32 +02:00
|
|
|
}
|
|
|
|
|
2020-12-18 20:42:12 +01:00
|
|
|
return fs.Abs(c.options.ImportPath)
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
2020-05-11 18:29:17 +02:00
|
|
|
// ExifToolBin returns the exiftool executable file name.
|
|
|
|
func (c *Config) ExifToolBin() string {
|
2020-12-18 20:42:12 +01:00
|
|
|
return findExecutable(c.options.ExifToolBin, "exiftool")
|
2020-05-11 18:29:17 +02:00
|
|
|
}
|
|
|
|
|
2021-10-07 13:00:32 +02:00
|
|
|
// ExifToolJson tests if creating JSON metadata sidecar files with Exiftool is enabled.
|
2020-12-18 09:11:42 +01:00
|
|
|
func (c *Config) ExifToolJson() bool {
|
|
|
|
return !c.DisableExifTool()
|
2020-05-11 18:29:17 +02:00
|
|
|
}
|
|
|
|
|
2021-10-07 13:00:32 +02:00
|
|
|
// BackupYaml tests if creating YAML backups is enabled.
|
2020-12-18 09:11:42 +01:00
|
|
|
func (c *Config) BackupYaml() bool {
|
|
|
|
return !c.DisableBackups()
|
2020-05-18 22:18:58 +02:00
|
|
|
}
|
|
|
|
|
2020-09-06 14:18:40 +02:00
|
|
|
// SidecarPath returns the storage path for generated sidecar files (relative or absolute).
|
2020-06-07 10:09:35 +02:00
|
|
|
func (c *Config) SidecarPath() string {
|
2020-12-18 20:42:12 +01:00
|
|
|
if c.options.SidecarPath == "" {
|
|
|
|
c.options.SidecarPath = filepath.Join(c.StoragePath(), "sidecar")
|
2020-06-07 10:09:35 +02:00
|
|
|
}
|
|
|
|
|
2020-12-18 20:42:12 +01:00
|
|
|
return c.options.SidecarPath
|
2020-05-19 11:00:17 +02:00
|
|
|
}
|
|
|
|
|
2020-12-18 10:59:21 +01:00
|
|
|
// SidecarPathIsAbs tests if sidecar path is absolute.
|
2020-06-07 14:33:07 +02:00
|
|
|
func (c *Config) SidecarPathIsAbs() bool {
|
|
|
|
return filepath.IsAbs(c.SidecarPath())
|
|
|
|
}
|
|
|
|
|
2020-12-18 10:59:21 +01:00
|
|
|
// SidecarWritable tests if sidecar files can be created.
|
2020-06-07 14:33:07 +02:00
|
|
|
func (c *Config) SidecarWritable() bool {
|
|
|
|
return !c.ReadOnly() || c.SidecarPathIsAbs()
|
|
|
|
}
|
|
|
|
|
2020-04-06 16:34:29 +02:00
|
|
|
// TempPath returns a temporary directory name for uploads and downloads.
|
|
|
|
func (c *Config) TempPath() string {
|
2020-12-18 20:42:12 +01:00
|
|
|
if c.options.TempPath == "" {
|
2020-05-05 18:26:44 +02:00
|
|
|
return filepath.Join(os.TempDir(), "photoprism")
|
2020-04-06 16:34:29 +02:00
|
|
|
}
|
|
|
|
|
2020-12-18 20:42:12 +01:00
|
|
|
return fs.Abs(c.options.TempPath)
|
2020-04-06 16:34:29 +02:00
|
|
|
}
|
|
|
|
|
2020-12-18 09:11:42 +01:00
|
|
|
// CachePath returns the path for cache files.
|
2020-01-02 00:03:07 +01:00
|
|
|
func (c *Config) CachePath() string {
|
2020-12-18 20:42:12 +01:00
|
|
|
if c.options.CachePath == "" {
|
2020-05-31 02:09:52 +02:00
|
|
|
return filepath.Join(c.StoragePath(), "cache")
|
|
|
|
}
|
|
|
|
|
2020-12-18 20:42:12 +01:00
|
|
|
return fs.Abs(c.options.CachePath)
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
2020-05-31 11:28:28 +02:00
|
|
|
// StoragePath returns the path for generated files like cache and index.
|
2020-05-31 02:09:52 +02:00
|
|
|
func (c *Config) StoragePath() string {
|
2020-12-18 20:42:12 +01:00
|
|
|
if c.options.StoragePath == "" {
|
2020-05-31 11:28:28 +02:00
|
|
|
const dirName = "storage"
|
|
|
|
|
|
|
|
// Default directories.
|
|
|
|
originalsDir := fs.Abs(filepath.Join(c.OriginalsPath(), fs.HiddenPath, dirName))
|
|
|
|
storageDir := fs.Abs(dirName)
|
|
|
|
|
|
|
|
// Find existing directories.
|
2020-12-04 11:37:31 +01:00
|
|
|
if fs.PathWritable(originalsDir) && !c.ReadOnly() {
|
2020-05-31 11:28:28 +02:00
|
|
|
return originalsDir
|
2020-12-04 11:37:31 +01:00
|
|
|
} else if fs.PathWritable(storageDir) && c.ReadOnly() {
|
2020-05-31 11:28:28 +02:00
|
|
|
return storageDir
|
|
|
|
}
|
|
|
|
|
2020-12-04 11:37:31 +01:00
|
|
|
// Fallback to backup storage path.
|
2020-12-18 20:42:12 +01:00
|
|
|
if fs.PathWritable(c.options.BackupPath) {
|
|
|
|
return fs.Abs(filepath.Join(c.options.BackupPath, dirName))
|
2020-12-04 11:37:31 +01:00
|
|
|
}
|
|
|
|
|
2020-05-31 11:28:28 +02:00
|
|
|
// Use .photoprism in home directory?
|
2020-05-31 02:09:52 +02:00
|
|
|
if usr, _ := user.Current(); usr.HomeDir != "" {
|
2020-05-31 11:28:28 +02:00
|
|
|
p := fs.Abs(filepath.Join(usr.HomeDir, fs.HiddenPath, dirName))
|
2020-01-02 00:03:07 +01:00
|
|
|
|
2020-12-04 11:37:31 +01:00
|
|
|
if fs.PathWritable(p) || c.ReadOnly() {
|
2020-05-31 02:09:52 +02:00
|
|
|
return p
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-31 11:28:28 +02:00
|
|
|
// Fallback directory in case nothing else works.
|
|
|
|
if c.ReadOnly() {
|
|
|
|
return fs.Abs(filepath.Join(fs.HiddenPath, dirName))
|
2020-05-31 02:09:52 +02:00
|
|
|
}
|
2020-05-31 11:28:28 +02:00
|
|
|
|
|
|
|
// Store cache and index in "originals/.photoprism/storage".
|
|
|
|
return originalsDir
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
2020-12-18 20:42:12 +01:00
|
|
|
return fs.Abs(c.options.StoragePath)
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
2020-12-04 11:02:19 +01:00
|
|
|
// BackupPath returns the backup storage path.
|
|
|
|
func (c *Config) BackupPath() string {
|
2020-12-18 20:42:12 +01:00
|
|
|
if fs.PathWritable(c.options.BackupPath) {
|
|
|
|
return fs.Abs(c.options.BackupPath)
|
2020-12-04 11:02:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return filepath.Join(c.StoragePath(), "backup")
|
|
|
|
}
|
|
|
|
|
2020-12-18 09:11:42 +01:00
|
|
|
// AssetsPath returns the path to static assets for models and templates.
|
2020-05-31 02:09:52 +02:00
|
|
|
func (c *Config) AssetsPath() string {
|
2020-12-18 20:42:12 +01:00
|
|
|
if c.options.AssetsPath == "" {
|
2020-08-18 19:09:46 +02:00
|
|
|
// Try to find the right directory by iterating through a list.
|
2020-12-18 20:42:12 +01:00
|
|
|
c.options.AssetsPath = fs.FindDir(fs.AssetPaths)
|
2020-08-18 19:09:46 +02:00
|
|
|
}
|
|
|
|
|
2020-12-18 20:42:12 +01:00
|
|
|
return fs.Abs(c.options.AssetsPath)
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
2020-07-15 01:26:54 +02:00
|
|
|
// LocalesPath returns the translation locales path.
|
|
|
|
func (c *Config) LocalesPath() string {
|
|
|
|
return filepath.Join(c.AssetsPath(), "locales")
|
|
|
|
}
|
|
|
|
|
2020-05-31 02:09:52 +02:00
|
|
|
// ExamplesPath returns the example files path.
|
|
|
|
func (c *Config) ExamplesPath() string {
|
|
|
|
return filepath.Join(c.AssetsPath(), "examples")
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
2020-05-31 02:09:52 +02:00
|
|
|
// TestdataPath returns the test files path.
|
|
|
|
func (c *Config) TestdataPath() string {
|
|
|
|
return filepath.Join(c.StoragePath(), "testdata")
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
2020-12-11 12:46:28 +01:00
|
|
|
|
2020-12-11 13:52:34 +01:00
|
|
|
// MysqlBin returns the mysql executable file name.
|
|
|
|
func (c *Config) MysqlBin() string {
|
|
|
|
return findExecutable("", "mysql")
|
|
|
|
}
|
|
|
|
|
2020-12-11 12:46:28 +01:00
|
|
|
// MysqldumpBin returns the mysqldump executable file name.
|
|
|
|
func (c *Config) MysqldumpBin() string {
|
|
|
|
return findExecutable("", "mysqldump")
|
|
|
|
}
|
|
|
|
|
|
|
|
// SqliteBin returns the sqlite executable file name.
|
|
|
|
func (c *Config) SqliteBin() string {
|
|
|
|
return findExecutable("", "sqlite3")
|
2020-12-11 13:52:34 +01:00
|
|
|
}
|
2020-12-17 18:24:55 +01:00
|
|
|
|
|
|
|
// AlbumsPath returns the storage path for album YAML files.
|
|
|
|
func (c *Config) AlbumsPath() string {
|
|
|
|
return filepath.Join(c.StoragePath(), "albums")
|
|
|
|
}
|
2021-02-21 15:48:46 +01:00
|
|
|
|
|
|
|
// OriginalsAlbumsPath returns the optional album YAML backup folder inside originals.
|
|
|
|
func (c *Config) OriginalsAlbumsPath() string {
|
|
|
|
return filepath.Join(c.OriginalsPath(), "albums")
|
|
|
|
}
|