diff --git a/files/file.go b/files/file.go index e0fdb162..f181f185 100644 --- a/files/file.go +++ b/files/file.go @@ -7,6 +7,7 @@ import ( "crypto/sha512" "encoding/hex" "hash" + "image" "io" "log" "mime" @@ -44,6 +45,7 @@ type FileInfo struct { Checksums map[string]string `json:"checksums,omitempty"` Token string `json:"token,omitempty"` currentDir []os.FileInfo `json:"-"` + Resolution *ImageResolution `json:"resolution,omitempty"` } // FileOptions are the options when getting a file info. @@ -58,6 +60,11 @@ type FileOptions struct { Content bool } +type ImageResolution struct { + Width int `json:"width"` + Height int `json:"height"` +} + // NewFileInfo creates a File object from a path and a given user. This File // object will be automatically filled depending on if it is a directory // or a file. If it's a video file, it will also detect any subtitles. @@ -236,6 +243,12 @@ func (i *FileInfo) detectType(modify, saveContent, readHeader bool) error { return nil case strings.HasPrefix(mimetype, "image"): i.Type = "image" + resolution, err := calculateImageResolution(i.Fs, i.Path) + if err != nil { + log.Printf("Error calculating image resolution: %v", err) + } else { + i.Resolution = resolution + } return nil case strings.HasSuffix(mimetype, "pdf"): i.Type = "pdf" @@ -264,6 +277,28 @@ func (i *FileInfo) detectType(modify, saveContent, readHeader bool) error { return nil } +func calculateImageResolution(fs afero.Fs, filePath string) (*ImageResolution, error) { + file, err := fs.Open(filePath) + if err != nil { + return nil, err + } + defer func() { + if cErr := file.Close(); cErr != nil { + log.Printf("Failed to close file: %v", cErr) + } + }() + + config, _, err := image.DecodeConfig(file) + if err != nil { + return nil, err + } + + return &ImageResolution{ + Width: config.Width, + Height: config.Height, + }, nil +} + func (i *FileInfo) readFirstBytes() []byte { reader, err := i.Fs.Open(i.Path) if err != nil { @@ -361,6 +396,15 @@ func (i *FileInfo) readListing(checker rules.Checker, readHeader bool) error { currentDir: dir, } + if !file.IsDir && strings.HasPrefix(mime.TypeByExtension(file.Extension), "image/") { + resolution, err := calculateImageResolution(file.Fs, file.Path) + if err != nil { + log.Printf("Error calculating resolution for image %s: %v", file.Path, err) + } else { + file.Resolution = resolution + } + } + if file.IsDir { listing.NumDirs++ } else { diff --git a/frontend/src/components/prompts/Info.vue b/frontend/src/components/prompts/Info.vue index 78227821..03bbfb58 100644 --- a/frontend/src/components/prompts/Info.vue +++ b/frontend/src/components/prompts/Info.vue @@ -12,10 +12,17 @@

{{ $t("prompts.displayName") }} {{ name }}

+

{{ $t("prompts.size") }}: {{ humanSize }}

+ +
+ {{ $t("prompts.resolution") }}: + {{ resolution.width }} x {{ resolution.height }} +
+

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

@@ -126,6 +133,18 @@ export default { : this.req.items[this.selected[0]].isDir) ); }, + resolution: function() { + if (this.selectedCount === 1) { + const selectedItem = this.req.items[this.selected[0]]; + if (selectedItem && selectedItem.type === 'image') { + return selectedItem.resolution; + } + } + else if (this.req && this.req.type === 'image') { + return this.req.resolution; + } + return null; + }, }, methods: { checksum: async function (event, algo) { diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json index 9889f880..8e392694 100644 --- a/frontend/src/i18n/en.json +++ b/frontend/src/i18n/en.json @@ -161,7 +161,8 @@ "upload": "Upload", "uploadFiles": "Uploading {files} files...", "uploadMessage": "Select an option to upload.", - "optionalPassword": "Optional password" + "optionalPassword": "Optional password", + "resolution": "Resolution" }, "search": { "images": "Images",