mirror of https://github.com/stashapp/stash.git
Scene player fixes and improvements (#5340)
* Don't log context canceled error during live transcode * Pause live transcode if still scrubbing * Debounce loading live transcode source to avoid multiple ffmpeg instances * Don't start from start or resume time if seeking before playing * Play video when seeked before playing
This commit is contained in:
parent
3e4515e62a
commit
4697271294
|
@ -1,6 +1,7 @@
|
|||
package ffmpeg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
|
@ -230,7 +231,10 @@ func (sm *StreamManager) ServeTranscode(w http.ResponseWriter, r *http.Request,
|
|||
handler, err := sm.getTranscodeStream(lockCtx, options)
|
||||
|
||||
if err != nil {
|
||||
logger.Errorf("[transcode] error transcoding video file: %v", err)
|
||||
// don't log context canceled errors
|
||||
if !errors.Is(err, context.Canceled) {
|
||||
logger.Errorf("[transcode] error transcoding video file: %v", err)
|
||||
}
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
if _, err := w.Write([]byte(err.Error())); err != nil {
|
||||
logger.Warnf("[transcode] error writing response: %v", err)
|
||||
|
|
|
@ -451,12 +451,28 @@ export const ScenePlayer: React.FC<IScenePlayerProps> = ({
|
|||
if (!player) return;
|
||||
|
||||
function canplay(this: VideoJsPlayer) {
|
||||
// if we're seeking before starting, don't set the initial timestamp
|
||||
// when starting from the beginning, there is a small delay before the event
|
||||
// is triggered, so we can't just check if the time is 0
|
||||
if (this.currentTime() >= 0.1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (initialTimestamp.current !== -1) {
|
||||
this.currentTime(initialTimestamp.current);
|
||||
initialTimestamp.current = -1;
|
||||
}
|
||||
}
|
||||
|
||||
function timeupdate(this: VideoJsPlayer) {
|
||||
// fired when seeking
|
||||
// check if we haven't started playing yet
|
||||
// if so, start playing
|
||||
if (!started.current) {
|
||||
this.play();
|
||||
}
|
||||
}
|
||||
|
||||
function playing(this: VideoJsPlayer) {
|
||||
// This still runs even if autoplay failed on Safari,
|
||||
// only set flag if actually playing
|
||||
|
@ -477,12 +493,14 @@ export const ScenePlayer: React.FC<IScenePlayerProps> = ({
|
|||
player.on("playing", playing);
|
||||
player.on("loadstart", loadstart);
|
||||
player.on("fullscreenchange", fullscreenchange);
|
||||
player.on("timeupdate", timeupdate);
|
||||
|
||||
return () => {
|
||||
player.off("canplay", canplay);
|
||||
player.off("playing", playing);
|
||||
player.off("loadstart", loadstart);
|
||||
player.off("fullscreenchange", fullscreenchange);
|
||||
player.off("timeupdate", timeupdate);
|
||||
};
|
||||
}, [getPlayer]);
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { debounce } from "lodash-es";
|
||||
import videojs, { VideoJsPlayer } from "video.js";
|
||||
|
||||
export interface ISource extends videojs.Tech.SourceObject {
|
||||
|
@ -10,6 +11,9 @@ interface ICue extends TextTrackCue {
|
|||
_endTime?: number;
|
||||
}
|
||||
|
||||
// delay before loading new source after setting currentTime
|
||||
const loadDelay = 200;
|
||||
|
||||
function offsetMiddleware(player: VideoJsPlayer) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- allow access to private tech methods
|
||||
let tech: any;
|
||||
|
@ -50,6 +54,34 @@ function offsetMiddleware(player: VideoJsPlayer) {
|
|||
}
|
||||
}
|
||||
|
||||
const loadSource = debounce(
|
||||
(seconds: number) => {
|
||||
const srcUrl = new URL(source.src);
|
||||
srcUrl.searchParams.set("start", seconds.toString());
|
||||
source.src = srcUrl.toString();
|
||||
|
||||
const poster = player.poster();
|
||||
const playbackRate = tech.playbackRate();
|
||||
seeking = tech.paused() ? 1 : 2;
|
||||
player.poster("");
|
||||
tech.setSource(source);
|
||||
tech.setPlaybackRate(playbackRate);
|
||||
tech.one("canplay", () => {
|
||||
player.poster(poster);
|
||||
if (seeking === 1 || tech.scrubbing()) {
|
||||
tech.pause();
|
||||
}
|
||||
seeking = 0;
|
||||
});
|
||||
tech.trigger("timeupdate");
|
||||
tech.trigger("pause");
|
||||
tech.trigger("seeking");
|
||||
tech.play();
|
||||
},
|
||||
loadDelay,
|
||||
{ leading: true }
|
||||
);
|
||||
|
||||
return {
|
||||
setTech(newTech: videojs.Tech) {
|
||||
tech = newTech;
|
||||
|
@ -144,27 +176,7 @@ function offsetMiddleware(player: VideoJsPlayer) {
|
|||
|
||||
updateOffsetStart(seconds);
|
||||
|
||||
const srcUrl = new URL(source.src);
|
||||
srcUrl.searchParams.set("start", seconds.toString());
|
||||
source.src = srcUrl.toString();
|
||||
|
||||
const poster = player.poster();
|
||||
const playbackRate = tech.playbackRate();
|
||||
seeking = tech.paused() ? 1 : 2;
|
||||
player.poster("");
|
||||
tech.setSource(source);
|
||||
tech.setPlaybackRate(playbackRate);
|
||||
tech.one("canplay", () => {
|
||||
player.poster(poster);
|
||||
if (seeking === 1) {
|
||||
tech.pause();
|
||||
}
|
||||
seeking = 0;
|
||||
});
|
||||
tech.trigger("timeupdate");
|
||||
tech.trigger("pause");
|
||||
tech.trigger("seeking");
|
||||
tech.play();
|
||||
loadSource(seconds);
|
||||
|
||||
return 0;
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue