Backups: Rename album backups to exports and improve command help #1887

This commit is contained in:
Michael Mayer
2022-01-05 11:40:44 +01:00
parent 1a4158c7bc
commit 58a5f94069
40 changed files with 90 additions and 89 deletions

View File

@@ -48,7 +48,7 @@ func DeleteFile(router *gin.RouterGroup) {
} }
if file.FilePrimary { if file.FilePrimary {
log.Errorf("photo: can't delete primary file") log.Errorf("photo: cannot delete primary file")
AbortDeleteFailed(c) AbortDeleteFailed(c)
return return
} }

View File

@@ -42,11 +42,11 @@ func PhotoUnstack(router *gin.RouterGroup) {
} }
if file.FilePrimary { if file.FilePrimary {
log.Errorf("photo: can't unstack primary file") log.Errorf("photo: cannot unstack primary file")
AbortBadRequest(c) AbortBadRequest(c)
return return
} else if file.FileSidecar { } else if file.FileSidecar {
log.Errorf("photo: can't unstack sidecar files") log.Errorf("photo: cannot unstack sidecar files")
AbortBadRequest(c) AbortBadRequest(c)
return return
} else if file.FileRoot != entity.RootOriginals { } else if file.FileRoot != entity.RootOriginals {
@@ -70,7 +70,7 @@ func PhotoUnstack(router *gin.RouterGroup) {
stackPrimary, err := stackPhoto.PrimaryFile() stackPrimary, err := stackPhoto.PrimaryFile()
if err != nil { if err != nil {
log.Errorf("photo: can't find primary file for %s (unstack)", sanitize.Log(baseName)) log.Errorf("photo: cannot find primary file for %s (unstack)", sanitize.Log(baseName))
AbortUnexpected(c) AbortUnexpected(c)
return return
} }
@@ -96,7 +96,7 @@ func PhotoUnstack(router *gin.RouterGroup) {
if unstackFile.BasePrefix(false) == stackPhoto.PhotoName { if unstackFile.BasePrefix(false) == stackPhoto.PhotoName {
if conf.ReadOnly() { if conf.ReadOnly() {
log.Errorf("photo: can't rename files in read only mode (unstack %s)", sanitize.Log(baseName)) log.Errorf("photo: cannot rename files in read only mode (unstack %s)", sanitize.Log(baseName))
AbortFeatureDisabled(c) AbortFeatureDisabled(c)
return return
} }
@@ -104,7 +104,7 @@ func PhotoUnstack(router *gin.RouterGroup) {
destName := fmt.Sprintf("%s.%s%s", unstackFile.AbsPrefix(false), unstackFile.Checksum(), unstackFile.Extension()) destName := fmt.Sprintf("%s.%s%s", unstackFile.AbsPrefix(false), unstackFile.Checksum(), unstackFile.Extension())
if err := unstackFile.Move(destName); err != nil { if err := unstackFile.Move(destName); err != nil {
log.Errorf("photo: can't rename %s to %s (unstack)", sanitize.Log(unstackFile.BaseName()), sanitize.Log(filepath.Base(destName))) log.Errorf("photo: cannot rename %s to %s (unstack)", sanitize.Log(unstackFile.BaseName()), sanitize.Log(filepath.Base(destName)))
AbortUnexpected(c) AbortUnexpected(c)
return return
} }

View File

@@ -21,7 +21,7 @@ func TestPhotoUnstack(t *testing.T) {
app, router, _ := NewApiTest() app, router, _ := NewApiTest()
PhotoUnstack(router) PhotoUnstack(router)
r := PerformRequest(app, "POST", "/api/v1/photos/pt9jtdre2lvl0yh7/files/ft2es49whhbnlqdn/unstack") r := PerformRequest(app, "POST", "/api/v1/photos/pt9jtdre2lvl0yh7/files/ft2es49whhbnlqdn/unstack")
// TODO: Have a real file in place for testing the success case. This file does not exist, so it can't be unstacked. // TODO: Have a real file in place for testing the success case. This file does not exist, so it cannot be unstacked.
assert.Equal(t, http.StatusNotFound, r.Code) assert.Equal(t, http.StatusNotFound, r.Code)
// t.Logf("RESP: %s", r.Body.String()) // t.Logf("RESP: %s", r.Body.String())
}) })

View File

@@ -20,35 +20,41 @@ import (
"github.com/photoprism/photoprism/pkg/sanitize" "github.com/photoprism/photoprism/pkg/sanitize"
) )
const backupDescription = "A user-defined SQL dump FILENAME or - for stdout can be passed as the first argument. " +
"The -i parameter can be omitted in this case.\n" +
" Make sure to run the command with exec -T when using Docker to prevent log messages from being sent to stdout.\n" +
" The index backup and album exports paths are automatically detected if not specified explicitly."
// BackupCommand configures the backup cli command. // BackupCommand configures the backup cli command.
var BackupCommand = cli.Command{ var BackupCommand = cli.Command{
Name: "backup", Name: "backup",
Usage: "Creates index database dumps and optional YAML album backups", Description: backupDescription,
UsageText: `A custom database SQL dump FILENAME may be passed as first argument. Use - for stdout. The backup paths will be detected automatically if not provided.`, Usage: "Creates an index SQL dump and optionally album YAML exports organized by type",
Flags: backupFlags, ArgsUsage: "[FILENAME | -]",
Action: backupAction, Flags: backupFlags,
Action: backupAction,
} }
var backupFlags = []cli.Flag{ var backupFlags = []cli.Flag{
cli.BoolFlag{ cli.BoolFlag{
Name: "force, f", Name: "force, f",
Usage: "replace existing backup files", Usage: "replace existing files",
}, },
cli.BoolFlag{ cli.BoolFlag{
Name: "albums, a", Name: "albums, a",
Usage: "create YAML album backups", Usage: "create album YAML exports organized by type",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "albums-path", Name: "albums-path",
Usage: "custom albums backup `PATH`", Usage: "custom album exports `PATH`",
}, },
cli.BoolFlag{ cli.BoolFlag{
Name: "index, i", Name: "index, i",
Usage: "create index database SQL dump", Usage: "create index SQL dump",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "index-path", Name: "index-path",
Usage: "custom database backup `PATH`", Usage: "custom index backup `PATH`",
}, },
} }
@@ -64,13 +70,7 @@ func backupAction(ctx *cli.Context) error {
backupAlbums := ctx.Bool("albums") || albumsPath != "" backupAlbums := ctx.Bool("albums") || albumsPath != ""
if !backupIndex && !backupAlbums { if !backupIndex && !backupAlbums {
fmt.Printf("OPTIONS:\n") return cli.ShowSubcommandHelp(ctx)
for _, flag := range backupFlags {
fmt.Printf(" %s\n", flag.String())
}
return nil
} }
start := time.Now() start := time.Now()
@@ -101,9 +101,9 @@ func backupAction(ctx *cli.Context) error {
if indexFileName != "-" { if indexFileName != "-" {
if _, err := os.Stat(indexFileName); err == nil && !ctx.Bool("force") { if _, err := os.Stat(indexFileName); err == nil && !ctx.Bool("force") {
return fmt.Errorf("backup file already exists: %s", indexFileName) return fmt.Errorf("SQL dump already exists: %s", indexFileName)
} else if err == nil { } else if err == nil {
log.Warnf("replacing existing backup file") log.Warnf("replacing existing SQL dump")
} }
// Create backup directory if not exists. // Create backup directory if not exists.
@@ -113,7 +113,7 @@ func backupAction(ctx *cli.Context) error {
} }
} }
log.Infof("backing up database to %s", sanitize.Log(indexFileName)) log.Infof("writing SQL dump to %s", sanitize.Log(indexFileName))
} }
var cmd *exec.Cmd var cmd *exec.Cmd
@@ -169,18 +169,18 @@ func backupAction(ctx *cli.Context) error {
if !fs.PathWritable(albumsPath) { if !fs.PathWritable(albumsPath) {
if albumsPath != "" { if albumsPath != "" {
log.Warnf("albums backup path not writable, using default") log.Warnf("album exports path not writable, using default")
} }
albumsPath = conf.AlbumsPath() albumsPath = conf.AlbumsPath()
} }
log.Infof("backing up albums to %s", sanitize.Log(albumsPath)) log.Infof("exporting albums to %s", sanitize.Log(albumsPath))
if count, err := photoprism.BackupAlbums(albumsPath, true); err != nil { if count, err := photoprism.BackupAlbums(albumsPath, true); err != nil {
return err return err
} else { } else {
log.Infof("created %s", english.Plural(count, "YAML album backup", "YAML album backups")) log.Infof("created %s", english.Plural(count, "YAML album export", "YAML album exports"))
} }
} }

View File

@@ -17,7 +17,7 @@ import (
var ConvertCommand = cli.Command{ var ConvertCommand = cli.Command{
Name: "convert", Name: "convert",
Usage: "Converts files in other formats to JPEG and AVC", Usage: "Converts files in other formats to JPEG and AVC",
ArgsUsage: "[originals subfolder]", ArgsUsage: "[ORIGINALS SUB-FOLDER]",
Action: convertAction, Action: convertAction,
} }

View File

@@ -19,7 +19,7 @@ var CopyCommand = cli.Command{
Name: "cp", Name: "cp",
Aliases: []string{"copy"}, Aliases: []string{"copy"},
Usage: "Copies media files to originals", Usage: "Copies media files to originals",
ArgsUsage: "[path]", ArgsUsage: "[PATH]",
Action: copyAction, Action: copyAction,
} }

View File

@@ -53,7 +53,7 @@ var FacesCommand = cli.Command{
{ {
Name: "index", Name: "index",
Usage: "Searches originals for faces", Usage: "Searches originals for faces",
ArgsUsage: "[originals subfolder]", ArgsUsage: "[ORIGINALS SUB-FOLDER]",
Action: facesIndexAction, Action: facesIndexAction,
}, },
{ {

View File

@@ -19,7 +19,7 @@ var ImportCommand = cli.Command{
Name: "mv", Name: "mv",
Aliases: []string{"import"}, Aliases: []string{"import"},
Usage: "Moves media files to originals", Usage: "Moves media files to originals",
ArgsUsage: "[path]", ArgsUsage: "[PATH]",
Action: importAction, Action: importAction,
} }

View File

@@ -21,7 +21,7 @@ import (
var IndexCommand = cli.Command{ var IndexCommand = cli.Command{
Name: "index", Name: "index",
Usage: "Indexes original media files", Usage: "Indexes original media files",
ArgsUsage: "[originals subfolder]", ArgsUsage: "[ORIGINALS SUB-FOLDER]",
Flags: indexFlags, Flags: indexFlags,
Action: indexAction, Action: indexAction,
} }

View File

@@ -22,13 +22,18 @@ import (
"github.com/photoprism/photoprism/pkg/sanitize" "github.com/photoprism/photoprism/pkg/sanitize"
) )
const restoreDescription = "A user-defined SQL dump FILENAME can be passed as the first argument. " +
"The -i parameter can be omitted in this case.\n" +
" The index backup and album exports paths are automatically detected if not specified explicitly."
// RestoreCommand configures the backup cli command. // RestoreCommand configures the backup cli command.
var RestoreCommand = cli.Command{ var RestoreCommand = cli.Command{
Name: "restore", Name: "restore",
Usage: "Restores the index from database dumps and YAML album backups", Description: restoreDescription,
UsageText: `A custom database SQL dump FILENAME may be passed as first argument. The backup paths will be detected automatically if not provided.`, Usage: "Restores the index from an SQL dump and optionally albums from YAML exports",
Flags: restoreFlags, ArgsUsage: "[FILENAME]",
Action: restoreAction, Flags: restoreFlags,
Action: restoreAction,
} }
var restoreFlags = []cli.Flag{ var restoreFlags = []cli.Flag{
@@ -38,19 +43,19 @@ var restoreFlags = []cli.Flag{
}, },
cli.BoolFlag{ cli.BoolFlag{
Name: "albums, a", Name: "albums, a",
Usage: "restore albums from YAML backups", Usage: "restore albums from YAML exports",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "albums-path", Name: "albums-path",
Usage: "custom albums backup `PATH`", Usage: "custom album exports `PATH`",
}, },
cli.BoolFlag{ cli.BoolFlag{
Name: "index, i", Name: "index, i",
Usage: "restore index from database SQL dump", Usage: "restore index from SQL dump",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "index-path", Name: "index-path",
Usage: "custom database backup `PATH`", Usage: "custom index backup `PATH`",
}, },
} }
@@ -65,11 +70,7 @@ func restoreAction(ctx *cli.Context) error {
restoreAlbums := ctx.Bool("albums") || albumsPath != "" restoreAlbums := ctx.Bool("albums") || albumsPath != ""
if !restoreIndex && !restoreAlbums { if !restoreIndex && !restoreAlbums {
for _, flag := range restoreFlags { return cli.ShowSubcommandHelp(ctx)
fmt.Println(flag.String())
}
return nil
} }
start := time.Now() start := time.Now()
@@ -97,7 +98,7 @@ func restoreAction(ctx *cli.Context) error {
} }
if len(matches) == 0 { if len(matches) == 0 {
log.Errorf("no backup files found in %s", indexPath) log.Errorf("no SQL dumps found in %s", indexPath)
return nil return nil
} }
@@ -105,7 +106,7 @@ func restoreAction(ctx *cli.Context) error {
} }
if !fs.FileExists(indexFileName) { if !fs.FileExists(indexFileName) {
log.Errorf("backup file not found: %s", indexFileName) log.Errorf("SQL dump not found: %s", indexFileName)
return nil return nil
} }
@@ -199,21 +200,21 @@ func restoreAction(ctx *cli.Context) error {
} }
if !fs.PathExists(albumsPath) { if !fs.PathExists(albumsPath) {
log.Warnf("albums backup path %s not found", sanitize.Log(albumsPath)) log.Warnf("album exports path %s not found", sanitize.Log(albumsPath))
} else { } else {
log.Infof("restoring albums from %s", sanitize.Log(albumsPath)) log.Infof("restoring albums from %s", sanitize.Log(albumsPath))
if count, err := photoprism.RestoreAlbums(albumsPath, true); err != nil { if count, err := photoprism.RestoreAlbums(albumsPath, true); err != nil {
return err return err
} else { } else {
log.Infof("restored %s from YAML backups", english.Plural(count, "album", "albums")) log.Infof("restored %s from YAML exports", english.Plural(count, "album", "albums"))
} }
} }
} }
elapsed := time.Since(start) elapsed := time.Since(start)
log.Infof("backup restored in %s", elapsed) log.Infof("restored in %s", elapsed)
conf.Shutdown() conf.Shutdown()

