package playback import ( "errors" "fmt" "time" ) var ( ErrPlaylistFeedUUIDRequired = errors.New("playlist feed UUID is required when its domain is configured") ErrPlaylistEntryEmpty = errors.New("playlist entry must contain at least one feed") ErrPlaylistSyncFeedsRequired = errors.New("synchronized playlist entry requires both video and audio feeds") ErrPlaylistDurationNegative = errors.New("playlist entry duration cannot be negative") ErrPlaylistFailurePolicy = errors.New("invalid playlist failure policy") ) type PlaylistFailurePolicy uint8 const ( PlaylistFailureWait PlaylistFailurePolicy = iota PlaylistFailureNext ) func (p PlaylistFailurePolicy) String() string { switch p { case PlaylistFailureWait: return "wait" case PlaylistFailureNext: return "next" default: return fmt.Sprintf("PlaylistFailurePolicy(%d)", uint8(p)) } } func (p PlaylistFailurePolicy) Validate() error { if p != PlaylistFailureWait && p != PlaylistFailureNext { return ErrPlaylistFailurePolicy } return nil } type PlaylistFeed struct { Domain string UUID string } type PlaylistEntry struct { Name string Video PlaylistFeed Audio PlaylistFeed SyncRequested bool Duration time.Duration } type Playlist struct { Entries []PlaylistEntry Loop bool OnFailure PlaylistFailurePolicy Retry *RetryPolicy } func (f PlaylistFeed) IsConfigured() bool { return f.UUID != "" } func (f PlaylistFeed) Validate() error { if f.UUID != "" && f.Domain == "" { return ErrFeedDomainRequired } if f.Domain != "" && f.UUID == "" { return ErrPlaylistFeedUUIDRequired } return nil } func (e PlaylistEntry) Validate() error { if err := e.Video.Validate(); err != nil { return fmt.Errorf("video: %w", err) } if err := e.Audio.Validate(); err != nil { return fmt.Errorf("audio: %w", err) } if !e.Video.IsConfigured() && !e.Audio.IsConfigured() { return ErrPlaylistEntryEmpty } if e.SyncRequested && (!e.Video.IsConfigured() || !e.Audio.IsConfigured()) { return ErrPlaylistSyncFeedsRequired } if e.Duration < 0 { return ErrPlaylistDurationNegative } return nil } func (p Playlist) Validate() error { if err := p.OnFailure.Validate(); err != nil { return err } if p.Retry != nil { if err := p.Retry.Validate(); err != nil { return fmt.Errorf("playlist retry: %w", err) } } for index, entry := range p.Entries { if err := entry.Validate(); err != nil { return fmt.Errorf("playlist entry %d: %w", index, err) } } return nil } func (e PlaylistEntry) SessionConfig(retry RetryPolicy) SessionConfig { return SessionConfig{ Video: FeedConfig{ Domain: e.Video.Domain, UUID: e.Video.UUID, Active: e.Video.UUID != "", }, Audio: FeedConfig{ Domain: e.Audio.Domain, UUID: e.Audio.UUID, Active: e.Audio.UUID != "", }, SyncRequested: e.SyncRequested, Retry: retry, } }