diff options
| -rw-r--r-- | main.go | 214 | 
1 files changed, 115 insertions, 99 deletions
@@ -73,62 +73,63 @@ func dbCollectStrings(query string) ([]string, error) {  	return result, nil  } -type directoryManager struct { -	cache map[string]int64 // Unix-style paths to directory.id -} - -func (dm *directoryManager) uncachedIDForPath( -	tx *sql.Tx, path []string) (int64, error) { -	var parent sql.NullInt64 -	for _, name := range path { -		if err := tx.QueryRow( -			`SELECT id FROM directory WHERE name = ? AND parent IS ?`, -			name, parent).Scan(&parent); err == nil { -			continue -		} else if !errors.Is(err, sql.ErrNoRows) { -			return 0, err -		} +// --- Initialization ---------------------------------------------------------- -		if result, err := tx.Exec( -			`INSERT INTO directory(name, parent) VALUES (?, ?)`, -			name, parent); err != nil { -			return 0, err -		} else if id, err := result.LastInsertId(); err != nil { -			return 0, err -		} else { -			parent = sql.NullInt64{Int64: id, Valid: true} -		} +// cmdInit initializes a "gallery directory" that contains gallery.sqlite, +// images, thumbs. +func cmdInit(args []string) error { +	if len(args) != 1 { +		return errors.New("usage: GD")  	} -	return parent.Int64, nil -} -func (dm *directoryManager) IDForDirectoryPath( -	tx *sql.Tx, path string) (int64, error) { -	// Relative paths could be handled differently, -	// but right now, they're assumed to start at the root. -	path = filepath.ToSlash(filepath.Clean(path)) -	list := strings.Split(path, "/") -	if len(list) > 1 && list[0] == "" { -		list = list[1:] +	if err := openDB(args[0]); err != nil { +		return err  	} -	if len(list) == 0 { -		return 0, nil +	if _, err := db.Exec(initializeSQL); err != nil { +		return err  	} -	if dm.cache == nil { -		dm.cache = make(map[string]int64) -	} else if id, ok := dm.cache[path]; ok { -		return id, nil +	// XXX: There's technically no reason to keep images as symlinks, +	// we might just keep absolute paths in the database as well. +	if err := os.MkdirAll( +		filepath.Join(galleryDirectory, "images"), 0755); err != nil { +		return err  	} - -	id, err := dm.uncachedIDForPath(tx, list) -	if err != nil { -		return 0, err +	if err := os.MkdirAll( +		filepath.Join(galleryDirectory, "thumbs"), 0755); err != nil { +		return err  	} -	dm.cache[path] = id -	return id, nil +	return nil  } +// --- Web --------------------------------------------------------------------- + +var hashRE = regexp.MustCompile(`^/.*?/([0-9a-f]{40})$`) +var staticHandler http.Handler + +var page = template.Must(template.New("/").Parse(`<!DOCTYPE html><html><head> +	<title>Gallery</title> +	<meta charset="utf-8" /> +	<meta name="viewport" content="width=device-width, initial-scale=1"> +	<link rel=stylesheet href=style.css> +</head><body> +	<h1>{{ .Name }}</h1> +	<ul> +		{{ range .Children }} +		<li><a href="?id={{ . }}">{{ . }}</a></li> +		{{ end }} +	</ul> + +	{{ range .Entries }} +	<a href="/image/{{ .Sha1 }}"> +		<img width={{ .Thumbw }} height={{ .Thumbh }} src="/thumb/{{ .Sha1 }}" +			title="{{ .Name }}"> +	</a> +	{{ end }} + +	<script src=gallery.js></script> +</body></html>`)) +  // XXX: This is preliminary.  type entry struct {  	Parent int64 @@ -197,59 +198,6 @@ func dbCollectDirectory(id int64) (directory, error) {  	return d, rows2.Err()  } -// cmdInit initializes a "gallery directory" that contains gallery.sqlite, -// images, thumbs. -func cmdInit(args []string) error { -	if len(args) != 1 { -		return errors.New("usage: GD") -	} - -	if err := openDB(args[0]); err != nil { -		return err -	} -	if _, err := db.Exec(initializeSQL); err != nil { -		return err -	} - -	// XXX: There's technically no reason to keep images as symlinks, -	// we might just keep absolute paths in the database as well. -	if err := os.MkdirAll( -		filepath.Join(galleryDirectory, "images"), 0755); err != nil { -		return err -	} -	if err := os.MkdirAll( -		filepath.Join(galleryDirectory, "thumbs"), 0755); err != nil { -		return err -	} -	return nil -} - -var hashRE = regexp.MustCompile(`^/.*?/([0-9a-f]{40})$`) -var staticHandler http.Handler - -var page = template.Must(template.New("/").Parse(`<!DOCTYPE html><html><head> -	<title>Gallery</title> -	<meta charset="utf-8" /> -	<meta name="viewport" content="width=device-width, initial-scale=1"> -	<link rel=stylesheet href=style.css> -</head><body> -	<h1>{{ .Name }}</h1> -	<ul> -		{{ range .Children }} -		<li><a href="?id={{ . }}">{{ . }}</a></li> -		{{ end }} -	</ul> - -	{{ range .Entries }} -	<a href="/image/{{ .Sha1 }}"> -		<img width={{ .Thumbw }} height={{ .Thumbh }} src="/thumb/{{ .Sha1 }}" -			title="{{ .Name }}"> -	</a> -	{{ end }} - -	<script src=gallery.js></script> -</body></html>`)) -  func handleRequest(w http.ResponseWriter, r *http.Request) {  	if r.URL.Path != "/" {  		staticHandler.ServeHTTP(w, r) @@ -321,8 +269,68 @@ func cmdRun(args []string) error {  	return s.ListenAndServe()  } +// --- Import ------------------------------------------------------------------ + +type directoryManager struct { +	cache map[string]int64 // Unix-style paths to directory.id +} + +func (dm *directoryManager) uncachedIDForPath( +	tx *sql.Tx, path []string) (int64, error) { +	var parent sql.NullInt64 +	for _, name := range path { +		if err := tx.QueryRow( +			`SELECT id FROM directory WHERE name = ? AND parent IS ?`, +			name, parent).Scan(&parent); err == nil { +			continue +		} else if !errors.Is(err, sql.ErrNoRows) { +			return 0, err +		} + +		if result, err := tx.Exec( +			`INSERT INTO directory(name, parent) VALUES (?, ?)`, +			name, parent); err != nil { +			return 0, err +		} else if id, err := result.LastInsertId(); err != nil { +			return 0, err +		} else { +			parent = sql.NullInt64{Int64: id, Valid: true} +		} +	} +	return parent.Int64, nil +} + +func (dm *directoryManager) IDForDirectoryPath( +	tx *sql.Tx, path string) (int64, error) { +	// Relative paths could be handled differently, +	// but right now, they're assumed to start at the root. +	path = filepath.ToSlash(filepath.Clean(path)) +	list := strings.Split(path, "/") +	if len(list) > 1 && list[0] == "" { +		list = list[1:] +	} +	if len(list) == 0 { +		return 0, nil +	} + +	if dm.cache == nil { +		dm.cache = make(map[string]int64) +	} else if id, ok := dm.cache[path]; ok { +		return id, nil +	} + +	id, err := dm.uncachedIDForPath(tx, list) +	if err != nil { +		return 0, err +	} +	dm.cache[path] = id +	return id, nil +} +  func isImage(path string) (bool, error) {  	cmd := exec.Command("xdg-mime", "query", "filetype", path) + +	// XXX: Early returns may leak resources.  	stdout, err := cmd.StdoutPipe()  	if err != nil {  		return false, err @@ -481,6 +489,8 @@ func cmdSync(args []string) error {  	return nil  } +// --- Check ------------------------------------------------------------------- +  // cmdCheck checks if all files tracked in the DB are accessible.  func cmdCheck(args []string) error {  	if len(args) != 1 { @@ -495,6 +505,8 @@ func cmdCheck(args []string) error {  	return nil  } +// --- Thumbnailing ------------------------------------------------------------ +  func makeThumbnail(pathImage, pathThumb string) (int, int, error) {  	thumbDirname, _ := filepath.Split(pathThumb)  	if err := os.MkdirAll(thumbDirname, 0755); err != nil { @@ -597,6 +609,8 @@ func cmdThumbnail(args []string) error {  	return nil  } +// --- Perceptual hash --------------------------------------------------------- +  func makeDhash(hasher, pathThumb string) (uint64, error) {  	cmd := exec.Command(hasher, pathThumb) @@ -658,6 +672,8 @@ func cmdDhash(args []string) error {  	return nil  } +// --- Main -------------------------------------------------------------------- +  var commands = map[string]struct {  	handler func(args []string) error  }{  | 
