Downloads: Configure file name schema #675

There is no UI for this setting yet.
This commit is contained in:
Michael Mayer 2020-12-16 11:59:16 +01:00
parent 373578b9a9
commit 717da1bd34
11 changed files with 96 additions and 23 deletions

View file

@ -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)
}

View file

@ -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)
})

View file

@ -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))

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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 {

View file

@ -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,
},
}
}

View file

@ -36,3 +36,5 @@ stack:
name: false
share:
title: ""
download:
name: file

View 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 {

View file

@ -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")
})

View file

@ -38,3 +38,7 @@ func SetLocale(loc string) {
gotext.Configure(localeDir, string(locale), "default")
}
func (l Locale) Locale() string {
return string(l)
}