Giter VIP home page Giter VIP logo

go-plex-client's Introduction

Plex.tv and Plex Media Server client written in Go

godoc

go get -u github.com/jrudio/go-plex-client

Cli

You can tinker with this library using the command-line over here

Usage

plexConnection, err := plex.New("http://192.168.1.2:32400", "myPlexToken")

// Test your connection to your Plex server
result, err := plexConnection.Test()

// Search for media in your plex server
results, err := plexConnection.Search("The Walking Dead")

// Webhook handler to easily handle events on your server
	wh := plex.NewWebhook()

	wh.OnPlay(func(w plex.Webhook) {
		fmt.Printf("%s is playing\n", w.Metadata.Title)
	})

	wh.OnPause(func(w plex.Webhook) {
		fmt.Printf("%s is paused\n", w.Metadata.Title)
	})

	wh.OnResume(func(w plex.Webhook) {
		fmt.Printf("%s has resumed\n", w.Metadata.Title)
	})

	wh.OnStop(func(w plex.Webhook) {
		fmt.Printf("%s has stopped\n", w.Metadata.Title)
	})

	http.HandleFunc("/", wh.Handler)

	http.ListenAndServe("192.168.1.14:8080", nil)

// connect to your server via websockets to listen for events

ctrlC := make(chan os.Signal, 1)
onError := func(err error) {
	fmt.Println(err)
}

events := plex.NewNotificationEvents()
events.OnPlaying(func(n NotificationContainer) {
	mediaID := n.PlaySessionStateNotification[0].RatingKey
	sessionID := n.PlaySessionStateNotification[0].SessionKey
	var title

	sessions, err := plexConnection.GetSessions()

	if err != nil {
		fmt.Printf("failed to fetch sessions on plex server: %v\n", err)
		return
	}

	for _, session := range sessions.MediaContainer.Video {
		if sessionID != session.SessionKey {
			continue
		}

		userID = session.User.ID
		username = session.User.Title

		break
	}

	metadata, err := plexConnection.GetMetadata(mediaID)

	if err != nil {
		fmt.Printf("failed to get metadata for key %s: %v\n", mediaID, err)
	} else {
		title = metadata.MediaContainer.Metadata[0].Title
	}

	fmt.Printf("user (id: %s) has started playing %s (id: %s) %s\n", username, userID, title, mediaID)
})

plexConnection.SubscribeToNotifications(events, ctrlC, onError)

// ... and more! Please checkout plex.go for more methods

go-plex-client's People

Contributors

adampetrovic avatar brenekh avatar denislituev avatar djcrock avatar dparrish avatar halkeye avatar jille avatar johnhamelink avatar jrudio avatar jyggen avatar m1kep avatar mooseburgr avatar notbaab avatar tmm1 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

go-plex-client's Issues

convert message to json failed: json: cannot unmarshal string into Go struct field TranscodeSession.progress of type float64

I'm getting this error when using the websockets.

convert message to json failed: json: cannot unmarshal string into Go struct field TranscodeSession.progress of type float64

This is what it chokes on:

{"NotificationContainer":{"type":"transcodeSession.end","size":1,"TranscodeSession":[{"key":"/transcode/sessions/f546195c58e8a52a-com-plexapp-android","throttled":true,"complete":false,"progress":"27.5","speed":"0","duration":2665119,"remaining":13519,"context":"streaming","sourceVideoCodec":"mpeg4","sourceAudioCodec":"mp3","videoDecision":"transcode","audioDecision":"copy","protocol":"hls","container":"mpegts","videoCodec":"h264","audioCodec":"mp3","audioChannels":2,"transcodeHwRequested":false,"timeStamp":"1569248610.8982544"}]}}

It doesn't seem to like Plex encoding integers as strings. (https://play.golang.org/p/NsXo0qlUMQw confirms that Go refuses to decode the string into an integer)

Fix error messages

  • Error messages that are returned should not start capitalized

Screenshot 2021-11-06 121437

  • Change some error messages to be more descriptive

getLibraries returns nothing

plex.GetLibraries() is just returning a unpopulated struct.

Ex.

func main() {
	Plex, err := plex.New("plex_url", "<token>")
	if err != nil {
		log.Println(err)
	}

	libraries, err := Plex.GetLibraries()

	fmt.Println("Libraries")
	spew.Dump(libraries)
}

Outputs

Libraries
(plex.LibrarySections) {
 ElementType: (string) "",
 AllowSync: (string) "",
 Identifier: (string) "",
 MediaTagPrefix: (string) "",
 MediaTagVersion: (string) "",
 Title1: (string) "",
 Children: ([]struct { Children []struct { ElementType string "json:\"_elementType\""; ID int "json:\"id\""; Path string "json:\"path\"" } "json:\"_children\""; ElementType string "json:\"_elementType\""; Agent string "json:\"agent\""; AllowSync string "json:\"allowSync\""; Art string "json:\"art\""; Composite string "json:\"composite\""; CreatedAt string "json:\"createdAt\""; Filters string "json:\"filters\""; Key string "json:\"key\""; Language string "json:\"language\""; Refreshing string "json:\"refreshing\""; Scanner string "json:\"scanner\""; Thumb string "json:\"thumb\""; Title string "json:\"title\""; Type string "json:\"type\""; UpdatedAt string "json:\"updatedAt\""; UUID string "json:\"uuid\"" }) <nil>
}

Note, using github.com/davecgh/go-spew/spew to pretty print the struct.

Implement GetFromLibrary

I couldn't find an easy way to retrieve all movies or shows of a library, is there a way of doing that?

Ability to download with folder hierarchy

This would be a nice addition to the download feature if we could (re)create the folder hierarchy using the Grandparent and Parent fields. Downloading media.ext would be put in

Grandparent/Parent/media.ext.

This would be quite useful when downloading TV Shows for example

"Game of Thrones"/"Season 01"/"01 Winter is Coming.mkv" or even for Music
"Oliver Koletzki"/"The Arc of Tension"/"1-01 A Tribe Called Kotori.flac".

Is field "userRating" available ?

I would have expected the "userRating" field (= the note you can give through PLEXUI), but i don't see it anywhere (neither at runtime nor in the code).
Is it somehow missing ?
Or is there another way to retrieve this information ?

For ex. I always have :

    "ratingCount": 0,
    "rating": 0,
    "ratingKey": "13",
    "sessionKey": "",

Clarification on usage of `SignIn`

SignIn does not set the Plex.URL field. Because of this if I sign in with a username and password I can't use any of the query methods (e.g. GetSessions()) because the HTTP request goes to fmt.Sprintf("%s/status/sessions", p.URL) (code). So I get an error: Get "/status/sessions": unsupported protocol scheme ""

To overcome this I'm calling SignIn(), then using Plex.Token in a call to New(). New() sets the URL.

Is my use of SignIn the intended way?

Add functions to wrap GET, DELETE /api/invites/requested APIs

I plan on pushing up an implementation of these unless there are any concerns. Feel free to assign me this to me if that's cool.

Planning on following "friends" namings but open to ideas & opinions:

  • GetInvitedFriends: GET /api/invites/requested
  • RemoveInvitedFriend: DELETE /api/invites/requested/{ID}

fail to build cli

I'm currently facing issue to build the cli tool. As i'm a go beginner i prefer to ask :)
i'm trying to build it with go1.12

(venv) henrylf@othallaptop:~/.gvm/pkgsets/go1.12/global/src/github.com/jrudio/go-plex-client/cmd (master) # go build
# github.com/jrudio/go-plex-client/cmd
./store.go:91:16: assignment mismatch: 2 variables but item.Value returns 1 values
./store.go:91:29: not enough arguments in call to item.Value
	have ()
	want (func([]byte) error)
./store.go:107:17: too many arguments in call to txn.Set
	have ([]byte, []byte, number)
	want ([]byte, []byte)
./store.go:121:19: assignment mismatch: 2 variables but item.Value returns 1 values
./store.go:121:32: not enough arguments in call to item.Value
	have ()
	want (func([]byte) error)
./store.go:143:17: too many arguments in call to txn.Set
	have ([]byte, []byte, number)
	want ([]byte, []byte)
./store.go:165:25: assignment mismatch: 2 variables but item.Value returns 1 values
./store.go:165:38: not enough arguments in call to item.Value
	have ()
	want (func([]byte) error)
./store.go:192:17: too many arguments in call to txn.Set
	have ([]byte, []byte, number)
	want ([]byte, []byte)

convert message to json failed: json: cannot unmarshal number into Go struct field Setting.default of type string

I got this error from the websockets stuff:

convert message to json failed: json: cannot unmarshal number into Go struct field Setting.default of type string
 {"NotificationContainer":{"type":"preference","size":1,"Setting":[{"id":"LastAutomaticMappedPort","label":"","summary":"","type":"int","default":0,"value":25018,"hidden":true,"advanced":false,"group":""}]}}

Looks like default can be either a string or int, so we have to deal with that :/ Presumably value has the same properties.

Unable to parse JSON response from sessions endpoint

The following code:

plexClient, err := plex.New("host", "token")
sessions, err := plexClient.GetSessions()

Yields this error:

json: cannot unmarshal string into Go struct field MetadataV1.viewCount of type int

This is a json response from a plain curl command against the sessions endpoint:

curl --insecure -X GET 'https://host:32400/status/sessions?X-Plex-Token=token' -H "Accept: application/json" | jq .

