feat(stream): report watch progress to API via HTTP Range tracking

Track the highest byte offset served by the stream server to estimate
playback progress (0-100%). A WatchReporter goroutine sends progress
to POST /api/internal/agent/watch-progress every 10s during streaming.

- Add maxByteOffset + totalFileSize to StreamServer for Range tracking
- Add FileSize() to fileProvider interface (all 3 providers)
- New WatchReporter: periodic progress reporter tied to daemon context
- New WatchProgressUpdate type with optional progress/position/duration
- Wire reporter into all 3 stream paths (task stream, disk stream, active download stream)
This commit is contained in:
Deivid Soto 2026-04-01 12:16:45 +02:00
parent 932312fc56
commit 0dafeaa70d
8 changed files with 366 additions and 10 deletions

View file

@ -178,6 +178,15 @@ func (c *Client) SyncLibrary(ctx context.Context, req LibrarySyncRequest) (*Libr
return &resp, nil
}
// ReportWatchProgress sends playback position to the server for watch tracking.
func (c *Client) ReportWatchProgress(ctx context.Context, update WatchProgressUpdate) error {
var resp WatchProgressResponse
if err := c.doPost(ctx, "/api/internal/agent/watch-progress", update, &resp); err != nil {
return fmt.Errorf("watch progress: %w", err)
}
return nil
}
// doPost sends a JSON POST request and decodes the response.
func (c *Client) doPost(ctx context.Context, path string, body any, dst any) error {
jsonBody, err := json.Marshal(body)

View file

@ -304,3 +304,24 @@ type LibrarySyncResponse struct {
Matched int `json:"matched"`
Removed int `json:"removed"`
}
// ---------------------------------------------------------------------------
// Watch progress types (used by stream tracking)
// ---------------------------------------------------------------------------
// WatchProgressUpdate reports playback position during streaming.
// Two modes:
// - Estimated (range): set Progress (0-100). Position/Duration omitted.
// - Precise (browser): set Position + Duration in seconds. Progress computed server-side.
type WatchProgressUpdate struct {
TaskID string `json:"taskId"`
Source string `json:"source"` // "range" or "browser"
Progress *int `json:"progress,omitempty"` // 0-100 (range source)
Position *int `json:"position,omitempty"` // seconds (browser source)
Duration *int `json:"duration,omitempty"` // seconds (browser source)
}
// WatchProgressResponse is returned after reporting watch progress.
type WatchProgressResponse struct {
Success bool `json:"success"`
}