112 lines
2.8 KiB
Go
112 lines
2.8 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"time"
|
|
|
|
"mxl-player/internal/playback"
|
|
)
|
|
|
|
type playlistFile struct {
|
|
Entries []playlistFileEntry `json:"entries"`
|
|
Loop bool `json:"loop"`
|
|
OnFailure string `json:"on_failure"`
|
|
}
|
|
|
|
type playlistFileEntry struct {
|
|
Name string `json:"name"`
|
|
Video *playlistFileFeed `json:"video"`
|
|
Audio *playlistFileFeed `json:"audio"`
|
|
Sync bool `json:"sync"`
|
|
Duration string `json:"duration"`
|
|
}
|
|
|
|
type playlistFileFeed struct {
|
|
Domain string `json:"domain"`
|
|
UUID string `json:"uuid"`
|
|
}
|
|
|
|
func loadPlaylistFile(path string) (playback.Playlist, error) {
|
|
file, err := os.Open(path)
|
|
if err != nil {
|
|
return playback.Playlist{}, fmt.Errorf("open playlist %q: %w", path, err)
|
|
}
|
|
defer file.Close()
|
|
|
|
playlist, err := decodePlaylistFile(file)
|
|
if err != nil {
|
|
return playback.Playlist{}, fmt.Errorf("decode playlist %q: %w", path, err)
|
|
}
|
|
return playlist, nil
|
|
}
|
|
|
|
func decodePlaylistFile(reader io.Reader) (playback.Playlist, error) {
|
|
decoder := json.NewDecoder(reader)
|
|
decoder.DisallowUnknownFields()
|
|
|
|
var file playlistFile
|
|
if err := decoder.Decode(&file); err != nil {
|
|
return playback.Playlist{}, fmt.Errorf("decode JSON: %w", err)
|
|
}
|
|
var extra any
|
|
if err := decoder.Decode(&extra); err != io.EOF {
|
|
if err == nil {
|
|
return playback.Playlist{}, fmt.Errorf("decode JSON: multiple root values")
|
|
}
|
|
return playback.Playlist{}, fmt.Errorf("decode trailing JSON: %w", err)
|
|
}
|
|
|
|
playlist := playback.Playlist{
|
|
Entries: make([]playback.PlaylistEntry, len(file.Entries)),
|
|
Loop: file.Loop,
|
|
}
|
|
switch file.OnFailure {
|
|
case "", "wait":
|
|
playlist.OnFailure = playback.PlaylistFailureWait
|
|
case "next":
|
|
playlist.OnFailure = playback.PlaylistFailureNext
|
|
default:
|
|
return playback.Playlist{}, fmt.Errorf(
|
|
"on_failure %q: %w", file.OnFailure, playback.ErrPlaylistFailurePolicy,
|
|
)
|
|
}
|
|
for index, entry := range file.Entries {
|
|
duration := time.Duration(0)
|
|
if entry.Duration != "" {
|
|
parsed, err := time.ParseDuration(entry.Duration)
|
|
if err != nil {
|
|
return playback.Playlist{}, fmt.Errorf(
|
|
"playlist entry %d duration %q: %w",
|
|
index,
|
|
entry.Duration,
|
|
err,
|
|
)
|
|
}
|
|
duration = parsed
|
|
}
|
|
|
|
playlist.Entries[index] = playback.PlaylistEntry{
|
|
Name: entry.Name,
|
|
Video: playlistFileFeedToPlayback(entry.Video),
|
|
Audio: playlistFileFeedToPlayback(entry.Audio),
|
|
SyncRequested: entry.Sync,
|
|
Duration: duration,
|
|
}
|
|
}
|
|
|
|
if err := playlist.Validate(); err != nil {
|
|
return playback.Playlist{}, fmt.Errorf("validate playlist: %w", err)
|
|
}
|
|
return playlist, nil
|
|
}
|
|
|
|
func playlistFileFeedToPlayback(feed *playlistFileFeed) playback.PlaylistFeed {
|
|
if feed == nil {
|
|
return playback.PlaylistFeed{}
|
|
}
|
|
return playback.PlaylistFeed{Domain: feed.Domain, UUID: feed.UUID}
|
|
}
|