mirror of
https://github.com/stashapp/stash.git
synced 2025-12-06 08:26:00 +01:00
Use gallery updated at for cover mod time (#5225)
This commit is contained in:
parent
2b288fd67c
commit
ca970b9706
3 changed files with 56 additions and 13 deletions
|
|
@ -78,7 +78,7 @@ func (rs galleryRoutes) Cover(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rs.imageRoutes.serveThumbnail(w, r, i)
|
rs.imageRoutes.serveThumbnail(w, r, i, &g.UpdatedAt)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rs galleryRoutes) Preview(w http.ResponseWriter, r *http.Request) {
|
func (rs galleryRoutes) Preview(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
@ -116,7 +116,7 @@ func (rs galleryRoutes) Preview(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rs.imageRoutes.serveThumbnail(w, r, i)
|
rs.imageRoutes.serveThumbnail(w, r, i, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rs galleryRoutes) GalleryCtx(next http.Handler) http.Handler {
|
func (rs galleryRoutes) GalleryCtx(next http.Handler) http.Handler {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
|
|
||||||
|
|
@ -47,17 +48,21 @@ func (rs imageRoutes) Routes() chi.Router {
|
||||||
|
|
||||||
func (rs imageRoutes) Thumbnail(w http.ResponseWriter, r *http.Request) {
|
func (rs imageRoutes) Thumbnail(w http.ResponseWriter, r *http.Request) {
|
||||||
img := r.Context().Value(imageKey).(*models.Image)
|
img := r.Context().Value(imageKey).(*models.Image)
|
||||||
rs.serveThumbnail(w, r, img)
|
rs.serveThumbnail(w, r, img, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rs imageRoutes) serveThumbnail(w http.ResponseWriter, r *http.Request, img *models.Image) {
|
func (rs imageRoutes) serveThumbnail(w http.ResponseWriter, r *http.Request, img *models.Image, modTime *time.Time) {
|
||||||
mgr := manager.GetInstance()
|
mgr := manager.GetInstance()
|
||||||
filepath := mgr.Paths.Generated.GetThumbnailPath(img.Checksum, models.DefaultGthumbWidth)
|
filepath := mgr.Paths.Generated.GetThumbnailPath(img.Checksum, models.DefaultGthumbWidth)
|
||||||
|
|
||||||
// if the thumbnail doesn't exist, encode on the fly
|
// if the thumbnail doesn't exist, encode on the fly
|
||||||
exists, _ := fsutil.FileExists(filepath)
|
exists, _ := fsutil.FileExists(filepath)
|
||||||
if exists {
|
if exists {
|
||||||
|
if modTime == nil {
|
||||||
utils.ServeStaticFile(w, r, filepath)
|
utils.ServeStaticFile(w, r, filepath)
|
||||||
|
} else {
|
||||||
|
utils.ServeStaticFileModTime(w, r, filepath, *modTime)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const useDefault = true
|
const useDefault = true
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,10 @@ package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stashapp/stash/pkg/hash/md5"
|
"github.com/stashapp/stash/pkg/hash/md5"
|
||||||
|
|
@ -15,14 +18,18 @@ func GenerateETag(data []byte) string {
|
||||||
return `"` + hash + `"`
|
return `"` + hash + `"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serves static content, adding Cache-Control: no-cache and a generated ETag header.
|
func setStaticContentCacheControl(w http.ResponseWriter, r *http.Request) {
|
||||||
// Responds to conditional requests using the ETag.
|
|
||||||
func ServeStaticContent(w http.ResponseWriter, r *http.Request, data []byte) {
|
|
||||||
if r.URL.Query().Has("t") {
|
if r.URL.Query().Has("t") {
|
||||||
w.Header().Set("Cache-Control", "private, max-age=31536000, immutable")
|
w.Header().Set("Cache-Control", "private, max-age=31536000, immutable")
|
||||||
} else {
|
} else {
|
||||||
w.Header().Set("Cache-Control", "no-cache")
|
w.Header().Set("Cache-Control", "no-cache")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serves static content, adding Cache-Control: no-cache and a generated ETag header.
|
||||||
|
// Responds to conditional requests using the ETag.
|
||||||
|
func ServeStaticContent(w http.ResponseWriter, r *http.Request, data []byte) {
|
||||||
|
setStaticContentCacheControl(w, r)
|
||||||
w.Header().Set("ETag", GenerateETag(data))
|
w.Header().Set("ETag", GenerateETag(data))
|
||||||
|
|
||||||
http.ServeContent(w, r, "", time.Time{}, bytes.NewReader(data))
|
http.ServeContent(w, r, "", time.Time{}, bytes.NewReader(data))
|
||||||
|
|
@ -31,11 +38,42 @@ func ServeStaticContent(w http.ResponseWriter, r *http.Request, data []byte) {
|
||||||
// Serves static content at filepath, adding Cache-Control: no-cache.
|
// Serves static content at filepath, adding Cache-Control: no-cache.
|
||||||
// Responds to conditional requests using the file modtime.
|
// Responds to conditional requests using the file modtime.
|
||||||
func ServeStaticFile(w http.ResponseWriter, r *http.Request, filepath string) {
|
func ServeStaticFile(w http.ResponseWriter, r *http.Request, filepath string) {
|
||||||
if r.URL.Query().Has("t") {
|
setStaticContentCacheControl(w, r)
|
||||||
w.Header().Set("Cache-Control", "private, max-age=31536000, immutable")
|
|
||||||
} else {
|
|
||||||
w.Header().Set("Cache-Control", "no-cache")
|
|
||||||
}
|
|
||||||
|
|
||||||
http.ServeFile(w, r, filepath)
|
http.ServeFile(w, r, filepath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toHTTPError(err error) (msg string, httpStatus int) {
|
||||||
|
if errors.Is(err, fs.ErrNotExist) {
|
||||||
|
return "404 page not found", http.StatusNotFound
|
||||||
|
}
|
||||||
|
if errors.Is(err, fs.ErrPermission) {
|
||||||
|
return "403 Forbidden", http.StatusForbidden
|
||||||
|
}
|
||||||
|
return "500 Internal Server Error", http.StatusInternalServerError
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeStaticFileModTime serves a static file at the given path using the given modTime instead of the file modTime.
|
||||||
|
func ServeStaticFileModTime(w http.ResponseWriter, r *http.Request, path string, modTime time.Time) {
|
||||||
|
setStaticContentCacheControl(w, r)
|
||||||
|
|
||||||
|
dir, file := filepath.Split(path)
|
||||||
|
fs := http.Dir(dir)
|
||||||
|
|
||||||
|
f, err := fs.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
msg, code := toHTTPError(err)
|
||||||
|
http.Error(w, msg, code)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
d, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
msg, code := toHTTPError(err)
|
||||||
|
http.Error(w, msg, code)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
http.ServeContent(w, r, d.Name(), modTime, f)
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue