Skip to content

Commit

Permalink
Merge pull request #7 from jrudio/cli-choose-server
Browse files Browse the repository at this point in the history
updated cli
  • Loading branch information
jrudio authored Oct 26, 2017
2 parents 7e2ab6c + 5529779 commit 1a072f3
Show file tree
Hide file tree
Showing 5 changed files with 236 additions and 73 deletions.
129 changes: 106 additions & 23 deletions cmd/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,85 @@ func checkPIN(c *cli.Context) error {
return nil
}

func pickServer(db store) func(c *cli.Context) error {
return func(c *cli.Context) error {
// look up servers - hopefully a token is already in store
plexToken, err := db.getPlexToken()

if err != nil {
db.Close()
return fmt.Errorf("failed to retreive plex token: %v", err)
}

plexConn, err := plex.New("", plexToken)

if err != nil {
db.Close()
return err
}

// load list of servers
servers, err := plexConn.GetServers()

if err != nil {
db.Close()
return fmt.Errorf("failed getting plex servers: %v", err)
}

fmt.Println("Server list:")

for i, server := range servers {
fmt.Printf("[%d] - %s\n", i, server.Name)
}

fmt.Print("\nSelect a server: ")

var serverIndex int
fmt.Scanln(&serverIndex)

// bound check input
if serverIndex < 0 || serverIndex > (len(servers)-1) {
db.Close()
return errors.New("invalid selection")
}

selectedServer := servers[serverIndex]

// choose to connect via local or remote
fmt.Printf("\nshowing local and remote addresses for %s:\n", selectedServer.Name)

for i, conn := range selectedServer.Connection {
fmt.Printf("\t[%d] uri: %s, is local: %t\n", i, conn.Address, conn.Local == 1)
}

fmt.Print("\nPick the appropriate address: ")

var urlIndex int
fmt.Scanln(&urlIndex)

// bound check again
if urlIndex < 0 || urlIndex > (len(selectedServer.Connection)-1) {
db.Close()
return errors.New("invalid selection")
}

// persist selection to disk
fmt.Printf("\nsetting %s as the default server using url %s...\n", selectedServer.Name, selectedServer.Connection[urlIndex].URI)

if err := db.savePlexServer(server{
Name: selectedServer.Name,
URL: selectedServer.Connection[urlIndex].URI,
}); err != nil {
db.Close()
return fmt.Errorf("failed to save server info: %v", err)
}

fmt.Println("success!")

return nil
}
}

