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

stats

package
v0.3.2 Latest Latest
Warning

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

Go to latest
Published: Nov 3, 2026 License: GPL-3.0 Imports: 21 Imported by: 0

Documentation

Overview

Package stats records owner analytics from observed playback and provider data. It never reconstructs device or bandwidth information from watch counts.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type AccountRef added in v0.3.0

type AccountRef struct {
	ID   int64
	Name string
}

AccountRef labels an account id on a statistics page.

type BandwidthRow added in v0.3.0

type BandwidthRow struct {
	AccountID int64
	DeviceID  int64
	Timespan  int
	At        int64
	Lan       int
	Bytes     int64
}

BandwidthRow is one StatisticsBandwidth series point.

type Bucket

type Bucket struct {
	Label     string `json:"label"`
	Plays     int64  `json:"plays"`
	WatchedMS int64  `json:"watchedMs"`
	Bytes     int64  `json:"bytes"`
}

type Cost

type Cost struct {
	Kind     string  `json:"kind"`
	ID       string  `json:"id"`
	Monthly  float64 `json:"monthly"`
	Currency string  `json:"currency"`
}

type Dashboard added in v0.3.0

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

Dashboard is this node's in-memory Plex Dashboard store: devices, per-second bandwidth, and completed-play counts. Clustered nodes each keep their own rings; there is no request pin, so a client reads the node it connected to. Request paths only walk the requested buckets.

func NewDashboard added in v0.3.0

func NewDashboard(opts DashboardOptions) *Dashboard

NewDashboard creates the anonymous device (id 1) the way a PMS does at start.

func (*Dashboard) Bandwidth added in v0.3.0

func (d *Dashboard) Bandwidth(q Query) Page

Bandwidth returns the filtered, paged bandwidth series.

func (*Dashboard) Device added in v0.3.0

func (d *Dashboard) Device(id int64) (Device, bool)

Device returns one row by numeric id.

func (*Dashboard) Devices added in v0.3.0

func (d *Dashboard) Devices() []Device

Devices lists every device this process has seen, id order.

func (*Dashboard) Media added in v0.3.0

func (d *Dashboard) Media(q Query) Page

Media returns the filtered, paged completed-play series.

func (*Dashboard) RecordBytes added in v0.3.0

func (d *Dashboard) RecordBytes(accountID int64, accountName, clientID, deviceName, platform string, lan bool, n int64)

RecordBytes adds client-facing playback bytes to the per-second series.

func (*Dashboard) RecordPlay added in v0.3.0

func (d *Dashboard) RecordPlay(accountID int64, accountName, clientID string, metadataType int, durationSec int64)

RecordPlay counts one completed play into the media series. durationSec is the item's length in seconds (PMS StatisticsMedia duration), summed per bucket.

func (*Dashboard) SeeDevice added in v0.3.0

func (d *Dashboard) SeeDevice(clientID, name, platform string) Device

SeeDevice records or updates a client. An empty client identifier is device 1.

type DashboardOptions added in v0.3.0

type DashboardOptions struct {
	Now func() time.Time
	Loc *time.Location
}

DashboardOptions configure the clock (tests).

type Device added in v0.3.0

type Device struct {
	ID               int64
	ClientIdentifier string
	Name             string
	Platform         string
	CreatedAt        int64
}

Device is one client the Dashboard can name.

type IndexerUsage

type IndexerUsage struct {
	Indexer     string `json:"indexer"`
	Searches    int64  `json:"searches"`
	Successes   int64  `json:"successes"`
	Grabs       int64  `json:"grabs"`
	GrabSuccess int64  `json:"grabSuccess"`
	Budgets     int64  `json:"budgets"`
	LatencyMS   int64  `json:"latencyMs"`
	AgeSeconds  int64  `json:"ageSeconds"`
	AgeSamples  int64  `json:"ageSamples"`
}

type MediaRow added in v0.3.0

type MediaRow struct {
	AccountID    int64
	DeviceID     int64
	Timespan     int
	At           int64
	MetadataType int
	Count        int64
	Duration     int64 // sum of item lengths in seconds
}

MediaRow is one StatisticsMedia series point.

type NewsletterSettings

type NewsletterSettings struct {
	Enabled     bool   `json:"enabled"`
	Weekday     int    `json:"weekday"`
	Hour        int    `json:"hour"`
	UserID      int64  `json:"userId"`
	NextDue     int64  `json:"nextDue"`
	LastAttempt int64  `json:"lastAttempt"`
	LastStatus  string `json:"lastStatus"`
}

type Options

type Options struct {
	Newsletter    func(context.Context, string) error
	StopSession   func(string) error
	SessionExists func(string) bool
	Playback      core.Playback
	Providers     func() []usenet.ProviderStats
	Indexers      func(context.Context) ([]newznab.Remaining, error)
	Logger        *slog.Logger
	Now           func() time.Time
	// NodeID identifies this node in a cluster. Plays are tagged with it and
	// New closes only this node's unfinished plays. Empty for a standalone
	// server, which therefore closes every unfinished play as before.
	NodeID string
	// Availability, when set (clustered nodes), holds play records the
	// database cannot take through an outage (the latest per play) and
	// skips periodic provider snapshots while the database is down; their
	// counters are cumulative, so nothing is lost. Nil writes synchronously.
	Availability *store.Availability
	// MetadataType maps a rating key to a Plex metadata type number for
	// Dashboard media rows. Zero or nil skips the play.
	MetadataType func(core.RatingKey) int
	// AccountName labels a user on Dashboard media rows.
	AccountName func(int64) string
}

type Page added in v0.3.0

type Page struct {
	Rows      []BandwidthRow
	Media     []MediaRow
	Devices   []Device
	Accounts  []AccountRef
	TotalSize int
}

Page is one statistics answer: the series window and the Device/Account lookup for ids that window names.

type Play

type Play struct {
	ID         string `json:"id"`
	UserID     int64  `json:"userId"`
	Key        int64  `json:"ratingKey"`
	Username   string `json:"username"`
	Title      string `json:"title"`
	Platform   string `json:"platform"`
	Device     string `json:"device"`
	Decision   string `json:"decision"`
	Started    int64  `json:"started"`
	Updated    int64  `json:"updated"`
	Ended      int64  `json:"ended"`
	Bytes      int64  `json:"bytes"`
	WatchedMS  int64  `json:"watchedMs"`
	DurationMS int64  `json:"durationMs"`
	Completed  bool   `json:"completed"`
	Failovers  int    `json:"failovers"`
	Error      string `json:"error"`
}

type Query added in v0.3.0

type Query struct {
	Timespans                     []int
	AccountID                     int64
	DeviceID                      int64
	HasAccount, HasDevice, HasLan bool
	Lan                           int
	AtMin, AtMax                  int64
	ExactAt                       int64
	HasExact, HasAtMin, HasAtMax  bool
	Offset                        int
	Limit                         int
	HasLimit                      bool
	Sort                          string
	Desc                          bool
}

Query is the parsed Dashboard filter. Timespans empty means "every kept rollup except per-second" for bandwidth/media (0..4).

type Service

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

func New

func New(db *sql.DB, opts Options) *Service

func (*Service) Close

func (s *Service) Close(ctx context.Context) error

Close writes play records still held from a database outage (bounded by ctx); a no-op on a standalone server.

func (*Service) Dashboard added in v0.3.0

func (s *Service) Dashboard() *Dashboard

Dashboard is the in-memory Plex Dashboard store for this node.

func (*Service) Handler

func (s *Service) Handler() http.Handler

Handler must be mounted behind owner authentication with /stats stripped.

func (*Service) History

func (s *Service) History(ctx context.Context, since, until time.Time, user int64, limit int) ([]Play, error)

func (*Service) IndexerMonth

func (s *Service) IndexerMonth(ctx context.Context) ([]IndexerUsage, error)

func (*Service) MaybeNewsletter

func (s *Service) MaybeNewsletter(ctx context.Context) error

MaybeNewsletter claims each weekly slot in a durable ledger before enqueueing. Queue errors remain visible and are not retried automatically, preventing duplicates after an ambiguous process interruption.

func (*Service) NewsletterSettings

func (s *Service) NewsletterSettings(ctx context.Context) (NewsletterSettings, error)

func (*Service) ObserveIndexer

func (s *Service) ObserveIndexer(e newznab.Event)

func (*Service) ObservePlayback

func (s *Service) ObservePlayback(ev playback.Event)

ObservePlayback is suitable for playback.Manager.Subscribe.

func (*Service) ObserveState

func (s *Service) ObserveState(ev state.Event)

ObserveState correlates timeline data only when exactly one active session matches the user/title. Plex state events do not identify the client.

func (*Service) OutboxStatus

func (s *Service) OutboxStatus() (st store.OutboxStatus, ok bool)

OutboxStatus reports play records held through a database outage; ok is false on a standalone server.

func (*Service) Prune

func (s *Service) Prune(ctx context.Context) (int64, error)

Prune deletes per-day indexer/provider counters older than dailyRetentionDays. It is meant to be called periodically.

func (*Service) Record

func (s *Service) Record(ctx context.Context, p *core.PlaySession, ended bool, problem string) error

Record upserts a play. On a clustered node a record the database cannot take is held and written when it is back.

func (*Service) RecordIndexer

func (s *Service) RecordIndexer(ctx context.Context, e newznab.Event) error

func (*Service) RenderNewsletter

func (s *Service) RenderNewsletter(ctx context.Context, userID int64) (string, error)

func (*Service) Run

func (s *Service) Run(ctx context.Context)

Run samples active bandwidth and provider counters until cancellation.

func (*Service) Sample

func (s *Service) Sample(ctx context.Context)

func (*Service) SaveNewsletter

func (s *Service) SaveNewsletter(ctx context.Context, v NewsletterSettings) error

func (*Service) SetCost

func (s *Service) SetCost(ctx context.Context, c Cost) error

func (*Service) SnapshotProviders

func (s *Service) SnapshotProviders(ctx context.Context, ps []usenet.ProviderStats) error

func (*Service) Summary

func (s *Service) Summary(ctx context.Context, since, until time.Time, user int64) (Summary, error)

type Summary

type Summary struct {
	Plays     int64     `json:"plays"`
	WatchedMS int64     `json:"watchedMs"`
	Bytes     int64     `json:"bytes"`
	Completed int64     `json:"completed"`
	Daily     []Bucket  `json:"daily"`
	Hourly    []Bucket  `json:"hourly"`
	Platforms []Bucket  `json:"platforms"`
	Titles    []Bucket  `json:"titles"`
	Users     []Bucket  `json:"users"`
	Since     time.Time `json:"since"`
	Until     time.Time `json:"until"`
}

Jump to

Keyboard shortcuts

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