aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2023-12-23 23:03:31 +0100
committerPřemysl Eric Janouch <p@janouch.name>2023-12-23 23:03:31 +0100
commite25b07e5987bd7a2b70b57e21b0eed6607f1b2d9 (patch)
treea667926419c496b0875213e5c3582a5af6dc2361
parent20a102a047c85053db96c85b7bf2f760d7e90c6d (diff)
downloadgallery-e25b07e5987bd7a2b70b57e21b0eed6607f1b2d9.tar.gz
gallery-e25b07e5987bd7a2b70b57e21b0eed6607f1b2d9.tar.xz
gallery-e25b07e5987bd7a2b70b57e21b0eed6607f1b2d9.zip
Implement orphans
-rw-r--r--main.go85
-rw-r--r--public/gallery.js36
-rw-r--r--public/style.css7
3 files changed, 119 insertions, 9 deletions
diff --git a/main.go b/main.go
index 5275c26..0ea4ab6 100644
--- a/main.go
+++ b/main.go
@@ -667,7 +667,7 @@ func handleAPIDuplicates(w http.ResponseWriter, r *http.Request) {
}
var (
- result [][]webDuplicateImage
+ result = [][]webDuplicateImage{}
err error
)
if result, err = getDuplicates1(result); err != nil {
@@ -686,6 +686,81 @@ func handleAPIDuplicates(w http.ResponseWriter, r *http.Request) {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+type webOrphanImage struct {
+ SHA1 string `json:"sha1"`
+ ThumbW int64 `json:"thumbW"`
+ ThumbH int64 `json:"thumbH"`
+ Tags int64 `json:"tags"`
+}
+
+type webOrphan struct {
+ webOrphanImage
+ LastPath string `json:"lastPath"`
+ Replacement *webOrphanImage `json:"replacement"`
+}
+
+func getOrphanReplacement(webPath string) (*webOrphanImage, error) {
+ tx, err := db.Begin()
+ if err != nil {
+ return nil, err
+ }
+ defer tx.Rollback()
+
+ path := decodeWebPath(webPath)
+ if len(path) == 0 {
+ return nil, nil
+ }
+
+ parent, err := idForPath(tx, path[:len(path)-1], false)
+ if err != nil {
+ return nil, err
+ }
+
+ var image webOrphanImage
+ err = db.QueryRow(`SELECT i.sha1,
+ IFNULL(i.thumbw, 0), IFNULL(i.thumbh, 0), COUNT(*) AS tags
+ FROM node AS n
+ JOIN image AS i ON n.sha1 = i.sha1
+ JOIN tag_assignment AS ta ON n.sha1 = ta.sha1
+ WHERE n.parent = ? AND n.name = ?
+ GROUP BY ta.sha1`, parent, path[len(path)-1]).Scan(
+ &image.SHA1, &image.ThumbW, &image.ThumbH, &image.Tags)
+ if err != nil {
+ return nil, err
+ }
+ return &image, nil
+}
+
+func getOrphans() (result []webOrphan, err error) {
+ rows, err := db.Query(`SELECT o.sha1, o.path,
+ IFNULL(i.thumbw, 0), IFNULL(i.thumbh, 0), COUNT(*) AS tags
+ FROM orphan AS o
+ JOIN image AS i ON o.sha1 = i.sha1
+ JOIN tag_assignment AS ta ON o.sha1 = ta.sha1
+ GROUP BY ta.sha1`)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ result = []webOrphan{}
+ for rows.Next() {
+ var orphan webOrphan
+ if err = rows.Scan(&orphan.SHA1, &orphan.LastPath,
+ &orphan.ThumbW, &orphan.ThumbH, &orphan.Tags); err != nil {
+ return nil, err
+ }
+
+ orphan.Replacement, err = getOrphanReplacement(orphan.LastPath)
+ if err != nil {
+ return nil, err
+ }
+
+ result = append(result, orphan)
+ }
+ return result, rows.Err()
+}
+
func handleAPIOrphans(w http.ResponseWriter, r *http.Request) {
var params struct{}
if err := json.NewDecoder(r.Body).Decode(&params); err != nil {
@@ -693,8 +768,12 @@ func handleAPIOrphans(w http.ResponseWriter, r *http.Request) {
return
}
- // TODO
- result := false
+ result, err := getOrphans()
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
if err := json.NewEncoder(w).Encode(result); err != nil {
log.Println(err)
}
diff --git a/public/gallery.js b/public/gallery.js
index 222bde5..96f0216 100644
--- a/public/gallery.js
+++ b/public/gallery.js
@@ -371,15 +371,45 @@ let OrphansModel = {
},
}
+let OrphansReplacement = {
+ view(node) {
+ const info = vnode.attrs.info
+ if (!info)
+ return []
+
+ return [
+ ` → `,
+ m(m.route.Link, {href: `/view/${info.sha1}`},
+ m('img.thumbnail', {src: `/thumb/${info.sha1}`,
+ width: info.thumbW, height: info.thumbH, loading})),
+ `${info.tags} tags`,
+ ]
+ },
+}
+
+let OrphansRow = {
+ view(node) {
+ const info = vnode.attrs.info
+ return m('.row', [
+ m('img.thumbnail', {src: `/thumb/${info.sha1}`,
+ width: info.thumbW, height: info.thumbH, loading}),
+ `${info.tags} tags`,
+ m(OrphansReplacement, {info: info.replacement}),
+ ])
+ },
+}
+
let OrphansList = {
// See BrowseView.
oncreate(vnode) { vnode.dom.focus() },
view(vnode) {
- let children = (DuplicatesModel.entries.length == 0)
+ let children = (OrphansModel.entries.length == 0)
? "No orphans"
- : OrphansModel.entries.map(group =>
- m('.row', "TODO"))
+ : OrphansModel.entries.map(info => [
+ m("h3", info.lastPath),
+ m(OrphansRow, {info}),
+ ])
return m('.orphans[tabindex=0]', {}, children)
},
}
diff --git a/public/style.css b/public/style.css
index 978e01b..0276e43 100644
--- a/public/style.css
+++ b/public/style.css
@@ -59,6 +59,7 @@ img.thumbnail { display: block; box-shadow: 0 0 3px rgba(0, 0, 0, 0.75);
.similar .row { display: flex; margin: .5rem 0; }
.similar .row ul { margin: 0; padding: 0 0 0 1.25em; list-style-type: "- "; }
-.duplicates { padding: .5rem; flex-grow: 1; overflow: auto; }
-.duplicates .row { display: flex; margin: .5rem 0;
- align-items: center; gap: 3px; }
+.duplicates,
+.orphans { padding: .5rem; flex-grow: 1; overflow: auto; }
+.duplicates .row,
+.orphans .row { display: flex; margin: .5rem 0; align-items: center; gap: 3px; }