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
|
|
@ -130,6 +130,27 @@ func (c *Client) MarkSessionReady(ctx context.Context, sessionID string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// RefreshStreamURL re-resolves a fresh debrid direct URL for a live streaming
|
||||
// session (hueco #2 / 2c). Called by the daemon when a debrid source expires
|
||||
// mid-stream (the link is time-limited; the content is still cached). Returns
|
||||
// the new URL on success; an error (incl. 409/410) means refresh isn't
|
||||
// possible and the caller should stop trying.
|
||||
func (c *Client) RefreshStreamURL(ctx context.Context, sessionID string) (string, error) {
|
||||
req := struct {
|
||||
SessionID string `json:"sessionId"`
|
||||
}{SessionID: sessionID}
|
||||
var resp struct {
|
||||
DirectURL string `json:"directUrl"`
|
||||
}
|
||||
if err := c.doPost(ctx, "/api/internal/agent/stream-url", req, &resp); err != nil {
|
||||
return "", fmt.Errorf("refresh stream url: %w", err)
|
||||
}
|
||||
if resp.DirectURL == "" {
|
||||
return "", fmt.Errorf("refresh stream url: empty url in response")
|
||||
}
|
||||
return resp.DirectURL, nil
|
||||
}
|
||||
|
||||
// ReportStatus reports download progress. Returns server-side flags the CLI must act on.
|
||||
func (c *Client) ReportStatus(ctx context.Context, update StatusUpdate) (*StatusResponse, error) {
|
||||
var resp StatusResponse
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue