diff options
author | Přemysl Eric Janouch <p@janouch.name> | 2023-12-12 05:57:04 +0100 |
---|---|---|
committer | Přemysl Eric Janouch <p@janouch.name> | 2023-12-12 05:57:04 +0100 |
commit | 39e7cce65fdb49f624bc3dccd4ef279db7f7b389 (patch) | |
tree | b0b4f7702cbe5b192819947102c3f7a4e3fddc7e /main.go | |
parent | e0283d0f1b16b919c34ff703f1302f7e0186c118 (diff) | |
download | gallery-39e7cce65fdb49f624bc3dccd4ef279db7f7b389.tar.gz gallery-39e7cce65fdb49f624bc3dccd4ef279db7f7b389.tar.xz gallery-39e7cce65fdb49f624bc3dccd4ef279db7f7b389.zip |
Add a tag import command
Diffstat (limited to 'main.go')
-rw-r--r-- | main.go | 84 |
1 files changed, 82 insertions, 2 deletions
@@ -1,6 +1,7 @@ package main import ( + "bufio" "bytes" "context" "crypto/sha1" @@ -38,8 +39,8 @@ var ( func openDB(directory string) error { var err error - db, err = sql.Open("sqlite3", - "file:"+filepath.Join(directory, "gallery.db?_foreign_keys=1")) + db, err = sql.Open("sqlite3", "file:"+filepath.Join(directory, + "gallery.db?_foreign_keys=1&_busy_timeout=1000")) galleryDirectory = directory return err } @@ -279,6 +280,7 @@ func cmdRun(args []string) error { // but having an elementary level of security doesn't hurt either. staticHandler = http.FileServer(http.Dir("public")) + // TODO: Make sure the database handle isn't used concurrently. http.HandleFunc("/", handleRequest) http.HandleFunc("/image/", handleImages) http.HandleFunc("/thumb/", handleThumbs) @@ -534,6 +536,83 @@ func cmdSync(args []string) error { return nil } +// --- Tagging ----------------------------------------------------------------- + +// cmdTag mass imports tags from data passed on stdin as a TSV +// of SHA1 TAG WEIGHT entries. +func cmdTag(args []string) error { + if len(args) < 2 || len(args) > 3 { + return errors.New("usage: GD SPACE [DESCRIPTION]") + } + if err := openDB(args[0]); err != nil { + return err + } + + space := args[1] + + var description sql.NullString + if len(args) >= 3 { + description = sql.NullString{String: args[2], Valid: true} + } + + // Note that starting as a write transaction prevents deadlocks. + // Imports are rare, and just bulk load data, so this scope is fine. + tx, err := db.Begin() + if err != nil { + return err + } + defer tx.Rollback() + + if _, err := tx.Exec(`INSERT OR IGNORE INTO tag_space(name, description) + VALUES (?, ?)`, space, description); err != nil { + return err + } + + var spaceID int64 + if err := tx.QueryRow(`SELECT id FROM tag_space WHERE name = ?`, + space).Scan(&spaceID); err != nil { + return err + } + + // TODO: Prepare statements for tag/assignment updates. + + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + fields := strings.Split(scanner.Text(), "\t") + if len(fields) != 3 { + return errors.New("invalid input format") + } + + sha1, tag := fields[0], fields[1] + weight, err := strconv.ParseFloat(fields[2], 64) + if err != nil { + return err + } + + if _, err := tx.Exec(`INSERT OR IGNORE INTO tag(space, name) + VALUES (?, ?);`, spaceID, tag); err != nil { + return nil + } + + var tagID int64 + if err := tx.QueryRow(`SELECT id FROM tag WHERE space = ? AND name = ?`, + spaceID, tag).Scan(&tagID); err != nil { + return err + } + + if _, err := tx.Exec(`INSERT INTO tag_assignment(sha1, tag, weight) + VALUES (?, ?, ?) ON CONFLICT DO UPDATE SET weight = ?`, + sha1, tagID, weight, weight); err != nil { + return err + } + } + if err := scanner.Err(); err != nil { + return err + } + + return tx.Commit() +} + // --- Check ------------------------------------------------------------------- // cmdCheck checks if all files tracked in the DB are accessible. @@ -735,6 +814,7 @@ var commands = map[string]struct { "init": {cmdInit}, "run": {cmdRun}, "import": {cmdImport}, + "tag": {cmdTag}, "sync": {cmdSync}, "check": {cmdCheck}, "thumbnail": {cmdThumbnail}, |