feat(mediaserver): Plex/Jellyfin/Emby auto-refresh + .strm instant mode
Sprint 1 — Auto-refresh after download:
- New [[mediaserver]] TOML section with kind/url/token/sections
- mediaserver.Refresh() fans out to Plex (partial via section ID auto-mapping
from file path prefix) and Jellyfin/Emby (full library scan)
- Manager.OnFinalized callback wired in daemon to trigger refresh after
organize() completes — keeps engine package free of mediaserver dep
- New unarr mediaserver {setup,list,remove,test} commands
- unarr init wizard offers to configure refresh when a server is detected
Sprint 2 — .strm instant mode (cloud + agent):
- Mode strm-to-library handled in daemon dispatch: writes a one-line .strm
file pointing to the cloud-resolved debrid HTTPS URL, then triggers refresh
- engine.WriteStrm + StrmDestForTask mirror organize()'s naming so Plex/Jellyfin
see the expected folder structure (Movies/Title (Year)/, TV Shows/Show/Season XX/)
- Atomic write (temp + rename) so partial files never get indexed
- Reports completed/failed status to the cloud via existing agent client
This commit is contained in:
parent
6955b6144b
commit
6adf1e2c4c
13 changed files with 1065 additions and 16 deletions
70
internal/cmd/strm_handler.go
Normal file
70
internal/cmd/strm_handler.go
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"github.com/torrentclaw/unarr/internal/agent"
|
||||
"github.com/torrentclaw/unarr/internal/config"
|
||||
"github.com/torrentclaw/unarr/internal/engine"
|
||||
"github.com/torrentclaw/unarr/internal/mediaserver"
|
||||
)
|
||||
|
||||
// handleStrmToLibrary processes a Mode="strm-to-library" task by writing a
|
||||
// one-line .strm file to the user's media library and triggering a
|
||||
// Plex/Jellyfin/Emby refresh. No actual download happens; the .strm points
|
||||
// at the cloud-resolved debrid HTTPS URL, and the media server streams from
|
||||
// there when the user presses play.
|
||||
//
|
||||
// Reports completion (or failure) back to the cloud via the agent client.
|
||||
func handleStrmToLibrary(ctx context.Context, t agent.Task, cfg config.Config, agentClient *agent.Client) {
|
||||
short := agent.ShortID(t.ID)
|
||||
|
||||
if t.DirectURL == "" {
|
||||
log.Printf("[%s] strm-to-library: missing directUrl from server", short)
|
||||
reportStrmFailure(ctx, agentClient, t.ID, "missing directUrl")
|
||||
return
|
||||
}
|
||||
|
||||
organizeCfg := engine.OrganizeConfig{
|
||||
Enabled: cfg.Organize.Enabled,
|
||||
MoviesDir: cfg.Organize.MoviesDir,
|
||||
TVShowsDir: cfg.Organize.TVShowsDir,
|
||||
OutputDir: cfg.Download.Dir,
|
||||
}
|
||||
|
||||
finalPath, err := engine.WriteStrm(t, organizeCfg)
|
||||
if err != nil {
|
||||
log.Printf("[%s] strm-to-library write failed: %v", short, err)
|
||||
reportStrmFailure(ctx, agentClient, t.ID, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("[%s] strm-to-library wrote %s", short, finalPath)
|
||||
|
||||
// Trigger media-server refresh if any are configured. Errors are logged
|
||||
// inside Refresh and never propagate — the .strm is on disk, so the
|
||||
// next periodic scan would pick it up regardless.
|
||||
if len(cfg.MediaServers) > 0 {
|
||||
mediaserver.Refresh(cfg.MediaServers, finalPath)
|
||||
}
|
||||
|
||||
if _, reportErr := agentClient.ReportStatus(ctx, agent.StatusUpdate{
|
||||
TaskID: t.ID,
|
||||
Status: "completed",
|
||||
Progress: 100,
|
||||
FilePath: finalPath,
|
||||
}); reportErr != nil {
|
||||
log.Printf("[%s] strm-to-library: status report failed: %v", short, reportErr)
|
||||
}
|
||||
}
|
||||
|
||||
func reportStrmFailure(ctx context.Context, agentClient *agent.Client, taskID, msg string) {
|
||||
if _, err := agentClient.ReportStatus(ctx, agent.StatusUpdate{
|
||||
TaskID: taskID,
|
||||
Status: "failed",
|
||||
ErrorMessage: msg,
|
||||
}); err != nil {
|
||||
log.Printf("[%s] strm failure report failed: %v", agent.ShortID(taskID), err)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue