Documentation
¶
Overview ¶
Package livetv implements owner-defined virtual channels and IPTV playout. Ether supplies neither programming nor IPTV accounts.
Index ¶
- Constants
- type Block
- type Channel
- type GuideChannel
- type GuideCoverage
- type GuideMatchChange
- type GuideMatchRequest
- type GuideMatchResult
- type Item
- type Options
- type Policy
- type PreviewSession
- type Programme
- type Service
- func (s *Service) AllowedChannels(ctx context.Context, u *core.User) ([]Channel, error)
- func (s *Service) Channel(ctx context.Context, id string) (Channel, error)
- func (s *Service) Channels(ctx context.Context) ([]Channel, error)
- func (s *Service) Close() error
- func (s *Service) Coverage(ctx context.Context) ([]GuideCoverage, error)
- func (s *Service) DeleteChannel(ctx context.Context, id string) error
- func (s *Service) DeleteSource(ctx context.Context, id string) error
- func (s *Service) Failover(ctx context.Context, id string, u *core.User) error
- func (s *Service) Guide(ctx context.Context, u *core.User, from, until time.Time) ([]Programme, error)
- func (s *Service) GuideChannels(ctx context.Context, q, source string, limit int) ([]GuideChannel, error)
- func (s *Service) Handler() http.Handler
- func (s *Service) HomeHub(ctx context.Context, u *core.User, limit int) (core.Hub, error)
- func (s *Service) ImportSource(ctx context.Context, src Source) (Source, error)
- func (s *Service) MatchGuides(ctx context.Context, req GuideMatchRequest) (GuideMatchResult, error)
- func (s *Service) Policy(ctx context.Context, userID int64) (Policy, error)
- func (s *Service) RefreshSource(ctx context.Context, id string) (Source, error)
- func (s *Service) SaveChannel(ctx context.Context, c Channel) (Channel, error)
- func (s *Service) SavePolicy(ctx context.Context, p Policy) error
- func (s *Service) ServeDASH(w http.ResponseWriter, r *http.Request, id string, u *core.User)
- func (s *Service) ServeDASHPart(w http.ResponseWriter, r *http.Request, id string, u *core.User, rep, n int, ...)
- func (s *Service) ServePlaylist(w http.ResponseWriter, r *http.Request, id string, u *core.User)
- func (s *Service) ServeRelaySegment(w http.ResponseWriter, r *http.Request, id string, n int, u *core.User)
- func (s *Service) ServeResource(w http.ResponseWriter, r *http.Request, id, key string, u *core.User)
- func (s *Service) ServeSegment(w http.ResponseWriter, r *http.Request, id string, start int64, n int, ...)
- func (s *Service) ServeXMLTV(w http.ResponseWriter, r *http.Request, u *core.User)
- func (s *Service) Session(ctx context.Context, id string, u *core.User) (SessionInfo, error)
- func (s *Service) Sessions(ctx context.Context, u *core.User) []SessionInfo
- func (s *Service) Sources(ctx context.Context) ([]Source, error)
- func (s *Service) Stop(ctx context.Context, id string, u *core.User) error
- func (s *Service) StreamSourceStatuses(ctx context.Context, channelID string) ([]StreamSourceStatus, error)
- func (s *Service) TranscodeFor(playbackID string) (core.HLSSession, bool)
- func (s *Service) Tune(ctx context.Context, u *core.User, channelID string, caps core.ClientCaps) (SessionInfo, error)
- func (s *Service) TuneWithSession(ctx context.Context, u *core.User, channelID string, caps core.ClientCaps, ...) (SessionInfo, error)
- func (s *Service) ViewingSessions() []*core.PlaySession
- type SessionInfo
- type Source
- type SourceAuth
- type StreamSource
- type StreamSourceStatus
- type XMLGuide
Constants ¶
const Provider = "tv.plex.providers.epg.xmltv:1"
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Channel ¶
type Channel struct {
Transcode bool `json:"transcode"` // IPTV: normalize codecs through the transcoder
ID string `json:"id"`
Number string `json:"number"`
Name string `json:"name"`
Kind string `json:"kind"` // virtual or iptv
Enabled bool `json:"enabled"`
Favorite bool `json:"favorite"`
Logo string `json:"logo,omitempty"`
Rating string `json:"rating,omitempty"`
AllowedUsers []int64 `json:"allowedUsers,omitempty"`
SourceID string `json:"sourceId,omitempty"`
GuideID string `json:"guideId,omitempty"`
StreamURL string `json:"streamUrl,omitempty"` // owner API only
StreamSources []StreamSource `json:"streamSources,omitempty"` // ordered inputs; independent of channel/guide identity
Group string `json:"group,omitempty"`
Epoch time.Time `json:"epoch"`
Seed int64 `json:"seed"`
Shuffle bool `json:"shuffle"`
Items []Item `json:"items"`
Blocks []Block `json:"blocks,omitempty"`
// Collection/Filter are expanded into a stable item snapshot when saved.
Collection core.RatingKey `json:"collection,omitempty"`
Filter *core.Filter `json:"filter,omitempty"`
// GuideSourceID names the source whose XMLTV supplies GuideID's
// programmes. Empty means SourceID. Virtual channels ignore it.
GuideSourceID string `json:"guideSourceId,omitempty"`
}
type GuideChannel ¶
type GuideChannel struct {
SourceID string `json:"sourceId"`
SourceName string `json:"sourceName"`
GuideID string `json:"guideId"`
Name string `json:"name"`
Logo string `json:"logo"`
Programmes int `json:"programmes"`
Until *time.Time `json:"until,omitempty"`
}
GuideChannel is an XMLTV <channel> (or programme channel id) stored for a source. Programmes counts programmes that have not yet ended; Until is the end of the last stored programme.
type GuideCoverage ¶
type GuideMatchChange ¶
type GuideMatchChange struct {
ChannelID string `json:"channelId"`
ChannelName string `json:"channelName"`
GuideSourceID string `json:"guideSourceId"`
GuideSourceName string `json:"guideSourceName"`
GuideID string `json:"guideId"`
GuideName string `json:"guideName"`
Method string `json:"method"` // "id" or "name"
}
type GuideMatchRequest ¶
type GuideMatchResult ¶
type GuideMatchResult struct {
Matched int `json:"matched"`
Unmatched int `json:"unmatched"`
Skipped int `json:"skipped"` // already had guide data and overwrite was false
DryRun bool `json:"dryRun"`
Changes []GuideMatchChange `json:"changes"`
}
type Options ¶
type Options struct {
CanPlay func(*core.User, bool, time.Time) bool
StreamLimit func(*core.User) int
DB *sql.DB
Catalog core.Catalog
Collections core.Collections
Playback core.Playback
Transcoder core.Transcoder
HTTPClient *http.Client
Now func() time.Time
// Allowed applies additional owner title/genre policies to every surface.
Allowed func(*core.User, *core.Title) bool
// Warm resolves/primes a title without opening a viewer stream.
Warm func(context.Context, core.RatingKey) error
}
type PreviewSession ¶
type PreviewSession struct {
ID string `json:"id"`
Playlist string `json:"playlist"`
ActiveSourceID string `json:"activeSourceId,omitempty"`
ActiveSourceName string `json:"activeSourceName,omitempty"`
Programme *Programme `json:"programme,omitempty"`
}
PreviewSession is what the owner UI needs to play a tuned channel. Stream URLs never leave the server; the playlist is relative to the livetv API.
type Programme ¶
type Programme struct {
ID string `json:"id"`
ChannelID string `json:"channelId"`
Start time.Time `json:"start"`
End time.Time `json:"end"`
Item Item `json:"item"`
}
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
func (*Service) AllowedChannels ¶
func (*Service) Coverage ¶
func (s *Service) Coverage(ctx context.Context) ([]GuideCoverage, error)
Coverage reports, for every channel, how many programmes the guide has in the next 24 hours and when its guide data runs out.
func (*Service) DeleteChannel ¶
func (*Service) Failover ¶
Failover keeps the channel timestamp when Playback resolves another release.
func (*Service) GuideChannels ¶
func (s *Service) GuideChannels(ctx context.Context, q, source string, limit int) ([]GuideChannel, error)
GuideChannels searches stored XMLTV channels by case-insensitive substring of name, any display name, or guide id.
func (*Service) Handler ¶
Handler is the owner management API. Mount behind the application's owner authentication and CSRF middleware; it deliberately cannot authenticate Plex tokens on its own. Streaming uses the separately authenticated Plex adapter.
func (*Service) HomeHub ¶
HomeHub is the domain-level row used by the owner's final-layout preview. The Plex adapter preserves its own channel/tune metadata for this row ID; these synthetic keys are never sent to catalogue playback.
func (*Service) ImportSource ¶
ImportSource atomically replaces source programming. Owner changes to mapping, enabled state, channel numbering, favourites and restrictions survive refresh.
func (*Service) MatchGuides ¶
func (s *Service) MatchGuides(ctx context.Context, req GuideMatchRequest) (GuideMatchResult, error)
MatchGuides maps IPTV channels to guide channels from any source: an exact tvg-id wins, then a case-insensitive id, then a normalized display name. Only guide channels with upcoming programmes are considered, and disabled channels only when listed in ChannelIDs. Without Overwrite, channels that already have programmes in the next 24 hours are left alone.
func (*Service) RefreshSource ¶
RefreshSource re-imports a stored source. On failure the existing channels and guide are kept and the error is recorded on the source.
func (*Service) SaveChannel ¶
func (*Service) ServeDASHPart ¶
func (*Service) ServePlaylist ¶
ServePlaylist creates a sliding live playlist. Each programme has its own transcoder, with a discontinuity at the exact programme boundary. Offsets are measured in programme time and never in release byte positions.
func (*Service) ServeRelaySegment ¶
func (*Service) ServeResource ¶
func (*Service) ServeSegment ¶
func (*Service) ServeXMLTV ¶
func (*Service) StreamSourceStatuses ¶
func (s *Service) StreamSourceStatuses(ctx context.Context, channelID string) ([]StreamSourceStatus, error)
StreamSourceStatuses is owner-only health, without source URLs or upstream errors that might contain credentials. Transient failures never ban a source.
func (*Service) TranscodeFor ¶
func (s *Service) TranscodeFor(playbackID string) (core.HLSSession, bool)
TranscodeFor lets the playback manager's existing release failover locate programme sessions, preserving the same media timestamp after a source swap.
func (*Service) Tune ¶
func (s *Service) Tune(ctx context.Context, u *core.User, channelID string, caps core.ClientCaps) (SessionInfo, error)
func (*Service) TuneWithSession ¶
func (s *Service) TuneWithSession(ctx context.Context, u *core.User, channelID string, caps core.ClientCaps, clientSessionID string) (SessionInfo, error)
TuneWithSession preserves the client's playback-session identity independently of the server's stream, subscription and activity identities.
func (*Service) ViewingSessions ¶
func (s *Service) ViewingSessions() []*core.PlaySession
ViewingSessions reports one trusted reservation unit per live viewer. It is used by ordinary playback's limit accounting, including HLS passthrough viewers that do not own an ffmpeg/source session.
type SessionInfo ¶
type SessionInfo struct {
ActiveSourceID string `json:"activeSourceId,omitempty"`
// Plex's rolling subscription identity is numeric; the stream UUID remains
// separate. These are ephemeral identities, scoped to this service lifetime.
SubscriptionID int64 `json:"-"`
ActivityID string `json:"-"`
ClientSessionID string `json:"-"`
MediaInfo *core.MediaInfo `json:"-"` // Actual source analysis for native clients.
ID string `json:"id"`
UserID int64 `json:"userId"`
Channel Channel `json:"channel"`
Programme *Programme `json:"programme,omitempty"`
StartedAt time.Time `json:"startedAt"`
}
type Source ¶
type Source struct {
ID string `json:"id"`
Name string `json:"name"`
PlaylistURL string `json:"playlistUrl,omitempty"`
GuideURL string `json:"guideUrl,omitempty"`
// Inline uploads are supplied on import and are not returned by GET.
Playlist string `json:"playlist,omitempty"`
Guide string `json:"guide,omitempty"`
UpdatedAt time.Time `json:"updatedAt"`
// Auth authenticates playlist, guide and stream requests to the provider.
// Secrets are redacted by the owner API (see auth.go).
Auth *SourceAuth `json:"auth,omitempty"`
// RefreshHours caps the time between automatic refreshes of URL-backed
// sources: 0 uses the 6 hour default, a negative value means manual
// refresh only. Sources also refresh before mapped listings run out.
// Inline uploads never refresh automatically.
RefreshHours int `json:"refreshHours,omitempty"`
// LastError/LastErrorAt/RefreshFailures record failed refreshes; a
// successful import clears them. Existing data is kept on failure.
LastError string `json:"lastError,omitempty"`
LastErrorAt *time.Time `json:"lastErrorAt,omitempty"`
RefreshFailures int `json:"refreshFailures,omitempty"`
// Read-only statistics computed by Sources(); ignored on import.
HasPlaylist bool `json:"hasPlaylist"`
HasGuide bool `json:"hasGuide"`
ChannelCount int `json:"channelCount"`
GuideChannelCount int `json:"guideChannelCount"`
ProgrammeCount int `json:"programmeCount"`
GuideFrom *time.Time `json:"guideFrom,omitempty"`
GuideUntil *time.Time `json:"guideUntil,omitempty"`
NextRefresh *time.Time `json:"nextRefresh,omitempty"`
}
type SourceAuth ¶
type SourceAuth struct {
// Type: "" (none), "basic", "bearer", "headers" or "xtream".
Type string `json:"type,omitempty"`
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"` // secret
Token string `json:"token,omitempty"` // secret (bearer)
// UserAgent and Referer apply to every type; many providers reject
// unknown clients.
UserAgent string `json:"userAgent,omitempty"`
Referer string `json:"referer,omitempty"`
// Headers are extra request headers (cookies, API keys); values are secret.
Headers map[string]string `json:"headers,omitempty"`
// Xtream Codes: the panel URL. Playlist and guide URLs are derived from
// it and the credentials, and never returned by the API.
Server string `json:"server,omitempty"`
// Output selects the Xtream stream format: "ts" (default) or "m3u8".
Output string `json:"output,omitempty"`
}
SourceAuth is how Ether authenticates to an IPTV or guide provider. It is used for the source's playlist and guide fetches and, through upstreamHeaders, for the streams of channel inputs that belong to it.
type StreamSource ¶
type StreamSource struct {
ID string `json:"id"`
Name string `json:"name"`
URL string `json:"url"`
Enabled bool `json:"enabled"`
Priority int `json:"priority"` // smaller first; equal priorities retain list order
// AuthSourceID names the source whose provider credentials this input
// uses; empty means the channel's own source.
AuthSourceID string `json:"authSourceId,omitempty"`
}
StreamSource is one independently replaceable input to a channel. EPG, numbering, favourites and viewer policy belong to Channel, never to an input.
type StreamSourceStatus ¶
type StreamSourceStatus struct {
ID string `json:"id"`
State string `json:"state"`
Failures int `json:"failures"`
LastChecked time.Time `json:"lastChecked,omitempty"`
LastPlayed time.Time `json:"lastPlayed,omitempty"`
RetryAfter time.Time `json:"retryAfter,omitempty"`
LastError string `json:"lastError,omitempty"`
}