Read current X-Plex-Token in an agent & ensure that a HTTP.Request gets executed exactly once

Hi
As you can see from the title, I really got 2 questions - hope it is ok to put them into the same thread.

I am working on a simple small (secondary) agent that should mark a plex item as read if a external xml file in the media folder states so and if the Plex server does not already have marked it as watched via viewCount > 0. I managed to get this to work with the command:

HTTP.Request("http://localhost:32400/:/scrobble?identifier=com.plexapp.plugins.library&key=12345&X-Plex-Token=xxxxxxxxxxxxxxx

where the key is coming from media.id and the X-Plex-Token was hardcoded for the time being.
I believe that authorization is already covered when the agent gets called (e.g. via a manual refresh of an item in Plex Web) and am looking for a way to extract the active X-Plex-Token, so that I can concatenate it into the URL of the HTTP.Request and avoid hard coding it.
I have seen proposals that the answer to a request to URL http://localhost:32400/myplex/account would contain the token, but all such request fail for me.

My first question therefore is:
Does anyone know how to get a hold of the current Auth Token from the Update funtion of a Agent?

My other challenge is that the above mentioned HTTP.Request to set the watched status actually gets called 3 times, as can be seen in the agent logs as well as in the main Plex Media Server.log. While this kind of achieves the task to set an item as ‘watched’, I would prefer to avoid getting the play count set to 3 in one go (thin increment of 3 can also be confirmed by looking at the XML of the respective item via Info on the Web Client).
The code I am currently using assigns the response of the HTTP.Request to a variable and then Logs that variable (only to show that it is empty). The weird thing is, that if I comment out the logging of the response, the request does NOT get executed at all. This behavior was reproducible several times (e.g. Logging directly or not assigning the response to a variable). Each a time a logging of the response occurred, the HTTP.Request was tripled.
Another observation I made, which may look even weirder but may as well help to get a solution is the following:
If I “use” the response of the HTTP.Request in a second function other than Log(), for example by calling XML.ElementFromString(response), the original HTTP.Request also gets executed, this time however double instead of triple. This may have to do with the fact that the second function fails (as could be expected) due to the response being empty.

I admit to being quite a beginner with all this, so if there is something obvious to try I am more that happy to hear it. So far I have tried things like:

  • add a sleep time of 0.1 to 1 sec
  • include the auth token in the main URL or pass it to the header dict
  • try a Precache of the main URL
  • try to clear the cache immediately after the call

All of the attempts above let to the same behavior: If I Log the (blank) reply of the request, it gets executed 3 times, if I don’t log it, the HTTP.Request does not get executed at all and the watched status does not change.
Some of ‘tweaks’ attempts above probably don’t make any sense to the educated reader, but I was just in trial and error mode and was hoping to stumble over a working solution like that.
Of course it is needles to say that calling that same function from within the Web Client works flawlessly and logs as well as resulting viewCount show that the request got sent exactly once.

My second question therefore is:
Does anyone know how to tweak the HTTP.Request call in the agent to ensure it gets called exactly once?

Thanks in advance for any help or hints (ideally with a small code example)

@dettwild said:
HTTP.Request("http://localhost:32400/:/scrobble?identifier=com.plexapp.plugins.library&key=12345&X-Plex-Token=xxxxxxxxxxxxxxx
[…]
All of the attempts above let to the same behavior: If I Log the (blank) reply of the request, it gets executed 3 times, if I don’t log it, the HTTP.Request does not get executed at all and the watched status does not change.

HTTP.Request without .headers or .content doesn’t really do a full GET (or POST) request. Try HTTP.Request("http://localhost:32400/:/scrobble?identifier=com.plexapp.plugins.library&key=12345&X-Plex-Token=xxxxxxxxxxxxxxx").content

Maybe that will fix the multiple executions already. Otherwise, to work around the issue of the counter being upped with 3, you could try to cache the request for a short period. If the request is executed multiple times, at least the 2nd and later requests will not up the counter:
HTTP.Request("http://localhost:32400/:/scrobble?identifier=com.plexapp.plugins.library&key=12345&X-Plex-Token=xxxxxxxxxxxxxxx", cacheTime=30).content
(cacheTime is in seconds).

@sander1
Damn you’re good! :wink:
I can confirm that both versions (either asking for .content or .headers of the HTTP.Request) do the trick to avoid the triple executions. This also works without logging of the response of the HTTP.Request and therefore also fixes the issue that the request was sometimes not executed at all. Btw., I did not even try to play with the cacheTime, as things worked just fine with your other proposal.
I am therefore happily closing the second question I had. Thanks for your prompt and good reply!

Regarding the first question I believe to have found a solution myself, I am just not sure if that should be considered “clever” or labeled a “hack” :-p
Basically I am counting on the hierarchy of Plug-ins, i.e. the init.py file always being 5 levels below the main “Plex Media Server” folder where the Preferences.xml file resides, which contains the token in attribute “PlexOnlineToken”.

The following code worked for me and allows me to extract the token from said PMS preferences file:
thispath = inspect.getfile(inspect.currentframe()) for i in range(1, 6): thispath = os.path.dirname(thispath) myprefpath = os.path.join(thispath, "Preferences.xml") if os.path.exists(myprefpath): mypreftext = Core.storage.load(myprefpath) else: Log("Did NOT find Preferences file - please check logfile and hierarchy. Aborting!") return mytoken = XML.ElementFromString(mypreftext).xpath('//Preferences/@PlexOnlineToken')[0]
I am open to hearing pro and con arguments though and am still curious if there are other ways to get to the token that is needed to execute the HTTP.Request and have therefore purposely not marked my question as answered yet, but can do that anytime.

Thanks again for the quick feedback.

Thanks for the idea @dettwild. I’ve arrived at a less hacky approach not involving frame inspection:

def get_universal_plex_token(): pref_path = os.path.join(Core.app_support_path, "Preferences.xml") if os.path.exists(pref_path): try: global_prefs = Core.storage.load(pref_path) return XML.ElementFromString(global_prefs).xpath('//Preferences/@PlexOnlineToken')[0] except: Log.Warn("Couldn't determine Plex Token") else: Log("Did NOT find Preferences file - please check logfile and hierarchy. Aborting!")

Is this still the best version of doing this?

The preferences are now in a binary plist-file on macosx so it’s quite dificult for me to get stuff out of it. anyone know a whay to do it. preferably cross platform.