feat(stream): refresh expired debrid links mid-stream (hueco #2/2c)
Debrid direct links are time-limited; a long playback can outlive the link the session was created with. When a debrid source dies mid-stream the daemon now re-resolves a fresh link for the same content and resumes — no torrent fallback, no playback restart. - debridFileProvider holds the URL behind a mutex; on an expired-link status (401/403/404/410) the ranged reader re-resolves via a refresh callback and retries (bounded: 1 initial + 1 post-refresh attempt). A browser opens several range connections, so the refresh is coalesced singleflight-style — N readers hitting the dead link share ONE re-resolution, not N. - HLS-from-URL: the auto-restart supervisor re-resolves the link before relaunching ffmpeg (else it just retries the dead URL and burns the retry budget). The mutable URL lives in s.liveURL under s.mu — restartFromSegment reads it from the HTTP handler goroutine too (seek-restart), so cfg stays immutable and the write races nothing. - agentClient.RefreshStreamURL → POST /api/internal/agent/stream-url. Cross-source torrent<->debrid swap (the rare "debrid genuinely gone" case) is intentionally deferred. Reader refresh + coalescing covered by unit tests (incl. -race); the web endpoint re-resolves against a real AllDebrid account.
This commit is contained in:
parent
4946982783
commit
7562b62241
5 changed files with 337 additions and 56 deletions
|
|
@ -598,10 +598,16 @@ func runDaemonStart() error {
|
|||
// no-op (matches the HLS branch's handoff rationale).
|
||||
if sess.DirectURL != "" && sess.PlayMethod != "hls" {
|
||||
playerSessionRegistry.add(sess.SessionID, func() { streamSrv.ClearFile() })
|
||||
// refresh re-resolves a fresh debrid link when this one expires
|
||||
// mid-stream (hueco #2 / 2c). Bound to the daemon ctx so a shutdown
|
||||
// cancels an in-flight refresh.
|
||||
refresh := func(rctx context.Context) (string, error) {
|
||||
return agentClient.RefreshStreamURL(rctx, sess.SessionID)
|
||||
}
|
||||
go func() {
|
||||
bctx, cancel := context.WithTimeout(ctx, 15*time.Second)
|
||||
defer cancel()
|
||||
provider, perr := engine.NewDebridFileProvider(bctx, sess.DirectURL, sess.FileName, sess.FileSize)
|
||||
provider, perr := engine.NewDebridFileProvider(bctx, sess.DirectURL, sess.FileName, sess.FileSize, refresh)
|
||||
if perr != nil {
|
||||
playerSessionRegistry.remove(sess.SessionID)
|
||||
log.Printf("[stream %s] debrid provider failed: %v", agent.ShortID(sess.SessionID), perr)
|
||||
|
|
@ -640,6 +646,11 @@ func runDaemonStart() error {
|
|||
AudioIndex: sess.AudioIndex,
|
||||
Transcode: tcRuntime,
|
||||
Cache: hlsCache,
|
||||
// 2c: refresh the debrid link if it expires mid-transcode; the
|
||||
// auto-restart supervisor calls this before relaunching ffmpeg.
|
||||
RefreshURL: func(rctx context.Context) (string, error) {
|
||||
return agentClient.RefreshStreamURL(rctx, sess.SessionID)
|
||||
},
|
||||
}, hlsCtx, hlsCancel)
|
||||
log.Printf("[hls %s] debrid HLS-from-URL: %s", agent.ShortID(sess.SessionID), sess.FileName)
|
||||
return
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue