causefx / Organizr

HTPC/Homelab Services Organizer - Written in PHP
GNU General Public License v3.0
5.18k stars 291 forks source link

Media Invites Emby #1124

Closed noah-witt closed 5 years ago

noah-witt commented 5 years ago
Organizr Version: V 2.0.70
Branch: v2-develop
WebSever: nginx/1.15.5, PHP 7.2.15
Operating System: Ubuntu

Problem Description:

Media Invites does not show anything when attempting to configure Emby. Ideally, this extension would also interact with ombi by triggering user import from Emby and then adding the email address to ombi because ombi can't import email addresses from Emby.

causefx commented 5 years ago

I havent figured out the api endpoints for sending media invites. for the user importer I will look into that.

noah-witt commented 5 years ago

Inside Postman I figured out how to create a user. Emby provides no direct API endpoint for user creation although it is straightforward to create a user. It will take two requests and at least one additional request to create the user.


Post request server/emby/Users/authenticatebyname with an X-Emby-Authorization header with the value of :

MediaBrowser Client="Emby Mobile", Device="Firefox", DeviceId="TW96aWxssS81LgAggFdpbmRvd3MgTlQgMTAuMDsgV2luNjxx7IHf2NCkgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzcyLjAuMzYyNi4xMTkgU2FmYXJpLzUzNy4zNnwxNTUxNTczMTAyNDI4", Version="4.0.2.0"

This post request has a json body of {"Username":"USERNAME","Pw":"USERPASSWORD"}. With USERNAME and USERPASSWORD replaced with an administrative users username and password. This will return json with info about the loged in user. It will include one paramater we care about: "AccessToken": "Aa#1".


Unfortunately the AccessToken expires so Organizer will have to re-login automatically.


I would recomend organizer randomly generating a username and password because this design would require clear text storage of the user password in the orginizer database. Organizer could then prompt the user to create a user in emby with the random name and password with administrator permissions or Organizer could login as the administrator once, not store their credentials and create its random user automatically.


Now a second post request must be made to create the user. The new username is passed as a parameter to the endpoint: /emby/Users/New?name=NEWUSERNAME. This post request has no body but needs one header. It uses X-Emby-Authorization with the value of

MediaBrowser Client="Emby Mobile", Device="Firefox", DeviceId="MUST BE SAME AS BEFORE", Version="4.0.2.0", Token="TOKEN RETURNED FROM FIRST CALL".

This then returns JSON with the new user id with the key value "Id" this is needed to modify the user settings. Which will be a separate third request that I will investigate later.

noah-witt commented 5 years ago

To update the Policy attached to the created user requires another post request to /emby/Users/USERID/Policy. with USERID replaced with the id that we got back in our second request. so for example /emby/Users/33207a921a13427497fdc27054fa8058/Policy In order to make this request we need to attach a X-Emby-Authorization header just like in the second request. The body of the request is just the JSON values you wish to update.

{ "IsAdministrator": true, "EnableMediaPlayback": false }

Here is an example of a full update request body:

{ "IsAdministrator": false, "IsHidden": true, "IsDisabled": false, "BlockedTags": [], "EnableUserPreferenceAccess": true, "AccessSchedules": [], "BlockUnratedItems": [], "EnableRemoteControlOfOtherUsers": false, "EnableSharedDeviceControl": true, "EnableRemoteAccess": true, "EnableLiveTvManagement": true, "EnableLiveTvAccess": true, "EnableMediaPlayback": true, "EnableAudioPlaybackTranscoding": true, "EnableVideoPlaybackTranscoding": true, "EnablePlaybackRemuxing": true, "EnableContentDeletion": false, "EnableContentDeletionFromFolders": [], "EnableContentDownloading": true, "EnableSubtitleDownloading": false, "EnableSubtitleManagement": false, "EnableSyncTranscoding": true, "EnableMediaConversion": true, "EnabledDevices": [], "EnableAllDevices": true, "EnabledChannels": [], "EnableAllChannels": true, "EnabledFolders": [ "4e985121ed7f570b595f04d82adc02f3", "f137a2cd21bbc1b99aa5d0f6bf02d805", "7e64e219657a9516ec72490da03efccb", "79a2716d3c50e769a8af2e4184e4dccf", "767bfde4f11c93ef34b803451a697a4e" ], "EnableAllFolders": false, "InvalidLoginAttemptCount": 0, "EnablePublicSharing": true, "RemoteClientBitrateLimit": 0, "ExcludedSubFolders": [], "DisablePremiumFeatures": false }

