mirror of
https://github.com/stashapp/stash.git
synced 2025-12-06 08:26:00 +01:00
Suppress benign broken pipe and context closed warnings (#2927)
This commit is contained in:
parent
5e97ecd260
commit
8efbcc1c4d
11 changed files with 172 additions and 105 deletions
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi"
|
||||||
"github.com/stashapp/stash/internal/manager"
|
"github.com/stashapp/stash/internal/manager"
|
||||||
|
|
@ -84,11 +85,11 @@ func (rs imageRoutes) Thumbnail(w http.ResponseWriter, r *http.Request) {
|
||||||
if manager.GetInstance().Config.IsWriteImageThumbnails() {
|
if manager.GetInstance().Config.IsWriteImageThumbnails() {
|
||||||
logger.Debugf("writing thumbnail to disk: %s", img.Path)
|
logger.Debugf("writing thumbnail to disk: %s", img.Path)
|
||||||
if err := fsutil.WriteFile(filepath, data); err != nil {
|
if err := fsutil.WriteFile(filepath, data); err != nil {
|
||||||
logger.Errorf("error writing thumbnail for image %s: %s", img.Path, err)
|
logger.Errorf("error writing thumbnail for image %s: %v", img.Path, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if n, err := w.Write(data); err != nil {
|
if n, err := w.Write(data); err != nil && !errors.Is(err, syscall.EPIPE) {
|
||||||
logger.Errorf("error writing thumbnail response. Wrote %v bytes: %v", n, err)
|
logger.Errorf("error serving thumbnail (wrote %v bytes out of %v): %v", n, len(data), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -114,7 +115,7 @@ func (rs imageRoutes) ImageCtx(next http.Handler) http.Handler {
|
||||||
imageID, _ := strconv.Atoi(imageIdentifierQueryParam)
|
imageID, _ := strconv.Atoi(imageIdentifierQueryParam)
|
||||||
|
|
||||||
var image *models.Image
|
var image *models.Image
|
||||||
readTxnErr := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
_ = txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
||||||
qb := rs.imageFinder
|
qb := rs.imageFinder
|
||||||
if imageID == 0 {
|
if imageID == 0 {
|
||||||
images, _ := qb.FindByChecksum(ctx, imageIdentifierQueryParam)
|
images, _ := qb.FindByChecksum(ctx, imageIdentifierQueryParam)
|
||||||
|
|
@ -131,10 +132,6 @@ func (rs imageRoutes) ImageCtx(next http.Handler) http.Handler {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if readTxnErr != nil {
|
|
||||||
logger.Warnf("read transaction failure while trying to read image by id: %v", readTxnErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if image == nil {
|
if image == nil {
|
||||||
http.Error(w, http.StatusText(404), 404)
|
http.Error(w, http.StatusText(404), 404)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
|
@ -40,12 +41,15 @@ func (rs movieRoutes) FrontImage(w http.ResponseWriter, r *http.Request) {
|
||||||
defaultParam := r.URL.Query().Get("default")
|
defaultParam := r.URL.Query().Get("default")
|
||||||
var image []byte
|
var image []byte
|
||||||
if defaultParam != "true" {
|
if defaultParam != "true" {
|
||||||
err := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
readTxnErr := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
||||||
image, _ = rs.movieFinder.GetFrontImage(ctx, movie.ID)
|
image, _ = rs.movieFinder.GetFrontImage(ctx, movie.ID)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if errors.Is(readTxnErr, context.Canceled) {
|
||||||
logger.Warnf("read transaction error while getting front image: %v", err)
|
return
|
||||||
|
}
|
||||||
|
if readTxnErr != nil {
|
||||||
|
logger.Warnf("read transaction error on fetch movie front image: %v", readTxnErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,7 +58,7 @@ func (rs movieRoutes) FrontImage(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := utils.ServeImage(image, w, r); err != nil {
|
if err := utils.ServeImage(image, w, r); err != nil {
|
||||||
logger.Warnf("error serving front image: %v", err)
|
logger.Warnf("error serving movie front image: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,12 +67,15 @@ func (rs movieRoutes) BackImage(w http.ResponseWriter, r *http.Request) {
|
||||||
defaultParam := r.URL.Query().Get("default")
|
defaultParam := r.URL.Query().Get("default")
|
||||||
var image []byte
|
var image []byte
|
||||||
if defaultParam != "true" {
|
if defaultParam != "true" {
|
||||||
err := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
readTxnErr := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
||||||
image, _ = rs.movieFinder.GetBackImage(ctx, movie.ID)
|
image, _ = rs.movieFinder.GetBackImage(ctx, movie.ID)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if errors.Is(readTxnErr, context.Canceled) {
|
||||||
logger.Warnf("read transaction error on fetch back image: %v", err)
|
return
|
||||||
|
}
|
||||||
|
if readTxnErr != nil {
|
||||||
|
logger.Warnf("read transaction error on fetch movie back image: %v", readTxnErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,7 +84,7 @@ func (rs movieRoutes) BackImage(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := utils.ServeImage(image, w, r); err != nil {
|
if err := utils.ServeImage(image, w, r); err != nil {
|
||||||
logger.Warnf("error while serving image: %v", err)
|
logger.Warnf("error serving movie back image: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,11 +97,11 @@ func (rs movieRoutes) MovieCtx(next http.Handler) http.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
var movie *models.Movie
|
var movie *models.Movie
|
||||||
if err := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
_ = txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
||||||
var err error
|
movie, _ = rs.movieFinder.Find(ctx, movieID)
|
||||||
movie, err = rs.movieFinder.Find(ctx, movieID)
|
return nil
|
||||||
return err
|
})
|
||||||
}); err != nil {
|
if movie == nil {
|
||||||
http.Error(w, http.StatusText(404), 404)
|
http.Error(w, http.StatusText(404), 404)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
|
@ -44,8 +45,11 @@ func (rs performerRoutes) Image(w http.ResponseWriter, r *http.Request) {
|
||||||
image, _ = rs.performerFinder.GetImage(ctx, performer.ID)
|
image, _ = rs.performerFinder.GetImage(ctx, performer.ID)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
if errors.Is(readTxnErr, context.Canceled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
if readTxnErr != nil {
|
if readTxnErr != nil {
|
||||||
logger.Warnf("couldn't execute getting a performer image from read transaction: %v", readTxnErr)
|
logger.Warnf("read transaction error on fetch performer image: %v", readTxnErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,7 +58,7 @@ func (rs performerRoutes) Image(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := utils.ServeImage(image, w, r); err != nil {
|
if err := utils.ServeImage(image, w, r); err != nil {
|
||||||
logger.Warnf("error serving image: %v", err)
|
logger.Warnf("error serving performer image: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,11 +71,12 @@ func (rs performerRoutes) PerformerCtx(next http.Handler) http.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
var performer *models.Performer
|
var performer *models.Performer
|
||||||
if err := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
_ = txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
||||||
var err error
|
var err error
|
||||||
performer, err = rs.performerFinder.Find(ctx, performerID)
|
performer, err = rs.performerFinder.Find(ctx, performerID)
|
||||||
return err
|
return err
|
||||||
}); err != nil {
|
})
|
||||||
|
if performer == nil {
|
||||||
http.Error(w, http.StatusText(404), 404)
|
http.Error(w, http.StatusText(404), 404)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package api
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -253,12 +254,12 @@ func (rs sceneRoutes) Webp(w http.ResponseWriter, r *http.Request) {
|
||||||
http.ServeFile(w, r, filepath)
|
http.ServeFile(w, r, filepath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rs sceneRoutes) getChapterVttTitle(ctx context.Context, marker *models.SceneMarker) string {
|
func (rs sceneRoutes) getChapterVttTitle(ctx context.Context, marker *models.SceneMarker) (*string, error) {
|
||||||
if marker.Title != "" {
|
if marker.Title != "" {
|
||||||
return marker.Title
|
return &marker.Title, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var ret string
|
var title string
|
||||||
if err := txn.WithTxn(ctx, rs.txnManager, func(ctx context.Context) error {
|
if err := txn.WithTxn(ctx, rs.txnManager, func(ctx context.Context) error {
|
||||||
qb := rs.tagFinder
|
qb := rs.tagFinder
|
||||||
primaryTag, err := qb.Find(ctx, marker.PrimaryTagID)
|
primaryTag, err := qb.Find(ctx, marker.PrimaryTagID)
|
||||||
|
|
@ -266,7 +267,7 @@ func (rs sceneRoutes) getChapterVttTitle(ctx context.Context, marker *models.Sce
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = primaryTag.Name
|
title = primaryTag.Name
|
||||||
|
|
||||||
tags, err := qb.FindBySceneMarkerID(ctx, marker.ID)
|
tags, err := qb.FindBySceneMarkerID(ctx, marker.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -274,26 +275,31 @@ func (rs sceneRoutes) getChapterVttTitle(ctx context.Context, marker *models.Sce
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, t := range tags {
|
for _, t := range tags {
|
||||||
ret += ", " + t.Name
|
title += ", " + t.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret
|
return &title, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rs sceneRoutes) ChapterVtt(w http.ResponseWriter, r *http.Request) {
|
func (rs sceneRoutes) ChapterVtt(w http.ResponseWriter, r *http.Request) {
|
||||||
scene := r.Context().Value(sceneKey).(*models.Scene)
|
scene := r.Context().Value(sceneKey).(*models.Scene)
|
||||||
var sceneMarkers []*models.SceneMarker
|
var sceneMarkers []*models.SceneMarker
|
||||||
if err := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
readTxnErr := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
||||||
var err error
|
var err error
|
||||||
sceneMarkers, err = rs.sceneMarkerFinder.FindBySceneID(ctx, scene.ID)
|
sceneMarkers, err = rs.sceneMarkerFinder.FindBySceneID(ctx, scene.ID)
|
||||||
return err
|
return err
|
||||||
}); err != nil {
|
})
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
if errors.Is(readTxnErr, context.Canceled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if readTxnErr != nil {
|
||||||
|
logger.Warnf("read transaction error on fetch scene markers: %v", readTxnErr)
|
||||||
|
http.Error(w, readTxnErr.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -302,7 +308,18 @@ func (rs sceneRoutes) ChapterVtt(w http.ResponseWriter, r *http.Request) {
|
||||||
vttLines = append(vttLines, strconv.Itoa(i+1))
|
vttLines = append(vttLines, strconv.Itoa(i+1))
|
||||||
time := utils.GetVTTTime(marker.Seconds)
|
time := utils.GetVTTTime(marker.Seconds)
|
||||||
vttLines = append(vttLines, time+" --> "+time)
|
vttLines = append(vttLines, time+" --> "+time)
|
||||||
vttLines = append(vttLines, rs.getChapterVttTitle(r.Context(), marker))
|
|
||||||
|
vttTitle, err := rs.getChapterVttTitle(r.Context(), marker)
|
||||||
|
if errors.Is(err, context.Canceled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
logger.Warnf("read transaction error on fetch scene marker title: %v", err)
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vttLines = append(vttLines, *vttTitle)
|
||||||
vttLines = append(vttLines, "")
|
vttLines = append(vttLines, "")
|
||||||
}
|
}
|
||||||
vtt := strings.Join(vttLines, "\n")
|
vtt := strings.Join(vttLines, "\n")
|
||||||
|
|
@ -327,35 +344,50 @@ func (rs sceneRoutes) InteractiveHeatmap(w http.ResponseWriter, r *http.Request)
|
||||||
func (rs sceneRoutes) Caption(w http.ResponseWriter, r *http.Request, lang string, ext string) {
|
func (rs sceneRoutes) Caption(w http.ResponseWriter, r *http.Request, lang string, ext string) {
|
||||||
s := r.Context().Value(sceneKey).(*models.Scene)
|
s := r.Context().Value(sceneKey).(*models.Scene)
|
||||||
|
|
||||||
if err := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
var captions []*models.VideoCaption
|
||||||
|
readTxnErr := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
||||||
var err error
|
var err error
|
||||||
primaryFile := s.Files.Primary()
|
primaryFile := s.Files.Primary()
|
||||||
if primaryFile == nil {
|
if primaryFile == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
captions, err := rs.captionFinder.GetCaptions(ctx, primaryFile.Base().ID)
|
captions, err = rs.captionFinder.GetCaptions(ctx, primaryFile.Base().ID)
|
||||||
|
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
if errors.Is(readTxnErr, context.Canceled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if readTxnErr != nil {
|
||||||
|
logger.Warnf("read transaction error on fetch scene captions: %v", readTxnErr)
|
||||||
|
http.Error(w, readTxnErr.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for _, caption := range captions {
|
for _, caption := range captions {
|
||||||
if lang == caption.LanguageCode && ext == caption.CaptionType {
|
if lang != caption.LanguageCode || ext != caption.CaptionType {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
sub, err := video.ReadSubs(caption.Path(s.Path))
|
sub, err := video.ReadSubs(caption.Path(s.Path))
|
||||||
if err == nil {
|
if err != nil {
|
||||||
|
logger.Warnf("error while reading subs: %v", err)
|
||||||
|
http.Error(w, readTxnErr.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
err = sub.WriteToWebVTT(&b)
|
err = sub.WriteToWebVTT(&b)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
|
http.Error(w, readTxnErr.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "text/vtt")
|
w.Header().Set("Content-Type", "text/vtt")
|
||||||
w.Header().Add("Cache-Control", "no-cache")
|
w.Header().Add("Cache-Control", "no-cache")
|
||||||
_, _ = b.WriteTo(w)
|
_, _ = b.WriteTo(w)
|
||||||
}
|
}
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Debugf("Error while reading subs: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rs sceneRoutes) CaptionLang(w http.ResponseWriter, r *http.Request) {
|
func (rs sceneRoutes) CaptionLang(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
@ -387,13 +419,17 @@ func (rs sceneRoutes) SceneMarkerStream(w http.ResponseWriter, r *http.Request)
|
||||||
scene := r.Context().Value(sceneKey).(*models.Scene)
|
scene := r.Context().Value(sceneKey).(*models.Scene)
|
||||||
sceneMarkerID, _ := strconv.Atoi(chi.URLParam(r, "sceneMarkerId"))
|
sceneMarkerID, _ := strconv.Atoi(chi.URLParam(r, "sceneMarkerId"))
|
||||||
var sceneMarker *models.SceneMarker
|
var sceneMarker *models.SceneMarker
|
||||||
if err := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
readTxnErr := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
||||||
var err error
|
var err error
|
||||||
sceneMarker, err = rs.sceneMarkerFinder.Find(ctx, sceneMarkerID)
|
sceneMarker, err = rs.sceneMarkerFinder.Find(ctx, sceneMarkerID)
|
||||||
return err
|
return err
|
||||||
}); err != nil {
|
})
|
||||||
logger.Warnf("Error when getting scene marker for stream: %s", err.Error())
|
if errors.Is(readTxnErr, context.Canceled) {
|
||||||
http.Error(w, http.StatusText(500), 500)
|
return
|
||||||
|
}
|
||||||
|
if readTxnErr != nil {
|
||||||
|
logger.Warnf("read transaction error on fetch scene marker: %v", readTxnErr)
|
||||||
|
http.Error(w, readTxnErr.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -410,13 +446,17 @@ func (rs sceneRoutes) SceneMarkerPreview(w http.ResponseWriter, r *http.Request)
|
||||||
scene := r.Context().Value(sceneKey).(*models.Scene)
|
scene := r.Context().Value(sceneKey).(*models.Scene)
|
||||||
sceneMarkerID, _ := strconv.Atoi(chi.URLParam(r, "sceneMarkerId"))
|
sceneMarkerID, _ := strconv.Atoi(chi.URLParam(r, "sceneMarkerId"))
|
||||||
var sceneMarker *models.SceneMarker
|
var sceneMarker *models.SceneMarker
|
||||||
if err := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
readTxnErr := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
||||||
var err error
|
var err error
|
||||||
sceneMarker, err = rs.sceneMarkerFinder.Find(ctx, sceneMarkerID)
|
sceneMarker, err = rs.sceneMarkerFinder.Find(ctx, sceneMarkerID)
|
||||||
return err
|
return err
|
||||||
}); err != nil {
|
})
|
||||||
logger.Warnf("Error when getting scene marker for stream: %s", err.Error())
|
if errors.Is(readTxnErr, context.Canceled) {
|
||||||
http.Error(w, http.StatusText(500), 500)
|
return
|
||||||
|
}
|
||||||
|
if readTxnErr != nil {
|
||||||
|
logger.Warnf("read transaction error on fetch scene marker preview: %v", readTxnErr)
|
||||||
|
http.Error(w, readTxnErr.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -443,13 +483,17 @@ func (rs sceneRoutes) SceneMarkerScreenshot(w http.ResponseWriter, r *http.Reque
|
||||||
scene := r.Context().Value(sceneKey).(*models.Scene)
|
scene := r.Context().Value(sceneKey).(*models.Scene)
|
||||||
sceneMarkerID, _ := strconv.Atoi(chi.URLParam(r, "sceneMarkerId"))
|
sceneMarkerID, _ := strconv.Atoi(chi.URLParam(r, "sceneMarkerId"))
|
||||||
var sceneMarker *models.SceneMarker
|
var sceneMarker *models.SceneMarker
|
||||||
if err := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
readTxnErr := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
||||||
var err error
|
var err error
|
||||||
sceneMarker, err = rs.sceneMarkerFinder.Find(ctx, sceneMarkerID)
|
sceneMarker, err = rs.sceneMarkerFinder.Find(ctx, sceneMarkerID)
|
||||||
return err
|
return err
|
||||||
}); err != nil {
|
})
|
||||||
logger.Warnf("Error when getting scene marker for stream: %s", err.Error())
|
if errors.Is(readTxnErr, context.Canceled) {
|
||||||
http.Error(w, http.StatusText(500), 500)
|
return
|
||||||
|
}
|
||||||
|
if readTxnErr != nil {
|
||||||
|
logger.Warnf("read transaction error on fetch scene marker screenshot: %v", readTxnErr)
|
||||||
|
http.Error(w, readTxnErr.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -480,7 +524,7 @@ func (rs sceneRoutes) SceneCtx(next http.Handler) http.Handler {
|
||||||
sceneID, _ := strconv.Atoi(sceneIdentifierQueryParam)
|
sceneID, _ := strconv.Atoi(sceneIdentifierQueryParam)
|
||||||
|
|
||||||
var scene *models.Scene
|
var scene *models.Scene
|
||||||
readTxnErr := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
_ = txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
||||||
qb := rs.sceneFinder
|
qb := rs.sceneFinder
|
||||||
if sceneID == 0 {
|
if sceneID == 0 {
|
||||||
var scenes []*models.Scene
|
var scenes []*models.Scene
|
||||||
|
|
@ -505,10 +549,6 @@ func (rs sceneRoutes) SceneCtx(next http.Handler) http.Handler {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if readTxnErr != nil {
|
|
||||||
logger.Warnf("error executing SceneCtx transaction: %v", readTxnErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if scene == nil {
|
if scene == nil {
|
||||||
http.Error(w, http.StatusText(404), 404)
|
http.Error(w, http.StatusText(404), 404)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi"
|
||||||
"github.com/stashapp/stash/pkg/logger"
|
"github.com/stashapp/stash/pkg/logger"
|
||||||
|
|
@ -42,12 +41,15 @@ func (rs studioRoutes) Image(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
var image []byte
|
var image []byte
|
||||||
if defaultParam != "true" {
|
if defaultParam != "true" {
|
||||||
err := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
readTxnErr := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
||||||
image, _ = rs.studioFinder.GetImage(ctx, studio.ID)
|
image, _ = rs.studioFinder.GetImage(ctx, studio.ID)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if errors.Is(readTxnErr, context.Canceled) {
|
||||||
logger.Warnf("read transaction error while fetching studio image: %v", err)
|
return
|
||||||
|
}
|
||||||
|
if readTxnErr != nil {
|
||||||
|
logger.Warnf("read transaction error on fetch studio image: %v", readTxnErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,12 +58,7 @@ func (rs studioRoutes) Image(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := utils.ServeImage(image, w, r); err != nil {
|
if err := utils.ServeImage(image, w, r); err != nil {
|
||||||
// Broken pipe errors are common when serving images and the remote
|
logger.Warnf("error serving studio image: %v", err)
|
||||||
// connection closes the connection. Filter them out of the error
|
|
||||||
// messages, as they are benign.
|
|
||||||
if !errors.Is(err, syscall.EPIPE) {
|
|
||||||
logger.Warnf("cannot serve studio image: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,11 +71,12 @@ func (rs studioRoutes) StudioCtx(next http.Handler) http.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
var studio *models.Studio
|
var studio *models.Studio
|
||||||
if err := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
_ = txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
||||||
var err error
|
var err error
|
||||||
studio, err = rs.studioFinder.Find(ctx, studioID)
|
studio, err = rs.studioFinder.Find(ctx, studioID)
|
||||||
return err
|
return err
|
||||||
}); err != nil {
|
})
|
||||||
|
if studio == nil {
|
||||||
http.Error(w, http.StatusText(404), 404)
|
http.Error(w, http.StatusText(404), 404)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
|
@ -40,12 +41,15 @@ func (rs tagRoutes) Image(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
var image []byte
|
var image []byte
|
||||||
if defaultParam != "true" {
|
if defaultParam != "true" {
|
||||||
err := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
readTxnErr := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
||||||
image, _ = rs.tagFinder.GetImage(ctx, tag.ID)
|
image, _ = rs.tagFinder.GetImage(ctx, tag.ID)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if errors.Is(readTxnErr, context.Canceled) {
|
||||||
logger.Warnf("read transaction error while getting tag image: %v", err)
|
return
|
||||||
|
}
|
||||||
|
if readTxnErr != nil {
|
||||||
|
logger.Warnf("read transaction error on fetch tag image: %v", readTxnErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,11 +71,12 @@ func (rs tagRoutes) TagCtx(next http.Handler) http.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
var tag *models.Tag
|
var tag *models.Tag
|
||||||
if err := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
_ = txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
||||||
var err error
|
var err error
|
||||||
tag, err = rs.tagFinder.Find(ctx, tagID)
|
tag, err = rs.tagFinder.Find(ctx, tagID)
|
||||||
return err
|
return err
|
||||||
}); err != nil {
|
})
|
||||||
|
if tag == nil {
|
||||||
http.Error(w, http.StatusText(404), 404)
|
http.Error(w, http.StatusText(404), 404)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package manager
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/stashapp/stash/internal/manager/config"
|
"github.com/stashapp/stash/internal/manager/config"
|
||||||
|
|
@ -81,16 +82,21 @@ func (s *SceneServer) ServeScreenshot(scene *models.Scene, w http.ResponseWriter
|
||||||
http.ServeFile(w, r, filepath)
|
http.ServeFile(w, r, filepath)
|
||||||
} else {
|
} else {
|
||||||
var cover []byte
|
var cover []byte
|
||||||
err := txn.WithTxn(r.Context(), s.TxnManager, func(ctx context.Context) error {
|
readTxnErr := txn.WithTxn(r.Context(), s.TxnManager, func(ctx context.Context) error {
|
||||||
cover, _ = s.SceneCoverGetter.GetCover(ctx, scene.ID)
|
cover, _ = s.SceneCoverGetter.GetCover(ctx, scene.ID)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if errors.Is(readTxnErr, context.Canceled) {
|
||||||
logger.Warnf("read transaction failed while serving screenshot: %v", err)
|
return
|
||||||
|
}
|
||||||
|
if readTxnErr != nil {
|
||||||
|
logger.Warnf("read transaction error on fetch screenshot: %v", readTxnErr)
|
||||||
|
http.Error(w, readTxnErr.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = utils.ServeImage(cover, w, r); err != nil {
|
if err := utils.ServeImage(cover, w, r); err != nil {
|
||||||
logger.Warnf("unable to serve screenshot image: %v", err)
|
logger.Warnf("error serving screenshot image: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,12 @@ package ffmpeg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"github.com/stashapp/stash/pkg/logger"
|
"github.com/stashapp/stash/pkg/logger"
|
||||||
)
|
)
|
||||||
|
|
@ -35,8 +37,8 @@ func (s *Stream) Serve(w http.ResponseWriter, r *http.Request) {
|
||||||
// process killing should be handled by command context
|
// process killing should be handled by command context
|
||||||
|
|
||||||
_, err := io.Copy(w, s.Stdout)
|
_, err := io.Copy(w, s.Stdout)
|
||||||
if err != nil {
|
if err != nil && !errors.Is(err, syscall.EPIPE) {
|
||||||
logger.Errorf("[stream] error serving transcoded video file: %s", err.Error())
|
logger.Errorf("[stream] error serving transcoded video file: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,12 @@ package file
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stashapp/stash/pkg/logger"
|
"github.com/stashapp/stash/pkg/logger"
|
||||||
|
|
@ -137,8 +139,9 @@ func (f *BaseFile) Serve(fs FS, w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if k, err := w.Write(data); err != nil {
|
k, err := w.Write(data)
|
||||||
logger.Warnf("failure while serving image (wrote %v bytes out of %v): %v", k, len(data), err)
|
if err != nil && !errors.Is(err, syscall.EPIPE) {
|
||||||
|
logger.Warnf("error serving file (wrote %v bytes out of %v): %v", k, len(data), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -84,14 +84,10 @@ func FileExists(path string) (bool, error) {
|
||||||
func WriteFile(path string, file []byte) error {
|
func WriteFile(path string, file []byte) error {
|
||||||
pathErr := EnsureDirAll(filepath.Dir(path))
|
pathErr := EnsureDirAll(filepath.Dir(path))
|
||||||
if pathErr != nil {
|
if pathErr != nil {
|
||||||
return fmt.Errorf("cannot ensure path %s", pathErr)
|
return fmt.Errorf("cannot ensure path exists: %w", pathErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := os.WriteFile(path, file, 0755)
|
return os.WriteFile(path, file, 0755)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("write error for thumbnail %s: %s ", path, err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNameFromPath returns the name of a file from its path
|
// GetNameFromPath returns the name of a file from its path
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,13 @@ import (
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -127,5 +129,11 @@ func ServeImage(image []byte, w http.ResponseWriter, r *http.Request) error {
|
||||||
w.Header().Add("Etag", etag)
|
w.Header().Add("Etag", etag)
|
||||||
w.Header().Set("Cache-Control", "public, max-age=604800, immutable")
|
w.Header().Set("Cache-Control", "public, max-age=604800, immutable")
|
||||||
_, err := w.Write(image)
|
_, err := w.Write(image)
|
||||||
|
// Broken pipe errors are common when serving images and the remote
|
||||||
|
// connection closes the connection. Filter them out of the error
|
||||||
|
// messages, as they are benign.
|
||||||
|
if errors.Is(err, syscall.EPIPE) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue