feat: display image resolutions in file details (#2830)
--------- Co-authored-by: MAERYO <maeryo@hanwha.com>pull/2823/head^2
parent
4dbc802972
commit
a09dfa8d9f
|
@ -7,6 +7,7 @@ import (
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"hash"
|
"hash"
|
||||||
|
"image"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"mime"
|
"mime"
|
||||||
|
@ -44,6 +45,7 @@ type FileInfo struct {
|
||||||
Checksums map[string]string `json:"checksums,omitempty"`
|
Checksums map[string]string `json:"checksums,omitempty"`
|
||||||
Token string `json:"token,omitempty"`
|
Token string `json:"token,omitempty"`
|
||||||
currentDir []os.FileInfo `json:"-"`
|
currentDir []os.FileInfo `json:"-"`
|
||||||
|
Resolution *ImageResolution `json:"resolution,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileOptions are the options when getting a file info.
|
// FileOptions are the options when getting a file info.
|
||||||
|
@ -58,6 +60,11 @@ type FileOptions struct {
|
||||||
Content bool
|
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
|
// 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
|
// 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.
|
// 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
|
return nil
|
||||||
case strings.HasPrefix(mimetype, "image"):
|
case strings.HasPrefix(mimetype, "image"):
|
||||||
i.Type = "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
|
return nil
|
||||||
case strings.HasSuffix(mimetype, "pdf"):
|
case strings.HasSuffix(mimetype, "pdf"):
|
||||||
i.Type = "pdf"
|
i.Type = "pdf"
|
||||||
|
@ -264,6 +277,28 @@ func (i *FileInfo) detectType(modify, saveContent, readHeader bool) error {
|
||||||
return nil
|
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 {
|
func (i *FileInfo) readFirstBytes() []byte {
|
||||||
reader, err := i.Fs.Open(i.Path)
|
reader, err := i.Fs.Open(i.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -361,6 +396,15 @@ func (i *FileInfo) readListing(checker rules.Checker, readHeader bool) error {
|
||||||
currentDir: dir,
|
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 {
|
if file.IsDir {
|
||||||
listing.NumDirs++
|
listing.NumDirs++
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -12,10 +12,17 @@
|
||||||
<p class="break-word" v-if="selected.length < 2">
|
<p class="break-word" v-if="selected.length < 2">
|
||||||
<strong>{{ $t("prompts.displayName") }}</strong> {{ name }}
|
<strong>{{ $t("prompts.displayName") }}</strong> {{ name }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p v-if="!dir || selected.length > 1">
|
<p v-if="!dir || selected.length > 1">
|
||||||
<strong>{{ $t("prompts.size") }}:</strong>
|
<strong>{{ $t("prompts.size") }}:</strong>
|
||||||
<span id="content_length"></span> {{ humanSize }}
|
<span id="content_length"></span> {{ humanSize }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<div v-if="resolution">
|
||||||
|
<strong>{{ $t("prompts.resolution") }}:</strong>
|
||||||
|
{{ resolution.width }} x {{ resolution.height }}
|
||||||
|
</div>
|
||||||
|
|
||||||
<p v-if="selected.length < 2" :title="modTime">
|
<p v-if="selected.length < 2" :title="modTime">
|
||||||
<strong>{{ $t("prompts.lastModified") }}:</strong> {{ humanTime }}
|
<strong>{{ $t("prompts.lastModified") }}:</strong> {{ humanTime }}
|
||||||
</p>
|
</p>
|
||||||
|
@ -126,6 +133,18 @@ export default {
|
||||||
: this.req.items[this.selected[0]].isDir)
|
: 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: {
|
methods: {
|
||||||
checksum: async function (event, algo) {
|
checksum: async function (event, algo) {
|
||||||
|
|
|
@ -161,7 +161,8 @@
|
||||||
"upload": "Upload",
|
"upload": "Upload",
|
||||||
"uploadFiles": "Uploading {files} files...",
|
"uploadFiles": "Uploading {files} files...",
|
||||||
"uploadMessage": "Select an option to upload.",
|
"uploadMessage": "Select an option to upload.",
|
||||||
"optionalPassword": "Optional password"
|
"optionalPassword": "Optional password",
|
||||||
|
"resolution": "Resolution"
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"images": "Images",
|
"images": "Images",
|
||||||
|
|
Loading…
Reference in New Issue