mirror of
https://github.com/stashapp/stash.git
synced 2025-12-15 21:03:22 +01:00
Fix VTT thumbnails (#3513)
* Fix VTT thumbnails * Add API key to sceneStreams query * Add scene ID routes
This commit is contained in:
parent
0c1b02380e
commit
7a2ee7cdda
5 changed files with 72 additions and 65 deletions
|
|
@ -179,13 +179,13 @@ func (r *sceneResolver) Paths(ctx context.Context, obj *models.Scene) (*ScenePat
|
|||
baseURL, _ := ctx.Value(BaseURLCtxKey).(string)
|
||||
config := manager.GetInstance().Config
|
||||
builder := urlbuilders.NewSceneURLBuilder(baseURL, obj.ID)
|
||||
builder.APIKey = config.GetAPIKey()
|
||||
screenshotPath := builder.GetScreenshotURL(obj.UpdatedAt)
|
||||
previewPath := builder.GetStreamPreviewURL()
|
||||
streamPath := builder.GetStreamURL().String()
|
||||
streamPath := builder.GetStreamURL(config.GetAPIKey()).String()
|
||||
webpPath := builder.GetStreamPreviewImageURL()
|
||||
vttPath := builder.GetSpriteVTTURL()
|
||||
spritePath := builder.GetSpriteURL()
|
||||
objHash := obj.GetHash(config.GetVideoFileNamingAlgorithm())
|
||||
vttPath := builder.GetSpriteVTTURL(objHash)
|
||||
spritePath := builder.GetSpriteURL(objHash)
|
||||
chaptersVttPath := builder.GetChaptersVTTURL()
|
||||
funscriptPath := builder.GetFunscriptURL()
|
||||
captionBasePath := builder.GetCaptionURL()
|
||||
|
|
@ -371,9 +371,9 @@ func (r *sceneResolver) SceneStreams(ctx context.Context, obj *models.Scene) ([]
|
|||
|
||||
baseURL, _ := ctx.Value(BaseURLCtxKey).(string)
|
||||
builder := urlbuilders.NewSceneURLBuilder(baseURL, obj.ID)
|
||||
builder.APIKey = config.GetAPIKey()
|
||||
apiKey := config.GetAPIKey()
|
||||
|
||||
return manager.GetSceneStreamPaths(obj, builder.GetStreamURL(), config.GetMaxStreamingTranscodeSize())
|
||||
return manager.GetSceneStreamPaths(obj, builder.GetStreamURL(apiKey), config.GetMaxStreamingTranscodeSize())
|
||||
}
|
||||
|
||||
func (r *sceneResolver) Interactive(ctx context.Context, obj *models.Scene) (bool, error) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import (
|
|||
|
||||
"github.com/stashapp/stash/internal/api/urlbuilders"
|
||||
"github.com/stashapp/stash/internal/manager"
|
||||
"github.com/stashapp/stash/internal/manager/config"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
|
||||
|
|
@ -32,8 +31,11 @@ func (r *queryResolver) SceneStreams(ctx context.Context, id *string) ([]*manage
|
|||
return nil, errors.New("nil scene")
|
||||
}
|
||||
|
||||
config := manager.GetInstance().Config
|
||||
|
||||
baseURL, _ := ctx.Value(BaseURLCtxKey).(string)
|
||||
builder := urlbuilders.NewSceneURLBuilder(baseURL, scene.ID)
|
||||
apiKey := config.GetAPIKey()
|
||||
|
||||
return manager.GetSceneStreamPaths(scene, builder.GetStreamURL(), config.GetInstance().GetMaxStreamingTranscodeSize())
|
||||
return manager.GetSceneStreamPaths(scene, builder.GetStreamURL(apiKey), config.GetMaxStreamingTranscodeSize())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,9 @@ func (rs sceneRoutes) Routes() chi.Router {
|
|||
r.Get("/screenshot", rs.Screenshot)
|
||||
r.Get("/preview", rs.Preview)
|
||||
r.Get("/webp", rs.Webp)
|
||||
r.Get("/vtt/chapter", rs.ChapterVtt)
|
||||
r.Get("/vtt/chapter", rs.VttChapter)
|
||||
r.Get("/vtt/thumbs", rs.VttThumbs)
|
||||
r.Get("/vtt/sprite", rs.VttSprite)
|
||||
r.Get("/funscript", rs.Funscript)
|
||||
r.Get("/interactive_heatmap", rs.InteractiveHeatmap)
|
||||
r.Get("/caption", rs.CaptionLang)
|
||||
|
|
@ -77,8 +79,8 @@ func (rs sceneRoutes) Routes() chi.Router {
|
|||
r.Get("/scene_marker/{sceneMarkerId}/preview", rs.SceneMarkerPreview)
|
||||
r.Get("/scene_marker/{sceneMarkerId}/screenshot", rs.SceneMarkerScreenshot)
|
||||
})
|
||||
r.With(rs.SceneCtx).Get("/{sceneId}_thumbs.vtt", rs.VttThumbs)
|
||||
r.With(rs.SceneCtx).Get("/{sceneId}_sprite.jpg", rs.VttSprite)
|
||||
r.Get("/{sceneHash}_thumbs.vtt", rs.VttThumbs)
|
||||
r.Get("/{sceneHash}_sprite.jpg", rs.VttSprite)
|
||||
|
||||
return r
|
||||
}
|
||||
|
|
@ -87,11 +89,9 @@ func (rs sceneRoutes) Routes() chi.Router {
|
|||
|
||||
func (rs sceneRoutes) StreamDirect(w http.ResponseWriter, r *http.Request) {
|
||||
scene := r.Context().Value(sceneKey).(*models.Scene)
|
||||
sceneHash := scene.GetHash(config.GetInstance().GetVideoFileNamingAlgorithm())
|
||||
|
||||
fileNamingAlgo := config.GetInstance().GetVideoFileNamingAlgorithm()
|
||||
hash := scene.GetHash(fileNamingAlgo)
|
||||
|
||||
filepath := manager.GetInstance().Paths.Scene.GetStreamPath(scene.Path, hash)
|
||||
filepath := manager.GetInstance().Paths.Scene.GetStreamPath(scene.Path, sceneHash)
|
||||
streamRequestCtx := ffmpeg.NewStreamRequestContext(w, r)
|
||||
|
||||
// #2579 - hijacking and closing the connection here causes video playback to fail in Safari
|
||||
|
|
@ -229,8 +229,7 @@ func (rs sceneRoutes) streamSegment(w http.ResponseWriter, r *http.Request, stre
|
|||
logger.Warnf("[transcode] error parsing query form: %v", err)
|
||||
}
|
||||
|
||||
fileNamingAlgo := config.GetInstance().GetVideoFileNamingAlgorithm()
|
||||
hash := scene.GetHash(fileNamingAlgo)
|
||||
sceneHash := scene.GetHash(config.GetInstance().GetVideoFileNamingAlgorithm())
|
||||
|
||||
segment := chi.URLParam(r, "segment")
|
||||
resolution := r.Form.Get("resolution")
|
||||
|
|
@ -239,7 +238,7 @@ func (rs sceneRoutes) streamSegment(w http.ResponseWriter, r *http.Request, stre
|
|||
StreamType: streamType,
|
||||
VideoFile: f,
|
||||
Resolution: resolution,
|
||||
Hash: hash,
|
||||
Hash: sceneHash,
|
||||
Segment: segment,
|
||||
}
|
||||
|
||||
|
|
@ -258,7 +257,8 @@ func (rs sceneRoutes) Screenshot(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
func (rs sceneRoutes) Preview(w http.ResponseWriter, r *http.Request) {
|
||||
scene := r.Context().Value(sceneKey).(*models.Scene)
|
||||
filepath := manager.GetInstance().Paths.Scene.GetVideoPreviewPath(scene.GetHash(config.GetInstance().GetVideoFileNamingAlgorithm()))
|
||||
sceneHash := scene.GetHash(config.GetInstance().GetVideoFileNamingAlgorithm())
|
||||
filepath := manager.GetInstance().Paths.Scene.GetVideoPreviewPath(sceneHash)
|
||||
serveFileNoCache(w, r, filepath)
|
||||
}
|
||||
|
||||
|
|
@ -272,7 +272,8 @@ func serveFileNoCache(w http.ResponseWriter, r *http.Request, filepath string) {
|
|||
|
||||
func (rs sceneRoutes) Webp(w http.ResponseWriter, r *http.Request) {
|
||||
scene := r.Context().Value(sceneKey).(*models.Scene)
|
||||
filepath := manager.GetInstance().Paths.Scene.GetWebpPreviewPath(scene.GetHash(config.GetInstance().GetVideoFileNamingAlgorithm()))
|
||||
sceneHash := scene.GetHash(config.GetInstance().GetVideoFileNamingAlgorithm())
|
||||
filepath := manager.GetInstance().Paths.Scene.GetWebpPreviewPath(sceneHash)
|
||||
http.ServeFile(w, r, filepath)
|
||||
}
|
||||
|
||||
|
|
@ -308,7 +309,7 @@ func (rs sceneRoutes) getChapterVttTitle(ctx context.Context, marker *models.Sce
|
|||
return &title, nil
|
||||
}
|
||||
|
||||
func (rs sceneRoutes) ChapterVtt(w http.ResponseWriter, r *http.Request) {
|
||||
func (rs sceneRoutes) VttChapter(w http.ResponseWriter, r *http.Request) {
|
||||
scene := r.Context().Value(sceneKey).(*models.Scene)
|
||||
var sceneMarkers []*models.SceneMarker
|
||||
readTxnErr := txn.WithReadTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
||||
|
|
@ -350,6 +351,32 @@ func (rs sceneRoutes) ChapterVtt(w http.ResponseWriter, r *http.Request) {
|
|||
_, _ = w.Write([]byte(vtt))
|
||||
}
|
||||
|
||||
func (rs sceneRoutes) VttThumbs(w http.ResponseWriter, r *http.Request) {
|
||||
scene, ok := r.Context().Value(sceneKey).(*models.Scene)
|
||||
var sceneHash string
|
||||
if ok && scene != nil {
|
||||
sceneHash = scene.GetHash(config.GetInstance().GetVideoFileNamingAlgorithm())
|
||||
} else {
|
||||
sceneHash = chi.URLParam(r, "sceneHash")
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/vtt")
|
||||
filepath := manager.GetInstance().Paths.Scene.GetSpriteVttFilePath(sceneHash)
|
||||
http.ServeFile(w, r, filepath)
|
||||
}
|
||||
|
||||
func (rs sceneRoutes) VttSprite(w http.ResponseWriter, r *http.Request) {
|
||||
scene, ok := r.Context().Value(sceneKey).(*models.Scene)
|
||||
var sceneHash string
|
||||
if ok && scene != nil {
|
||||
sceneHash = scene.GetHash(config.GetInstance().GetVideoFileNamingAlgorithm())
|
||||
} else {
|
||||
sceneHash = chi.URLParam(r, "sceneHash")
|
||||
}
|
||||
w.Header().Set("Content-Type", "image/jpeg")
|
||||
filepath := manager.GetInstance().Paths.Scene.GetSpriteImageFilePath(sceneHash)
|
||||
http.ServeFile(w, r, filepath)
|
||||
}
|
||||
|
||||
func (rs sceneRoutes) Funscript(w http.ResponseWriter, r *http.Request) {
|
||||
s := r.Context().Value(sceneKey).(*models.Scene)
|
||||
funscript := video.GetFunscriptPath(s.Path)
|
||||
|
|
@ -358,8 +385,9 @@ func (rs sceneRoutes) Funscript(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
func (rs sceneRoutes) InteractiveHeatmap(w http.ResponseWriter, r *http.Request) {
|
||||
scene := r.Context().Value(sceneKey).(*models.Scene)
|
||||
sceneHash := scene.GetHash(config.GetInstance().GetVideoFileNamingAlgorithm())
|
||||
w.Header().Set("Content-Type", "image/png")
|
||||
filepath := manager.GetInstance().Paths.Scene.GetInteractiveHeatmapPath(scene.GetHash(config.GetInstance().GetVideoFileNamingAlgorithm()))
|
||||
filepath := manager.GetInstance().Paths.Scene.GetInteractiveHeatmapPath(sceneHash)
|
||||
http.ServeFile(w, r, filepath)
|
||||
}
|
||||
|
||||
|
|
@ -423,22 +451,9 @@ func (rs sceneRoutes) CaptionLang(w http.ResponseWriter, r *http.Request) {
|
|||
rs.Caption(w, r, l, ext)
|
||||
}
|
||||
|
||||
func (rs sceneRoutes) VttThumbs(w http.ResponseWriter, r *http.Request) {
|
||||
scene := r.Context().Value(sceneKey).(*models.Scene)
|
||||
w.Header().Set("Content-Type", "text/vtt")
|
||||
filepath := manager.GetInstance().Paths.Scene.GetSpriteVttFilePath(scene.GetHash(config.GetInstance().GetVideoFileNamingAlgorithm()))
|
||||
http.ServeFile(w, r, filepath)
|
||||
}
|
||||
|
||||
func (rs sceneRoutes) VttSprite(w http.ResponseWriter, r *http.Request) {
|
||||
scene := r.Context().Value(sceneKey).(*models.Scene)
|
||||
w.Header().Set("Content-Type", "image/jpeg")
|
||||
filepath := manager.GetInstance().Paths.Scene.GetSpriteImageFilePath(scene.GetHash(config.GetInstance().GetVideoFileNamingAlgorithm()))
|
||||
http.ServeFile(w, r, filepath)
|
||||
}
|
||||
|
||||
func (rs sceneRoutes) SceneMarkerStream(w http.ResponseWriter, r *http.Request) {
|
||||
scene := r.Context().Value(sceneKey).(*models.Scene)
|
||||
sceneHash := scene.GetHash(config.GetInstance().GetVideoFileNamingAlgorithm())
|
||||
sceneMarkerID, _ := strconv.Atoi(chi.URLParam(r, "sceneMarkerId"))
|
||||
var sceneMarker *models.SceneMarker
|
||||
readTxnErr := txn.WithReadTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
||||
|
|
@ -460,12 +475,13 @@ func (rs sceneRoutes) SceneMarkerStream(w http.ResponseWriter, r *http.Request)
|
|||
return
|
||||
}
|
||||
|
||||
filepath := manager.GetInstance().Paths.SceneMarkers.GetVideoPreviewPath(scene.GetHash(config.GetInstance().GetVideoFileNamingAlgorithm()), int(sceneMarker.Seconds))
|
||||
filepath := manager.GetInstance().Paths.SceneMarkers.GetVideoPreviewPath(sceneHash, int(sceneMarker.Seconds))
|
||||
http.ServeFile(w, r, filepath)
|
||||
}
|
||||
|
||||
func (rs sceneRoutes) SceneMarkerPreview(w http.ResponseWriter, r *http.Request) {
|
||||
scene := r.Context().Value(sceneKey).(*models.Scene)
|
||||
sceneHash := scene.GetHash(config.GetInstance().GetVideoFileNamingAlgorithm())
|
||||
sceneMarkerID, _ := strconv.Atoi(chi.URLParam(r, "sceneMarkerId"))
|
||||
var sceneMarker *models.SceneMarker
|
||||
readTxnErr := txn.WithReadTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
||||
|
|
@ -487,7 +503,7 @@ func (rs sceneRoutes) SceneMarkerPreview(w http.ResponseWriter, r *http.Request)
|
|||
return
|
||||
}
|
||||
|
||||
filepath := manager.GetInstance().Paths.SceneMarkers.GetWebpPreviewPath(scene.GetHash(config.GetInstance().GetVideoFileNamingAlgorithm()), int(sceneMarker.Seconds))
|
||||
filepath := manager.GetInstance().Paths.SceneMarkers.GetWebpPreviewPath(sceneHash, int(sceneMarker.Seconds))
|
||||
|
||||
// If the image doesn't exist, send the placeholder
|
||||
exists, _ := fsutil.FileExists(filepath)
|
||||
|
|
@ -503,6 +519,7 @@ func (rs sceneRoutes) SceneMarkerPreview(w http.ResponseWriter, r *http.Request)
|
|||
|
||||
func (rs sceneRoutes) SceneMarkerScreenshot(w http.ResponseWriter, r *http.Request) {
|
||||
scene := r.Context().Value(sceneKey).(*models.Scene)
|
||||
sceneHash := scene.GetHash(config.GetInstance().GetVideoFileNamingAlgorithm())
|
||||
sceneMarkerID, _ := strconv.Atoi(chi.URLParam(r, "sceneMarkerId"))
|
||||
var sceneMarker *models.SceneMarker
|
||||
readTxnErr := txn.WithReadTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
||||
|
|
@ -524,7 +541,7 @@ func (rs sceneRoutes) SceneMarkerScreenshot(w http.ResponseWriter, r *http.Reque
|
|||
return
|
||||
}
|
||||
|
||||
filepath := manager.GetInstance().Paths.SceneMarkers.GetScreenshotPath(scene.GetHash(config.GetInstance().GetVideoFileNamingAlgorithm()), int(sceneMarker.Seconds))
|
||||
filepath := manager.GetInstance().Paths.SceneMarkers.GetScreenshotPath(sceneHash, int(sceneMarker.Seconds))
|
||||
|
||||
// If the image doesn't exist, send the placeholder
|
||||
exists, _ := fsutil.FileExists(filepath)
|
||||
|
|
@ -542,28 +559,16 @@ func (rs sceneRoutes) SceneMarkerScreenshot(w http.ResponseWriter, r *http.Reque
|
|||
|
||||
func (rs sceneRoutes) SceneCtx(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
sceneIdentifierQueryParam := chi.URLParam(r, "sceneId")
|
||||
sceneID, _ := strconv.Atoi(sceneIdentifierQueryParam)
|
||||
sceneID, err := strconv.Atoi(chi.URLParam(r, "sceneId"))
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var scene *models.Scene
|
||||
_ = txn.WithReadTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
|
||||
qb := rs.sceneFinder
|
||||
if sceneID == 0 {
|
||||
var scenes []*models.Scene
|
||||
// determine checksum/os by the length of the query param
|
||||
if len(sceneIdentifierQueryParam) == 32 {
|
||||
scenes, _ = qb.FindByChecksum(ctx, sceneIdentifierQueryParam)
|
||||
|
||||
} else {
|
||||
scenes, _ = qb.FindByOSHash(ctx, sceneIdentifierQueryParam)
|
||||
}
|
||||
|
||||
if len(scenes) > 0 {
|
||||
scene = scenes[0]
|
||||
}
|
||||
} else {
|
||||
scene, _ = qb.Find(ctx, sceneID)
|
||||
}
|
||||
scene, _ = qb.Find(ctx, sceneID)
|
||||
|
||||
if scene != nil {
|
||||
if err := scene.LoadPrimaryFile(ctx, rs.fileFinder); err != nil {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import (
|
|||
type SceneURLBuilder struct {
|
||||
BaseURL string
|
||||
SceneID string
|
||||
APIKey string
|
||||
}
|
||||
|
||||
func NewSceneURLBuilder(baseURL string, sceneID int) SceneURLBuilder {
|
||||
|
|
@ -20,16 +19,16 @@ func NewSceneURLBuilder(baseURL string, sceneID int) SceneURLBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
func (b SceneURLBuilder) GetStreamURL() *url.URL {
|
||||
func (b SceneURLBuilder) GetStreamURL(apiKey string) *url.URL {
|
||||
u, err := url.Parse(fmt.Sprintf("%s/scene/%s/stream", b.BaseURL, b.SceneID))
|
||||
if err != nil {
|
||||
// shouldn't happen
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if b.APIKey != "" {
|
||||
if apiKey != "" {
|
||||
v := u.Query()
|
||||
v.Set("apikey", b.APIKey)
|
||||
v.Set("apikey", apiKey)
|
||||
u.RawQuery = v.Encode()
|
||||
}
|
||||
return u
|
||||
|
|
@ -43,12 +42,12 @@ func (b SceneURLBuilder) GetStreamPreviewImageURL() string {
|
|||
return b.BaseURL + "/scene/" + b.SceneID + "/webp"
|
||||
}
|
||||
|
||||
func (b SceneURLBuilder) GetSpriteVTTURL() string {
|
||||
return b.BaseURL + "/scene/" + b.SceneID + "_thumbs.vtt"
|
||||
func (b SceneURLBuilder) GetSpriteVTTURL(checksum string) string {
|
||||
return b.BaseURL + "/scene/" + checksum + "_thumbs.vtt"
|
||||
}
|
||||
|
||||
func (b SceneURLBuilder) GetSpriteURL() string {
|
||||
return b.BaseURL + "/scene/" + b.SceneID + "_sprite.jpg"
|
||||
func (b SceneURLBuilder) GetSpriteURL(checksum string) string {
|
||||
return b.BaseURL + "/scene/" + checksum + "_sprite.jpg"
|
||||
}
|
||||
|
||||
func (b SceneURLBuilder) GetScreenshotURL(updateTime time.Time) string {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
* Overhauled and improved HLS streaming. ([#3274](https://github.com/stashapp/stash/pull/3274))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
* Fixed sprites not being displayed for scenes with numeric-only hashes. ([#3513](https://github.com/stashapp/stash/pull/3513))
|
||||
* Fixed Save button being disabled when stting Tag image. ([#3509](https://github.com/stashapp/stash/pull/3509))
|
||||
* Fixed incorrect performer with identical name being matched when scraping from stash-box. ([#3488](https://github.com/stashapp/stash/pull/3488))
|
||||
* Fixed scene cover not being included when submitting file-less scenes to stash-box. ([#3465](https://github.com/stashapp/stash/pull/3465))
|
||||
|
|
|
|||
Loading…
Reference in a new issue