From b864191a90a5b16e1f15d08c710813b826ec1de2 Mon Sep 17 00:00:00 2001 From: Jan Niggemann Date: Wed, 14 Jun 2023 17:55:08 +0200 Subject: [PATCH] Adds exif capability, closes #2024 --- files/file.go | 77 +++++++++++++++++++----- frontend/src/components/prompts/Info.vue | 28 +++++++++ frontend/src/i18n/de.json | 2 + frontend/src/i18n/en.json | 2 + frontend/src/i18n/fr.json | 2 + go.mod | 1 + go.sum | 2 + 7 files changed, 100 insertions(+), 14 deletions(-) diff --git a/files/file.go b/files/file.go index 1daf384b..3e3214dd 100644 --- a/files/file.go +++ b/files/file.go @@ -19,6 +19,8 @@ import ( "github.com/spf13/afero" + "github.com/rwcarlsen/goexif/exif" + "github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/rules" ) @@ -26,20 +28,22 @@ import ( // FileInfo describes a file. type FileInfo struct { *Listing - Fs afero.Fs `json:"-"` - Path string `json:"path"` - Name string `json:"name"` - Size int64 `json:"size"` - Extension string `json:"extension"` - ModTime time.Time `json:"modified"` - Mode os.FileMode `json:"mode"` - IsDir bool `json:"isDir"` - IsSymlink bool `json:"isSymlink"` - Type string `json:"type"` - Subtitles []string `json:"subtitles,omitempty"` - Content string `json:"content,omitempty"` - Checksums map[string]string `json:"checksums,omitempty"` - Token string `json:"token,omitempty"` + Fs afero.Fs `json:"-"` + Path string `json:"path"` + Name string `json:"name"` + Size int64 `json:"size"` + Extension string `json:"extension"` + ModTime time.Time `json:"modified"` + Mode os.FileMode `json:"mode"` + IsDir bool `json:"isDir"` + IsSymlink bool `json:"isSymlink"` + Type string `json:"type"` + ExifCam string `json:"exifcam,omitempty"` + ExifDate string `json:"exifdate,omitempty"` + Subtitles []string `json:"subtitles,omitempty"` + Content string `json:"content,omitempty"` + Checksums map[string]string `json:"checksums,omitempty"` + Token string `json:"token,omitempty"` } // FileOptions are the options when getting a file info. @@ -232,6 +236,7 @@ func (i *FileInfo) detectType(modify, saveContent, readHeader bool) error { return nil case strings.HasPrefix(mimetype, "image"): i.Type = "image" + i.decodeExif() return nil case strings.HasSuffix(mimetype, "pdf"): i.Type = "pdf" @@ -302,6 +307,50 @@ func (i *FileInfo) detectSubtitles() { } } +func (i *FileInfo) decodeExif() error { + if i.Type != "image" { + return nil + } + + f, err := i.Fs.Open(i.Path) + if err != nil { + return err + } + + x, err := exif.Decode(f) + if err != nil { + return err + } + + camModel, err := x.Get(exif.Model) + if err != nil { + return err + } + + MycamModel, err := camModel.StringVal() + if err != nil { + return err + } + + camMake, err := x.Get(exif.Make) + if err != nil { + return err + } + + MycamMake, err := camMake.StringVal() + if err != nil { + return err + } + + i.ExifCam = MycamMake + " " + MycamModel + + // Two convenience functions exist for date/time taken and GPS coords: + tm, _ := x.DateTime() + i.ExifDate = tm.String() + + return nil +} + func (i *FileInfo) readListing(checker rules.Checker, readHeader bool) error { afs := &afero.Afero{Fs: i.Fs} dir, err := afs.ReadDir(i.Path) diff --git a/frontend/src/components/prompts/Info.vue b/frontend/src/components/prompts/Info.vue index 5312ac76..422baade 100644 --- a/frontend/src/components/prompts/Info.vue +++ b/frontend/src/components/prompts/Info.vue @@ -19,6 +19,24 @@

{{ $t("prompts.lastModified") }}: {{ humanTime }}

+

+ {{ $t("prompts.exifcam") }}: {{ exifcam }} +

+

+ {{ $t("prompts.exifdate") }}: {{ exifdate }} +