View File

@@ -35,7 +35,7 @@ func statusAction(ctx *cli.Context) error {
var status string var status string
if resp, err := client.Do(req); err != nil { if resp, err := client.Do(req); err != nil {
return fmt.Errorf("can't connect to %s:%d", conf.HttpHost(), conf.HttpPort()) return fmt.Errorf("cannot connect to %s:%d", conf.HttpHost(), conf.HttpPort())
} else if resp.StatusCode != 200 { } else if resp.StatusCode != 200 {
return fmt.Errorf("server running at %s:%d, bad status %d\n", conf.HttpHost(), conf.HttpPort(), resp.StatusCode) return fmt.Errorf("server running at %s:%d, bad status %d\n", conf.HttpHost(), conf.HttpPort(), resp.StatusCode)
} else if body, err := io.ReadAll(resp.Body); err != nil { } else if body, err := io.ReadAll(resp.Body); err != nil {

View File

@@ -73,7 +73,7 @@ var UsersCommand = cli.Command{
Name: "delete", Name: "delete",
Usage: "Removes an existing user", Usage: "Removes an existing user",
Action: usersDeleteAction, Action: usersDeleteAction,
ArgsUsage: "[username]", ArgsUsage: "[USERNAME]",
}, },
}, },
} }

View File

@@ -69,7 +69,7 @@ type ClientConfig struct {
// Years represents a list of years. // Years represents a list of years.
type Years []int type Years []int
// ClientDisable represents disabled client features a user can't turn back on. // ClientDisable represents disabled client features a user cannot turn back on.
type ClientDisable struct { type ClientDisable struct {
Backups bool `json:"backups"` Backups bool `json:"backups"`
WebDAV bool `json:"webdav"` WebDAV bool `json:"webdav"`

View File

@@ -35,7 +35,7 @@ func (c *Config) CreateDirectories() error {
if fs.FileExists(path) { if fs.FileExists(path) {
result = fmt.Errorf("%s is a file, not a folder: please check your configuration", sanitize.Log(path)) result = fmt.Errorf("%s is a file, not a folder: please check your configuration", sanitize.Log(path))
} else { } else {
result = fmt.Errorf("can't create %s: please check configuration and permissions", sanitize.Log(path)) result = fmt.Errorf("cannot create %s: please check configuration and permissions", sanitize.Log(path))
} }
log.Debug(err) log.Debug(err)

View File

@@ -253,7 +253,7 @@ func (c *Options) SetContext(ctx *cli.Context) error {
fieldValue.SetBool(f) fieldValue.SetBool(f)
} }
default: default:
log.Warnf("can't assign value of type %s from cli flag %s", t, tagValue) log.Warnf("cannot assign value of type %s from cli flag %s", t, tagValue)
} }
} }
} }