In postman I have tested this and you do not need to send the values that your not changing.


The final step is setting the user password.

this is done by posting /emby/Users/USERID/Password with the same X-Emby-Authorization header and a body of:{ "CurrentPw": "", "NewPw": "abcd" }

causefx commented 5 years ago

is that only for local users? what about emby accts? is it the same? Sorry, i dont have nor use Emby.

noah-witt commented 5 years ago

The process I have been dealing with is only local accounts. You can attach an emby connect to a local account once it has been created by firing off a request in the same way to /emby/Users/33207a921a13427497fdc27054fa8058/Connect/Link with a body of type form data with

ConnectUsername : USEREMAIL/CONNECTUSERNAME

It then emails the user. If you implement emby connect support, it needs to be optional. If the email you pass doesn't have any emby.media account setup, then it responds with a 500 error, but if it works, it responds 200.

it is important to set a password for connect users because if it is not set, even if connect is enabled, anyone can login with a blank password, and if the user is not set to hidden the server returns a JSON to unauthenticated hosts with a list of usernames.

I think all of these requests except this last Emby Connect one will work with jellyfin too.


I don't know how easy it would be to expand your UI for invite settings but I would support allowing the configuration for most settings. I don't know php well, so I do not know how easy all of that JSON handling is.


emby documentation on logins. Here is an archive in case they change their wiki

It appears you might be able to pass an MD5 instead of the actual password but I do not think an MD5 is much better than plain text when we are dealing generally terrible user passwords, and It would be confusing when looking at server logs if organizer was logging in as an actual user.


If the user changes their password through organizer it would be nice if their emby password changed as well. To do this, a post request must be generated to /emby/Users/USERID/Password with the same header and a form type body with

resetPassword | true

This will blank the user password allowing the user to login with no password! Organizer could then immediately go through the password set procedure with the new user password as outlined above.

Because storing userIDs would be wasteful and it wouldnt allow Orginizier to update users who were not created in Orginizer I would recomend pulling the userID from /emby/users with the same header using a GET request. This will return JSON with an array of the users each with an id element like this:

[ { "Name": "abcdefg", "ServerId": "999999fff99999999", "ConnectUserName": "bob@example.com", "ConnectUserId": "1234", "ConnectLinkType": "LinkedUser", "Id": "ffffffffffffffff", "HasPassword": true, "HasConfiguredPassword": true, ..... "Configuration": { ... }, "Policy": { ... } }, { "Name": "bill", "ServerId": "999999fff99999999" "ConnectUserName": "bill@example.com", "ConnectUserId": "1235", "ConnectLinkType": "LinkedUser", "Id": "ffffffffffffffff1", "HasPassword": true, "HasConfiguredPassword": true, ..... "Configuration": { ... }, "Policy": { ... }, { "Name": "joe", "ServerId": "999999fff99999999" "Id": "ffffffffffffffff2", "HasPassword": true, "HasConfiguredPassword": true, ..... "Configuration": { ... }, "Policy": { ... } } } ]

So then it could find the element with the correct username and get the ID from there.

noah-witt commented 5 years ago

It appears that although the swagger API states that these calls are only accessible when logged in as a user it appears that all of these requests work with an API key specified instead as a parameter like /emby/Users/New?name=user4&api_key=938f66bb17094d1164c1dc98d9fd7311. This seems to work for all of the requests I outlined. This means that the first step of getting a toke and device ID is not needed nor are the X-Emby-Authorization headers.