Documentation
¶
Overview ¶
Package trakt is the optional per-user Trakt integration: OAuth device flow sign-in, scrobbling Ether watch events to Trakt, and pulling Trakt history / recommendations / watchlist back in as recommendation sources.
Tokens are stored per user in SQLite (table trakt_tokens). Everything is inert unless config.Trakt.Enabled and a client id/secret are set — Trakt requires each install to register its own app credentials, so Ether ships none.
Index ¶
- Constants
- Variables
- func Test(ctx context.Context, cfg config.Trakt) error
- type Client
- func (c *Client) AddHistory(ctx context.Context, userID int64, key core.RatingKey, watchedAt time.Time) error
- func (c *Client) AddRating(ctx context.Context, userID int64, key core.RatingKey, rating float64) error
- func (c *Client) Close()
- func (c *Client) Connected(ctx context.Context, userID int64) bool
- func (c *Client) Disconnect(ctx context.Context, userID int64) error
- func (c *Client) Enabled() bool
- func (c *Client) HandleEvent(ctx context.Context, ev state.Event)
- func (c *Client) MigrateSecrets(ctx context.Context) (int, error)
- func (c *Client) PollDevice(ctx context.Context, userID int64, deviceCode string) (PollStatus, error)
- func (c *Client) PublicEnabled() bool
- func (c *Client) PublicList(ctx context.Context, list string, typ core.MediaType, limit int) ([]core.Title, error)
- func (c *Client) PullHistory(ctx context.Context, userID int64, limit int) ([]core.HistoryEntry, error)
- func (c *Client) Recommendations(ctx context.Context, userID int64, limit int) ([]core.Title, error)
- func (c *Client) Refresh(ctx context.Context, userID int64) error
- func (c *Client) RemoveHistory(ctx context.Context, userID int64, key core.RatingKey) error
- func (c *Client) RemoveRating(ctx context.Context, userID int64, key core.RatingKey) error
- func (c *Client) Scrobble(ctx context.Context, userID int64, key core.RatingKey, action string, ...) error
- func (c *Client) Start(ctx context.Context)
- func (c *Client) StartDevice(ctx context.Context) (*DeviceCode, error)
- func (c *Client) SubscribeTo(src interface{ ... }) func()
- func (c *Client) Tokens(ctx context.Context, userID int64) (Tokens, bool, error)
- func (c *Client) UpdateConfig(cfg config.Trakt)
- func (c *Client) WaitDevice(ctx context.Context, userID int64, dc *DeviceCode) (PollStatus, error)
- func (c *Client) Watchlist(ctx context.Context, userID int64, limit int) ([]core.Title, error)
- type DeviceCode
- type Options
- type PollStatus
- type Tokens
Constants ¶
const ( PublicTrending = "trending" // people watching right now PublicWatchedWeekly = "watched_weekly" // most watched this week PublicPopular = "popular" // popular overall PublicAnticipated = "anticipated" // most added to lists ahead of release )
Public list names accepted by PublicList.
Variables ¶
var ErrNotConnected = errors.New("trakt: user not connected")
ErrNotConnected is returned by authenticated calls for users who have not linked Trakt.
var ErrRevoked = errors.New("trakt: authorization revoked; reconnect required")
ErrRevoked is returned when Trakt rejects a user's refresh token; the stored tokens are deleted and the user must reconnect.
Functions ¶
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client talks to the Trakt API for all users.
func New ¶
New builds a Client. Call Start to run the event-scrobble worker and SubscribeTo to feed it.
func (*Client) AddHistory ¶
func (c *Client) AddHistory(ctx context.Context, userID int64, key core.RatingKey, watchedAt time.Time) error
AddHistory marks a title watched on Trakt at the given time.
func (*Client) AddRating ¶
func (c *Client) AddRating(ctx context.Context, userID int64, key core.RatingKey, rating float64) error
AddRating rates a title on Trakt (1-10).
func (*Client) Disconnect ¶
Disconnect removes a user's stored tokens.
func (*Client) HandleEvent ¶
HandleEvent maps one state event to Trakt calls. Safe to call directly; SubscribeTo + Start give the queued, non-blocking version.
- Timeline progress becomes scrobble start/pause/stop (de-duplicated). A progress event that crosses the watched threshold sends scrobble/stop at 100%, which Trakt records as a play, so the EventCompleted that follows it is not pushed again.
- Explicit scrobble/unscrobble (mark watched/unwatched) go to /sync/history and /sync/history/remove.
- Ratings go to /sync/ratings (0 removes the rating).
func (*Client) MigrateSecrets ¶ added in v0.2.4
MigrateSecrets seals tokens stored in plaintext and returns how many users' rows it rewrote. Each row is replaced only if it still holds the values read (a concurrent refresh wins), so it is safe to run on every node and to rerun. A row that does not open is left untouched and reported in the error.
func (*Client) PollDevice ¶
func (c *Client) PollDevice(ctx context.Context, userID int64, deviceCode string) (PollStatus, error)
PollDevice makes one device-token poll. On PollAuthorized the tokens are stored for userID. The caller polls every DeviceCode.Interval seconds (backing off on PollSlowDown) until a terminal status.
func (*Client) PublicEnabled ¶
PublicEnabled reports whether public lists can be fetched: Trakt is enabled and a client id is configured.
func (*Client) PublicList ¶
func (c *Client) PublicList(ctx context.Context, list string, typ core.MediaType, limit int) ([]core.Title, error)
PublicList returns one of Trakt's public lists for films or shows, best first, as minimal titles (key, type, title, year, ids; no catalogue lookups, so it is cheap). Without a configured client id it returns (nil, nil). Results are cached for an hour and served stale while refreshing.
func (*Client) PullHistory ¶
func (c *Client) PullHistory(ctx context.Context, userID int64, limit int) ([]core.HistoryEntry, error)
PullHistory fetches the user's Trakt history and maps it to Ether history entries (via TMDB ids in the rating keys), for merging into the recommendation model. Entries Trakt has no TMDB id for are skipped.
Results are cached (Options.CacheTTL) and served stale while refreshing.
func (*Client) Recommendations ¶
func (c *Client) Recommendations(ctx context.Context, userID int64, limit int) ([]core.Title, error)
Recommendations returns Trakt's personalised movie+show recommendations, interleaved, as catalogue titles. Cached like PullHistory. Users who have not connected Trakt get (nil, nil).
func (*Client) RemoveHistory ¶
RemoveHistory removes a title from the user's Trakt history.
func (*Client) RemoveRating ¶
RemoveRating clears a title's rating on Trakt.
func (*Client) Scrobble ¶
func (c *Client) Scrobble(ctx context.Context, userID int64, key core.RatingKey, action string, progress float64) error
Scrobble pushes one playback state to Trakt: action is "start", "pause" or "stop"; progress is 0-100. Trakt marks >=80% on stop as watched.
func (*Client) Start ¶
Start runs the event worker until Close. Typical wiring:
go traktClient.Start(ctx) unsub := traktClient.SubscribeTo(stateSvc)
func (*Client) StartDevice ¶
func (c *Client) StartDevice(ctx context.Context) (*DeviceCode, error)
StartDevice begins the device-code flow for a user.
func (*Client) SubscribeTo ¶
SubscribeTo feeds state events into the scrobble queue (non-blocking).
func (*Client) UpdateConfig ¶
UpdateConfig swaps the Trakt settings.
func (*Client) WaitDevice ¶
func (c *Client) WaitDevice(ctx context.Context, userID int64, dc *DeviceCode) (PollStatus, error)
WaitDevice polls the device token endpoint at the code's interval until the user authorises (tokens are stored for userID), denies, or the code expires. slow_down responses add 5s to the interval (RFC 8628). The returned status is terminal unless err is non-nil (ctx cancelled or a transport failure).
type DeviceCode ¶
type DeviceCode struct {
DeviceCode string `json:"device_code"`
UserCode string `json:"user_code"`
VerificationURL string `json:"verification_url"`
ExpiresIn int `json:"expires_in"`
Interval int `json:"interval"`
}
DeviceCode is what the setup UI shows the user.
type Options ¶
type Options struct {
Config config.Trakt
DB *sql.DB
Catalog core.Catalog // resolves RatingKeys <-> TMDB/IMDb ids; may be nil for auth-only use
BaseURL string // override for tests
HTTPClient *http.Client
Logger *slog.Logger
Now func() time.Time
// MaxRetryAfter caps how long a 429 Retry-After wait may block (default 5s).
MaxRetryAfter time.Duration
// MinPostInterval spaces POST requests (Trakt allows ~1 authed POST per
// second). Default 1s; negative disables pacing.
MinPostInterval time.Duration
// CacheTTL is how long pulled recommendations, watchlist and history are
// served without a refresh (default 30m). Stale entries are still served
// while a background refresh runs, so Home never waits on Trakt twice.
CacheTTL time.Duration
// QueueSize bounds the event queue; the oldest events are dropped when
// Trakt is unreachable for long (default 1000).
QueueSize int
// NodeID names this node on import-queue rows it claims; a random id is
// used when empty.
NodeID string
// Sealer protects the stored tokens (secrets.go); nil stores them as
// given.
Sealer config.FieldSealer
}
Options build a Client.
type PollStatus ¶
type PollStatus string
PollStatus is the outcome of one device-token poll.
const ( PollPending PollStatus = "pending" // user hasn't entered the code yet (400) PollSlowDown PollStatus = "slow_down" // poll slower (429) PollDenied PollStatus = "denied" // user denied (418) PollExpired PollStatus = "expired" // code expired (410) PollUsed PollStatus = "already_used" // code already redeemed (409) PollInvalid PollStatus = "invalid" // unknown code (404) PollAuthorized PollStatus = "authorized" // tokens stored )