// signIn displays the auth token on successful sign in
func signIn(db store) func(c *cli.Context) error {
return func(c *cli.Context) error {
Expand Down Expand Up @@ -259,37 +338,41 @@ func signIn(db store) func(c *cli.Context) error {
}
}

func getLibraries(c *cli.Context) error {
plexToken := c.String("token")
plexURL := c.String("url")
func getLibraries(db store) func(c *cli.Context) error {
return func(c *cli.Context) error {
plexToken, err := db.getPlexToken()

if plexToken == "" {
return errors.New("token is required for this command")
}
if err != nil {
db.Close()
return fmt.Errorf("failed to retreive plex token: %v", err)
}

if plexURL == "" {
return errors.New("url is required for this command")
}
plexServer, err := db.getPlexServer()

fmt.Println("using plex token:", plexToken)
if err != nil {
db.Close()
return fmt.Errorf("failed to retreive plex server url: %v", err)
}

plexConn, err := plex.New(plexURL, plexToken)
plexConn, err := plex.New(plexServer.URL, plexToken)

if err != nil {
return err
}
if err != nil {
db.Close()
return err
}

fmt.Println("getting libraries...")
fmt.Println("getting libraries...")

libraries, err := plexConn.GetLibraries()
libraries, err := plexConn.GetLibraries()

if err != nil {
return err
}
if err != nil {
return err
}

for _, dir := range libraries.MediaContainer.Directory {
fmt.Println(dir.Title)
}
for _, dir := range libraries.MediaContainer.Directory {
fmt.Println(dir.Title)
}

return nil
return nil
}
}
37 changes: 24 additions & 13 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"encoding/json"
"fmt"
"os"
"path/filepath"
Expand All @@ -23,6 +24,23 @@ var (
appSecret = []byte("iAmAseCReTuSEdTOENcrYp")
)

type server struct {
Name string `json:"name"`
URL string `json:"url"`
}

func (s server) Serialize() ([]byte, error) {
return json.Marshal(s)
}

func unserializeServer(serializedServer []byte) (server, error) {
var s server

err := json.Unmarshal(serializedServer, &s)

return s, err
}

func main() {
app := cli.NewApp()

Expand Down Expand Up @@ -116,19 +134,7 @@ func main() {
{
Name: "library",
Usage: "display your libraries",
Action: getLibraries,
Flags: []cli.Flag{
cli.StringFlag{
Name: "token",
Value: "",
Usage: "plex token is required to access your server",
},
cli.StringFlag{
Name: "url",
Value: "",
Usage: "url to your plex server",
},
},
Action: getLibraries(db),
},
{
Name: "request-pin",
Expand Down Expand Up @@ -156,6 +162,11 @@ func main() {
Usage: "use your username and password to receive a plex auth token",
Action: signIn(db),
},
{
Name: "pick-server",
Usage: "choose a server to interact with",
Action: pickServer(db),
},
}

app.Run(os.Args)
Expand Down
52 changes: 48 additions & 4 deletions cmd/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ type store struct {
}

type storeKeys struct {
appSecret []byte
plexToken []byte
appSecret []byte
plexToken []byte
plexServer []byte
}

func initDataStore(dirName string) (store, error) {
Expand Down Expand Up @@ -53,12 +54,14 @@ func initDataStore(dirName string) (store, error) {

db.db = kvStore
db.keys = storeKeys{
appSecret: []byte("app-secret"),
plexToken: []byte("plex-token"),
appSecret: []byte("app-secret"),
plexToken: []byte("plex-token"),
plexServer: []byte("plex-server"),
}

return db, nil
}

func (s store) Close() {
if err := s.db.Close(); err != nil {
fmt.Printf("data store failed to closed: %v\n", err)
Expand Down Expand Up @@ -159,3 +162,44 @@ func (s store) savePlexToken(token string) error {

return nil
}

func (s store) getPlexServer() (server, error) {
var plexServer server

err := s.db.View(func(txn *badger.Txn) error {
item, err := txn.Get(s.keys.plexServer)

if err != nil {
return err
}

serializedServer, err := item.Value()

if err != nil {
return err
}

_plexServer, err := unserializeServer(serializedServer)

if err != nil {
return err
}

plexServer = _plexServer

return nil
})

return plexServer, err
}

func (s store) savePlexServer(plexServer server) error {
serializedServer, err := plexServer.Serialize()
if err != nil {
return err
}

return s.db.Update(func(txn *badger.Txn) error {
return txn.Set(s.keys.plexServer, serializedServer, 0)
})
}
51 changes: 28 additions & 23 deletions models.go
Original file line number Diff line number Diff line change
Expand Up @@ -625,29 +625,34 @@ type terminateSessionResponse struct {

// PMSDevices is the result of the https://plex.tv/pms/resources endpoint
type PMSDevices struct {
Name string `json:"name" xml:"name,attr"`
Product string `json:"product" xml:"product,attr"`
ProductVersion string `json:"productVersion" xml:"productVersion,attr"`
Platform string `json:"platform" xml:"platform,attr"`
PlatformVersion string `json:"platformVersion" xml:"platformVersion,attr"`
Device int `json:"device" xml:"device,attr"`
ClientIdentifier string `json:"clientIdentifier" xml:"clientIdentifier,attr"`
CreatedAt string `json:"createdAt" xml:"createdAt,attr"`
LastSeenAt string `json:"lastSeenAt" xml:"lastSeenAt,attr"`
Provides string `json:"provides" xml:"provides,attr"`
Owned string `json:"owned" xml:"owned,attr"`
AccessToken string `json:"accessToken" xml:"accessToken,attr"`
HTTPSRequired string `json:"httpsRequired" xml:"httpsRequired,attr"`
Synced string `json:"synced" xml:"synced,attr"`
PublicAddressMatches string `json:"publicAddressMatches" xml:"publicAddressMatches,attr"`
Presence string `json:"presence" xml:"presence,attr"`
Connection []struct {
Protocol string `json:"protocol" xml:"protocol,attr"`
Address string `json:"address" xml:"address,attr"`
Port string `json:"port" xml:"port,attr"`
URI string `json:"uri" xml:"uri,attr"`
Local int `json:"local" xml:"local,attr"`
} `json:"connection" xml:"Connection"`
Name string `json:"name" xml:"name,attr"`
Product string `json:"product" xml:"product,attr"`
ProductVersion string `json:"productVersion" xml:"productVersion,attr"`
Platform string `json:"platform" xml:"platform,attr"`
PlatformVersion string `json:"platformVersion" xml:"platformVersion,attr"`
Device string `json:"device" xml:"device,attr"`
ClientIdentifier string `json:"clientIdentifier" xml:"clientIdentifier,attr"`
CreatedAt string `json:"createdAt" xml:"createdAt,attr"`
LastSeenAt string `json:"lastSeenAt" xml:"lastSeenAt,attr"`
Provides string `json:"provides" xml:"provides,attr"`
Owned string `json:"owned" xml:"owned,attr"`
AccessToken string `json:"accessToken" xml:"accessToken,attr"`
HTTPSRequired int `json:"httpsRequired" xml:"httpsRequired,attr"`
Synced string `json:"synced" xml:"synced,attr"`
Relay int `json:"relay" xml:"relay,attr"`
PublicAddressMatches string `json:"publicAddressMatches" xml:"publicAddressMatches,attr"`
PublicAddress string `json:"publicAddress" xml:"publicAddress,attr"`
Presence string `json:"presence" xml:"presence,attr"`
Connection []Connection `json:"connection" xml:"Connection"`
}

// Connection lists options to connect to a device
type Connection struct {
Protocol string `json:"protocol" xml:"protocol,attr"`
Address string `json:"address" xml:"address,attr"`
Port string `json:"port" xml:"port,attr"`
URI string `json:"uri" xml:"uri,attr"`
Local int `json:"local" xml:"local,attr"`
}

// BaseAPIResponse info about the Plex Media Server
Expand Down
40 changes: 30 additions & 10 deletions plex.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,39 @@ func defaultHeaders() headers {
// New creates a new plex instance that is required to
// to make requests to your Plex Media Server
func New(baseURL, token string) (*Plex, error) {
if baseURL == "" {
return &Plex{}, errors.New("url is required")
var plexConn Plex

// allow empty url so caller can use GetServers() to set the server url later

if baseURL == "" && token == "" {
return &plexConn, errors.New("url or a token is required")
}

_, err := url.ParseRequestURI(baseURL)
plexConn.HTTPClient = http.Client{
Timeout: 3 * time.Second,
}

return &Plex{
URL: baseURL,
Token: token,
HTTPClient: http.Client{
Timeout: 3 * time.Second,
},
}, err
// has url and token
if baseURL != "" && token != "" {
_, err := url.ParseRequestURI(baseURL)

plexConn.URL = baseURL
plexConn.Token = token

return &plexConn, err
}

// just has token
if baseURL == "" && token != "" {
plexConn.Token = token

return &plexConn, nil
}

// just url
plexConn.URL = baseURL

return &plexConn, nil
}

// SignIn creates a plex instance using a user name and password instead of an auth
Expand Down

0 comments on commit 1a072f3

Please sign in to comment.