{
  "MediaContainer": {
    "size": 1,
    "Metadata": [
      {
        "addedAt": "1559614258",
        "art": "/library/metadata/885/art/1559614279",
        "contentRating": "TV-MA",
        "duration": "4346342",
        "grandparentArt": "/library/metadata/885/art/1559614279",
        "grandparentKey": "/library/metadata/885",
        "grandparentRatingKey": "885",
        "grandparentThumb": "/library/metadata/885/thumb/1559614279",
        "grandparentTitle": "Chernobyl",
        "guid": "com.plexapp.agents.thetvdb://360893/1/5?lang=en",
        "index": "5",
        "key": "/library/metadata/962",
        "lastViewedAt": "1559656628",
        "librarySectionID": "8",
        "librarySectionKey": "/library/sections/8",
        "librarySectionTitle": "TV Shows",
        "originallyAvailableAt": "2019-06-03",
        "parentIndex": "1",
        "parentKey": "/library/metadata/886",
        "parentRatingKey": "886",
        "parentThumb": "/library/metadata/886/thumb/1559614279",
        "parentTitle": "Season 1",
        "ratingKey": "962",
        "sessionKey": "1",
        "summary": "Valery Legasov, Boris Shcherbina and Ulana Khomyuk risk their lives and reputations to expose the truth about Chernobyl.",
        "thumb": "/library/metadata/962/thumb/1559614279",
        "title": "Vichnaya Pamyat",
        "type": "episode",
        "updatedAt": "1559614279",
        "viewCount": "1",
        "viewOffset": "2547000",
        "year": "2019",
        "Media": [
          {
            "id": "2088",
            "videoProfile": "high",
            "audioChannels": "6",
            "audioCodec": "aac",
            "bitrate": "3752",
            "container": "mpegts",
            "duration": "4346342",
            "height": "720",
            "protocol": "hls",
            "videoCodec": "h264",
            "videoFrameRate": "24p",
            "videoResolution": "720p",
            "width": "1280",
            "selected": true,
            "Part": [
              {
                "id": "2091",
                "videoProfile": "high",
                "bitrate": "3752",
                "container": "mpegts",
                "duration": "4346342",
                "height": "720",
                "protocol": "hls",
                "width": "1280",
                "decision": "transcode",
                "selected": true,
                "Stream": [
                  {
                    "bitrate": "3416",
                    "chromaLocation": "left",
                    "codec": "h264",
                    "colorPrimaries": "bt709",
                    "colorTrc": "bt709",
                    "default": "1",
                    "displayTitle": "1080p (H.264)",
                    "frameRate": "23.975999999999999",
                    "height": "720",
                    "id": "6074",
                    "streamType": "1",
                    "width": "1280",
                    "decision": "transcode",
                    "location": "segments-av"
                  },
                  {
                    "bitrate": "336",
                    "bitrateMode": "cbr",
                    "channels": "6",
                    "codec": "aac",
                    "default": "1",
                    "displayTitle": "English (EAC3 5.1)",
                    "id": "6075",
                    "language": "English",
                    "languageCode": "eng",
                    "selected": "1",
                    "streamType": "2",
                    "decision": "transcode",
                    "location": "segments-av"
                  },
                  {
                    "codec": "webvtt",
                    "displayTitle": "English (SRT)",
                    "format": "webvtt",
                    "id": "6076",
                    "language": "English",
                    "languageCode": "eng",
                    "selected": "1",
                    "streamType": "3",
                    "decision": "transcode",
                    "location": "segments-subs"
                  }
                ]
              }
            ]
          }
        ],
        "Director": [
          {
            "filter": "director=5738",
            "id": "5738",
            "tag": "Johan Renck"
          }
        ],
        "Writer": [
          {
            "filter": "writer=5737",
            "id": "5737",
            "tag": "Craig Mazin"
          }
        ],
        "User": {
          "id": "1",
          "thumb": "https://plex.tv/users/a9720769d16e7383/avatar?c=1557803913",
          "title": "eschizoid"
        },
        "Player": {
          "address": "::ffff:67.167.5.61",
          "device": "Roku Ultra",
          "machineIdentifier": "8c62469cd6201e362272f1e61c5ad365",
          "model": "4640X",
          "platform": "Roku",
          "platformVersion": "9.x",
          "product": "Plex for Roku",
          "profile": "Roku-7.x",
          "remotePublicAddress": "::ffff:67.167.5.61",
          "state": "paused",
          "title": "Roku Ultra - YP00AL737511",
          "vendor": "Roku",
          "version": "6.3.9.5772-1dfcf58b1-Plex",
          "local": false,
          "relayed": false,
          "secure": true,
          "userID": 1
        },
        "Session": {
          "id": "8c62469cd6201e362272f1e61c5ad365",
          "bandwidth": 3941,
          "location": "wan"
        },
        "TranscodeSession": {
          "key": "/transcode/sessions/80b23666-850a-4cd8-8114-dadd71eb2f7f-44",
          "throttled": true,
          "complete": false,
          "progress": "94.199996948242188",
          "speed": "0",
          "duration": 4346342,
          "context": "streaming",
          "sourceVideoCodec": "h264",
          "sourceAudioCodec": "eac3",
          "videoDecision": "transcode",
          "audioDecision": "transcode",
          "subtitleDecision": "transcode",
          "protocol": "hls",
          "container": "mpegts",
          "videoCodec": "h264",
          "audioCodec": "aac",
          "audioChannels": 6,
          "transcodeHwRequested": false,
          "timeStamp": "1559654408.9002578",
          "maxOffsetAvailable": "4093.0096779999999",
          "minOffsetAvailable": "1"
        }
      }
    ]
  }
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.