Files
iptv-proxy/vendor/github.com/tellytv/go.xtream-codes/structs.go
2019-04-27 17:46:02 +02:00

307 lines
10 KiB
Go

package xtreamcodes
import (
"bytes"
"encoding/json"
"fmt"
"strconv"
"strings"
"time"
)
// Timestamp is a helper struct to convert unix timestamp ints and strings to time.Time.
type Timestamp struct {
time.Time
quoted bool
}
// MarshalJSON returns the Unix timestamp as a string.
func (t Timestamp) MarshalJSON() ([]byte, error) {
if t.quoted {
return []byte(`"` + strconv.FormatInt(t.Time.Unix(), 10) + `"`), nil
}
return []byte(strconv.FormatInt(t.Time.Unix(), 10)), nil
}
// UnmarshalJSON converts the int or string to a Unix timestamp.
func (t *Timestamp) UnmarshalJSON(b []byte) error {
// Timestamps are sometimes quoted, sometimes not, lets just always remove quotes just in case...
t.quoted = strings.Contains(string(b), `"`)
ts, err := strconv.Atoi(strings.Replace(string(b), `"`, "", -1))
if err != nil {
return err
}
t.Time = time.Unix(int64(ts), 0)
return nil
}
// ConvertibleBoolean is a helper type to allow JSON documents using 0/1 or "true" and "false" be converted to bool.
type ConvertibleBoolean struct {
bool
quoted bool
}
// MarshalJSON returns a 0 or 1 depending on bool state.
func (bit ConvertibleBoolean) MarshalJSON() ([]byte, error) {
var bitSetVar int8
if bit.bool {
bitSetVar = 1
}
if bit.quoted {
return json.Marshal(fmt.Sprint(bitSetVar))
}
return json.Marshal(bitSetVar)
}
// UnmarshalJSON converts a 0, 1, true or false into a bool
func (bit *ConvertibleBoolean) UnmarshalJSON(data []byte) error {
bit.quoted = strings.Contains(string(data), `"`)
// Bools as ints are sometimes quoted, sometimes not, lets just always remove quotes just in case...
asString := strings.Replace(string(data), `"`, "", -1)
if asString == "1" || asString == "true" {
bit.bool = true
} else if asString == "0" || asString == "false" {
bit.bool = false
} else {
return fmt.Errorf("Boolean unmarshal error: invalid input %s", asString)
}
return nil
}
// jsonInt is a int64 which unmarshals from JSON
// as either unquoted or quoted (with any amount
// of internal leading/trailing whitespace).
// Originally found at https://bit.ly/2NkJ0SK and
// https://play.golang.org/p/KNPxDL1yqL
type jsonInt int64
func (f jsonInt) MarshalJSON() ([]byte, error) {
return json.Marshal(int64(f))
}
func (f *jsonInt) UnmarshalJSON(data []byte) error {
var v int64
data = bytes.Trim(data, `" `)
err := json.Unmarshal(data, &v)
*f = jsonInt(v)
return err
}
// ServerInfo describes the state of the Xtream-Codes server.
type ServerInfo struct {
HTTPSPort int `json:"https_port,string"`
Port int `json:"port,string"`
Process bool `json:"process"`
RTMPPort int `json:"rtmp_port,string"`
Protocol string `json:"server_protocol"`
TimeNow string `json:"time_now"`
TimestampNow Timestamp `json:"timestamp_now,string"`
Timezone string `json:"timezone"`
URL string `json:"url"`
}
// UserInfo is the current state of the user as it relates to the Xtream-Codes server.
type UserInfo struct {
ActiveConnections int `json:"active_cons,string"`
AllowedOutputFormats []string `json:"allowed_output_formats"`
Auth ConvertibleBoolean `json:"auth"`
CreatedAt Timestamp `json:"created_at"`
ExpDate *Timestamp `json:"exp_date"`
IsTrial ConvertibleBoolean `json:"is_trial,string"`
MaxConnections int `json:"max_connections,string"`
Message string `json:"message"`
Password string `json:"password"`
Status string `json:"status"`
Username string `json:"username"`
}
// AuthenticationResponse is a container for what the server returns after the initial authentication.
type AuthenticationResponse struct {
ServerInfo ServerInfo `json:"server_info"`
UserInfo UserInfo `json:"user_info"`
}
// Category describes a grouping of Stream.
type Category struct {
ID int `json:"category_id,string"`
Name string `json:"category_name"`
Parent int `json:"parent_id"`
// Set by us, not Xtream.
Type string `json:"-"`
}
// Stream is a streamble video source.
type Stream struct {
Added *Timestamp `json:"added"`
CategoryID int `json:"category_id,string"`
ContainerExtension string `json:"container_extension"`
CustomSid string `json:"custom_sid"`
DirectSource string `json:"direct_source,omitempty"`
EPGChannelID string `json:"epg_channel_id"`
Icon string `json:"stream_icon"`
ID int `json:"stream_id"`
Name string `json:"name"`
Number int `json:"num"`
Rating FlexFloat `json:"rating"`
Rating5based float64 `json:"rating_5based"`
TVArchive int `json:"tv_archive"`
TVArchiveDuration *jsonInt `json:"tv_archive_duration"`
Type string `json:"stream_type"`
}
type FlexFloat float64
func (ff *FlexFloat) UnmarshalJSON(b []byte) error {
if b[0] != '"' {
return json.Unmarshal(b, (*float64)(ff))
}
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
if len(s) == 0 {
s = "0"
}
f, err := strconv.ParseFloat(s, 64)
if err != nil {
f = 0
}
*ff = FlexFloat(f)
return nil
}
// SeriesInfo contains information about a TV series.
type SeriesInfo struct {
BackdropPath *JSONStringSlice `json:"backdrop_path,omitempty"`
Cast string `json:"cast"`
CategoryID *int `json:"category_id,string"`
Cover string `json:"cover"`
Director string `json:"director"`
EpisodeRunTime string `json:"episode_run_time"`
Genre string `json:"genre"`
LastModified *Timestamp `json:"last_modified,omitempty"`
Name string `json:"name"`
Num int `json:"num"`
Plot string `json:"plot"`
Rating int `json:"rating,string"`
Rating5 float64 `json:"rating_5based"`
ReleaseDate string `json:"releaseDate"`
SeriesID int `json:"series_id"`
StreamType string `json:"stream_type"`
YoutubeTrailer string `json:"youtube_trailer"`
}
type SeriesEpisode struct {
Added string `json:"added"`
ContainerExtension string `json:"container_extension"`
CustomSid string `json:"custom_sid"`
DirectSource string `json:"direct_source"`
EpisodeNum int `json:"episode_num"`
ID string `json:"id"`
Info struct {
Audio FFMPEGStreamInfo `json:"audio"`
Bitrate int `json:"bitrate"`
Duration string `json:"duration"`
DurationSecs int `json:"duration_secs"`
MovieImage string `json:"movie_image"`
Name string `json:"name"`
Plot string `json:"plot"`
Rating FlexFloat `json:"rating"`
ReleaseDate string `json:"releasedate"`
Video FFMPEGStreamInfo `json:"video"`
} `json:"info"`
Season int `json:"season"`
Title string `json:"title"`
}
type Series struct {
Episodes map[string][]SeriesEpisode `json:"episodes"`
Info SeriesInfo `json:"info"`
Seasons []interface{} `json:"seasons"`
}
// VideoOnDemandInfo contains information about a video on demand stream.
type VideoOnDemandInfo struct {
Info struct {
Audio FFMPEGStreamInfo `json:"audio"`
BackdropPath []string `json:"backdrop_path"`
Bitrate int `json:"bitrate"`
Cast string `json:"cast"`
Director string `json:"director"`
Duration string `json:"duration"`
DurationSecs int `json:"duration_secs"`
Genre string `json:"genre"`
MovieImage string `json:"movie_image"`
Plot string `json:"plot"`
Rating string `json:"rating"`
ReleaseDate string `json:"releasedate"`
TmdbID string `json:"tmdb_id"`
Video FFMPEGStreamInfo `json:"video"`
YoutubeTrailer string `json:"youtube_trailer"`
} `json:"info"`
MovieData struct {
Added Timestamp `json:"added"`
CategoryID int `json:"category_id,string"`
ContainerExtension string `json:"container_extension"`
CustomSid string `json:"custom_sid"`
DirectSource string `json:"direct_source"`
Name string `json:"name"`
StreamID int `json:"stream_id"`
} `json:"movie_data"`
}
type epgContainer struct {
EPGListings []EPGInfo `json:"epg_listings"`
}
// EPGInfo describes electronic programming guide information of a stream.
type EPGInfo struct {
ChannelID string `json:"channel_id"`
Description Base64Value `json:"description"`
End string `json:"end"`
EPGID int `json:"epg_id,string"`
HasArchive ConvertibleBoolean `json:"has_archive"`
ID int `json:"id,string"`
Lang string `json:"lang"`
NowPlaying ConvertibleBoolean `json:"now_playing"`
Start string `json:"start"`
StartTimestamp Timestamp `json:"start_timestamp"`
StopTimestamp Timestamp `json:"stop_timestamp"`
Title Base64Value `json:"title"`
}
// JSONStringSlice is a struct containing a slice of strings.
// It is needed for cases in which we may get an array or may get
// a single string in a JSON response.
type JSONStringSlice struct {
Slice []string `json:"-"`
SingleString bool `json:"-"`
}
// MarshalJSON returns b as the JSON encoding of b.
func (b JSONStringSlice) MarshalJSON() ([]byte, error) {
if !b.SingleString {
return json.Marshal(b.Slice)
}
return json.Marshal(b.Slice[0])
}
// UnmarshalJSON sets *b to a copy of data.
func (b *JSONStringSlice) UnmarshalJSON(data []byte) error {
if data[0] == '"' {
data = append([]byte(`[`), data...)
data = append(data, []byte(`]`)...)
b.SingleString = true
}
return json.Unmarshal(data, &b.Slice)
}