Files
tchivert ac44038346
docker / docker (push) Successful in 31s
build / build (push) Successful in 28s
fix options again
2024-11-16 23:43:49 +01:00

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)
}
}