aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2023-12-16 21:14:27 +0100
committerPřemysl Eric Janouch <p@janouch.name>2023-12-16 23:50:10 +0100
commite213f0792b0d52ee9e4768cd30cf7507d5d83f37 (patch)
tree9523cff275ef3cb6478d7f06ad1be8281d9ce62e
parent87eb786498e598a319dfa305cf43c4ab664d26a4 (diff)
downloadgallery-e213f0792b0d52ee9e4768cd30cf7507d5d83f37.tar.gz
gallery-e213f0792b0d52ee9e4768cd30cf7507d5d83f37.tar.xz
gallery-e213f0792b0d52ee9e4768cd30cf7507d5d83f37.zip
Store image dimensions in DB
-rw-r--r--initialize.sql10
-rw-r--r--main.go51
-rw-r--r--public/gallery.js8
-rw-r--r--public/style.css2
4 files changed, 61 insertions, 10 deletions
diff --git a/initialize.sql b/initialize.sql
index 31f4b13..f42e55b 100644
--- a/initialize.sql
+++ b/initialize.sql
@@ -1,8 +1,10 @@
CREATE TABLE IF NOT EXISTS image(
- sha1 TEXT NOT NULL, -- SHA-1 hash of file in lowercase hexadecimal
- thumbw INTEGER, -- cached thumbnail width, if known
- thumbh INTEGER, -- cached thumbnail height, if known
- dhash INTEGER, -- uint64 perceptual hash as a signed integer
+ sha1 TEXT NOT NULL, -- SHA-1 hash of file in lowercase hexadecimal
+ width INTEGER NOT NULL, -- cached media width
+ height INTEGER NOT NULL, -- cached media height
+ thumbw INTEGER, -- cached thumbnail width, if known
+ thumbh INTEGER, -- cached thumbnail height, if known
+ dhash INTEGER, -- uint64 perceptual hash as a signed integer
PRIMARY KEY (sha1)
) STRICT;
diff --git a/main.go b/main.go
index 8c154e8..4cad26f 100644
--- a/main.go
+++ b/main.go
@@ -275,6 +275,12 @@ func handleAPIBrowse(w http.ResponseWriter, r *http.Request) {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+func getImageDimensions(sha1 string) (w int64, h int64, err error) {
+ err = db.QueryRow(`SELECT width, height FROM image WHERE sha1 = ?`,
+ sha1).Scan(&w, &h)
+ return
+}
+
func getImagePaths(sha1 string) (paths []string, err error) {
rows, err := db.Query(`WITH RECURSIVE paths(parent, path) AS (
SELECT parent, name AS path FROM entry WHERE sha1 = ?
@@ -338,12 +344,19 @@ func handleAPIInfo(w http.ResponseWriter, r *http.Request) {
}
var result struct {
- Paths []string `json:"paths"`
- Tags map[string]map[string]float32 `json:"tags"`
+ Width int64 `json:"width"`
+ Height int64 `json:"height"`
+ Paths []string `json:"paths"`
+ Tags map[string]map[string]float32 `json:"tags"`
// TODO: Maybe add perceptual hash collisions.
}
var err error
+ result.Width, result.Height, err = getImageDimensions(params.SHA1)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
result.Paths, err = getImagePaths(params.SHA1)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
@@ -489,6 +502,31 @@ func isImage(path string) (bool, error) {
return bytes.HasPrefix(out, []byte("image/")), nil
}
+func pingImage(path string) (int, int, error) {
+ cmd := exec.Command("identify", "-limit", "thread", "1", "-ping",
+ "-format", "%w %h", path+"[0]")
+
+ // XXX: Early returns may leak resources.
+ stdout, err := cmd.StdoutPipe()
+ if err != nil {
+ return 0, 0, err
+ }
+ if err := cmd.Start(); err != nil {
+ return 0, 0, err
+ }
+ out, err := io.ReadAll(stdout)
+ if err != nil {
+ return 0, 0, err
+ }
+ if err := cmd.Wait(); err != nil {
+ return 0, 0, err
+ }
+
+ var w, h int
+ _, err = fmt.Fscanf(bytes.NewReader(out), "%d %d", &w, &h)
+ return w, h, err
+}
+
type importer struct {
dm directoryManager
dmMutex sync.Mutex
@@ -512,6 +550,11 @@ func (i *importer) Import(path string) error {
return nil
}
+ width, height, err := pingImage(path)
+ if err != nil {
+ return err
+ }
+
f, err := os.Open(path)
if err != nil {
return err
@@ -551,8 +594,8 @@ func (i *importer) Import(path string) error {
}
defer tx.Rollback()
- if _, err = tx.Exec(`INSERT INTO image(sha1) VALUES (?)
- ON CONFLICT(sha1) DO NOTHING`, hexSHA1); err != nil {
+ if _, err = tx.Exec(`INSERT INTO image(sha1, width, height) VALUES (?, ?, ?)
+ ON CONFLICT(sha1) DO NOTHING`, hexSHA1, width, height); err != nil {
return err
}
diff --git a/public/gallery.js b/public/gallery.js
index 1abb114..0967534 100644
--- a/public/gallery.js
+++ b/public/gallery.js
@@ -115,15 +115,20 @@ let Browse = {
let ViewModel = {
sha1: undefined,
+ width: 0,
+ height: 0,
paths: [],
tags: {},
async reload(sha1) {
this.sha1 = sha1
+ this.width = this.height = 0
this.paths = []
this.tags = {}
let resp = await call('info', {sha1: sha1})
+ this.width = resp.width
+ this.height = resp.height
this.paths = resp.paths
this.tags = resp.tags
},
@@ -185,7 +190,8 @@ let View = {
view(vnode) {
const view = m('.view', [
ViewModel.sha1 !== undefined
- ? m('img', {src: `/image/${ViewModel.sha1}`})
+ ? m('img', {src: `/image/${ViewModel.sha1}`,
+ width: ViewModel.width, height: ViewModel.height})
: "No image.",
])
return m('.container', {}, [
diff --git a/public/style.css b/public/style.css
index 7feddb2..8a42a51 100644
--- a/public/style.css
+++ b/public/style.css
@@ -36,7 +36,7 @@ ul.sidebar li.child a {
.view { display: flex; flex-grow: 1; overflow: hidden;
justify-content: center; align-items: center; }
-.view img { max-width: 100%; max-height: 100%; }
+.view img { max-width: 100%; max-height: 100%; object-fit: contain; }
.viewbar { padding: .25rem .5rem; background: #eee;
border-left: 1px solid #ccc; min-width: 20rem; overflow: auto; }