Downloads: Configure file name schema #675
There is no UI for this setting yet.
This commit is contained in:
parent
373578b9a9
commit
717da1bd34
|
@ -170,7 +170,7 @@ func ShareWithAccount(router *gin.RouterGroup) {
|
|||
}
|
||||
|
||||
for _, file := range files {
|
||||
dstFileName := path.Join(dst, file.ShareFileName())
|
||||
dstFileName := path.Join(dst, file.ShareBase())
|
||||
fileShare := entity.NewFileShare(file.ID, m.ID, dstFileName)
|
||||
entity.FirstOrCreateFileShare(fileShare)
|
||||
}
|
||||
|
|
|
@ -2,8 +2,11 @@ package api
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"net/http"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
|
@ -48,9 +51,31 @@ func GetDownload(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
downloadFileName := f.ShareFileName()
|
||||
name := entity.DownloadNameFile
|
||||
|
||||
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", downloadFileName))
|
||||
switch c.Query("name") {
|
||||
case "file":
|
||||
name = entity.DownloadNameFile
|
||||
case "share":
|
||||
name = entity.DownloadNameShare
|
||||
case "original":
|
||||
name = entity.DownloadNameOriginal
|
||||
default:
|
||||
name = service.Config().Settings().Download.Name
|
||||
}
|
||||
|
||||
var downloadName string
|
||||
|
||||
switch name {
|
||||
case entity.DownloadNameFile:
|
||||
downloadName = f.Base()
|
||||
case entity.DownloadNameOriginal:
|
||||
downloadName = f.OriginalBase()
|
||||
default:
|
||||
downloadName = f.ShareBase()
|
||||
}
|
||||
|
||||
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", downloadName))
|
||||
|
||||
c.File(fileName)
|
||||
})
|
||||
|
|
|
@ -144,7 +144,7 @@ func GetPhotoDownload(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
downloadFileName := f.ShareFileName()
|
||||
downloadFileName := f.ShareBase()
|
||||
|
||||
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", downloadFileName))
|
||||
|
||||
|
|
|
@ -159,13 +159,13 @@ func GetThumb(router *gin.RouterGroup) {
|
|||
}
|
||||
|
||||
// Cache thumbnail filename.
|
||||
if cached, err := json.Marshal(ThumbCache{thumbnail, f.ShareFileName()}); err == nil {
|
||||
if cached, err := json.Marshal(ThumbCache{thumbnail, f.ShareBase()}); err == nil {
|
||||
logError("thumbnail", cache.Set(cacheKey, cached))
|
||||
log.Debugf("cached %s [%s]", cacheKey, time.Since(start))
|
||||
}
|
||||
|
||||
if c.Query("download") != "" {
|
||||
c.FileAttachment(thumbnail, f.ShareFileName())
|
||||
c.FileAttachment(thumbnail, f.ShareBase())
|
||||
} else {
|
||||
c.File(thumbnail)
|
||||
}
|
||||
|
@ -272,13 +272,13 @@ func AlbumThumb(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
if cached, err := json.Marshal(ThumbCache{thumbnail, f.ShareFileName()}); err == nil {
|
||||
if cached, err := json.Marshal(ThumbCache{thumbnail, f.ShareBase()}); err == nil {
|
||||
logError("album-thumbnail", cache.Set(cacheKey, cached))
|
||||
log.Debugf("cached %s [%s]", cacheKey, time.Since(start))
|
||||
}
|
||||
|
||||
if c.Query("download") != "" {
|
||||
c.FileAttachment(thumbnail, f.ShareFileName())
|
||||
c.FileAttachment(thumbnail, f.ShareBase())
|
||||
} else {
|
||||
c.File(thumbnail)
|
||||
}
|
||||
|
@ -387,13 +387,13 @@ func LabelThumb(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
if cached, err := json.Marshal(ThumbCache{thumbnail, f.ShareFileName()}); err == nil {
|
||||
if cached, err := json.Marshal(ThumbCache{thumbnail, f.ShareBase()}); err == nil {
|
||||
logError("label-thumbnail", cache.Set(cacheKey, cached))
|
||||
log.Debugf("cached %s [%s]", cacheKey, time.Since(start))
|
||||
}
|
||||
|
||||
if c.Query("download") != "" {
|
||||
c.FileAttachment(thumbnail, f.ShareFileName())
|
||||
c.FileAttachment(thumbnail, f.ShareBase())
|
||||
} else {
|
||||
c.File(thumbnail)
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ func GetVideo(router *gin.RouterGroup) {
|
|||
c.Header("Content-Type", `video/mp4; codecs="avc1"`)
|
||||
|
||||
if c.Query("download") != "" {
|
||||
c.FileAttachment(fileName, f.ShareFileName())
|
||||
c.FileAttachment(fileName, f.ShareBase())
|
||||
} else {
|
||||
c.File(fileName)
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@ func CreateZip(router *gin.RouterGroup) {
|
|||
|
||||
for _, f := range files {
|
||||
fileName := photoprism.FileName(f.FileRoot, f.FileName)
|
||||
fileAlias := f.ShareFileName()
|
||||
fileAlias := f.ShareBase()
|
||||
|
||||
if fs.FileExists(fileName) {
|
||||
if err := addFileToZip(zipWriter, fileName, fileAlias); err != nil {
|
||||
|
|
|
@ -5,6 +5,8 @@ import (
|
|||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
|
@ -73,11 +75,16 @@ type StackSettings struct {
|
|||
Name bool `json:"name" yaml:"name"`
|
||||
}
|
||||
|
||||
// ShareSettings represents photo sharing settings.
|
||||
// ShareSettings represents content sharing settings.
|
||||
type ShareSettings struct {
|
||||
Title string `json:"title" yaml:"title"`
|
||||
}
|
||||
|
||||
// DownloadSettings represents content download settings.
|
||||
type DownloadSettings struct {
|
||||
Name entity.DownloadName `json:"name" yaml:"name"`
|
||||
}
|
||||
|
||||
// Settings represents user settings for Web UI, indexing, and import.
|
||||
type Settings struct {
|
||||
UI UISettings `json:"ui" yaml:"ui"`
|
||||
|
@ -88,6 +95,7 @@ type Settings struct {
|
|||
Index IndexSettings `json:"index" yaml:"index"`
|
||||
Stack StackSettings `json:"stack" yaml:"stack"`
|
||||
Share ShareSettings `json:"share" yaml:"share"`
|
||||
Download DownloadSettings `json:"download" yaml:"download"`
|
||||
}
|
||||
|
||||
// NewSettings creates a new Settings instance.
|
||||
|
@ -96,7 +104,7 @@ func NewSettings() *Settings {
|
|||
UI: UISettings{
|
||||
Scrollbar: true,
|
||||
Theme: "default",
|
||||
Language: "en",
|
||||
Language: i18n.Default.Locale(),
|
||||
},
|
||||
Templates: TemplateSettings{
|
||||
Default: "index.tmpl",
|
||||
|
@ -122,11 +130,11 @@ func NewSettings() *Settings {
|
|||
Logs: true,
|
||||
},
|
||||
Import: ImportSettings{
|
||||
Path: "/",
|
||||
Path: entity.RootPath,
|
||||
Move: false,
|
||||
},
|
||||
Index: IndexSettings{
|
||||
Path: "/",
|
||||
Path: entity.RootPath,
|
||||
Rescan: false,
|
||||
Convert: true,
|
||||
},
|
||||
|
@ -135,6 +143,12 @@ func NewSettings() *Settings {
|
|||
Meta: true,
|
||||
Name: false,
|
||||
},
|
||||
Share: ShareSettings{
|
||||
Title: "",
|
||||
},
|
||||
Download: DownloadSettings{
|
||||
Name: entity.DownloadNameDefault,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
2
internal/config/testdata/settings.yml
vendored
2
internal/config/testdata/settings.yml
vendored
|
@ -36,3 +36,5 @@ stack:
|
|||
name: false
|
||||
share:
|
||||
title: ""
|
||||
download:
|
||||
name: file
|
||||
|
|
|
@ -2,6 +2,7 @@ package entity
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -14,6 +15,15 @@ import (
|
|||
"github.com/ulule/deepcopier"
|
||||
)
|
||||
|
||||
type DownloadName string
|
||||
|
||||
const (
|
||||
DownloadNameFile DownloadName = "file"
|
||||
DownloadNameOriginal DownloadName = "original"
|
||||
DownloadNameShare DownloadName = "share"
|
||||
DownloadNameDefault = DownloadNameFile
|
||||
)
|
||||
|
||||
type Files []File
|
||||
|
||||
// File represents an image or sidecar file that belongs to a photo.
|
||||
|
@ -98,8 +108,26 @@ func (m *File) BeforeCreate(scope *gorm.Scope) error {
|
|||
return scope.SetColumn("FileUID", rnd.PPID('f'))
|
||||
}
|
||||
|
||||
// ShareFileName returns a meaningful file name useful for sharing.
|
||||
func (m *File) ShareFileName() string {
|
||||
// Base returns the file name without path.
|
||||
func (m *File) Base() string {
|
||||
if m.FileName == "" {
|
||||
return m.ShareBase()
|
||||
}
|
||||
|
||||
return filepath.Base(m.FileName)
|
||||
}
|
||||
|
||||
// OriginalBase returns the original file name without path.
|
||||
func (m *File) OriginalBase() string {
|
||||
if m.OriginalName == "" {
|
||||
return m.Base()
|
||||
}
|
||||
|
||||
return filepath.Base(m.OriginalName)
|
||||
}
|
||||
|
||||
// ShareBase returns a meaningful file name useful for sharing.
|
||||
func (m *File) ShareBase() string {
|
||||
photo := m.RelatedPhoto()
|
||||
|
||||
if photo == nil {
|
||||
|
|
|
@ -29,7 +29,7 @@ func TestFile_ShareFileName(t *testing.T) {
|
|||
photo := &Photo{TakenAtLocal: time.Date(2019, 01, 15, 0, 0, 0, 0, time.UTC), PhotoTitle: "Berlin / Morning Mood"}
|
||||
file := &File{Photo: photo, FileType: "jpg", FileUID: "foobar345678765", FileHash: "e98eb86480a72bd585d228a709f0622f90e86cbc"}
|
||||
|
||||
filename := file.ShareFileName()
|
||||
filename := file.ShareBase()
|
||||
|
||||
assert.Contains(t, filename, "20190115-000000-Berlin-Morning-Mood")
|
||||
assert.Contains(t, filename, fs.JpegExt)
|
||||
|
@ -38,14 +38,14 @@ func TestFile_ShareFileName(t *testing.T) {
|
|||
photo := &Photo{TakenAtLocal: time.Date(2019, 01, 15, 0, 0, 0, 0, time.UTC), PhotoTitle: ""}
|
||||
file := &File{Photo: photo, FileType: "jpg", PhotoUID: "123", FileUID: "foobar345678765", FileHash: "e98eb86480a72bd585d228a709f0622f90e86cbc"}
|
||||
|
||||
filename := file.ShareFileName()
|
||||
filename := file.ShareBase()
|
||||
|
||||
assert.Equal(t, filename, "e98eb86480a72bd585d228a709f0622f90e86cbc.jpg")
|
||||
})
|
||||
t.Run("photo without photo", func(t *testing.T) {
|
||||
file := &File{Photo: nil, FileType: "jpg", FileUID: "foobar345678765", FileHash: "e98eb86480a72bd585d228a709f0622f90e86cbc"}
|
||||
|
||||
filename := file.ShareFileName()
|
||||
filename := file.ShareBase()
|
||||
|
||||
assert.Equal(t, "e98eb86480a72bd585d228a709f0622f90e86cbc.jpg", filename)
|
||||
})
|
||||
|
@ -54,14 +54,14 @@ func TestFile_ShareFileName(t *testing.T) {
|
|||
|
||||
file := &File{Photo: photo, FileType: "jpg", FileUID: "foobar345678765", FileHash: "e98"}
|
||||
|
||||
filename := file.ShareFileName()
|
||||
filename := file.ShareBase()
|
||||
|
||||
assert.NotContains(t, filename, "20190115-000000-Berlin-Morning-Mood")
|
||||
})
|
||||
t.Run("no file uid", func(t *testing.T) {
|
||||
file := &File{Photo: nil, FileType: "jpg", FileHash: "e98ijhyt"}
|
||||
|
||||
filename := file.ShareFileName()
|
||||
filename := file.ShareBase()
|
||||
|
||||
assert.Equal(t, filename, "e98ijhyt.jpg")
|
||||
})
|
||||
|
|
|
@ -38,3 +38,7 @@ func SetLocale(loc string) {
|
|||
|
||||
gotext.Configure(localeDir, string(locale), "default")
|
||||
}
|
||||
|
||||
func (l Locale) Locale() string {
|
||||
return string(l)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue