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" | ||||
| 	"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 { | ||||
|  |  | |||
|  | @ -12,10 +12,17 @@ | |||
|       <p class="break-word" v-if="selected.length < 2"> | ||||
|         <strong>{{ $t("prompts.displayName") }}</strong> {{ name }} | ||||
|       </p> | ||||
| 
 | ||||
|       <p v-if="!dir || selected.length > 1"> | ||||
|         <strong>{{ $t("prompts.size") }}:</strong> | ||||
|         <span id="content_length"></span> {{ humanSize }} | ||||
|       </p> | ||||
| 
 | ||||
|       <div v-if="resolution"> | ||||
|         <strong>{{ $t("prompts.resolution") }}:</strong> | ||||
|         {{ resolution.width }} x {{ resolution.height }} | ||||
|       </div> | ||||
| 
 | ||||
|       <p v-if="selected.length < 2" :title="modTime"> | ||||
|         <strong>{{ $t("prompts.lastModified") }}:</strong> {{ humanTime }} | ||||
|       </p> | ||||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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", | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 M A E R Y O
						M A E R Y O