282 lines
6.4 KiB
Go
282 lines
6.4 KiB
Go
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"html/template"
|
|
"math/rand"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
defaultGallery = flag.String("g", "index", "Default page")
|
|
galleryDir = flag.String("i", "images", "Gallery directory")
|
|
recursion = flag.Bool("r", false, "Find galleries recursively")
|
|
title = flag.String("t", "Gallery", "Title")
|
|
description = flag.String("d", "A simple gallery", "Description")
|
|
port = flag.String("p", "8080", "Port")
|
|
galleries []Gallery
|
|
)
|
|
|
|
type Image struct {
|
|
Name string
|
|
URL string
|
|
Path string
|
|
}
|
|
|
|
type Gallery struct {
|
|
Name string
|
|
Images []Image
|
|
Path string
|
|
Length int
|
|
Title string
|
|
Desc string
|
|
}
|
|
|
|
type Index struct {
|
|
Galleries []Gallery
|
|
Title string
|
|
Desc string
|
|
}
|
|
|
|
func main() {
|
|
flag.Parse()
|
|
if count := loadGallery(*galleryDir); count == 0 {
|
|
fmt.Println("No images found in gallery directory")
|
|
return
|
|
}
|
|
http.HandleFunc("/", galleryHandler)
|
|
http.Handle("/i/", http.StripPrefix("/i/", http.FileServer(http.Dir(*galleryDir))))
|
|
http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("./assets"))))
|
|
http.Handle("/favicon.ico", http.StripPrefix("/favicon.ico", http.FileServer(http.Dir("./assets"))))
|
|
fmt.Println("Listening on port", *port)
|
|
http.ListenAndServe(":"+*port, nil)
|
|
}
|
|
|
|
func loadGallery(dir string) int {
|
|
var count int
|
|
|
|
// Load images from gallery directory
|
|
files, err := filepath.Glob(dir + "/*")
|
|
if err != nil {
|
|
fmt.Println("Error loading images:", err)
|
|
return 0
|
|
}
|
|
|
|
count += loadDir(dir, false)
|
|
|
|
// Load galleries
|
|
for _, file := range files {
|
|
count += loadDir(file, *recursion)
|
|
}
|
|
fmt.Println("Loaded", count, "images in", dir)
|
|
sortGalleries()
|
|
return count
|
|
}
|
|
|
|
func loadDir(dir string, r bool) int {
|
|
var count int
|
|
gallery := Gallery{Name: filepath.Base(dir), Path: strings.TrimPrefix(dir, *galleryDir)}
|
|
if gallery.Path == "" {
|
|
gallery.Path = "/" + gallery.Name
|
|
}
|
|
files, err := filepath.Glob(dir + "/*")
|
|
if err != nil {
|
|
fmt.Println("Error loading images:", err)
|
|
return 0
|
|
}
|
|
for _, f := range files {
|
|
if r && isDir(f) && !exclude(filepath.Base(f)) {
|
|
count += loadDir(f, true)
|
|
fmt.Println("Recursing into", filepath.Base(f), "found", count, "images")
|
|
} else if isImage(f) {
|
|
image := Image{}
|
|
image.Name = filepath.Base(f)
|
|
if gallery.Name == filepath.Base(*galleryDir) {
|
|
image.URL = filepath.Join("/i", filepath.Base(f))
|
|
} else {
|
|
image.URL = filepath.Join("/i", gallery.Path, filepath.Base(f))
|
|
}
|
|
image.Path = f
|
|
gallery.Images = append(gallery.Images, image)
|
|
continue
|
|
}
|
|
}
|
|
gallery.Length = len(gallery.Images)
|
|
if gallery.Length >= 12 {
|
|
galleries = append(galleries, gallery)
|
|
}
|
|
return gallery.Length + count
|
|
}
|
|
|
|
func sortGalleries() {
|
|
// Sort galleries by path
|
|
for i := 0; i < len(galleries); i++ {
|
|
for j := i + 1; j < len(galleries); j++ {
|
|
if galleries[i].Path > galleries[j].Path {
|
|
galleries[i], galleries[j] = galleries[j], galleries[i]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func exclude(file string) bool {
|
|
if strings.Contains(file, "thumb") {
|
|
return true
|
|
}
|
|
if strings.Contains(file, "preview") {
|
|
return true
|
|
}
|
|
if strings.ContainsAny(file, "!@#$%^&*()_+-=[]{}\\|;:'\",.<>?") {
|
|
return true
|
|
}
|
|
if file[0] == '.' {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isDir(file string) bool {
|
|
info, err := os.Stat(file)
|
|
if err != nil {
|
|
fmt.Println("Error getting file info:", err)
|
|
return false
|
|
}
|
|
return info.IsDir()
|
|
}
|
|
|
|
func isImage(file string) bool {
|
|
ext := filepath.Ext(file)
|
|
return ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".gif"
|
|
}
|
|
|
|
func galleryHandler(w http.ResponseWriter, r *http.Request) {
|
|
// Get gallery name from url
|
|
request := r.URL.Path
|
|
query := strings.Split(request, "/")
|
|
name := *defaultGallery
|
|
if len(query) > 1 {
|
|
name = query[1]
|
|
}
|
|
if name == "" {
|
|
name = *defaultGallery
|
|
}
|
|
option := ""
|
|
if len(query) > 2 {
|
|
option = query[2]
|
|
}
|
|
|
|
fmt.Println("Request:", name)
|
|
|
|
// Check if index is requested
|
|
if name == "index" {
|
|
// Render index page
|
|
data := Index{}
|
|
data.Galleries = galleries
|
|
data.Title = *title
|
|
data.Desc = *description
|
|
tmpl, err := template.ParseFiles("templates/index.html")
|
|
if err != nil {
|
|
fmt.Println("Error parsing template:", err)
|
|
return
|
|
}
|
|
tmpl.Execute(w, data)
|
|
return
|
|
}
|
|
|
|
// Find gallery
|
|
var images []Image
|
|
var length int
|
|
for _, gallery := range galleries {
|
|
if gallery.Name == name {
|
|
images = gallery.Images
|
|
length = gallery.Length
|
|
fmt.Println("Found gallery:", gallery.Name)
|
|
break
|
|
}
|
|
}
|
|
|
|
// Gallery not found, serve the 404.html page and a 404 status code
|
|
if length == 0 {
|
|
errorHandler(w, http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Cache-Control", "no-cache")
|
|
|
|
// Shuffle images randomly
|
|
if option != "all" {
|
|
rand.Shuffle(length, func(i, j int) {
|
|
images[i], images[j] = images[j], images[i]
|
|
})
|
|
}
|
|
|
|
// If url ends with /random, serve a random image from the gallery
|
|
if option == "random" {
|
|
// Return a random image
|
|
image := images[0]
|
|
// Check the image type
|
|
filetype := filepath.Ext(image.Name)
|
|
switch filetype {
|
|
case ".jpg":
|
|
w.Header().Set("Content-Type", "image/jpeg")
|
|
case ".jpeg":
|
|
w.Header().Set("Content-Type", "image/jpeg")
|
|
case ".png":
|
|
w.Header().Set("Content-Type", "image/png")
|
|
case ".gif":
|
|
w.Header().Set("Content-Type", "image/gif")
|
|
default:
|
|
w.Header().Set("Content-Type", "image/jpeg")
|
|
}
|
|
http.ServeFile(w, r, image.Path)
|
|
return
|
|
}
|
|
|
|
// Limit to 24 images
|
|
if length > 24 && option != "all" {
|
|
images = images[:24]
|
|
}
|
|
|
|
// Get base names
|
|
for i, img := range images {
|
|
images[i].Name = strings.TrimSuffix(img.Name, filepath.Ext(img.Name))
|
|
}
|
|
|
|
// Render template
|
|
data := Gallery{Name: name, Images: images}
|
|
data.Title = *title
|
|
data.Desc = *description
|
|
tmpl, err := template.ParseFiles("templates/gallery.html")
|
|
if err != nil {
|
|
fmt.Println("Error parsing template:", err)
|
|
return
|
|
}
|
|
tmpl.Execute(w, data)
|
|
}
|
|
|
|
func errorHandler(w http.ResponseWriter, status int) {
|
|
if status == http.StatusNotFound {
|
|
content, err := os.ReadFile("templates/404.html")
|
|
if err != nil {
|
|
fmt.Println("Error reading 404.html:", err)
|
|
return
|
|
}
|
|
w.WriteHeader(status)
|
|
w.Header().Set("Content-Type", "text/html")
|
|
w.Write(content)
|
|
} else if status == http.StatusForbidden {
|
|
content, err := os.ReadFile("templates/403.html")
|
|
if err != nil {
|
|
fmt.Println("Error reading 403.html:", err)
|
|
return
|
|
}
|
|
w.WriteHeader(status)
|
|
w.Header().Set("Content-Type", "text/html")
|
|
w.Write(content)
|
|
}
|
|
}
|