Help us shape the future of Go! Take the 2026 Go Developer Survey

trakt

package
v0.2.20 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 28, 2026 License: GPL-3.0 Imports: 19 Imported by: 0

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

View Source
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

View Source
var ErrNotConnected = errors.New("trakt: user not connected")

ErrNotConnected is returned by authenticated calls for users who have not linked Trakt.

View Source
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

func Test

func Test(ctx context.Context, cfg config.Trakt) error

Test checks the owner's Trakt application credentials: Trakt accepts the client ID on a lightweight public request. The client secret is only exercised when a viewer connects their account.

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client talks to the Trakt API for all users.

func New

func New(opts Options) *Client

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) Close

func (c *Client) Close()

Close stops the event worker.

func (*Client) Connected

func (c *Client) Connected(ctx context.Context, userID int64) bool

Connected reports whether a user has linked Trakt.

func (*Client) Disconnect

func (c *Client) Disconnect(ctx context.Context, userID int64) error

Disconnect removes a user's stored tokens.

func (*Client) Enabled

func (c *Client) Enabled() bool

Enabled reports whether Trakt is configured.

func (*Client) HandleEvent

func (c *Client) HandleEvent(ctx context.Context, ev state.Event)

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

func (c *Client) MigrateSecrets(ctx context.Context) (int, error)

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

func (c *Client) PublicEnabled() bool

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) Refresh

func (c *Client) Refresh(ctx context.Context, userID int64) error

Refresh exchanges a user's refresh token for a fresh token pair.

func (*Client) RemoveHistory

func (c *Client) RemoveHistory(ctx context.Context, userID int64, key core.RatingKey) error

RemoveHistory removes a title from the user's Trakt history.

func (*Client) RemoveRating

func (c *Client) RemoveRating(ctx context.Context, userID int64, key core.RatingKey) error

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

func (c *Client) Start(ctx context.Context)

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

func (c *Client) SubscribeTo(src interface {
	Subscribe(func(state.Event)) func()
}) func()

SubscribeTo feeds state events into the scrobble queue (non-blocking).

func (*Client) Tokens

func (c *Client) Tokens(ctx context.Context, userID int64) (Tokens, bool, error)

Tokens returns the stored tokens for a user.

func (*Client) UpdateConfig

func (c *Client) UpdateConfig(cfg config.Trakt)

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).

func (*Client) Watchlist

func (c *Client) Watchlist(ctx context.Context, userID int64, limit int) ([]core.Title, error)

Watchlist returns the user's Trakt watchlist as catalogue titles.

Cached like PullHistory.

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
)

type Tokens

type Tokens struct {
	UserID       int64
	AccessToken  string
	RefreshToken string
	ExpiresAt    time.Time
	Username     string
}

Tokens are a user's stored Trakt credentials.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL