From 93430a69ee8b6082c903bf4f3ca1f5ab8a216787 Mon Sep 17 00:00:00 2001 From: Pierre-Emmanuel Jacquier Date: Sat, 27 Apr 2019 17:46:02 +0200 Subject: [PATCH] Add iptv smarter android app AND xtream server api compatibility (#2) Signed-off-by: Pierre-Emmanuel Jacquier --- cmd/root.go | 24 +- docker-compose.yml | 8 + go.mod | 2 +- go.sum | 2 + pkg/config/config.go | 14 +- pkg/m3u/m3u.go | 2 +- pkg/routes/routes.go | 57 +- pkg/routes/xtream.go | 133 +++++ pkg/xtream-proxy/xtream-proxy.go | 64 +++ vendor/github.com/davecgh/go-spew/LICENSE | 15 - .../github.com/davecgh/go-spew/spew/bypass.go | 145 ----- .../davecgh/go-spew/spew/bypasssafe.go | 38 -- .../github.com/davecgh/go-spew/spew/common.go | 341 ------------ .../github.com/davecgh/go-spew/spew/config.go | 306 ----------- vendor/github.com/davecgh/go-spew/spew/doc.go | 211 -------- .../github.com/davecgh/go-spew/spew/dump.go | 509 ------------------ .../github.com/davecgh/go-spew/spew/format.go | 419 -------------- .../github.com/davecgh/go-spew/spew/spew.go | 148 ----- .../tellytv/go.xtream-codes/.gitignore | 12 + .../go.xtream-codes/.gometalinter.json | 33 ++ .../tellytv/go.xtream-codes/LICENSE | 21 + .../tellytv/go.xtream-codes/README.md | 2 + .../tellytv/go.xtream-codes/base64.go | 58 ++ .../tellytv/go.xtream-codes/ffmpeg.go | 181 +++++++ .../tellytv/go.xtream-codes/structs.go | 306 +++++++++++ .../tellytv/go.xtream-codes/xtream-codes.go | 316 +++++++++++ vendor/modules.txt | 4 +- 27 files changed, 1218 insertions(+), 2153 deletions(-) create mode 100644 pkg/routes/xtream.go create mode 100644 pkg/xtream-proxy/xtream-proxy.go delete mode 100644 vendor/github.com/davecgh/go-spew/LICENSE delete mode 100644 vendor/github.com/davecgh/go-spew/spew/bypass.go delete mode 100644 vendor/github.com/davecgh/go-spew/spew/bypasssafe.go delete mode 100644 vendor/github.com/davecgh/go-spew/spew/common.go delete mode 100644 vendor/github.com/davecgh/go-spew/spew/config.go delete mode 100644 vendor/github.com/davecgh/go-spew/spew/doc.go delete mode 100644 vendor/github.com/davecgh/go-spew/spew/dump.go delete mode 100644 vendor/github.com/davecgh/go-spew/spew/format.go delete mode 100644 vendor/github.com/davecgh/go-spew/spew/spew.go create mode 100644 vendor/github.com/tellytv/go.xtream-codes/.gitignore create mode 100644 vendor/github.com/tellytv/go.xtream-codes/.gometalinter.json create mode 100644 vendor/github.com/tellytv/go.xtream-codes/LICENSE create mode 100644 vendor/github.com/tellytv/go.xtream-codes/README.md create mode 100644 vendor/github.com/tellytv/go.xtream-codes/base64.go create mode 100644 vendor/github.com/tellytv/go.xtream-codes/ffmpeg.go create mode 100644 vendor/github.com/tellytv/go.xtream-codes/structs.go create mode 100644 vendor/github.com/tellytv/go.xtream-codes/xtream-codes.go diff --git a/cmd/root.go b/cmd/root.go index 4a39fe2..6671b15 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -3,14 +3,15 @@ package cmd import ( "fmt" "log" + "net/url" "os" "strings" + "github.com/jamesnetherton/m3u" "github.com/pierre-emmanuelJ/iptv-proxy/pkg/config" "github.com/pierre-emmanuelJ/iptv-proxy/pkg/routes" - "github.com/jamesnetherton/m3u" homedir "github.com/mitchellh/go-homedir" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -23,7 +24,13 @@ var rootCmd = &cobra.Command{ Use: "iptv-proxy", Short: "A brief description of your application", Run: func(cmd *cobra.Command, args []string) { - playlist, err := m3u.Parse(viper.GetString("m3u-url")) + m3uURL := viper.GetString("m3u-url") + playlist, err := m3u.Parse(m3uURL) + if err != nil { + log.Fatal(err) + } + + remoteHostURL, err := url.Parse(m3uURL) if err != nil { log.Fatal(err) } @@ -34,8 +41,12 @@ var rootCmd = &cobra.Command{ Hostname: viper.GetString("hostname"), Port: viper.GetInt64("port"), }, - User: viper.GetString("user"), - Password: viper.GetString("password"), + RemoteURL: remoteHostURL, + XtreamUser: viper.GetString("xtream-user"), + XtreamPassword: viper.GetString("xtream-password"), + XtreamBaseURL: viper.GetString("xtream-base-url"), + User: viper.GetString("user"), + Password: viper.GetString("password"), } if e := routes.Serve(conf); e != nil { @@ -64,7 +75,10 @@ func init() { rootCmd.Flags().Int64("port", 8080, "Port to expose the IPTVs endpoints") rootCmd.Flags().String("hostname", "", "Hostname or IP to expose the IPTVs endpoints") rootCmd.Flags().String("user", "usertest", "user UNSAFE(temp auth to access proxy)") - rootCmd.Flags().String("password", "passwordtest", "password UNSAFE(temp auth to access proxy)") + rootCmd.Flags().String("password", "passwordtest", "password UNSAFE(auth to access m3u proxy and xtream proxy)") + rootCmd.Flags().String("xtream-user", "xtream_user", "Xtream-code user login") + rootCmd.Flags().String("xtream-password", "xtream_password", "Xtream-code password login") + rootCmd.Flags().String("xtream-base-url", "http://expample.tv:8080", "Xtream-code base url") if e := viper.BindPFlags(rootCmd.Flags()); e != nil { log.Fatal("error binding PFlags to viper") diff --git a/docker-compose.yml b/docker-compose.yml index 22f2428..66e578b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,4 +22,12 @@ services: # Hostname or IP to expose the IPTVs endpoints (for machine not for docker) HOSTNAME: localhost GIN_MODE: release + ## Xtream-code proxy configuration + XTREAM_USER: xtream_user + XTREAM_PASSWORD: xtream_password + XTREAM_BASE_URL: "http://example.tv:8080" + ##### UNSAFE AUTH TODO ADD REAL AUTH + #will be used for m3u and xtream auth poxy + USER: test + PASSWORD: testpassword diff --git a/go.mod b/go.mod index c699b35..8a5bc95 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,6 @@ module github.com/pierre-emmanuelJ/iptv-proxy require ( - github.com/davecgh/go-spew v1.1.1 github.com/gin-contrib/cors v0.0.0-20190226021855-50921afdc5c1 github.com/gin-gonic/gin v1.3.0 github.com/inconshreveable/mousetrap v1.0.0 // indirect @@ -9,4 +8,5 @@ require ( github.com/mitchellh/go-homedir v1.1.0 github.com/spf13/cobra v0.0.3 github.com/spf13/viper v1.3.1 + github.com/tellytv/go.xtream-codes v0.0.0-20190114013623-9b74dcb500e4 ) diff --git a/go.sum b/go.sum index 2966c45..7002939 100644 --- a/go.sum +++ b/go.sum @@ -52,6 +52,8 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/viper v1.3.1 h1:5+8j8FTpnFV4nEImW/ofkzEt8VoOiLXxdYIDsB73T38= github.com/spf13/viper v1.3.1/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/tellytv/go.xtream-codes v0.0.0-20190114013623-9b74dcb500e4 h1:V5xNxrc8ApVSY2uHeVXDkByVB2iAgoOcmWOOu01oATM= +github.com/tellytv/go.xtream-codes v0.0.0-20190114013623-9b74dcb500e4/go.mod h1:gWtQ2uZJ49dBh4cWiFuz7Tb5ALxLB9hY1GFoz34lsGs= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v0.0.0-20181209151446-772ced7fd4c2 h1:EICbibRW4JNKMcY+LsWmuwob+CRS1BmdRdjphAm9mH4= github.com/ugorji/go/codec v0.0.0-20181209151446-772ced7fd4c2/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= diff --git a/pkg/config/config.go b/pkg/config/config.go index b83a467..c74fdda 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1,6 +1,10 @@ package config -import "github.com/jamesnetherton/m3u" +import ( + "net/url" + + "github.com/jamesnetherton/m3u" +) // HostConfiguration containt host infos type HostConfiguration struct { @@ -10,8 +14,12 @@ type HostConfiguration struct { // ProxyConfig Contain original m3u playlist and HostConfiguration type ProxyConfig struct { - Playlist *m3u.Playlist - HostConfig *HostConfiguration + Playlist *m3u.Playlist + HostConfig *HostConfiguration + XtreamUser string + XtreamPassword string + XtreamBaseURL string + RemoteURL *url.URL //XXX Very unsafe User, Password string } diff --git a/pkg/m3u/m3u.go b/pkg/m3u/m3u.go index b7c5e45..a6c50d9 100644 --- a/pkg/m3u/m3u.go +++ b/pkg/m3u/m3u.go @@ -37,7 +37,7 @@ func ReplaceURL(proxyConfig *config.ProxyConfig) (*m3u.Playlist, error) { } config := proxyConfig.HostConfig uri := fmt.Sprintf( - "http://%s:%d%s?user=%s&password=%s", + "http://%s:%d%s?username=%s&password=%s", config.Hostname, config.Port, oriURL.RequestURI(), diff --git a/pkg/routes/routes.go b/pkg/routes/routes.go index 4fc08a2..4f7cc4f 100644 --- a/pkg/routes/routes.go +++ b/pkg/routes/routes.go @@ -1,12 +1,15 @@ package routes import ( + "bytes" "fmt" "io" + "io/ioutil" "log" "net/http" "net/url" "strings" + "time" "github.com/jamesnetherton/m3u" @@ -46,10 +49,16 @@ func Routes(proxyConfig *config.ProxyConfig, r *gin.RouterGroup, newM3U []byte) } r.GET("/iptv.m3u", p.authenticate, p.getM3U) - // XXX Private need for external Android app r.POST("/iptv.m3u", p.authenticate, p.getM3U) + //Xtream, iptv Smarter android app compatibility + r.POST("/player_api.php", p.appAuthenticate, p.xtreamPlayerAPI) + r.GET("/xmltv.php", p.authenticate, p.xtreamXMLTV) + r.GET(fmt.Sprintf("/%s/%s/:id", proxyConfig.User, proxyConfig.Password), p.xtreamStream) + r.GET(fmt.Sprintf("/movie/%s/%s/:id", proxyConfig.User, proxyConfig.Password), p.xtreamStreamMovie) + r.GET(fmt.Sprintf("/series/%s/%s/:id", proxyConfig.User, proxyConfig.Password), p.xtreamStreamSeries) + for i, track := range proxyConfig.Playlist.Tracks { oriURL, err := url.Parse(track.URI) if err != nil { @@ -64,15 +73,25 @@ func Routes(proxyConfig *config.ProxyConfig, r *gin.RouterGroup, newM3U []byte) } } +func (p *proxy) getM3U(c *gin.Context) { + c.Header("Content-Disposition", "attachment; filename=\"iptv.m3u\"") + c.Data(http.StatusOK, "application/octet-stream", p.newM3U) +} + func (p *proxy) reverseProxy(c *gin.Context) { rpURL, err := url.Parse(p.Track.URI) if err != nil { log.Fatal(err) } - resp, err := http.Get(rpURL.String()) + stream(c, rpURL) +} + +func stream(c *gin.Context, oriURL *url.URL) { + resp, err := http.Get(oriURL.String()) if err != nil { - log.Fatal(err) + c.AbortWithError(http.StatusInternalServerError, err) + return } defer resp.Body.Close() @@ -90,14 +109,9 @@ func copyHTTPHeader(c *gin.Context, header http.Header) { } } -func (p *proxy) getM3U(c *gin.Context) { - c.Header("Content-Disposition", "attachment; filename=\"iptv.m3u\"") - c.Data(http.StatusOK, "application/octet-stream", p.newM3U) -} - // AuthRequest handle auth credentials type AuthRequest struct { - User string `form:"user" binding:"required"` + User string `form:"username" binding:"required"` Password string `form:"password" binding:"required"` } // XXX very unsafe @@ -113,6 +127,31 @@ func (p *proxy) authenticate(ctx *gin.Context) { } } +func (p *proxy) appAuthenticate(c *gin.Context) { + contents, err := ioutil.ReadAll(c.Request.Body) + if err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + return + } + + q, err := url.ParseQuery(string(contents)) + if err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + return + } + if len(q["username"]) == 0 || len(q["password"]) == 0 { + c.AbortWithError(http.StatusBadRequest, fmt.Errorf("bad body url query parameters")) + return + } + log.Printf("[iptv-proxy] %v | %s |App Auth\n", time.Now().Format("2006/01/02 - 15:04:05"), c.ClientIP()) + //XXX very unsafe + if p.ProxyConfig.User != q["username"][0] || p.ProxyConfig.Password != q["password"][0] { + c.AbortWithStatus(http.StatusUnauthorized) + } + + c.Request.Body = ioutil.NopCloser(bytes.NewReader(contents)) +} + func initm3u(p *config.ProxyConfig) ([]byte, error) { playlist, err := proxyM3U.ReplaceURL(p) if err != nil { diff --git a/pkg/routes/xtream.go b/pkg/routes/xtream.go new file mode 100644 index 0000000..f798b48 --- /dev/null +++ b/pkg/routes/xtream.go @@ -0,0 +1,133 @@ +package routes + +import ( + "fmt" + "io/ioutil" + "log" + "net/http" + "net/url" + "time" + + "github.com/gin-gonic/gin" + xtreamapi "github.com/pierre-emmanuelJ/iptv-proxy/pkg/xtream-proxy" +) + +func (p *proxy) xtreamPlayerAPI(c *gin.Context) { + contents, err := ioutil.ReadAll(c.Request.Body) + if err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + return + } + + log.Println(string(contents)) + + q, err := url.ParseQuery(string(contents)) + if err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + return + } + if len(q["username"]) == 0 || len(q["password"]) == 0 { + c.AbortWithError(http.StatusBadRequest, fmt.Errorf(`bad body url query parameters: missing "username" and "password"`)) + return + } + + var action string + if len(q["action"]) > 0 { + action = q["action"][0] + } + + client, err := xtreamapi.New(p.XtreamUser, p.XtreamPassword, p.XtreamBaseURL) + if err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + return + } + + var respBody interface{} + + switch action { + case xtreamapi.GetLiveCategories: + respBody, err = client.GetLiveCategories() + case xtreamapi.GetLiveStreams: + respBody, err = client.GetLiveStreams("") + case xtreamapi.GetVodCategories: + respBody, err = client.GetVideoOnDemandCategories() + case xtreamapi.GetVodStreams: + respBody, err = client.GetVideoOnDemandStreams("") + case xtreamapi.GetVodInfo: + if len(q["vod_id"]) < 1 { + c.AbortWithError(http.StatusBadRequest, fmt.Errorf(`bad body url query parameters: missing "vod_id"`)) + return + } + respBody, err = client.GetVideoOnDemandInfo(q["vod_id"][0]) + case xtreamapi.GetSeriesCategories: + respBody, err = client.GetSeriesCategories() + case xtreamapi.GetSeries: + respBody, err = client.GetSeries("") + case xtreamapi.GetSerieInfo: + if len(q["series_id"]) < 1 { + c.AbortWithError(http.StatusBadRequest, fmt.Errorf(`bad body url query parameters: missing "series_id"`)) + return + } + respBody, err = client.GetSeriesInfo(q["series_id"][0]) + default: + respBody, err = client.Login(p.User, p.Password, "http://"+p.HostConfig.Hostname, int(p.HostConfig.Port)) + } + + log.Printf("[iptv-proxy] %v | %s |Action\t%s\n", time.Now().Format("2006/01/02 - 15:04:05"), c.ClientIP(), action) + + if err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + return + } + + c.JSON(http.StatusOK, respBody) +} + +func (p *proxy) xtreamXMLTV(c *gin.Context) { + client, err := xtreamapi.New(p.XtreamUser, p.XtreamPassword, p.XtreamBaseURL) + if err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + return + } + + resp, err := client.GetXMLTV() + if err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + return + } + + c.Data(http.StatusOK, "application/xml", resp) +} + +func (p *proxy) xtreamStream(c *gin.Context) { + id := c.Param("id") + rpURL, err := url.Parse(fmt.Sprintf("%s/%s/%s/%s", p.XtreamBaseURL, p.XtreamUser, p.XtreamPassword, id)) + if err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + return + } + + stream(c, rpURL) +} + +func (p *proxy) xtreamStreamMovie(c *gin.Context) { + id := c.Param("id") + rpURL, err := url.Parse(fmt.Sprintf("%s/movie/%s/%s/%s", p.XtreamBaseURL, p.XtreamUser, p.XtreamPassword, id)) + if err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + return + } + + stream(c, rpURL) +} + +func (p *proxy) xtreamStreamSeries(c *gin.Context) { + id := c.Param("id") + rpURL, err := url.Parse(fmt.Sprintf("%s/series/%s/%s/%s", p.XtreamBaseURL, p.XtreamUser, p.XtreamPassword, id)) + if err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + return + } + + stream(c, rpURL) +} diff --git a/pkg/xtream-proxy/xtream-proxy.go b/pkg/xtream-proxy/xtream-proxy.go new file mode 100644 index 0000000..b66313f --- /dev/null +++ b/pkg/xtream-proxy/xtream-proxy.go @@ -0,0 +1,64 @@ +package xtreamproxy + +import ( + xtream "github.com/tellytv/go.xtream-codes" +) + +const ( + GetLiveCategories = "get_live_categories" + GetLiveStreams = "get_live_streams" + GetVodCategories = "get_vod_categories" + GetVodStreams = "get_vod_streams" + GetVodInfo = "get_vod_info" + GetSeriesCategories = "get_series_categories" + GetSeries = "get_series" + GetSerieInfo = "get_series_info" +) + +type Client struct { + *xtream.XtreamClient +} + +func New(user, password, baseURL string) (*Client, error) { + cli, err := xtream.NewClient(user, password, baseURL) + if err != nil { + return nil, err + } + + return &Client{cli}, nil +} + +type Login struct { + UserInfo xtream.UserInfo `json:"user_info"` + ServerInfo xtream.ServerInfo `json:"server_info"` +} + +func (c *Client) Login(proxyUser, proxyPassword, proxyURL string, proxyPort int) (Login, error) { + req := Login{ + UserInfo: xtream.UserInfo{ + Username: proxyUser, + Password: proxyPassword, + Message: c.UserInfo.Message, + Auth: c.UserInfo.Auth, + Status: c.UserInfo.Status, + ExpDate: c.UserInfo.ExpDate, + IsTrial: c.UserInfo.IsTrial, + ActiveConnections: c.UserInfo.ActiveConnections, + CreatedAt: c.UserInfo.CreatedAt, + MaxConnections: c.UserInfo.MaxConnections, + AllowedOutputFormats: c.UserInfo.AllowedOutputFormats, + }, + ServerInfo: xtream.ServerInfo{ + URL: proxyURL, + Port: proxyPort, + HTTPSPort: proxyPort, + Protocol: c.ServerInfo.Protocol, + RTMPPort: proxyPort, + Timezone: c.ServerInfo.Timezone, + TimestampNow: c.ServerInfo.TimestampNow, + TimeNow: c.ServerInfo.TimeNow, + }, + } + + return req, nil +} diff --git a/vendor/github.com/davecgh/go-spew/LICENSE b/vendor/github.com/davecgh/go-spew/LICENSE deleted file mode 100644 index bc52e96..0000000 --- a/vendor/github.com/davecgh/go-spew/LICENSE +++ /dev/null @@ -1,15 +0,0 @@ -ISC License - -Copyright (c) 2012-2016 Dave Collins - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/vendor/github.com/davecgh/go-spew/spew/bypass.go b/vendor/github.com/davecgh/go-spew/spew/bypass.go deleted file mode 100644 index 7929947..0000000 --- a/vendor/github.com/davecgh/go-spew/spew/bypass.go +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) 2015-2016 Dave Collins -// -// Permission to use, copy, modify, and distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -// NOTE: Due to the following build constraints, this file will only be compiled -// when the code is not running on Google App Engine, compiled by GopherJS, and -// "-tags safe" is not added to the go build command line. The "disableunsafe" -// tag is deprecated and thus should not be used. -// Go versions prior to 1.4 are disabled because they use a different layout -// for interfaces which make the implementation of unsafeReflectValue more complex. -// +build !js,!appengine,!safe,!disableunsafe,go1.4 - -package spew - -import ( - "reflect" - "unsafe" -) - -const ( - // UnsafeDisabled is a build-time constant which specifies whether or - // not access to the unsafe package is available. - UnsafeDisabled = false - - // ptrSize is the size of a pointer on the current arch. - ptrSize = unsafe.Sizeof((*byte)(nil)) -) - -type flag uintptr - -var ( - // flagRO indicates whether the value field of a reflect.Value - // is read-only. - flagRO flag - - // flagAddr indicates whether the address of the reflect.Value's - // value may be taken. - flagAddr flag -) - -// flagKindMask holds the bits that make up the kind -// part of the flags field. In all the supported versions, -// it is in the lower 5 bits. -const flagKindMask = flag(0x1f) - -// Different versions of Go have used different -// bit layouts for the flags type. This table -// records the known combinations. -var okFlags = []struct { - ro, addr flag -}{{ - // From Go 1.4 to 1.5 - ro: 1 << 5, - addr: 1 << 7, -}, { - // Up to Go tip. - ro: 1<<5 | 1<<6, - addr: 1 << 8, -}} - -var flagValOffset = func() uintptr { - field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") - if !ok { - panic("reflect.Value has no flag field") - } - return field.Offset -}() - -// flagField returns a pointer to the flag field of a reflect.Value. -func flagField(v *reflect.Value) *flag { - return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset)) -} - -// unsafeReflectValue converts the passed reflect.Value into a one that bypasses -// the typical safety restrictions preventing access to unaddressable and -// unexported data. It works by digging the raw pointer to the underlying -// value out of the protected value and generating a new unprotected (unsafe) -// reflect.Value to it. -// -// This allows us to check for implementations of the Stringer and error -// interfaces to be used for pretty printing ordinarily unaddressable and -// inaccessible values such as unexported struct fields. -func unsafeReflectValue(v reflect.Value) reflect.Value { - if !v.IsValid() || (v.CanInterface() && v.CanAddr()) { - return v - } - flagFieldPtr := flagField(&v) - *flagFieldPtr &^= flagRO - *flagFieldPtr |= flagAddr - return v -} - -// Sanity checks against future reflect package changes -// to the type or semantics of the Value.flag field. -func init() { - field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") - if !ok { - panic("reflect.Value has no flag field") - } - if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() { - panic("reflect.Value flag field has changed kind") - } - type t0 int - var t struct { - A t0 - // t0 will have flagEmbedRO set. - t0 - // a will have flagStickyRO set - a t0 - } - vA := reflect.ValueOf(t).FieldByName("A") - va := reflect.ValueOf(t).FieldByName("a") - vt0 := reflect.ValueOf(t).FieldByName("t0") - - // Infer flagRO from the difference between the flags - // for the (otherwise identical) fields in t. - flagPublic := *flagField(&vA) - flagWithRO := *flagField(&va) | *flagField(&vt0) - flagRO = flagPublic ^ flagWithRO - - // Infer flagAddr from the difference between a value - // taken from a pointer and not. - vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A") - flagNoPtr := *flagField(&vA) - flagPtr := *flagField(&vPtrA) - flagAddr = flagNoPtr ^ flagPtr - - // Check that the inferred flags tally with one of the known versions. - for _, f := range okFlags { - if flagRO == f.ro && flagAddr == f.addr { - return - } - } - panic("reflect.Value read-only flag has changed semantics") -} diff --git a/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go b/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go deleted file mode 100644 index 205c28d..0000000 --- a/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2015-2016 Dave Collins -// -// Permission to use, copy, modify, and distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -// NOTE: Due to the following build constraints, this file will only be compiled -// when the code is running on Google App Engine, compiled by GopherJS, or -// "-tags safe" is added to the go build command line. The "disableunsafe" -// tag is deprecated and thus should not be used. -// +build js appengine safe disableunsafe !go1.4 - -package spew - -import "reflect" - -const ( - // UnsafeDisabled is a build-time constant which specifies whether or - // not access to the unsafe package is available. - UnsafeDisabled = true -) - -// unsafeReflectValue typically converts the passed reflect.Value into a one -// that bypasses the typical safety restrictions preventing access to -// unaddressable and unexported data. However, doing this relies on access to -// the unsafe package. This is a stub version which simply returns the passed -// reflect.Value when the unsafe package is not available. -func unsafeReflectValue(v reflect.Value) reflect.Value { - return v -} diff --git a/vendor/github.com/davecgh/go-spew/spew/common.go b/vendor/github.com/davecgh/go-spew/spew/common.go deleted file mode 100644 index 1be8ce9..0000000 --- a/vendor/github.com/davecgh/go-spew/spew/common.go +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Copyright (c) 2013-2016 Dave Collins - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -package spew - -import ( - "bytes" - "fmt" - "io" - "reflect" - "sort" - "strconv" -) - -// Some constants in the form of bytes to avoid string overhead. This mirrors -// the technique used in the fmt package. -var ( - panicBytes = []byte("(PANIC=") - plusBytes = []byte("+") - iBytes = []byte("i") - trueBytes = []byte("true") - falseBytes = []byte("false") - interfaceBytes = []byte("(interface {})") - commaNewlineBytes = []byte(",\n") - newlineBytes = []byte("\n") - openBraceBytes = []byte("{") - openBraceNewlineBytes = []byte("{\n") - closeBraceBytes = []byte("}") - asteriskBytes = []byte("*") - colonBytes = []byte(":") - colonSpaceBytes = []byte(": ") - openParenBytes = []byte("(") - closeParenBytes = []byte(")") - spaceBytes = []byte(" ") - pointerChainBytes = []byte("->") - nilAngleBytes = []byte("") - maxNewlineBytes = []byte("\n") - maxShortBytes = []byte("") - circularBytes = []byte("") - circularShortBytes = []byte("") - invalidAngleBytes = []byte("") - openBracketBytes = []byte("[") - closeBracketBytes = []byte("]") - percentBytes = []byte("%") - precisionBytes = []byte(".") - openAngleBytes = []byte("<") - closeAngleBytes = []byte(">") - openMapBytes = []byte("map[") - closeMapBytes = []byte("]") - lenEqualsBytes = []byte("len=") - capEqualsBytes = []byte("cap=") -) - -// hexDigits is used to map a decimal value to a hex digit. -var hexDigits = "0123456789abcdef" - -// catchPanic handles any panics that might occur during the handleMethods -// calls. -func catchPanic(w io.Writer, v reflect.Value) { - if err := recover(); err != nil { - w.Write(panicBytes) - fmt.Fprintf(w, "%v", err) - w.Write(closeParenBytes) - } -} - -// handleMethods attempts to call the Error and String methods on the underlying -// type the passed reflect.Value represents and outputes the result to Writer w. -// -// It handles panics in any called methods by catching and displaying the error -// as the formatted value. -func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) { - // We need an interface to check if the type implements the error or - // Stringer interface. However, the reflect package won't give us an - // interface on certain things like unexported struct fields in order - // to enforce visibility rules. We use unsafe, when it's available, - // to bypass these restrictions since this package does not mutate the - // values. - if !v.CanInterface() { - if UnsafeDisabled { - return false - } - - v = unsafeReflectValue(v) - } - - // Choose whether or not to do error and Stringer interface lookups against - // the base type or a pointer to the base type depending on settings. - // Technically calling one of these methods with a pointer receiver can - // mutate the value, however, types which choose to satisify an error or - // Stringer interface with a pointer receiver should not be mutating their - // state inside these interface methods. - if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() { - v = unsafeReflectValue(v) - } - if v.CanAddr() { - v = v.Addr() - } - - // Is it an error or Stringer? - switch iface := v.Interface().(type) { - case error: - defer catchPanic(w, v) - if cs.ContinueOnMethod { - w.Write(openParenBytes) - w.Write([]byte(iface.Error())) - w.Write(closeParenBytes) - w.Write(spaceBytes) - return false - } - - w.Write([]byte(iface.Error())) - return true - - case fmt.Stringer: - defer catchPanic(w, v) - if cs.ContinueOnMethod { - w.Write(openParenBytes) - w.Write([]byte(iface.String())) - w.Write(closeParenBytes) - w.Write(spaceBytes) - return false - } - w.Write([]byte(iface.String())) - return true - } - return false -} - -// printBool outputs a boolean value as true or false to Writer w. -func printBool(w io.Writer, val bool) { - if val { - w.Write(trueBytes) - } else { - w.Write(falseBytes) - } -} - -// printInt outputs a signed integer value to Writer w. -func printInt(w io.Writer, val int64, base int) { - w.Write([]byte(strconv.FormatInt(val, base))) -} - -// printUint outputs an unsigned integer value to Writer w. -func printUint(w io.Writer, val uint64, base int) { - w.Write([]byte(strconv.FormatUint(val, base))) -} - -// printFloat outputs a floating point value using the specified precision, -// which is expected to be 32 or 64bit, to Writer w. -func printFloat(w io.Writer, val float64, precision int) { - w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision))) -} - -// printComplex outputs a complex value using the specified float precision -// for the real and imaginary parts to Writer w. -func printComplex(w io.Writer, c complex128, floatPrecision int) { - r := real(c) - w.Write(openParenBytes) - w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision))) - i := imag(c) - if i >= 0 { - w.Write(plusBytes) - } - w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision))) - w.Write(iBytes) - w.Write(closeParenBytes) -} - -// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x' -// prefix to Writer w. -func printHexPtr(w io.Writer, p uintptr) { - // Null pointer. - num := uint64(p) - if num == 0 { - w.Write(nilAngleBytes) - return - } - - // Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix - buf := make([]byte, 18) - - // It's simpler to construct the hex string right to left. - base := uint64(16) - i := len(buf) - 1 - for num >= base { - buf[i] = hexDigits[num%base] - num /= base - i-- - } - buf[i] = hexDigits[num] - - // Add '0x' prefix. - i-- - buf[i] = 'x' - i-- - buf[i] = '0' - - // Strip unused leading bytes. - buf = buf[i:] - w.Write(buf) -} - -// valuesSorter implements sort.Interface to allow a slice of reflect.Value -// elements to be sorted. -type valuesSorter struct { - values []reflect.Value - strings []string // either nil or same len and values - cs *ConfigState -} - -// newValuesSorter initializes a valuesSorter instance, which holds a set of -// surrogate keys on which the data should be sorted. It uses flags in -// ConfigState to decide if and how to populate those surrogate keys. -func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface { - vs := &valuesSorter{values: values, cs: cs} - if canSortSimply(vs.values[0].Kind()) { - return vs - } - if !cs.DisableMethods { - vs.strings = make([]string, len(values)) - for i := range vs.values { - b := bytes.Buffer{} - if !handleMethods(cs, &b, vs.values[i]) { - vs.strings = nil - break - } - vs.strings[i] = b.String() - } - } - if vs.strings == nil && cs.SpewKeys { - vs.strings = make([]string, len(values)) - for i := range vs.values { - vs.strings[i] = Sprintf("%#v", vs.values[i].Interface()) - } - } - return vs -} - -// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted -// directly, or whether it should be considered for sorting by surrogate keys -// (if the ConfigState allows it). -func canSortSimply(kind reflect.Kind) bool { - // This switch parallels valueSortLess, except for the default case. - switch kind { - case reflect.Bool: - return true - case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: - return true - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: - return true - case reflect.Float32, reflect.Float64: - return true - case reflect.String: - return true - case reflect.Uintptr: - return true - case reflect.Array: - return true - } - return false -} - -// Len returns the number of values in the slice. It is part of the -// sort.Interface implementation. -func (s *valuesSorter) Len() int { - return len(s.values) -} - -// Swap swaps the values at the passed indices. It is part of the -// sort.Interface implementation. -func (s *valuesSorter) Swap(i, j int) { - s.values[i], s.values[j] = s.values[j], s.values[i] - if s.strings != nil { - s.strings[i], s.strings[j] = s.strings[j], s.strings[i] - } -} - -// valueSortLess returns whether the first value should sort before the second -// value. It is used by valueSorter.Less as part of the sort.Interface -// implementation. -func valueSortLess(a, b reflect.Value) bool { - switch a.Kind() { - case reflect.Bool: - return !a.Bool() && b.Bool() - case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: - return a.Int() < b.Int() - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: - return a.Uint() < b.Uint() - case reflect.Float32, reflect.Float64: - return a.Float() < b.Float() - case reflect.String: - return a.String() < b.String() - case reflect.Uintptr: - return a.Uint() < b.Uint() - case reflect.Array: - // Compare the contents of both arrays. - l := a.Len() - for i := 0; i < l; i++ { - av := a.Index(i) - bv := b.Index(i) - if av.Interface() == bv.Interface() { - continue - } - return valueSortLess(av, bv) - } - } - return a.String() < b.String() -} - -// Less returns whether the value at index i should sort before the -// value at index j. It is part of the sort.Interface implementation. -func (s *valuesSorter) Less(i, j int) bool { - if s.strings == nil { - return valueSortLess(s.values[i], s.values[j]) - } - return s.strings[i] < s.strings[j] -} - -// sortValues is a sort function that handles both native types and any type that -// can be converted to error or Stringer. Other inputs are sorted according to -// their Value.String() value to ensure display stability. -func sortValues(values []reflect.Value, cs *ConfigState) { - if len(values) == 0 { - return - } - sort.Sort(newValuesSorter(values, cs)) -} diff --git a/vendor/github.com/davecgh/go-spew/spew/config.go b/vendor/github.com/davecgh/go-spew/spew/config.go deleted file mode 100644 index 2e3d22f..0000000 --- a/vendor/github.com/davecgh/go-spew/spew/config.go +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright (c) 2013-2016 Dave Collins - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -package spew - -import ( - "bytes" - "fmt" - "io" - "os" -) - -// ConfigState houses the configuration options used by spew to format and -// display values. There is a global instance, Config, that is used to control -// all top-level Formatter and Dump functionality. Each ConfigState instance -// provides methods equivalent to the top-level functions. -// -// The zero value for ConfigState provides no indentation. You would typically -// want to set it to a space or a tab. -// -// Alternatively, you can use NewDefaultConfig to get a ConfigState instance -// with default settings. See the documentation of NewDefaultConfig for default -// values. -type ConfigState struct { - // Indent specifies the string to use for each indentation level. The - // global config instance that all top-level functions use set this to a - // single space by default. If you would like more indentation, you might - // set this to a tab with "\t" or perhaps two spaces with " ". - Indent string - - // MaxDepth controls the maximum number of levels to descend into nested - // data structures. The default, 0, means there is no limit. - // - // NOTE: Circular data structures are properly detected, so it is not - // necessary to set this value unless you specifically want to limit deeply - // nested data structures. - MaxDepth int - - // DisableMethods specifies whether or not error and Stringer interfaces are - // invoked for types that implement them. - DisableMethods bool - - // DisablePointerMethods specifies whether or not to check for and invoke - // error and Stringer interfaces on types which only accept a pointer - // receiver when the current type is not a pointer. - // - // NOTE: This might be an unsafe action since calling one of these methods - // with a pointer receiver could technically mutate the value, however, - // in practice, types which choose to satisify an error or Stringer - // interface with a pointer receiver should not be mutating their state - // inside these interface methods. As a result, this option relies on - // access to the unsafe package, so it will not have any effect when - // running in environments without access to the unsafe package such as - // Google App Engine or with the "safe" build tag specified. - DisablePointerMethods bool - - // DisablePointerAddresses specifies whether to disable the printing of - // pointer addresses. This is useful when diffing data structures in tests. - DisablePointerAddresses bool - - // DisableCapacities specifies whether to disable the printing of capacities - // for arrays, slices, maps and channels. This is useful when diffing - // data structures in tests. - DisableCapacities bool - - // ContinueOnMethod specifies whether or not recursion should continue once - // a custom error or Stringer interface is invoked. The default, false, - // means it will print the results of invoking the custom error or Stringer - // interface and return immediately instead of continuing to recurse into - // the internals of the data type. - // - // NOTE: This flag does not have any effect if method invocation is disabled - // via the DisableMethods or DisablePointerMethods options. - ContinueOnMethod bool - - // SortKeys specifies map keys should be sorted before being printed. Use - // this to have a more deterministic, diffable output. Note that only - // native types (bool, int, uint, floats, uintptr and string) and types - // that support the error or Stringer interfaces (if methods are - // enabled) are supported, with other types sorted according to the - // reflect.Value.String() output which guarantees display stability. - SortKeys bool - - // SpewKeys specifies that, as a last resort attempt, map keys should - // be spewed to strings and sorted by those strings. This is only - // considered if SortKeys is true. - SpewKeys bool -} - -// Config is the active configuration of the top-level functions. -// The configuration can be changed by modifying the contents of spew.Config. -var Config = ConfigState{Indent: " "} - -// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were -// passed with a Formatter interface returned by c.NewFormatter. It returns -// the formatted string as a value that satisfies error. See NewFormatter -// for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b)) -func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) { - return fmt.Errorf(format, c.convertArgs(a)...) -} - -// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were -// passed with a Formatter interface returned by c.NewFormatter. It returns -// the number of bytes written and any write error encountered. See -// NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b)) -func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) { - return fmt.Fprint(w, c.convertArgs(a)...) -} - -// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were -// passed with a Formatter interface returned by c.NewFormatter. It returns -// the number of bytes written and any write error encountered. See -// NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b)) -func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { - return fmt.Fprintf(w, format, c.convertArgs(a)...) -} - -// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it -// passed with a Formatter interface returned by c.NewFormatter. See -// NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b)) -func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { - return fmt.Fprintln(w, c.convertArgs(a)...) -} - -// Print is a wrapper for fmt.Print that treats each argument as if it were -// passed with a Formatter interface returned by c.NewFormatter. It returns -// the number of bytes written and any write error encountered. See -// NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Print(c.NewFormatter(a), c.NewFormatter(b)) -func (c *ConfigState) Print(a ...interface{}) (n int, err error) { - return fmt.Print(c.convertArgs(a)...) -} - -// Printf is a wrapper for fmt.Printf that treats each argument as if it were -// passed with a Formatter interface returned by c.NewFormatter. It returns -// the number of bytes written and any write error encountered. See -// NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b)) -func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) { - return fmt.Printf(format, c.convertArgs(a)...) -} - -// Println is a wrapper for fmt.Println that treats each argument as if it were -// passed with a Formatter interface returned by c.NewFormatter. It returns -// the number of bytes written and any write error encountered. See -// NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Println(c.NewFormatter(a), c.NewFormatter(b)) -func (c *ConfigState) Println(a ...interface{}) (n int, err error) { - return fmt.Println(c.convertArgs(a)...) -} - -// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were -// passed with a Formatter interface returned by c.NewFormatter. It returns -// the resulting string. See NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b)) -func (c *ConfigState) Sprint(a ...interface{}) string { - return fmt.Sprint(c.convertArgs(a)...) -} - -// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were -// passed with a Formatter interface returned by c.NewFormatter. It returns -// the resulting string. See NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b)) -func (c *ConfigState) Sprintf(format string, a ...interface{}) string { - return fmt.Sprintf(format, c.convertArgs(a)...) -} - -// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it -// were passed with a Formatter interface returned by c.NewFormatter. It -// returns the resulting string. See NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b)) -func (c *ConfigState) Sprintln(a ...interface{}) string { - return fmt.Sprintln(c.convertArgs(a)...) -} - -/* -NewFormatter returns a custom formatter that satisfies the fmt.Formatter -interface. As a result, it integrates cleanly with standard fmt package -printing functions. The formatter is useful for inline printing of smaller data -types similar to the standard %v format specifier. - -The custom formatter only responds to the %v (most compact), %+v (adds pointer -addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb -combinations. Any other verbs such as %x and %q will be sent to the the -standard fmt package for formatting. In addition, the custom formatter ignores -the width and precision arguments (however they will still work on the format -specifiers not handled by the custom formatter). - -Typically this function shouldn't be called directly. It is much easier to make -use of the custom formatter by calling one of the convenience functions such as -c.Printf, c.Println, or c.Printf. -*/ -func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter { - return newFormatter(c, v) -} - -// Fdump formats and displays the passed arguments to io.Writer w. It formats -// exactly the same as Dump. -func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) { - fdump(c, w, a...) -} - -/* -Dump displays the passed parameters to standard out with newlines, customizable -indentation, and additional debug information such as complete types and all -pointer addresses used to indirect to the final value. It provides the -following features over the built-in printing facilities provided by the fmt -package: - - * Pointers are dereferenced and followed - * Circular data structures are detected and handled properly - * Custom Stringer/error interfaces are optionally invoked, including - on unexported types - * Custom types which only implement the Stringer/error interfaces via - a pointer receiver are optionally invoked when passing non-pointer - variables - * Byte arrays and slices are dumped like the hexdump -C command which - includes offsets, byte values in hex, and ASCII output - -The configuration options are controlled by modifying the public members -of c. See ConfigState for options documentation. - -See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to -get the formatted result as a string. -*/ -func (c *ConfigState) Dump(a ...interface{}) { - fdump(c, os.Stdout, a...) -} - -// Sdump returns a string with the passed arguments formatted exactly the same -// as Dump. -func (c *ConfigState) Sdump(a ...interface{}) string { - var buf bytes.Buffer - fdump(c, &buf, a...) - return buf.String() -} - -// convertArgs accepts a slice of arguments and returns a slice of the same -// length with each argument converted to a spew Formatter interface using -// the ConfigState associated with s. -func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) { - formatters = make([]interface{}, len(args)) - for index, arg := range args { - formatters[index] = newFormatter(c, arg) - } - return formatters -} - -// NewDefaultConfig returns a ConfigState with the following default settings. -// -// Indent: " " -// MaxDepth: 0 -// DisableMethods: false -// DisablePointerMethods: false -// ContinueOnMethod: false -// SortKeys: false -func NewDefaultConfig() *ConfigState { - return &ConfigState{Indent: " "} -} diff --git a/vendor/github.com/davecgh/go-spew/spew/doc.go b/vendor/github.com/davecgh/go-spew/spew/doc.go deleted file mode 100644 index aacaac6..0000000 --- a/vendor/github.com/davecgh/go-spew/spew/doc.go +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (c) 2013-2016 Dave Collins - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/* -Package spew implements a deep pretty printer for Go data structures to aid in -debugging. - -A quick overview of the additional features spew provides over the built-in -printing facilities for Go data types are as follows: - - * Pointers are dereferenced and followed - * Circular data structures are detected and handled properly - * Custom Stringer/error interfaces are optionally invoked, including - on unexported types - * Custom types which only implement the Stringer/error interfaces via - a pointer receiver are optionally invoked when passing non-pointer - variables - * Byte arrays and slices are dumped like the hexdump -C command which - includes offsets, byte values in hex, and ASCII output (only when using - Dump style) - -There are two different approaches spew allows for dumping Go data structures: - - * Dump style which prints with newlines, customizable indentation, - and additional debug information such as types and all pointer addresses - used to indirect to the final value - * A custom Formatter interface that integrates cleanly with the standard fmt - package and replaces %v, %+v, %#v, and %#+v to provide inline printing - similar to the default %v while providing the additional functionality - outlined above and passing unsupported format verbs such as %x and %q - along to fmt - -Quick Start - -This section demonstrates how to quickly get started with spew. See the -sections below for further details on formatting and configuration options. - -To dump a variable with full newlines, indentation, type, and pointer -information use Dump, Fdump, or Sdump: - spew.Dump(myVar1, myVar2, ...) - spew.Fdump(someWriter, myVar1, myVar2, ...) - str := spew.Sdump(myVar1, myVar2, ...) - -Alternatively, if you would prefer to use format strings with a compacted inline -printing style, use the convenience wrappers Printf, Fprintf, etc with -%v (most compact), %+v (adds pointer addresses), %#v (adds types), or -%#+v (adds types and pointer addresses): - spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) - spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) - spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) - spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) - -Configuration Options - -Configuration of spew is handled by fields in the ConfigState type. For -convenience, all of the top-level functions use a global state available -via the spew.Config global. - -It is also possible to create a ConfigState instance that provides methods -equivalent to the top-level functions. This allows concurrent configuration -options. See the ConfigState documentation for more details. - -The following configuration options are available: - * Indent - String to use for each indentation level for Dump functions. - It is a single space by default. A popular alternative is "\t". - - * MaxDepth - Maximum number of levels to descend into nested data structures. - There is no limit by default. - - * DisableMethods - Disables invocation of error and Stringer interface methods. - Method invocation is enabled by default. - - * DisablePointerMethods - Disables invocation of error and Stringer interface methods on types - which only accept pointer receivers from non-pointer variables. - Pointer method invocation is enabled by default. - - * DisablePointerAddresses - DisablePointerAddresses specifies whether to disable the printing of - pointer addresses. This is useful when diffing data structures in tests. - - * DisableCapacities - DisableCapacities specifies whether to disable the printing of - capacities for arrays, slices, maps and channels. This is useful when - diffing data structures in tests. - - * ContinueOnMethod - Enables recursion into types after invoking error and Stringer interface - methods. Recursion after method invocation is disabled by default. - - * SortKeys - Specifies map keys should be sorted before being printed. Use - this to have a more deterministic, diffable output. Note that - only native types (bool, int, uint, floats, uintptr and string) - and types which implement error or Stringer interfaces are - supported with other types sorted according to the - reflect.Value.String() output which guarantees display - stability. Natural map order is used by default. - - * SpewKeys - Specifies that, as a last resort attempt, map keys should be - spewed to strings and sorted by those strings. This is only - considered if SortKeys is true. - -Dump Usage - -Simply call spew.Dump with a list of variables you want to dump: - - spew.Dump(myVar1, myVar2, ...) - -You may also call spew.Fdump if you would prefer to output to an arbitrary -io.Writer. For example, to dump to standard error: - - spew.Fdump(os.Stderr, myVar1, myVar2, ...) - -A third option is to call spew.Sdump to get the formatted output as a string: - - str := spew.Sdump(myVar1, myVar2, ...) - -Sample Dump Output - -See the Dump example for details on the setup of the types and variables being -shown here. - - (main.Foo) { - unexportedField: (*main.Bar)(0xf84002e210)({ - flag: (main.Flag) flagTwo, - data: (uintptr) - }), - ExportedField: (map[interface {}]interface {}) (len=1) { - (string) (len=3) "one": (bool) true - } - } - -Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C -command as shown. - ([]uint8) (len=32 cap=32) { - 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... | - 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0| - 00000020 31 32 |12| - } - -Custom Formatter - -Spew provides a custom formatter that implements the fmt.Formatter interface -so that it integrates cleanly with standard fmt package printing functions. The -formatter is useful for inline printing of smaller data types similar to the -standard %v format specifier. - -The custom formatter only responds to the %v (most compact), %+v (adds pointer -addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb -combinations. Any other verbs such as %x and %q will be sent to the the -standard fmt package for formatting. In addition, the custom formatter ignores -the width and precision arguments (however they will still work on the format -specifiers not handled by the custom formatter). - -Custom Formatter Usage - -The simplest way to make use of the spew custom formatter is to call one of the -convenience functions such as spew.Printf, spew.Println, or spew.Printf. The -functions have syntax you are most likely already familiar with: - - spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) - spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) - spew.Println(myVar, myVar2) - spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) - spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) - -See the Index for the full list convenience functions. - -Sample Formatter Output - -Double pointer to a uint8: - %v: <**>5 - %+v: <**>(0xf8400420d0->0xf8400420c8)5 - %#v: (**uint8)5 - %#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5 - -Pointer to circular struct with a uint8 field and a pointer to itself: - %v: <*>{1 <*>} - %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)} - %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)} - %#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)} - -See the Printf example for details on the setup of variables being shown -here. - -Errors - -Since it is possible for custom Stringer/error interfaces to panic, spew -detects them and handles them internally by printing the panic information -inline with the output. Since spew is intended to provide deep pretty printing -capabilities on structures, it intentionally does not return any errors. -*/ -package spew diff --git a/vendor/github.com/davecgh/go-spew/spew/dump.go b/vendor/github.com/davecgh/go-spew/spew/dump.go deleted file mode 100644 index f78d89f..0000000 --- a/vendor/github.com/davecgh/go-spew/spew/dump.go +++ /dev/null @@ -1,509 +0,0 @@ -/* - * Copyright (c) 2013-2016 Dave Collins - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -package spew - -import ( - "bytes" - "encoding/hex" - "fmt" - "io" - "os" - "reflect" - "regexp" - "strconv" - "strings" -) - -var ( - // uint8Type is a reflect.Type representing a uint8. It is used to - // convert cgo types to uint8 slices for hexdumping. - uint8Type = reflect.TypeOf(uint8(0)) - - // cCharRE is a regular expression that matches a cgo char. - // It is used to detect character arrays to hexdump them. - cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`) - - // cUnsignedCharRE is a regular expression that matches a cgo unsigned - // char. It is used to detect unsigned character arrays to hexdump - // them. - cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`) - - // cUint8tCharRE is a regular expression that matches a cgo uint8_t. - // It is used to detect uint8_t arrays to hexdump them. - cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`) -) - -// dumpState contains information about the state of a dump operation. -type dumpState struct { - w io.Writer - depth int - pointers map[uintptr]int - ignoreNextType bool - ignoreNextIndent bool - cs *ConfigState -} - -// indent performs indentation according to the depth level and cs.Indent -// option. -func (d *dumpState) indent() { - if d.ignoreNextIndent { - d.ignoreNextIndent = false - return - } - d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth)) -} - -// unpackValue returns values inside of non-nil interfaces when possible. -// This is useful for data types like structs, arrays, slices, and maps which -// can contain varying types packed inside an interface. -func (d *dumpState) unpackValue(v reflect.Value) reflect.Value { - if v.Kind() == reflect.Interface && !v.IsNil() { - v = v.Elem() - } - return v -} - -// dumpPtr handles formatting of pointers by indirecting them as necessary. -func (d *dumpState) dumpPtr(v reflect.Value) { - // Remove pointers at or below the current depth from map used to detect - // circular refs. - for k, depth := range d.pointers { - if depth >= d.depth { - delete(d.pointers, k) - } - } - - // Keep list of all dereferenced pointers to show later. - pointerChain := make([]uintptr, 0) - - // Figure out how many levels of indirection there are by dereferencing - // pointers and unpacking interfaces down the chain while detecting circular - // references. - nilFound := false - cycleFound := false - indirects := 0 - ve := v - for ve.Kind() == reflect.Ptr { - if ve.IsNil() { - nilFound = true - break - } - indirects++ - addr := ve.Pointer() - pointerChain = append(pointerChain, addr) - if pd, ok := d.pointers[addr]; ok && pd < d.depth { - cycleFound = true - indirects-- - break - } - d.pointers[addr] = d.depth - - ve = ve.Elem() - if ve.Kind() == reflect.Interface { - if ve.IsNil() { - nilFound = true - break - } - ve = ve.Elem() - } - } - - // Display type information. - d.w.Write(openParenBytes) - d.w.Write(bytes.Repeat(asteriskBytes, indirects)) - d.w.Write([]byte(ve.Type().String())) - d.w.Write(closeParenBytes) - - // Display pointer information. - if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 { - d.w.Write(openParenBytes) - for i, addr := range pointerChain { - if i > 0 { - d.w.Write(pointerChainBytes) - } - printHexPtr(d.w, addr) - } - d.w.Write(closeParenBytes) - } - - // Display dereferenced value. - d.w.Write(openParenBytes) - switch { - case nilFound: - d.w.Write(nilAngleBytes) - - case cycleFound: - d.w.Write(circularBytes) - - default: - d.ignoreNextType = true - d.dump(ve) - } - d.w.Write(closeParenBytes) -} - -// dumpSlice handles formatting of arrays and slices. Byte (uint8 under -// reflection) arrays and slices are dumped in hexdump -C fashion. -func (d *dumpState) dumpSlice(v reflect.Value) { - // Determine whether this type should be hex dumped or not. Also, - // for types which should be hexdumped, try to use the underlying data - // first, then fall back to trying to convert them to a uint8 slice. - var buf []uint8 - doConvert := false - doHexDump := false - numEntries := v.Len() - if numEntries > 0 { - vt := v.Index(0).Type() - vts := vt.String() - switch { - // C types that need to be converted. - case cCharRE.MatchString(vts): - fallthrough - case cUnsignedCharRE.MatchString(vts): - fallthrough - case cUint8tCharRE.MatchString(vts): - doConvert = true - - // Try to use existing uint8 slices and fall back to converting - // and copying if that fails. - case vt.Kind() == reflect.Uint8: - // We need an addressable interface to convert the type - // to a byte slice. However, the reflect package won't - // give us an interface on certain things like - // unexported struct fields in order to enforce - // visibility rules. We use unsafe, when available, to - // bypass these restrictions since this package does not - // mutate the values. - vs := v - if !vs.CanInterface() || !vs.CanAddr() { - vs = unsafeReflectValue(vs) - } - if !UnsafeDisabled { - vs = vs.Slice(0, numEntries) - - // Use the existing uint8 slice if it can be - // type asserted. - iface := vs.Interface() - if slice, ok := iface.([]uint8); ok { - buf = slice - doHexDump = true - break - } - } - - // The underlying data needs to be converted if it can't - // be type asserted to a uint8 slice. - doConvert = true - } - - // Copy and convert the underlying type if needed. - if doConvert && vt.ConvertibleTo(uint8Type) { - // Convert and copy each element into a uint8 byte - // slice. - buf = make([]uint8, numEntries) - for i := 0; i < numEntries; i++ { - vv := v.Index(i) - buf[i] = uint8(vv.Convert(uint8Type).Uint()) - } - doHexDump = true - } - } - - // Hexdump the entire slice as needed. - if doHexDump { - indent := strings.Repeat(d.cs.Indent, d.depth) - str := indent + hex.Dump(buf) - str = strings.Replace(str, "\n", "\n"+indent, -1) - str = strings.TrimRight(str, d.cs.Indent) - d.w.Write([]byte(str)) - return - } - - // Recursively call dump for each item. - for i := 0; i < numEntries; i++ { - d.dump(d.unpackValue(v.Index(i))) - if i < (numEntries - 1) { - d.w.Write(commaNewlineBytes) - } else { - d.w.Write(newlineBytes) - } - } -} - -// dump is the main workhorse for dumping a value. It uses the passed reflect -// value to figure out what kind of object we are dealing with and formats it -// appropriately. It is a recursive function, however circular data structures -// are detected and handled properly. -func (d *dumpState) dump(v reflect.Value) { - // Handle invalid reflect values immediately. - kind := v.Kind() - if kind == reflect.Invalid { - d.w.Write(invalidAngleBytes) - return - } - - // Handle pointers specially. - if kind == reflect.Ptr { - d.indent() - d.dumpPtr(v) - return - } - - // Print type information unless already handled elsewhere. - if !d.ignoreNextType { - d.indent() - d.w.Write(openParenBytes) - d.w.Write([]byte(v.Type().String())) - d.w.Write(closeParenBytes) - d.w.Write(spaceBytes) - } - d.ignoreNextType = false - - // Display length and capacity if the built-in len and cap functions - // work with the value's kind and the len/cap itself is non-zero. - valueLen, valueCap := 0, 0 - switch v.Kind() { - case reflect.Array, reflect.Slice, reflect.Chan: - valueLen, valueCap = v.Len(), v.Cap() - case reflect.Map, reflect.String: - valueLen = v.Len() - } - if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 { - d.w.Write(openParenBytes) - if valueLen != 0 { - d.w.Write(lenEqualsBytes) - printInt(d.w, int64(valueLen), 10) - } - if !d.cs.DisableCapacities && valueCap != 0 { - if valueLen != 0 { - d.w.Write(spaceBytes) - } - d.w.Write(capEqualsBytes) - printInt(d.w, int64(valueCap), 10) - } - d.w.Write(closeParenBytes) - d.w.Write(spaceBytes) - } - - // Call Stringer/error interfaces if they exist and the handle methods flag - // is enabled - if !d.cs.DisableMethods { - if (kind != reflect.Invalid) && (kind != reflect.Interface) { - if handled := handleMethods(d.cs, d.w, v); handled { - return - } - } - } - - switch kind { - case reflect.Invalid: - // Do nothing. We should never get here since invalid has already - // been handled above. - - case reflect.Bool: - printBool(d.w, v.Bool()) - - case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: - printInt(d.w, v.Int(), 10) - - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: - printUint(d.w, v.Uint(), 10) - - case reflect.Float32: - printFloat(d.w, v.Float(), 32) - - case reflect.Float64: - printFloat(d.w, v.Float(), 64) - - case reflect.Complex64: - printComplex(d.w, v.Complex(), 32) - - case reflect.Complex128: - printComplex(d.w, v.Complex(), 64) - - case reflect.Slice: - if v.IsNil() { - d.w.Write(nilAngleBytes) - break - } - fallthrough - - case reflect.Array: - d.w.Write(openBraceNewlineBytes) - d.depth++ - if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { - d.indent() - d.w.Write(maxNewlineBytes) - } else { - d.dumpSlice(v) - } - d.depth-- - d.indent() - d.w.Write(closeBraceBytes) - - case reflect.String: - d.w.Write([]byte(strconv.Quote(v.String()))) - - case reflect.Interface: - // The only time we should get here is for nil interfaces due to - // unpackValue calls. - if v.IsNil() { - d.w.Write(nilAngleBytes) - } - - case reflect.Ptr: - // Do nothing. We should never get here since pointers have already - // been handled above. - - case reflect.Map: - // nil maps should be indicated as different than empty maps - if v.IsNil() { - d.w.Write(nilAngleBytes) - break - } - - d.w.Write(openBraceNewlineBytes) - d.depth++ - if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { - d.indent() - d.w.Write(maxNewlineBytes) - } else { - numEntries := v.Len() - keys := v.MapKeys() - if d.cs.SortKeys { - sortValues(keys, d.cs) - } - for i, key := range keys { - d.dump(d.unpackValue(key)) - d.w.Write(colonSpaceBytes) - d.ignoreNextIndent = true - d.dump(d.unpackValue(v.MapIndex(key))) - if i < (numEntries - 1) { - d.w.Write(commaNewlineBytes) - } else { - d.w.Write(newlineBytes) - } - } - } - d.depth-- - d.indent() - d.w.Write(closeBraceBytes) - - case reflect.Struct: - d.w.Write(openBraceNewlineBytes) - d.depth++ - if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { - d.indent() - d.w.Write(maxNewlineBytes) - } else { - vt := v.Type() - numFields := v.NumField() - for i := 0; i < numFields; i++ { - d.indent() - vtf := vt.Field(i) - d.w.Write([]byte(vtf.Name)) - d.w.Write(colonSpaceBytes) - d.ignoreNextIndent = true - d.dump(d.unpackValue(v.Field(i))) - if i < (numFields - 1) { - d.w.Write(commaNewlineBytes) - } else { - d.w.Write(newlineBytes) - } - } - } - d.depth-- - d.indent() - d.w.Write(closeBraceBytes) - - case reflect.Uintptr: - printHexPtr(d.w, uintptr(v.Uint())) - - case reflect.UnsafePointer, reflect.Chan, reflect.Func: - printHexPtr(d.w, v.Pointer()) - - // There were not any other types at the time this code was written, but - // fall back to letting the default fmt package handle it in case any new - // types are added. - default: - if v.CanInterface() { - fmt.Fprintf(d.w, "%v", v.Interface()) - } else { - fmt.Fprintf(d.w, "%v", v.String()) - } - } -} - -// fdump is a helper function to consolidate the logic from the various public -// methods which take varying writers and config states. -func fdump(cs *ConfigState, w io.Writer, a ...interface{}) { - for _, arg := range a { - if arg == nil { - w.Write(interfaceBytes) - w.Write(spaceBytes) - w.Write(nilAngleBytes) - w.Write(newlineBytes) - continue - } - - d := dumpState{w: w, cs: cs} - d.pointers = make(map[uintptr]int) - d.dump(reflect.ValueOf(arg)) - d.w.Write(newlineBytes) - } -} - -// Fdump formats and displays the passed arguments to io.Writer w. It formats -// exactly the same as Dump. -func Fdump(w io.Writer, a ...interface{}) { - fdump(&Config, w, a...) -} - -// Sdump returns a string with the passed arguments formatted exactly the same -// as Dump. -func Sdump(a ...interface{}) string { - var buf bytes.Buffer - fdump(&Config, &buf, a...) - return buf.String() -} - -/* -Dump displays the passed parameters to standard out with newlines, customizable -indentation, and additional debug information such as complete types and all -pointer addresses used to indirect to the final value. It provides the -following features over the built-in printing facilities provided by the fmt -package: - - * Pointers are dereferenced and followed - * Circular data structures are detected and handled properly - * Custom Stringer/error interfaces are optionally invoked, including - on unexported types - * Custom types which only implement the Stringer/error interfaces via - a pointer receiver are optionally invoked when passing non-pointer - variables - * Byte arrays and slices are dumped like the hexdump -C command which - includes offsets, byte values in hex, and ASCII output - -The configuration options are controlled by an exported package global, -spew.Config. See ConfigState for options documentation. - -See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to -get the formatted result as a string. -*/ -func Dump(a ...interface{}) { - fdump(&Config, os.Stdout, a...) -} diff --git a/vendor/github.com/davecgh/go-spew/spew/format.go b/vendor/github.com/davecgh/go-spew/spew/format.go deleted file mode 100644 index b04edb7..0000000 --- a/vendor/github.com/davecgh/go-spew/spew/format.go +++ /dev/null @@ -1,419 +0,0 @@ -/* - * Copyright (c) 2013-2016 Dave Collins - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -package spew - -import ( - "bytes" - "fmt" - "reflect" - "strconv" - "strings" -) - -// supportedFlags is a list of all the character flags supported by fmt package. -const supportedFlags = "0-+# " - -// formatState implements the fmt.Formatter interface and contains information -// about the state of a formatting operation. The NewFormatter function can -// be used to get a new Formatter which can be used directly as arguments -// in standard fmt package printing calls. -type formatState struct { - value interface{} - fs fmt.State - depth int - pointers map[uintptr]int - ignoreNextType bool - cs *ConfigState -} - -// buildDefaultFormat recreates the original format string without precision -// and width information to pass in to fmt.Sprintf in the case of an -// unrecognized type. Unless new types are added to the language, this -// function won't ever be called. -func (f *formatState) buildDefaultFormat() (format string) { - buf := bytes.NewBuffer(percentBytes) - - for _, flag := range supportedFlags { - if f.fs.Flag(int(flag)) { - buf.WriteRune(flag) - } - } - - buf.WriteRune('v') - - format = buf.String() - return format -} - -// constructOrigFormat recreates the original format string including precision -// and width information to pass along to the standard fmt package. This allows -// automatic deferral of all format strings this package doesn't support. -func (f *formatState) constructOrigFormat(verb rune) (format string) { - buf := bytes.NewBuffer(percentBytes) - - for _, flag := range supportedFlags { - if f.fs.Flag(int(flag)) { - buf.WriteRune(flag) - } - } - - if width, ok := f.fs.Width(); ok { - buf.WriteString(strconv.Itoa(width)) - } - - if precision, ok := f.fs.Precision(); ok { - buf.Write(precisionBytes) - buf.WriteString(strconv.Itoa(precision)) - } - - buf.WriteRune(verb) - - format = buf.String() - return format -} - -// unpackValue returns values inside of non-nil interfaces when possible and -// ensures that types for values which have been unpacked from an interface -// are displayed when the show types flag is also set. -// This is useful for data types like structs, arrays, slices, and maps which -// can contain varying types packed inside an interface. -func (f *formatState) unpackValue(v reflect.Value) reflect.Value { - if v.Kind() == reflect.Interface { - f.ignoreNextType = false - if !v.IsNil() { - v = v.Elem() - } - } - return v -} - -// formatPtr handles formatting of pointers by indirecting them as necessary. -func (f *formatState) formatPtr(v reflect.Value) { - // Display nil if top level pointer is nil. - showTypes := f.fs.Flag('#') - if v.IsNil() && (!showTypes || f.ignoreNextType) { - f.fs.Write(nilAngleBytes) - return - } - - // Remove pointers at or below the current depth from map used to detect - // circular refs. - for k, depth := range f.pointers { - if depth >= f.depth { - delete(f.pointers, k) - } - } - - // Keep list of all dereferenced pointers to possibly show later. - pointerChain := make([]uintptr, 0) - - // Figure out how many levels of indirection there are by derferencing - // pointers and unpacking interfaces down the chain while detecting circular - // references. - nilFound := false - cycleFound := false - indirects := 0 - ve := v - for ve.Kind() == reflect.Ptr { - if ve.IsNil() { - nilFound = true - break - } - indirects++ - addr := ve.Pointer() - pointerChain = append(pointerChain, addr) - if pd, ok := f.pointers[addr]; ok && pd < f.depth { - cycleFound = true - indirects-- - break - } - f.pointers[addr] = f.depth - - ve = ve.Elem() - if ve.Kind() == reflect.Interface { - if ve.IsNil() { - nilFound = true - break - } - ve = ve.Elem() - } - } - - // Display type or indirection level depending on flags. - if showTypes && !f.ignoreNextType { - f.fs.Write(openParenBytes) - f.fs.Write(bytes.Repeat(asteriskBytes, indirects)) - f.fs.Write([]byte(ve.Type().String())) - f.fs.Write(closeParenBytes) - } else { - if nilFound || cycleFound { - indirects += strings.Count(ve.Type().String(), "*") - } - f.fs.Write(openAngleBytes) - f.fs.Write([]byte(strings.Repeat("*", indirects))) - f.fs.Write(closeAngleBytes) - } - - // Display pointer information depending on flags. - if f.fs.Flag('+') && (len(pointerChain) > 0) { - f.fs.Write(openParenBytes) - for i, addr := range pointerChain { - if i > 0 { - f.fs.Write(pointerChainBytes) - } - printHexPtr(f.fs, addr) - } - f.fs.Write(closeParenBytes) - } - - // Display dereferenced value. - switch { - case nilFound: - f.fs.Write(nilAngleBytes) - - case cycleFound: - f.fs.Write(circularShortBytes) - - default: - f.ignoreNextType = true - f.format(ve) - } -} - -// format is the main workhorse for providing the Formatter interface. It -// uses the passed reflect value to figure out what kind of object we are -// dealing with and formats it appropriately. It is a recursive function, -// however circular data structures are detected and handled properly. -func (f *formatState) format(v reflect.Value) { - // Handle invalid reflect values immediately. - kind := v.Kind() - if kind == reflect.Invalid { - f.fs.Write(invalidAngleBytes) - return - } - - // Handle pointers specially. - if kind == reflect.Ptr { - f.formatPtr(v) - return - } - - // Print type information unless already handled elsewhere. - if !f.ignoreNextType && f.fs.Flag('#') { - f.fs.Write(openParenBytes) - f.fs.Write([]byte(v.Type().String())) - f.fs.Write(closeParenBytes) - } - f.ignoreNextType = false - - // Call Stringer/error interfaces if they exist and the handle methods - // flag is enabled. - if !f.cs.DisableMethods { - if (kind != reflect.Invalid) && (kind != reflect.Interface) { - if handled := handleMethods(f.cs, f.fs, v); handled { - return - } - } - } - - switch kind { - case reflect.Invalid: - // Do nothing. We should never get here since invalid has already - // been handled above. - - case reflect.Bool: - printBool(f.fs, v.Bool()) - - case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: - printInt(f.fs, v.Int(), 10) - - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: - printUint(f.fs, v.Uint(), 10) - - case reflect.Float32: - printFloat(f.fs, v.Float(), 32) - - case reflect.Float64: - printFloat(f.fs, v.Float(), 64) - - case reflect.Complex64: - printComplex(f.fs, v.Complex(), 32) - - case reflect.Complex128: - printComplex(f.fs, v.Complex(), 64) - - case reflect.Slice: - if v.IsNil() { - f.fs.Write(nilAngleBytes) - break - } - fallthrough - - case reflect.Array: - f.fs.Write(openBracketBytes) - f.depth++ - if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { - f.fs.Write(maxShortBytes) - } else { - numEntries := v.Len() - for i := 0; i < numEntries; i++ { - if i > 0 { - f.fs.Write(spaceBytes) - } - f.ignoreNextType = true - f.format(f.unpackValue(v.Index(i))) - } - } - f.depth-- - f.fs.Write(closeBracketBytes) - - case reflect.String: - f.fs.Write([]byte(v.String())) - - case reflect.Interface: - // The only time we should get here is for nil interfaces due to - // unpackValue calls. - if v.IsNil() { - f.fs.Write(nilAngleBytes) - } - - case reflect.Ptr: - // Do nothing. We should never get here since pointers have already - // been handled above. - - case reflect.Map: - // nil maps should be indicated as different than empty maps - if v.IsNil() { - f.fs.Write(nilAngleBytes) - break - } - - f.fs.Write(openMapBytes) - f.depth++ - if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { - f.fs.Write(maxShortBytes) - } else { - keys := v.MapKeys() - if f.cs.SortKeys { - sortValues(keys, f.cs) - } - for i, key := range keys { - if i > 0 { - f.fs.Write(spaceBytes) - } - f.ignoreNextType = true - f.format(f.unpackValue(key)) - f.fs.Write(colonBytes) - f.ignoreNextType = true - f.format(f.unpackValue(v.MapIndex(key))) - } - } - f.depth-- - f.fs.Write(closeMapBytes) - - case reflect.Struct: - numFields := v.NumField() - f.fs.Write(openBraceBytes) - f.depth++ - if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { - f.fs.Write(maxShortBytes) - } else { - vt := v.Type() - for i := 0; i < numFields; i++ { - if i > 0 { - f.fs.Write(spaceBytes) - } - vtf := vt.Field(i) - if f.fs.Flag('+') || f.fs.Flag('#') { - f.fs.Write([]byte(vtf.Name)) - f.fs.Write(colonBytes) - } - f.format(f.unpackValue(v.Field(i))) - } - } - f.depth-- - f.fs.Write(closeBraceBytes) - - case reflect.Uintptr: - printHexPtr(f.fs, uintptr(v.Uint())) - - case reflect.UnsafePointer, reflect.Chan, reflect.Func: - printHexPtr(f.fs, v.Pointer()) - - // There were not any other types at the time this code was written, but - // fall back to letting the default fmt package handle it if any get added. - default: - format := f.buildDefaultFormat() - if v.CanInterface() { - fmt.Fprintf(f.fs, format, v.Interface()) - } else { - fmt.Fprintf(f.fs, format, v.String()) - } - } -} - -// Format satisfies the fmt.Formatter interface. See NewFormatter for usage -// details. -func (f *formatState) Format(fs fmt.State, verb rune) { - f.fs = fs - - // Use standard formatting for verbs that are not v. - if verb != 'v' { - format := f.constructOrigFormat(verb) - fmt.Fprintf(fs, format, f.value) - return - } - - if f.value == nil { - if fs.Flag('#') { - fs.Write(interfaceBytes) - } - fs.Write(nilAngleBytes) - return - } - - f.format(reflect.ValueOf(f.value)) -} - -// newFormatter is a helper function to consolidate the logic from the various -// public methods which take varying config states. -func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter { - fs := &formatState{value: v, cs: cs} - fs.pointers = make(map[uintptr]int) - return fs -} - -/* -NewFormatter returns a custom formatter that satisfies the fmt.Formatter -interface. As a result, it integrates cleanly with standard fmt package -printing functions. The formatter is useful for inline printing of smaller data -types similar to the standard %v format specifier. - -The custom formatter only responds to the %v (most compact), %+v (adds pointer -addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb -combinations. Any other verbs such as %x and %q will be sent to the the -standard fmt package for formatting. In addition, the custom formatter ignores -the width and precision arguments (however they will still work on the format -specifiers not handled by the custom formatter). - -Typically this function shouldn't be called directly. It is much easier to make -use of the custom formatter by calling one of the convenience functions such as -Printf, Println, or Fprintf. -*/ -func NewFormatter(v interface{}) fmt.Formatter { - return newFormatter(&Config, v) -} diff --git a/vendor/github.com/davecgh/go-spew/spew/spew.go b/vendor/github.com/davecgh/go-spew/spew/spew.go deleted file mode 100644 index 32c0e33..0000000 --- a/vendor/github.com/davecgh/go-spew/spew/spew.go +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (c) 2013-2016 Dave Collins - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -package spew - -import ( - "fmt" - "io" -) - -// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were -// passed with a default Formatter interface returned by NewFormatter. It -// returns the formatted string as a value that satisfies error. See -// NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b)) -func Errorf(format string, a ...interface{}) (err error) { - return fmt.Errorf(format, convertArgs(a)...) -} - -// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were -// passed with a default Formatter interface returned by NewFormatter. It -// returns the number of bytes written and any write error encountered. See -// NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b)) -func Fprint(w io.Writer, a ...interface{}) (n int, err error) { - return fmt.Fprint(w, convertArgs(a)...) -} - -// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were -// passed with a default Formatter interface returned by NewFormatter. It -// returns the number of bytes written and any write error encountered. See -// NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b)) -func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { - return fmt.Fprintf(w, format, convertArgs(a)...) -} - -// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it -// passed with a default Formatter interface returned by NewFormatter. See -// NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b)) -func Fprintln(w io.Writer, a ...interface{}) (n int, err error) { - return fmt.Fprintln(w, convertArgs(a)...) -} - -// Print is a wrapper for fmt.Print that treats each argument as if it were -// passed with a default Formatter interface returned by NewFormatter. It -// returns the number of bytes written and any write error encountered. See -// NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b)) -func Print(a ...interface{}) (n int, err error) { - return fmt.Print(convertArgs(a)...) -} - -// Printf is a wrapper for fmt.Printf that treats each argument as if it were -// passed with a default Formatter interface returned by NewFormatter. It -// returns the number of bytes written and any write error encountered. See -// NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b)) -func Printf(format string, a ...interface{}) (n int, err error) { - return fmt.Printf(format, convertArgs(a)...) -} - -// Println is a wrapper for fmt.Println that treats each argument as if it were -// passed with a default Formatter interface returned by NewFormatter. It -// returns the number of bytes written and any write error encountered. See -// NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b)) -func Println(a ...interface{}) (n int, err error) { - return fmt.Println(convertArgs(a)...) -} - -// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were -// passed with a default Formatter interface returned by NewFormatter. It -// returns the resulting string. See NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b)) -func Sprint(a ...interface{}) string { - return fmt.Sprint(convertArgs(a)...) -} - -// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were -// passed with a default Formatter interface returned by NewFormatter. It -// returns the resulting string. See NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b)) -func Sprintf(format string, a ...interface{}) string { - return fmt.Sprintf(format, convertArgs(a)...) -} - -// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it -// were passed with a default Formatter interface returned by NewFormatter. It -// returns the resulting string. See NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b)) -func Sprintln(a ...interface{}) string { - return fmt.Sprintln(convertArgs(a)...) -} - -// convertArgs accepts a slice of arguments and returns a slice of the same -// length with each argument converted to a default spew Formatter interface. -func convertArgs(args []interface{}) (formatters []interface{}) { - formatters = make([]interface{}, len(args)) - for index, arg := range args { - formatters[index] = NewFormatter(arg) - } - return formatters -} diff --git a/vendor/github.com/tellytv/go.xtream-codes/.gitignore b/vendor/github.com/tellytv/go.xtream-codes/.gitignore new file mode 100644 index 0000000..f1c181e --- /dev/null +++ b/vendor/github.com/tellytv/go.xtream-codes/.gitignore @@ -0,0 +1,12 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out diff --git a/vendor/github.com/tellytv/go.xtream-codes/.gometalinter.json b/vendor/github.com/tellytv/go.xtream-codes/.gometalinter.json new file mode 100644 index 0000000..b66dd8a --- /dev/null +++ b/vendor/github.com/tellytv/go.xtream-codes/.gometalinter.json @@ -0,0 +1,33 @@ +{ + "Enable": [ + "deadcode", + "errcheck", + "gochecknoinits", + "goconst", + "gofmt", + "goimports", + "golint", + "gosec", + "gotype", + "gotypex", + "ineffassign", + "interfacer", + "megacheck", + "misspell", + "nakedret", + "safesql", + "structcheck", + "test", + "testify", + "unconvert", + "unparam", + "varcheck", + "vet", + "vetshadow" + ], + "Deadline": "5m", + "Sort": [ + "path", + "linter" + ] +} diff --git a/vendor/github.com/tellytv/go.xtream-codes/LICENSE b/vendor/github.com/tellytv/go.xtream-codes/LICENSE new file mode 100644 index 0000000..9e848f7 --- /dev/null +++ b/vendor/github.com/tellytv/go.xtream-codes/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Telly + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/tellytv/go.xtream-codes/README.md b/vendor/github.com/tellytv/go.xtream-codes/README.md new file mode 100644 index 0000000..a5cd6fe --- /dev/null +++ b/vendor/github.com/tellytv/go.xtream-codes/README.md @@ -0,0 +1,2 @@ +# go.xtream-codes +A Go library for accessing Xtream-Codes servers diff --git a/vendor/github.com/tellytv/go.xtream-codes/base64.go b/vendor/github.com/tellytv/go.xtream-codes/base64.go new file mode 100644 index 0000000..9c5d2d8 --- /dev/null +++ b/vendor/github.com/tellytv/go.xtream-codes/base64.go @@ -0,0 +1,58 @@ +package xtreamcodes + +// Package base64 provides a byte slice type that marshals into json as +// a raw (no padding) base64url value. +// Originally from https://github.com/manifoldco/go-base64 + +import ( + "encoding/base64" + "errors" + "reflect" +) + +// Base64Value is a base64url encoded json object, +type Base64Value []byte + +// New returns a pointer to a Base64Value, cast from the given byte slice. +// This is a convenience function, handling the address creation that a direct +// cast would not allow. +func New(b []byte) *Base64Value { + v := Base64Value(b) + return &v +} + +// NewFromString returns a Base64Value containing the decoded data in encoded. +func NewFromString(encoded string) (*Base64Value, error) { + out, err := base64.RawURLEncoding.DecodeString(encoded) + if err != nil { + return nil, err + } + + return New(out), nil +} + +// MarshalJSON returns the ba64url encoding of bv for JSON representation. +func (bv *Base64Value) MarshalJSON() ([]byte, error) { + return []byte("\"" + base64.RawURLEncoding.EncodeToString(*bv) + "\""), nil +} + +func (bv *Base64Value) String() string { + return base64.RawURLEncoding.EncodeToString(*bv) +} + +// UnmarshalJSON sets bv to the bytes represented in the base64url encoding b. +func (bv *Base64Value) UnmarshalJSON(b []byte) error { + if len(b) < 2 || b[0] != byte('"') || b[len(b)-1] != byte('"') { + return errors.New("value is not a string") + } + + out := make([]byte, base64.RawURLEncoding.DecodedLen(len(b)-2)) + n, err := base64.RawURLEncoding.Decode(out, b[1:len(b)-1]) + if err != nil { + return err + } + + v := reflect.ValueOf(bv).Elem() + v.SetBytes(out[:n]) + return nil +} diff --git a/vendor/github.com/tellytv/go.xtream-codes/ffmpeg.go b/vendor/github.com/tellytv/go.xtream-codes/ffmpeg.go new file mode 100644 index 0000000..0483ecc --- /dev/null +++ b/vendor/github.com/tellytv/go.xtream-codes/ffmpeg.go @@ -0,0 +1,181 @@ +// Copyright 2015 Dave Gradwell +// Under BSD-style license (see LICENSE file) +// Originally from https://github.com/xdave/ff + +package xtreamcodes + +import ( + "encoding/json" + "strconv" +) + +// abs is a quick integer function (math package doesn't use ints). +func abs(num int) int { + if num >= 0 { + return num + } + return -num +} + +// Tags represents either Format or FFMPEGStreamInfo tags embedded in the file. +type Tags map[string]interface{} + +// Format describes container format info. +type Format struct { + BitRate string `json:"bit_rate"` + Duration string `json:"duration"` + Filename string `json:"filename"` + LongName string `json:"format_long_name"` + Name string `json:"format_name"` + NumPrograms int `json:"nb_programs"` + NumStreams int `json:"nb_streams"` + ProbeScore int `json:"probe_score"` + Size string `json:"size"` + StartTime string `json:"start_time"` + Tags Tags `json:"tags"` +} + +// Disposition of FFMPEGStreamInfo +type Disposition struct { + AttachedPic int `json:"attached_pic"` + CleanEffects int `json:"clean_effects"` + Comment int `json:"comment"` + Default int `json:"default"` + Dub int `json:"dub"` + Forced int `json:"forced"` + HearingImpaired int `json:"hearing_impaired"` + Karaoke int `json:"karaoke"` + Lyrics int `json:"lyrics"` + Original int `json:"original"` + VisualImpaired int `json:"visual_impaired"` +} + +// SideData describes metadata. More fields may be added later as needed. +type SideData struct { + Displaymatrix string `json:"displaymatrix"` + Rotation int `json:"rotation"` + Size int `json:"side_data_size"` + Type string `json:"side_data_type"` +} + +// StreamType is used for enumerating the CodecType field in FFMPEGStreamInfo. +type StreamType string + +const ( + // VideoStream is the constant for video streams. + VideoStream StreamType = "video" + // AudioStream is the constant for audio streams. + AudioStream = "audio" +) + +// FFMPEGStreamInfo represents any kind of stream (Audio, Video, etc) +type FFMPEGStreamInfo struct { + AvgFrameRate string `json:"avg_frame_rate"` + BitRate string `json:"bit_rate"` + BitsPerRawSample string `json:"bits_per_raw_sample"` + BitsPerSample int `json:"bits_per_sample"` + ChannelLayout string `json:"channel_layout"` + Channels int `json:"channels"` + ChromaLocation string `json:"chroma_location"` + CodecLongName string `json:"codec_long_name"` + CodecName string `json:"codec_name"` + CodecTag string `json:"codec_tag"` + CodecTagString string `json:"codec_tag_string"` + CodecTimeBase string `json:"codec_time_base"` + CodecType StreamType `json:"codec_type"` + CodedHeight int `json:"coded_height"` + CodedWidth int `json:"coded_width"` + ColorPrimaries string `json:"color_primaries"` + ColorRange string `json:"color_range"` + ColorSpace string `json:"color_space"` + ColorTransfer string `json:"color_transfer"` + DisplayAspectRatio string `json:"display_aspect_ratio"` + DivxPacked string `json:"divx_packed"` + DmixMode string `json:"dmix_mode"` + Duration string `json:"duration"` + DurationTs int `json:"duration_ts"` + HasBFrames int `json:"has_b_frames"` + Height int `json:"height"` + ID string `json:"id"` + Index int `json:"index"` + IsAvc string `json:"is_avc"` + Level int `json:"level"` + LoroCmixlev string `json:"loro_cmixlev"` + LoroSurmixlev string `json:"loro_surmixlev"` + LtrtCmixlev string `json:"ltrt_cmixlev"` + LtrtSurmixlev string `json:"ltrt_surmixlev"` + MaxBitRate string `json:"max_bit_rate"` + NalLengthSize string `json:"nal_length_size"` + NumFrames string `json:"nb_frames"` + PixFmt string `json:"pix_fmt"` + Profile string `json:"profile"` + QuarterSample string `json:"quarter_sample"` + FrameRate string `json:"r_frame_rate"` + Refs int `json:"refs"` + SampleAspectRatio string `json:"sample_aspect_ratio"` + SampleFmt string `json:"sample_fmt"` + SampleRate string `json:"sample_rate"` + StartPts int `json:"start_pts"` + StartTime string `json:"start_time"` + TimeBase string `json:"time_base"` + Timecode string `json:"timecode"` + Width int `json:"width"` + Tags Tags `json:"tags"` + Disposition Disposition `json:"disposition"` + SideDataList []SideData `json:"side_data_list"` +} + +// IsRotated returns true if the stream is rotated. +func (s FFMPEGStreamInfo) IsRotated() (bool, error) { + rot, err := s.Rotation() + return rot != 0, err +} + +// Rotation gets the rotation value of video stream, either from stream side-data, or tags. +// Returns 0 if it's not rotated, or if we can't figure it out. +func (s FFMPEGStreamInfo) Rotation() (int, error) { + for _, sdata := range s.SideDataList { + return abs(sdata.Rotation), nil + } + rotationTag, ok := s.Tags["rotate"] + if ok { + rotation, ok := rotationTag.(string) + if ok { + val, valErr := strconv.Atoi(rotation) + if valErr != nil { + return 0, valErr + } + return val, nil + } + } + return 0, nil +} + +// ProbeInfo is just a nice structure that represents the JSON data returned by ffprobe. +// Mostly auto-generated with http://mholt.github.io/json-to-go +type ProbeInfo struct { + Format Format `json:"format"` + Streams []FFMPEGStreamInfo `json:"streams"` +} + +// NewInfo returns a new ProbeInfo structure from the input JSON +func NewInfo(jsonData string) (*ProbeInfo, error) { + info := new(ProbeInfo) + err := json.Unmarshal([]byte(jsonData), info) + if err != nil { + return nil, err + } + return info, nil +} + +// FilterStreams filters out streams of the given StreamType. +// Returns a []FFMPEGStreamInfo slice, even if it's empty +func (info ProbeInfo) FilterStreams(t StreamType) []FFMPEGStreamInfo { + streams := []FFMPEGStreamInfo{} + for _, stream := range info.Streams { + if stream.CodecType == t { + streams = append(streams, stream) + } + } + return streams +} diff --git a/vendor/github.com/tellytv/go.xtream-codes/structs.go b/vendor/github.com/tellytv/go.xtream-codes/structs.go new file mode 100644 index 0000000..eb055fe --- /dev/null +++ b/vendor/github.com/tellytv/go.xtream-codes/structs.go @@ -0,0 +1,306 @@ +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) +} diff --git a/vendor/github.com/tellytv/go.xtream-codes/xtream-codes.go b/vendor/github.com/tellytv/go.xtream-codes/xtream-codes.go new file mode 100644 index 0000000..ff71896 --- /dev/null +++ b/vendor/github.com/tellytv/go.xtream-codes/xtream-codes.go @@ -0,0 +1,316 @@ +// Package xtreamcodes provides a Golang interface to the Xtream-Codes IPTV Server API. +package xtreamcodes + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strconv" +) + +var userAgent = "go.xstream-codes (Go-http-client/1.1)" + +// XtreamClient is the client used to communicate with a Xtream-Codes server. +type XtreamClient struct { + Username string + Password string + BaseURL string + + ServerInfo ServerInfo + UserInfo UserInfo + + // Our HTTP client to communicate with Xtream + HTTP *http.Client + + // We store an internal map of Streams for use with GetStreamURL + streams map[int]Stream +} + +// NewClient returns an initialized XtreamClient with the given values. +func NewClient(username, password, baseURL string) (*XtreamClient, error) { + + _, parseURLErr := url.Parse(baseURL) + if parseURLErr != nil { + return nil, fmt.Errorf("error parsing url: %s", parseURLErr.Error()) + } + + client := &XtreamClient{ + Username: username, + Password: password, + BaseURL: baseURL, + + HTTP: http.DefaultClient, + + streams: make(map[int]Stream), + } + + authData, authErr := client.sendRequest("", nil) + if authErr != nil { + return nil, fmt.Errorf("error sending authentication request: %s", authErr.Error()) + } + + a := &AuthenticationResponse{} + + if jsonErr := json.Unmarshal(authData, &a); jsonErr != nil { + return nil, fmt.Errorf("error unmarshaling json: %s", jsonErr.Error()) + } + + client.ServerInfo = a.ServerInfo + client.UserInfo = a.UserInfo + + return client, nil +} + +// GetStreamURL will return a stream URL string for the given streamID and wantedFormat. +func (c *XtreamClient) GetStreamURL(streamID int, wantedFormat string) (string, error) { + + // For Live Streams the main format is + // http(s)://domain:port/live/username/password/streamID.ext ( In allowed_output_formats element you have the available ext ) + // For VOD Streams the format is: + // http(s)://domain:port/movie/username/password/streamID.ext ( In target_container element you have the available ext ) + // For Series Streams the format is + // http(s)://domain:port/series/username/password/streamID.ext ( In target_container element you have the available ext ) + + validFormat := false + + for _, allowedFormat := range c.UserInfo.AllowedOutputFormats { + if wantedFormat == allowedFormat { + validFormat = true + } + } + + if !validFormat { + return "", fmt.Errorf("%s is not an allowed output format", wantedFormat) + } + + if _, ok := c.streams[streamID]; !ok { + return "", fmt.Errorf("%d is not a valid stream id", streamID) + } + + stream := c.streams[streamID] + + return fmt.Sprintf("%s/%s/%s/%s/%d.%s", c.BaseURL, stream.Type, c.Username, c.Password, stream.ID, wantedFormat), nil +} + +// GetLiveCategories will return a slice of categories for live streams. +func (c *XtreamClient) GetLiveCategories() ([]Category, error) { + return c.GetCategories("live") +} + +// GetVideoOnDemandCategories will return a slice of categories for VOD streams. +func (c *XtreamClient) GetVideoOnDemandCategories() ([]Category, error) { + return c.GetCategories("vod") +} + +// GetSeriesCategories will return a slice of categories for series streams. +func (c *XtreamClient) GetSeriesCategories() ([]Category, error) { + return c.GetCategories("series") +} + +// GetCategories is a helper function used by GetLiveCategories, GetVideoOnDemandCategories and +// GetSeriesCategories to reduce duplicate code. +func (c *XtreamClient) GetCategories(catType string) ([]Category, error) { + catData, catErr := c.sendRequest(fmt.Sprintf("get_%s_categories", catType), nil) + if catErr != nil { + return nil, catErr + } + + cats := make([]Category, 0) + + jsonErr := json.Unmarshal(catData, &cats) + + for idx := range cats { + cats[idx].Type = catType + } + + return cats, jsonErr +} + +// GetLiveStreams will return a slice of live streams. +// You can also optionally provide a categoryID to limit the output to members of that category. +func (c *XtreamClient) GetLiveStreams(categoryID string) ([]Stream, error) { + return c.GetStreams("live", categoryID) +} + +// GetVideoOnDemandStreams will return a slice of VOD streams. +// You can also optionally provide a categoryID to limit the output to members of that category. +func (c *XtreamClient) GetVideoOnDemandStreams(categoryID string) ([]Stream, error) { + return c.GetStreams("vod", categoryID) +} + +// GetStreams is a helper function used by GetLiveStreams and GetVideoOnDemandStreams +// to reduce duplicate code. +func (c *XtreamClient) GetStreams(streamAction, categoryID string) ([]Stream, error) { + var params url.Values + if categoryID != "" { + params = url.Values{} + params.Add("category_id", categoryID) + } + + // For whatever reason, unlike live and vod, series streams action doesn't have "_streams". + if streamAction != "series" { + streamAction = fmt.Sprintf("%s_streams", streamAction) + } + + streamData, streamErr := c.sendRequest(fmt.Sprintf("get_%s", streamAction), params) + if streamErr != nil { + return nil, streamErr + } + + streams := make([]Stream, 0) + + if jsonErr := json.Unmarshal(streamData, &streams); jsonErr != nil { + return nil, jsonErr + } + + for _, stream := range streams { + c.streams[stream.ID] = stream + } + + return streams, nil +} + +// GetSeries will return a slice of all available Series. +// You can also optionally provide a categoryID to limit the output to members of that category. +func (c *XtreamClient) GetSeries(categoryID string) ([]SeriesInfo, error) { + var params url.Values + if categoryID != "" { + params = url.Values{} + params.Add("category_id", categoryID) + } + + seriesData, seriesErr := c.sendRequest("get_series", params) + if seriesErr != nil { + return nil, seriesErr + } + + seriesInfos := make([]SeriesInfo, 0) + + if jsonErr := json.Unmarshal(seriesData, &seriesInfos); jsonErr != nil { + return nil, jsonErr + } + + return seriesInfos, nil +} + +// GetSeriesInfo will return a series info for the given seriesID. +func (c *XtreamClient) GetSeriesInfo(seriesID string) (*Series, error) { + if seriesID == "" { + return nil, fmt.Errorf("series ID can not be empty") + } + + seriesData, seriesErr := c.sendRequest("get_series_info", url.Values{"series_id": []string{seriesID}}) + if seriesErr != nil { + return nil, seriesErr + } + + seriesInfo := &Series{} + + jsonErr := json.Unmarshal(seriesData, &seriesInfo) + + return seriesInfo, jsonErr +} + +// GetVideoOnDemandInfo will return VOD info for the given vodID. +func (c *XtreamClient) GetVideoOnDemandInfo(vodID string) (*VideoOnDemandInfo, error) { + if vodID == "" { + return nil, fmt.Errorf("vod ID can not be empty") + } + + vodData, vodErr := c.sendRequest("get_vod_info", url.Values{"vod_id": []string{vodID}}) + if vodErr != nil { + return nil, vodErr + } + + vodInfo := &VideoOnDemandInfo{} + + jsonErr := json.Unmarshal(vodData, &vodInfo) + + return vodInfo, jsonErr +} + +// GetShortEPG returns a short version of the EPG for the given streamID. If no limit is provided, the next 4 items in the EPG will be returned. +func (c *XtreamClient) GetShortEPG(streamID string, limit int) ([]EPGInfo, error) { + return c.getEPG("get_short_epg", streamID, limit) +} + +// GetEPG returns the full EPG for the given streamID. +func (c *XtreamClient) GetEPG(streamID string) ([]EPGInfo, error) { + return c.getEPG("get_simple_data_table", streamID, 0) +} + +// GetXMLTV will return a slice of bytes for the XMLTV EPG file available from the provider. +func (c *XtreamClient) GetXMLTV() ([]byte, error) { + xmlTVData, xmlTVErr := c.sendRequest("xmltv.php", nil) + if xmlTVErr != nil { + return nil, xmlTVErr + } + + return xmlTVData, xmlTVErr +} + +func (c *XtreamClient) getEPG(action, streamID string, limit int) ([]EPGInfo, error) { + if streamID == "" { + return nil, fmt.Errorf("stream ID can not be empty") + } + + params := url.Values{"stream_id": []string{streamID}} + if limit > 0 { + params.Add("limit", strconv.Itoa(limit)) + } + + epgData, epgErr := c.sendRequest(action, params) + if epgErr != nil { + return nil, epgErr + } + + epgContainer := &epgContainer{} + + jsonErr := json.Unmarshal(epgData, &epgContainer) + + return epgContainer.EPGListings, jsonErr +} + +func (c *XtreamClient) sendRequest(action string, parameters url.Values) ([]byte, error) { + file := "player_api.php" + if action == "xmltv.php" { + file = action + } + url := fmt.Sprintf("%s/%s?username=%s&password=%s", c.BaseURL, file, c.Username, c.Password) + if action != "" { + url = fmt.Sprintf("%s&action=%s", url, action) + } + if parameters != nil { + url = fmt.Sprintf("%s&%s", url, parameters.Encode()) + } + + request, httpErr := http.NewRequest("GET", url, nil) + if httpErr != nil { + return nil, httpErr + } + + request.Header.Set("User-Agent", userAgent) + + response, httpErr := c.HTTP.Do(request) + if httpErr != nil { + return nil, fmt.Errorf("cannot reach server. %v", httpErr) + } + + if response.StatusCode > 399 { + return nil, fmt.Errorf("status code was %d, expected 2XX-3XX", response.StatusCode) + } + + buf := &bytes.Buffer{} + if _, copyErr := io.Copy(buf, response.Body); copyErr != nil { + return nil, copyErr + } + + if closeErr := response.Body.Close(); closeErr != nil { + return nil, fmt.Errorf("cannot read response. %v", closeErr) + } + + return buf.Bytes(), nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 9b9f18c..3abb82e 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,5 +1,3 @@ -# github.com/davecgh/go-spew v1.1.1 -github.com/davecgh/go-spew/spew # github.com/fsnotify/fsnotify v1.4.7 github.com/fsnotify/fsnotify # github.com/gin-contrib/cors v0.0.0-20190226021855-50921afdc5c1 @@ -57,6 +55,8 @@ github.com/spf13/jwalterweatherman github.com/spf13/pflag # github.com/spf13/viper v1.3.1 github.com/spf13/viper +# github.com/tellytv/go.xtream-codes v0.0.0-20190114013623-9b74dcb500e4 +github.com/tellytv/go.xtream-codes # github.com/ugorji/go/codec v0.0.0-20181209151446-772ced7fd4c2 github.com/ugorji/go/codec # golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb