123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360 |
- package plexapi
- import (
- "encoding/xml"
- "fmt"
- "io/ioutil"
- "net/http"
- "strings"
- "sync"
- "code.senomas.com/go/util"
- log "github.com/Sirupsen/logrus"
- )
- // Server struct
- type Server struct {
- api *API
- host string
- XMLName xml.Name `xml:"Server"`
- AccessToken string `xml:"accessToken,attr"`
- Name string `xml:"name,attr"`
- Address string `xml:"address,attr"`
- Port int `xml:"port,attr"`
- Version string `xml:"version,attr"`
- Scheme string `xml:"scheme,attr"`
- Host string `xml:"host,attr"`
- LocalAddresses string `xml:"localAddresses,attr"`
- MachineIdentifier string `xml:"machineIdentifier,attr"`
- CreatedAt int `xml:"createdAt,attr"`
- UpdatedAt int `xml:"updatedAt,attr"`
- Owned int `xml:"owned,attr"`
- Synced int `xml:"synced,attr"`
- }
- func (server *Server) setHeader(req *http.Request) {
- req.Header.Add("X-Plex-Product", "plex-sync")
- req.Header.Add("X-Plex-Version", "1.0.0")
- req.Header.Add("X-Plex-Client-Identifier", "donkey")
- if server.api.userInfo.Token != "" {
- req.Header.Add("X-Plex-Token", server.api.userInfo.Token)
- }
- }
- func (server *Server) getContainer(url string) (container MediaContainer, err error) {
- log.WithField("url", url).Debugf("GET")
- req, err := http.NewRequest("GET", url, nil)
- if err != nil {
- log.WithField("url", url).WithError(err).Errorf("http.GET")
- return container, err
- }
- server.setHeader(req)
- resp, err := server.api.client.Do(req)
- if err != nil {
- return container, err
- }
- defer resp.Body.Close()
- log.WithField("url", url).WithField("status", resp.Status).Debugf("RESP")
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- log.WithField("url", url).WithError(err).Errorf("RESP")
- return container, err
- }
- err = xml.Unmarshal(body, &container)
- return container, err
- }
- // Check func
- func (server *Server) Check() {
- var urls []string
- urls = append(urls, fmt.Sprintf("https://%s:%v", server.Address, server.Port))
- for _, la := range strings.Split(server.LocalAddresses, ",") {
- urls = append(urls, fmt.Sprintf("https://%s:32400", la))
- }
- var checkOnce util.CheckOnce
- var wg sync.WaitGroup
- out := make(chan string)
- defer close(out)
- for _, pu := range urls {
- url := pu
- wg.Add(1)
- go func() error {
- defer wg.Done()
- if !checkOnce.IsDone() {
- log.Debugf("CHECK %s %s", server.Name, url)
- c, err := server.getContainer(url)
- if err != nil {
- log.Debugf("ERROR GET %s '%s': %v", server.Name, url, err)
- return err
- }
- if c.FriendlyName != server.Name {
- log.Fatal("WRONG SERVER ", c.FriendlyName, " ", server.Name)
- }
- // log.Debugf("RESP BODY\n%s", util.JSONPrettyPrint(c))
- checkOnce.Done(func() {
- out <- url
- })
- }
- return nil
- }()
- }
- go func() {
- wg.Wait()
- checkOnce.Done(func() {
- out <- ""
- })
- }()
- server.host = <-out
- log.Debugf("URL %s %s", server.Name, server.host)
- }
- // Perform func
- func (server *Server) Perform(cmd, path string) error {
- if server.host != "" {
- url := server.host + path
- log.WithField("url", url).Debugf(cmd)
- req, err := http.NewRequest(cmd, url, nil)
- if err != nil {
- log.WithField("url", url).WithError(err).Errorf("http.%s", cmd)
- return err
- }
- server.setHeader(req)
- resp, err := server.api.client.Do(req)
- if err != nil {
- return err
- }
- defer resp.Body.Close()
- log.WithField("url", url).WithField("status", resp.Status).Debugf("RESP")
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- log.WithField("url", url).WithError(err).Errorf("RESP")
- return err
- }
- log.Debugf("RESP BODY\n%s", body)
- return err
- }
- return fmt.Errorf("NO HOST")
- }
- // GetContainer func
- func (server *Server) GetContainer(path string) (container MediaContainer, err error) {
- if server.host != "" {
- container, err = server.getContainer(server.host + path)
- return container, err
- }
- return container, fmt.Errorf("NO HOST")
- }
- // GetDirectories func
- func (server *Server) GetDirectories() (directories []Directory, err error) {
- container, err := server.GetContainer("/library/sections")
- if err != nil {
- return directories, err
- }
- for _, d := range container.Directories {
- nd := d
- // nd.server = server
- directories = append(directories, nd)
- }
- return directories, err
- }
- // GetVideos func
- func (server *Server) GetVideos(wg *sync.WaitGroup, out chan<- interface{}) {
- cs := make(chan MediaContainer)
- dirs := make(chan Directory)
- wg.Add(1)
- go func() {
- container, err := server.GetContainer("/library/sections")
- if err == nil {
- cs <- container
- } else {
- wg.Done()
- }
- }()
- for i, il := 0, server.api.HTTP.WorkerSize; i < il; i++ {
- go func() {
- for c := range cs {
- func() {
- defer wg.Done()
- for _, d := range c.Directories {
- wg.Add(1)
- d.Paths = c.Paths
- d.Keys = c.Keys
- d.Keys = append(d.Keys, KeyInfo{Key: d.Key, Type: d.Type, Title: d.Title})
- dirs <- d
- }
- for _, v := range c.Videos {
- // if v.GUID == "" {
- // meta, err := server.GetMeta(v)
- // if err != nil {
- // log.Fatal("GetMeta ", err)
- // }
- // v = meta
- // }
- v.server = server
- v.Paths = c.Paths
- v.Keys = c.Keys
- var idx []string
- for _, px := range v.Media.Parts {
- for _, kk := range v.Paths {
- if strings.HasPrefix(px.File, kk) {
- idx = append(idx, px.File[len(kk):])
- }
- }
- }
- if len(idx) > 0 {
- v.FID = strings.Join(idx, ":")
- v.FID = strings.Replace(v.FID, "\\", "/", -1)
- } else {
- v.FID = v.GUID
- }
- wg.Add(1)
- out <- v
- }
- }()
- }
- }()
- go func() {
- for d := range dirs {
- func() {
- defer wg.Done()
- if strings.HasPrefix(d.Key, "/library/") {
- cc, err := server.GetContainer(d.Key)
- if err == nil {
- wg.Add(1)
- cc.Keys = d.Keys
- cc.Paths = d.Paths
- for _, l := range d.Locations {
- if l.Path != "" {
- cc.Paths = append(cc.Paths, l.Path)
- }
- }
- cs <- cc
- }
- } else {
- cc, err := server.GetContainer(fmt.Sprintf("/library/sections/%v/all", d.Key))
- if err == nil {
- wg.Add(1)
- cc.Keys = d.Keys
- cc.Paths = d.Paths
- for _, l := range d.Locations {
- if l.Path != "" {
- cc.Paths = append(cc.Paths, l.Path)
- }
- }
- cs <- cc
- }
- }
- }()
- }
- }()
- }
- }
- // GetMeta func
- func (server *Server) GetMeta(video Video) (meta Video, err error) {
- if video.GUID != "" {
- return video, nil
- }
- var mc MediaContainer
- mc, err = server.GetContainer(video.Key)
- if err != nil {
- return video, err
- }
- return mc.Videos[0], nil
- }
- // MarkWatched func
- func (server *Server) MarkWatched(key string) error {
- url := server.host + "/:/scrobble?identifier=com.plexapp.plugins.library&key=" + key
- log.WithField("url", url).WithField("server", server.Name).Debug("MarkWatched.GET")
- req, err := http.NewRequest("GET", url, nil)
- if err != nil {
- return err
- }
- server.setHeader(req)
- resp, err := server.api.client.Do(req)
- if err != nil {
- return err
- }
- defer resp.Body.Close()
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return err
- }
- log.WithField("url", url).WithField("server", server.Name).Debugf("MarkWatched.RESULT\n%s", body)
- return err
- }
- // MarkUnwatched func
- func (server *Server) MarkUnwatched(key string) error {
- url := server.host + "/:/unscrobble?identifier=com.plexapp.plugins.library&key=" + key
- log.WithField("url", url).WithField("server", server.Name).Debug("MarkUnwatched.GET")
- req, err := http.NewRequest("GET", url, nil)
- if err != nil {
- return err
- }
- server.setHeader(req)
- resp, err := server.api.client.Do(req)
- if err != nil {
- return err
- }
- defer resp.Body.Close()
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return err
- }
- log.WithField("url", url).WithField("server", server.Name).Debugf("MarkUnwatched.RESULT\n%s", body)
- return err
- }
- // SetViewOffset func
- func (server *Server) SetViewOffset(key, offset string) error {
- url := server.host + "/:/progress?key=" + key + "&identifier=com.plexapp.plugins.library&time=" + offset
- log.WithField("url", url).WithField("server", server.Name).Debug("SetViewOffset.GET")
- req, err := http.NewRequest("GET", url, nil)
- if err != nil {
- return err
- }
- server.setHeader(req)
- resp, err := server.api.client.Do(req)
- if err != nil {
- return err
- }
- defer resp.Body.Close()
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return err
- }
- log.WithField("url", url).WithField("server", server.Name).Debugf("SetViewOffset.RESULT\n%s", body)
- return err
- }
|