plex

package
v1.1.1 Latest Latest
Warning

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

Go to latest
Published: May 14, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrPINExpired indicates the authentication PIN has expired
	ErrPINExpired = errors.New("authentication PIN has expired")
	// ErrServerOffline indicates the media server is unreachable
	ErrServerOffline = errors.New("media server is unreachable")
)

Plex-specific errors

Functions

func MapEpisodes

func MapEpisodes(metadata []Metadata, serverURL string) []*domain.MediaItem

MapEpisodes converts Plex metadata to domain media items (episodes)

func MapLibraries

func MapLibraries(dirs []Directory) []domain.Library

MapLibraries converts Plex directories to domain libraries

func MapLibraryContent

func MapLibraryContent(metadata []Metadata, serverURL string) []domain.ListItem

MapLibraryContent converts Plex metadata to domain.ListItem for mixed libraries. This handles both movies and shows in a single response, returning them as a polymorphic slice that the UI can display uniformly.

func MapMediaItem

func MapMediaItem(m Metadata, serverURL string) domain.MediaItem

MapMediaItem converts a single Plex metadata to domain media item

func MapMovies

func MapMovies(metadata []Metadata, serverURL string) []*domain.MediaItem

MapMovies converts Plex metadata to domain media items (movies)

func MapOnDeck

func MapOnDeck(metadata []Metadata, serverURL string) []*domain.MediaItem

MapOnDeck converts Plex metadata to domain media items for On Deck

func MapPlaylists

func MapPlaylists(metadata []Metadata, serverURL string) []*domain.Playlist

MapPlaylists converts Plex metadata to domain playlists

func MapSeasons

func MapSeasons(metadata []Metadata, serverURL string) []*domain.Season

MapSeasons converts Plex metadata to domain seasons

func MapShows

func MapShows(metadata []Metadata, serverURL string) []*domain.Show

MapShows converts Plex metadata to domain shows

Types

type APIResponse

type APIResponse struct {
	MediaContainer MediaContainer `json:"MediaContainer"`
}

APIResponse wraps the MediaContainer for JSON unmarshaling

type AuthClient

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

AuthClient handles Plex authentication

func NewAuthClient

func NewAuthClient(logger *slog.Logger) *AuthClient

NewAuthClient creates a new authentication client

func (*AuthClient) CheckPIN

func (a *AuthClient) CheckPIN(ctx context.Context, pinID int) (token string, claimed bool, err error)

CheckPIN polls for PIN claim status and returns the auth token

func (*AuthClient) GetPIN

func (a *AuthClient) GetPIN(ctx context.Context) (pin string, id int, err error)

GetPIN generates a new authentication PIN

func (*AuthClient) WaitForPIN

func (a *AuthClient) WaitForPIN(ctx context.Context, pinID int, timeout time.Duration) (string, error)

WaitForPIN polls for PIN claim with exponential backoff

type AuthFlow

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

AuthFlow handles Plex PIN-based authentication

func NewAuthFlow

func NewAuthFlow(logger *slog.Logger) *AuthFlow

NewAuthFlow creates a new Plex authentication flow

func (*AuthFlow) Run

func (f *AuthFlow) Run(ctx context.Context, serverURL string) (*AuthResult, error)

Run executes the Plex PIN-based authentication flow. It prompts the user to visit plex.tv/link and enter the displayed PIN.

type AuthResult

type AuthResult struct {
	Token    string
	UserID   string
	Username string
}

AuthResult contains the result of a successful Plex authentication

type Client

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

Client implements domain.LibraryRepository, domain.SearchRepository, domain.MetadataRepository, and domain.Scrobbler for Plex

func NewClient

func NewClient(baseURL, token string, logger *slog.Logger) *Client

NewClient creates a new Plex API client

func (*Client) AddToPlaylist

func (c *Client) AddToPlaylist(ctx context.Context, playlistID string, itemIDs []string) error

AddToPlaylist adds items to an existing playlist

func (*Client) CreatePlaylist

func (c *Client) CreatePlaylist(ctx context.Context, title string, itemIDs []string) (*domain.Playlist, error)

CreatePlaylist creates a new playlist with the given title and initial items. Plex does not support creating empty playlists, so at least one itemID is required.

func (*Client) DeletePlaylist

func (c *Client) DeletePlaylist(ctx context.Context, playlistID string) error

DeletePlaylist deletes a playlist

func (*Client) FetchIdentity

func (c *Client) FetchIdentity(ctx context.Context) error

FetchIdentity fetches and stores the server's machineIdentifier

func (*Client) GetContinueWatching added in v1.1.0

func (c *Client) GetContinueWatching(ctx context.Context) ([]*domain.MediaItem, error)

GetContinueWatching returns items that are currently in progress or "on deck". Note: Plex's /library/onDeck follows "next unwatched episode" semantics, which includes episodes you haven't started yet but are next in a show's sequence. This differs from Jellyfin's /Resume which only returns items with an active playback position.

func (*Client) GetEpisodes

func (c *Client) GetEpisodes(ctx context.Context, seasonID string) ([]*domain.MediaItem, error)

GetEpisodes returns all episodes for a season

func (*Client) GetLibraries

func (c *Client) GetLibraries(ctx context.Context) ([]domain.Library, error)

GetLibraries returns all available libraries

func (*Client) GetMediaItem

func (c *Client) GetMediaItem(ctx context.Context, itemID string) (*domain.MediaItem, error)

GetMediaItem returns detailed metadata for a specific item

func (*Client) GetMixedContent

func (c *Client) GetMixedContent(ctx context.Context, libID string, offset, limit int) ([]domain.ListItem, int, error)

GetMixedContent returns paginated content (movies AND shows) from a library. Note: Plex doesn't truly support "mixed" libraries at the API level like Jellyfin, so this method fetches all items and returns both types. For pure movie or show libraries, this still works but is less efficient than GetMovies/GetShows.

func (*Client) GetMovies

func (c *Client) GetMovies(ctx context.Context, libID string, offset, limit int) ([]*domain.MediaItem, int, error)

GetMovies returns movies from a movie library with pagination support Returns (items, totalSize, error) Note: If limit=0, Plex uses its default page size (typically 50-100). The SERVICE layer is responsible for pagination loops if "all" items are needed.

func (*Client) GetPlaylistItems

func (c *Client) GetPlaylistItems(ctx context.Context, playlistID string) ([]*domain.MediaItem, error)

GetPlaylistItems returns all items in a playlist

func (*Client) GetPlaylists

func (c *Client) GetPlaylists(ctx context.Context) ([]*domain.Playlist, error)

GetPlaylists returns all user playlists

func (*Client) GetSeasons

func (c *Client) GetSeasons(ctx context.Context, showID string) ([]*domain.Season, error)

GetSeasons returns all seasons for a TV show

func (*Client) GetShows

func (c *Client) GetShows(ctx context.Context, libID string, offset, limit int) ([]*domain.Show, int, error)

GetShows returns TV shows from a show library with pagination support Returns (items, totalSize, error) Note: If limit=0, Plex uses its default page size (typically 50-100). The SERVICE layer is responsible for pagination loops if "all" items are needed.

func (*Client) MarkPlayed

func (c *Client) MarkPlayed(ctx context.Context, itemID string) error

MarkPlayed marks an item as fully watched

func (*Client) MarkUnplayed

func (c *Client) MarkUnplayed(ctx context.Context, itemID string) error

MarkUnplayed marks an item as unwatched

func (*Client) RemoveFromPlaylist

func (c *Client) RemoveFromPlaylist(ctx context.Context, playlistID string, itemID string) error

RemoveFromPlaylist removes an item from a playlist. Plex requires the playlist-specific entry ID (playlistItemID), not the media's ratingKey. This method fetches playlist items to resolve the correct entry ID internally.

func (*Client) ResolvePlayable added in v1.0.1

func (c *Client) ResolvePlayable(ctx context.Context, itemID string) (domain.PlayableMedia, error)

ResolvePlayable returns a direct playback URL plus any external subtitle tracks for an item.

func (*Client) Search

func (c *Client) Search(ctx context.Context, query string) ([]*domain.MediaItem, error)

Search performs a search across all libraries

func (*Client) UpdateProgress added in v0.0.2

func (c *Client) UpdateProgress(ctx context.Context, itemID string, positionMs int64) error

UpdateProgress reports the current playback position to the server

type Directory

type Directory struct {
	Art              string `json:"art,omitempty"`
	Composite        string `json:"composite,omitempty"`
	Thumb            string `json:"thumb,omitempty"`
	Key              string `json:"key"`
	Type             string `json:"type"`
	Title            string `json:"title"`
	UpdatedAt        int64  `json:"updatedAt,omitempty"`
	CreatedAt        int64  `json:"createdAt,omitempty"`
	ContentChangedAt int64  `json:"contentChangedAt,omitempty"`
}

Directory represents a library section

type Guid

type Guid struct {
	ID string `json:"id"` // e.g. "imdb://tt1234567", "tmdb://12345", "tvdb://12345"
}

Guid represents an external identifier (IMDB, TMDB, TVDB, etc.)

type Media

type Media struct {
	ID              int    `json:"id"`
	Duration        int    `json:"duration,omitempty"`
	Bitrate         int    `json:"bitrate,omitempty"`
	Width           int    `json:"width,omitempty"`
	Height          int    `json:"height,omitempty"`
	AudioChannels   int    `json:"audioChannels,omitempty"`
	AudioCodec      string `json:"audioCodec,omitempty"`
	VideoCodec      string `json:"videoCodec,omitempty"`
	VideoResolution string `json:"videoResolution,omitempty"`
	Container       string `json:"container,omitempty"`
	VideoFrameRate  string `json:"videoFrameRate,omitempty"`
	Part            []Part `json:"Part,omitempty"`
}

Media represents media information (video streams, codecs, etc.)

type MediaContainer

type MediaContainer struct {
	Size                int         `json:"size"`
	TotalSize           int         `json:"totalSize,omitempty"`
	Offset              int         `json:"offset,omitempty"`
	AllowSync           bool        `json:"allowSync,omitempty"`
	Identifier          string      `json:"identifier,omitempty"`
	LibrarySectionID    int         `json:"librarySectionID,omitempty"`
	LibrarySectionTitle string      `json:"librarySectionTitle,omitempty"`
	LibrarySectionUUID  string      `json:"librarySectionUUID,omitempty"`
	MediaTagPrefix      string      `json:"mediaTagPrefix,omitempty"`
	MediaTagVersion     int         `json:"mediaTagVersion,omitempty"`
	Directory           []Directory `json:"Directory,omitempty"`
	Metadata            []Metadata  `json:"Metadata,omitempty"`
}

MediaContainer is the root container for Plex API responses

type Metadata

type Metadata struct {
	RatingKey             string   `json:"ratingKey"`
	Key                   string   `json:"key"`
	ParentRatingKey       string   `json:"parentRatingKey,omitempty"`
	GrandparentRatingKey  string   `json:"grandparentRatingKey,omitempty"`
	GUID                  string   `json:"guid,omitempty"` // Plex internal GUID
	Guids                 []Guid   `json:"Guid,omitempty"` // External IDs (IMDB, TMDB, TVDB)
	Studio                string   `json:"studio,omitempty"`
	Type                  string   `json:"type"`
	Title                 string   `json:"title"`
	GrandparentKey        string   `json:"grandparentKey,omitempty"`
	ParentKey             string   `json:"parentKey,omitempty"`
	GrandparentTitle      string   `json:"grandparentTitle,omitempty"`
	ParentTitle           string   `json:"parentTitle,omitempty"`
	ContentRating         string   `json:"contentRating,omitempty"`
	Summary               string   `json:"summary,omitempty"`
	Index                 int      `json:"index,omitempty"`
	ParentIndex           int      `json:"parentIndex,omitempty"`
	Rating                float64  `json:"rating,omitempty"`         // Critic rating
	Ratings               []Rating `json:"Rating,omitempty"`         // External ratings
	AudienceRating        float64  `json:"audienceRating,omitempty"` // Audience rating
	ViewOffset            int      `json:"viewOffset,omitempty"`
	LastViewedAt          int64    `json:"lastViewedAt,omitempty"`
	Year                  int      `json:"year,omitempty"`
	Tagline               string   `json:"tagline,omitempty"`
	Thumb                 string   `json:"thumb,omitempty"`
	Art                   string   `json:"art,omitempty"`
	ParentThumb           string   `json:"parentThumb,omitempty"`
	GrandparentThumb      string   `json:"grandparentThumb,omitempty"`
	GrandparentArt        string   `json:"grandparentArt,omitempty"`
	Duration              int      `json:"duration,omitempty"`
	OriginallyAvailableAt string   `json:"originallyAvailableAt,omitempty"`
	AddedAt               int64    `json:"addedAt,omitempty"`
	UpdatedAt             int64    `json:"updatedAt,omitempty"`
	TitleSort             string   `json:"titleSort,omitempty"`
	ViewCount             int      `json:"viewCount,omitempty"`
	ChildCount            int      `json:"childCount,omitempty"`
	LeafCount             int      `json:"leafCount,omitempty"`
	ViewedLeafCount       int      `json:"viewedLeafCount,omitempty"`
	LibrarySectionID      int      `json:"librarySectionID,omitempty"`
	LibrarySectionKey     string   `json:"librarySectionKey,omitempty"`
	LibrarySectionTitle   string   `json:"librarySectionTitle,omitempty"`
	PlaylistItemID        int      `json:"playlistItemID,omitempty"`
	Media                 []Media  `json:"Media,omitempty"`
}

Metadata represents a media item (movie, show, season, or episode)

type PINCheckResponse

type PINCheckResponse struct {
	ID        int    `json:"id"`
	Code      string `json:"code"`
	AuthToken string `json:"authToken"`
	ExpiresAt string `json:"expiresAt"`
}

PINCheckResponse represents the response from PIN check

type PINResponse

type PINResponse struct {
	ID        int    `json:"id"`
	Code      string `json:"code"`
	Product   string `json:"product"`
	Trusted   bool   `json:"trusted"`
	ClientID  string `json:"clientIdentifier"`
	AuthToken string `json:"authToken,omitempty"`
	ExpiresAt string `json:"expiresAt"`
}

PINResponse represents the response from PIN generation

type Part

type Part struct {
	ID        int      `json:"id"`
	Key       string   `json:"key"`
	Duration  int      `json:"duration,omitempty"`
	File      string   `json:"file,omitempty"`
	Size      int64    `json:"size,omitempty"`
	Container string   `json:"container,omitempty"`
	Stream    []Stream `json:"Stream,omitempty"`
}

Part represents a media file part

type PlaylistMetadata

type PlaylistMetadata struct {
	RatingKey    string `json:"ratingKey"`
	Key          string `json:"key"`
	GUID         string `json:"guid,omitempty"` // Plex internal GUID
	Guids        []Guid `json:"Guid,omitempty"` // External IDs
	Type         string `json:"type"`
	Title        string `json:"title"`
	Summary      string `json:"summary,omitempty"`
	Smart        int    `json:"smart"` // 1 = smart playlist, 0 = regular
	PlaylistType string `json:"playlistType"`
	Composite    string `json:"composite,omitempty"`
	Duration     int    `json:"duration,omitempty"`
	LeafCount    int    `json:"leafCount,omitempty"`
	AddedAt      int64  `json:"addedAt,omitempty"`
	UpdatedAt    int64  `json:"updatedAt,omitempty"`
}

PlaylistMetadata represents a Plex playlist

type Rating

type Rating struct {
	Image string  `json:"image,omitempty"` // e.g. "imdb://image.rating"
	Type  string  `json:"type,omitempty"`
	Value float64 `json:"value,omitempty"`
}

Rating represents a rating from an external source

type Stream added in v1.0.1

type Stream struct {
	ID                   int      `json:"id"`
	StreamType           int      `json:"streamType"`
	Key                  string   `json:"key,omitempty"` // e.g. "/library/streams/12345" for external subs
	Codec                string   `json:"codec,omitempty"`
	Format               string   `json:"format,omitempty"`
	Language             string   `json:"language,omitempty"`
	LanguageCode         string   `json:"languageCode,omitempty"`
	LanguageTag          string   `json:"languageTag,omitempty"`
	DisplayTitle         string   `json:"displayTitle,omitempty"`
	ExtendedDisplayTitle string   `json:"extendedDisplayTitle,omitempty"`
	Title                string   `json:"title,omitempty"`
	Default              flexBool `json:"default,omitempty"`
	Forced               flexBool `json:"forced,omitempty"`
	Selected             flexBool `json:"selected,omitempty"`
	External             flexBool `json:"external,omitempty"`
}

Stream represents a video, audio, or subtitle stream inside a Plex Part. streamType: 1 = video, 2 = audio, 3 = subtitle.

Jump to

Keyboard shortcuts

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