View File

@@ -317,7 +317,7 @@ func (m *File) AllFilesMissing() bool {
// Create inserts a new row to the database. // Create inserts a new row to the database.
func (m *File) Create() error { func (m *File) Create() error {
if m.PhotoID == 0 { if m.PhotoID == 0 {
return fmt.Errorf("file: can't create file with empty photo id") return fmt.Errorf("file: cannot create file with empty photo id")
} }
if err := UnscopedDb().Create(m).Error; err != nil { if err := UnscopedDb().Create(m).Error; err != nil {
@@ -345,7 +345,7 @@ func (m *File) ResolvePrimary() error {
// Save stores the file in the database. // Save stores the file in the database.
func (m *File) Save() error { func (m *File) Save() error {
if m.PhotoID == 0 { if m.PhotoID == 0 {
return fmt.Errorf("file %s: can't save file with empty photo id", m.FileUID) return fmt.Errorf("file %s: cannot save file with empty photo id", m.FileUID)
} }
if err := UnscopedDb().Save(m).Error; err != nil { if err := UnscopedDb().Save(m).Error; err != nil {

View File

@@ -189,7 +189,7 @@ func TestFile_Save(t *testing.T) {
t.Fatalf("file id should be 0: %d", file.ID) t.Fatalf("file id should be 0: %d", file.ID)
} }
assert.Equal(t, "file 123: can't save file with empty photo id", err.Error()) assert.Equal(t, "file 123: cannot save file with empty photo id", err.Error())
}) })
t.Run("success", func(t *testing.T) { t.Run("success", func(t *testing.T) {
photo := &Photo{TakenAtLocal: time.Date(2019, 01, 15, 0, 0, 0, 0, time.UTC), PhotoTitle: "Berlin / Morning Mood"} photo := &Photo{TakenAtLocal: time.Date(2019, 01, 15, 0, 0, 0, 0, time.UTC), PhotoTitle: "Berlin / Morning Mood"}

View File

@@ -486,7 +486,7 @@ func (m *Marker) ClearSubject(src string) error {
// Face returns a matching face entity if possible. // Face returns a matching face entity if possible.
func (m *Marker) Face() (f *Face) { func (m *Marker) Face() (f *Face) {
if m.MarkerUID == "" { if m.MarkerUID == "" {
log.Debugf("markers: can't find face when uid is empty") log.Debugf("markers: cannot find face when uid is empty")
return nil return nil
} }

View File

@@ -17,7 +17,7 @@ type Password struct {
// NewPassword creates a new password instance. // NewPassword creates a new password instance.
func NewPassword(uid, password string) Password { func NewPassword(uid, password string) Password {
if uid == "" { if uid == "" {
panic("auth: can't set password without uid") panic("auth: cannot set password without uid")
} }
m := Password{UID: uid} m := Password{UID: uid}

View File

@@ -146,7 +146,7 @@ func SavePhotoForm(model Photo, form form.Photo) error {
} }
if !model.HasID() { if !model.HasID() {
return errors.New("can't save form when photo id is missing") return errors.New("cannot save form when photo id is missing")
} }
// Update time fields. // Update time fields.
@@ -300,7 +300,7 @@ func (m *Photo) Find() error {
// SaveLabels updates the photo after labels have changed. // SaveLabels updates the photo after labels have changed.
func (m *Photo) SaveLabels() error { func (m *Photo) SaveLabels() error {
if !m.HasID() { if !m.HasID() {
return errors.New("photo: can't save to database, id is empty") return errors.New("photo: cannot save to database, id is empty")
} }
labels := m.ClassifyLabels() labels := m.ClassifyLabels()

View File

@@ -11,7 +11,7 @@ import (
// Optimize photo data, improve if possible. // Optimize photo data, improve if possible.
func (m *Photo) Optimize(mergeMeta, mergeUuid, estimatePlace, force bool) (updated bool, merged Photos, err error) { func (m *Photo) Optimize(mergeMeta, mergeUuid, estimatePlace, force bool) (updated bool, merged Photos, err error) {
if !m.HasID() { if !m.HasID() {
return false, merged, errors.New("photo: can't maintain, id is empty") return false, merged, errors.New("photo: cannot maintain, id is empty")
} }
current := *m current := *m

View File

@@ -109,7 +109,7 @@ func TestPhoto_SaveLabels(t *testing.T) {
err := photo.SaveLabels() err := photo.SaveLabels()
assert.EqualError(t, err, "photo: can't save to database, id is empty") assert.EqualError(t, err, "photo: cannot save to database, id is empty")
}) })
t.Run("existing photo", func(t *testing.T) { t.Run("existing photo", func(t *testing.T) {

View File

@@ -171,7 +171,7 @@ func (m *Photo) UpdateTitle(labels classify.Labels) error {
// UpdateAndSaveTitle updates the photo title and saves it. // UpdateAndSaveTitle updates the photo title and saves it.
func (m *Photo) UpdateAndSaveTitle() error { func (m *Photo) UpdateAndSaveTitle() error {
if !m.HasID() { if !m.HasID() {
return fmt.Errorf("can't save photo whithout id") return fmt.Errorf("cannot save photo whithout id")
} }
m.PhotoFaces = m.FaceCount() m.PhotoFaces = m.FaceCount()

View File

@@ -201,7 +201,7 @@ func FindUserByUID(uid string) *User {
// Delete marks the entity as deleted. // Delete marks the entity as deleted.
func (m *User) Delete() error { func (m *User) Delete() error {
if m.ID <= 1 { if m.ID <= 1 {
return fmt.Errorf("can't delete system user") return fmt.Errorf("cannot delete system user")
} }
return Db().Delete(m).Error return Db().Delete(m).Error

View File

@@ -335,7 +335,7 @@ func TestUser_InitPassword(t *testing.T) {
p := User{UserUID: "u000000000000010", UserName: "Hans", FullName: ""} p := User{UserUID: "u000000000000010", UserName: "Hans", FullName: ""}
if err := p.Save(); err != nil { if err := p.Save(); err != nil {
t.Logf("can't user %s: ", err) t.Logf("cannot user %s: ", err)
} }
if err := p.SetPassword("hutfdt"); err != nil { if err := p.SetPassword("hutfdt"); err != nil {

View File

@@ -75,7 +75,7 @@ func (data Data) AspectRatio() float32 {
return aspectRatio return aspectRatio
} }
// Portrait returns true if it's a portrait picture or video based on width and height. // Portrait returns true if it is a portrait picture or video based on width and height.
func (data Data) Portrait() bool { func (data Data) Portrait() bool {
return data.ActualWidth() < data.ActualHeight() return data.ActualWidth() < data.ActualHeight()
} }

View File

@@ -39,7 +39,7 @@ func (data *Data) JSON(jsonName, originalName string) (err error) {
jsonData, err := os.ReadFile(jsonName) jsonData, err := os.ReadFile(jsonName)
if err != nil { if err != nil {
return fmt.Errorf("can't read json file %s", quotedName) return fmt.Errorf("cannot read json file %s", quotedName)
} }
if bytes.Contains(jsonData, []byte("ExifToolVersion")) { if bytes.Contains(jsonData, []byte("ExifToolVersion")) {

View File

@@ -130,7 +130,7 @@ func (data *Data) Exiftool(jsonData []byte, originalName string) (err error) {
fieldValue.SetBool(jsonValue.Bool()) fieldValue.SetBool(jsonValue.Bool())
default: default:
log.Warnf("metadata: can't assign value of type %s to %s (exiftool)", t, tagValue) log.Warnf("metadata: cannot assign value of type %s to %s (exiftool)", t, tagValue)
} }
} }
} }

View File

@@ -26,7 +26,7 @@ func (data *Data) XMP(fileName string) (err error) {
doc := XmpDocument{} doc := XmpDocument{}
if err := doc.Load(fileName); err != nil { if err := doc.Load(fileName); err != nil {
return fmt.Errorf("metadata: can't read %s (xmp)", sanitize.Log(filepath.Base(fileName))) return fmt.Errorf("metadata: cannot read %s (xmp)", sanitize.Log(filepath.Base(fileName)))
} }
if doc.Title() != "" { if doc.Title() != "" {

View File

@@ -203,7 +203,7 @@ func TestThumb_Create(t *testing.T) {
img, err := imaging.Open(conf.ExamplesPath()+"/elephants.jpg", imaging.AutoOrientation(true)) img, err := imaging.Open(conf.ExamplesPath()+"/elephants.jpg", imaging.AutoOrientation(true))
if err != nil { if err != nil {
t.Errorf("can't open original: %s", err) t.Errorf("cannot open original: %s", err)
} }
res, err := thumb.Create(img, expectedFilename, 150, 150, thumb.ResampleFit, thumb.ResampleNearestNeighbor) res, err := thumb.Create(img, expectedFilename, 150, 150, thumb.ResampleFit, thumb.ResampleNearestNeighbor)
@@ -230,7 +230,7 @@ func TestThumb_Create(t *testing.T) {
img, err := imaging.Open(conf.ExamplesPath()+"/elephants.jpg", imaging.AutoOrientation(true)) img, err := imaging.Open(conf.ExamplesPath()+"/elephants.jpg", imaging.AutoOrientation(true))
if err != nil { if err != nil {
t.Errorf("can't open original: %s", err) t.Errorf("cannot open original: %s", err)
} }
res, err := thumb.Create(img, expectedFilename, -1, 150, thumb.ResampleFit, thumb.ResampleNearestNeighbor) res, err := thumb.Create(img, expectedFilename, -1, 150, thumb.ResampleFit, thumb.ResampleNearestNeighbor)
@@ -256,7 +256,7 @@ func TestThumb_Create(t *testing.T) {
img, err := imaging.Open(conf.ExamplesPath()+"/elephants.jpg", imaging.AutoOrientation(true)) img, err := imaging.Open(conf.ExamplesPath()+"/elephants.jpg", imaging.AutoOrientation(true))
if err != nil { if err != nil {
t.Errorf("can't open original: %s", err) t.Errorf("cannot open original: %s", err)
} }
res, err := thumb.Create(img, expectedFilename, 150, -1, thumb.ResampleFit, thumb.ResampleNearestNeighbor) res, err := thumb.Create(img, expectedFilename, 150, -1, thumb.ResampleFit, thumb.ResampleNearestNeighbor)

View File

@@ -137,7 +137,7 @@ func MergeFaces(merge entity.Faces) (merged *entity.Face, err error) {
for i := 1; i < len(merge); i++ { for i := 1; i < len(merge); i++ {
if merge[i].SubjUID != subjUID { if merge[i].SubjUID != subjUID {
return merged, fmt.Errorf("faces: can't merge clusters with conflicting subjects %s <> %s", return merged, fmt.Errorf("faces: cannot merge clusters with conflicting subjects %s <> %s",
sanitize.Log(subjUID), sanitize.Log(merge[i].SubjUID)) sanitize.Log(subjUID), sanitize.Log(merge[i].SubjUID))
} }
} }

View File

@@ -202,7 +202,7 @@ func TestMergeFaces(t *testing.T) {
result, err := MergeFaces(faces) result, err := MergeFaces(faces)
assert.EqualError(t, err, "faces: can't merge clusters with conflicting subjects jqynvsf28rhn6b0c <> jqynvt925h8c1asv") assert.EqualError(t, err, "faces: cannot merge clusters with conflicting subjects jqynvsf28rhn6b0c <> jqynvt925h8c1asv")
assert.Nil(t, result) assert.Nil(t, result)
}) })
t.Run("OneSubject", func(t *testing.T) { t.Run("OneSubject", func(t *testing.T) {

View File

@@ -10,7 +10,7 @@ import (
// SetDownloadFileID updates the local file id for remote downloads. // SetDownloadFileID updates the local file id for remote downloads.
func SetDownloadFileID(filename string, fileId uint) error { func SetDownloadFileID(filename string, fileId uint) error {
if len(filename) == 0 { if len(filename) == 0 {
return errors.New("sync: can't update, filename empty") return errors.New("sync: cannot update, filename empty")
} }
// TODO: Might break on Windows // TODO: Might break on Windows

View File

@@ -18,6 +18,6 @@ func TestSetDownloadFileID(t *testing.T) {
if err == nil { if err == nil {
t.Fatal() t.Fatal()
} }
assert.Equal(t, "sync: can't update, filename empty", err.Error()) assert.Equal(t, "sync: cannot update, filename empty", err.Error())
}) })
} }

View File

@@ -95,7 +95,7 @@ func FileByHash(fileHash string) (file entity.File, err error) {
// RenameFile renames an indexed file. // RenameFile renames an indexed file.
func RenameFile(srcRoot, srcName, destRoot, destName string) error { func RenameFile(srcRoot, srcName, destRoot, destName string) error {
if srcRoot == "" || srcName == "" || destRoot == "" || destName == "" { if srcRoot == "" || srcName == "" || destRoot == "" || destName == "" {
return fmt.Errorf("can't rename %s/%s to %s/%s", srcRoot, srcName, destRoot, destName) return fmt.Errorf("cannot rename %s/%s to %s/%s", srcRoot, srcName, destRoot, destName)
} }
return Db().Exec("UPDATE files SET file_root = ?, file_name = ?, file_missing = 0, deleted_at = NULL WHERE file_root = ? AND file_name = ?", destRoot, destName, srcRoot, srcName).Error return Db().Exec("UPDATE files SET file_root = ?, file_name = ?, file_missing = 0, deleted_at = NULL WHERE file_root = ? AND file_name = ?", destRoot, destName, srcRoot, srcName).Error
@@ -114,7 +114,7 @@ func SetPhotoPrimary(photoUID, fileUID string) error {
} else if err := Db().Model(entity.File{}).Where("photo_uid = ? AND file_missing = 0 AND file_type = 'jpg'", photoUID).Order("file_width DESC").Limit(1).Pluck("file_uid", &files).Error; err != nil { } else if err := Db().Model(entity.File{}).Where("photo_uid = ? AND file_missing = 0 AND file_type = 'jpg'", photoUID).Order("file_width DESC").Limit(1).Pluck("file_uid", &files).Error; err != nil {
return err return err
} else if len(files) == 0 { } else if len(files) == 0 {
return fmt.Errorf("can't find primary file for %s", photoUID) return fmt.Errorf("cannot find primary file for %s", photoUID)
} else { } else {
fileUID = files[0] fileUID = files[0]
} }

View File

@@ -200,7 +200,7 @@ func TestSetPhotoPrimary(t *testing.T) {
if err == nil { if err == nil {
t.Fatal("error expected") t.Fatal("error expected")
} }
assert.Contains(t, err.Error(), "can't find primary file") assert.Contains(t, err.Error(), "cannot find primary file")
}) })
} }

View File

@@ -166,7 +166,7 @@ func (c Client) Download(from, to string, force bool) (err error) {
if err != nil { if err != nil {
// Create directory // Create directory
if err := os.MkdirAll(dir, os.ModePerm); err != nil { if err := os.MkdirAll(dir, os.ModePerm); err != nil {
return fmt.Errorf("webdav: can't create %s (%s)", dir, err) return fmt.Errorf("webdav: cannot create %s (%s)", dir, err)
} }
} else if !dirInfo.IsDir() { } else if !dirInfo.IsDir() {
return fmt.Errorf("webdav: %s is not a folder", dir) return fmt.Errorf("webdav: %s is not a folder", dir)

View File

@@ -45,7 +45,7 @@ func stack(skip int) []byte {
if !ok { if !ok {
break break
} }
// Print this much at least. If we can't find the source, it won't show. // Print this much at least. If we cannot find the source, it won't show.
fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc) fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
if file != lastFile { if file != lastFile {
data, err := os.ReadFile(file) data, err := os.ReadFile(file)

View File

@@ -10,7 +10,7 @@ import (
) )
func registerRoutes(router *gin.Engine, conf *config.Config) { func registerRoutes(router *gin.Engine, conf *config.Config) {
// Enables automatic redirection if the current route can't be matched but a // Enables automatic redirection if the current route cannot be matched but a
// handler for the path with (without) the trailing slash exists. // handler for the path with (without) the trailing slash exists.
router.RedirectTrailingSlash = true router.RedirectTrailingSlash = true

View File

@@ -12,7 +12,7 @@ func Jpeg(srcFilename, jpgFilename string, orientation int) (img image.Image, er
img, err = imaging.Open(srcFilename) img, err = imaging.Open(srcFilename)
if err != nil { if err != nil {
log.Errorf("resample: can't open %s", sanitize.Log(filepath.Base(srcFilename))) log.Errorf("resample: cannot open %s", sanitize.Log(filepath.Base(srcFilename)))
return img, err return img, err
} }