feat(seed-file): unarr-side handler for browser-on-demand seeding (Fase 4.7.c)
Closes the agent half of in-browser playback for arbitrary files. When the web app inserts a download_task with mode="seed_file", the daemon now wraps the on-disk file as a single-file torrent, adds it to the existing WebRTC-enabled torrent client, and reports the generated info_hash back so the browser can target /stream/<hash>. Pieces: - internal/agent/types.go: Task.FilePath (received from claim) + StatusUpdate.InfoHash (sent back). Both serialise compatibly with the matching Zod schemas in the Next.js sync route. - internal/engine/seed_file.go: SeedFile(client, filePath, trackers) builds the metainfo via metainfo.Info.BuildFromFilePath + bencode.Marshal, then AddTorrent + DownloadAll() so anacrolix hashes the file and flips pieces to "have" as it goes. The libtorrent piece-size ladder is mirrored from wstracker-probe so generated torrents are interoperable with mainstream clients. SeedFileOnDownloader is the daemon-facing convenience wrapper — bails loud when [downloads.webrtc].enabled = false instead of silently producing a torrent no browser can find. - internal/cmd/seed_file_handler.go: handleSeedFileTask invoked from the existing OnTasksClaimed dispatcher in daemon.go for mode= seed_file. Validates filePath, calls the engine helper, and pushes the resulting info_hash via Client.ReportStatus. Failures (missing file, WebRTC disabled, ffmpeg-style oddities) report status="failed" + errorMessage so the browser's WatchInBrowserButton can show the reason instead of timing out at 60 s. - internal/cmd/daemon.go: dispatcher learns the seed_file branch in the same shape as the existing stream branch. Tests (6 unit, all green): - SeedFile rejects missing files + directories. - SeedFile yields a deterministic info_hash for the same payload across fresh clients (web client polls expecting this). - SeedFileOnDownloader errors when WebRTC is disabled. - chooseSeedPieceLength matches the ladder breakpoints. - makeAnnounceList handles nil/empty/partial inputs. Web side compatible: mode=seed_file is already accepted by the sync schema; agent.Task.filePath + StatusUpdate.infoHash now propagate through the existing claim/report endpoints. End-to-end browser ↔ unarr smoke is the next concrete verification step (needs a running unarr-dev daemon plus library scan + a file with no source torrent).
This commit is contained in:
parent
2aeabe6b50
commit
e50dd17a00
5 changed files with 383 additions and 1 deletions
|
|
@ -72,6 +72,12 @@ type Task struct {
|
|||
Episode *int `json:"episode,omitempty"` // Episode number
|
||||
ContentYear *int `json:"contentYear,omitempty"` // Year from TMDB (avoids regex on torrent title)
|
||||
CollectionName string `json:"collectionName,omitempty"` // Collection name (e.g., "Harry Potter Collection")
|
||||
|
||||
// FilePath is the on-disk path of the file the agent is being asked
|
||||
// to operate on. Currently used by mode=seed_file to know which
|
||||
// arbitrary file to wrap as a single-file torrent for browser
|
||||
// streaming; populated by the server from libraryItem.filePath.
|
||||
FilePath string `json:"filePath,omitempty"`
|
||||
}
|
||||
|
||||
// StreamRequest is a request to stream a completed download from disk.
|
||||
|
|
@ -95,6 +101,9 @@ type StatusUpdate struct {
|
|||
StreamURL string `json:"streamUrl,omitempty"`
|
||||
StreamReady bool `json:"streamReady,omitempty"`
|
||||
ErrorMessage string `json:"errorMessage,omitempty"`
|
||||
// mode=seed_file: agent computes the info_hash from the local file
|
||||
// and reports it back so the web player can target /stream/<hash>.
|
||||
InfoHash string `json:"infoHash,omitempty"`
|
||||
}
|
||||
|
||||
// StatusResponse is returned by the status endpoint.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue