luigi311 / JellyPlex-Watched

Sync watched between jellyfin and plex locally
GNU General Public License v3.0
394 stars 22 forks source link

[Feature Request] Sync playback offset #47

Closed JChris246 closed 3 months ago

JChris246 commented 1 year ago

It would be nice to also, in addition to syncing whether an item has been watched, have the playback state of an item synced. For example, if I start a movie in plex and complete an hour (1:00:00) out of the hour and forty five mins (1:45:00) total duration, that state could be sync to jellyfin and I can resume the movie there.

It may not be possible to mutate the viewOffset plex uses to keep track of watch position for an item, due to the closed source nature of plex. A workaround could be to use a PlexClient to seekTo the position. Similarly for jellyfin, using the Playstate endpoints could be helpful.

luigi311 commented 1 year ago

This would be a great feature. We would need to extend the dictionaries that hold the watch status to include the watch time and we would need to change the search functions from only unwatched=False so it would have to do a full library search since i dont think partial watches remove the unwatched tag. We can definitely take a look at this after we close up the other two tickets and create a release since im expecting it this to be pretty stable at that point.

luigi311 commented 1 year ago

I started working on this and have the gathering mostly completed so it will fetch watched and in progress movies/shows for plex and jellyfin without any issues and storing it in a way that i think makes sense. I still need to adjust the comparison functions so it will keep the most watched entry instead of keeping them both. Also need to research on how to actually change it on the accounts, for jellyfin i found this ticket but it says it doesnt work https://github.com/jellyfin/jellyfin/discussions/7259 for plex we might need to go with your option of starting a client and seeking to it.

JChris246 commented 1 year ago

I also stumbled on the that issue while doing some research on it, it seems hitting the /Users/{userId}/PlayingItems/{itemId}?positionTicks={positionTicks} works fine to mark progress on an item for a given user (I tested it). It is a DELETE endpoint. Reference: Jellyfin API - OnPlaybackStopped

As for plex, I found that either the timeline or progress endpoints should work, however I'm not sure that they are user specific and would obviously require that you hit them directly instead of by the plex api package you have😅🤷‍♂️.

Timeline endpoint:

GET - http://{plex-host}/:/timeline?ratingKey={ratingKey}&key={key}&state=stopped&time={time-in-milliseconds}&duration={duration-in-milliseconds}&X-Plex-Client-Identifier={plex-client-id}

where:

Progress endpoint:

PUT - http://{plex-host}/:/progress?key={key}&identifier=com.plexapp.plugins.library&time={time-in-milliseconds}&state=stopped&X-Plex-Token={plex-token}

where:

Plex progress endpoint

luigi311 commented 1 year ago

Great finds!

I am playing around with the jellyfin api using your query and it doesnt seem to be doing anything on my end.

Ive never used a delete query so i went ahead and created what i would assume would be a correct delete call and added in a if response status of 204 return None since there is nothing to return. It seems to execute successfully and does seem to be doing something because i did a post /Users/{userId}/PlayingItems/{itemId}/Progress?positionTicks={positionTicks} and when i looked in the dashboard there was a session playing with the user in question with the correct media file and at the correct timestamp. I then executing the delete /Users/{userId}/PlayingItems/{itemId}?positionTicks={positionTicks} it did in fact terminate the session but when i check the users resume media it still has the old progress on it and another media file didnt have progress on it.

Maybe there is something im missing, can you take a look at my test branch https://github.com/luigi311/JellyPlex-Watched/tree/partial_watch_test you should be able to just start something on plex and set it to say 15 mins and then run that branch and it should attempt to sync the 15 min playtime to jellyfin.

As for plex since it does look like there is some APIs we can use i wonder if it would be better to just submit a feature request to the plex api package i am using to add in a method such as SeekTo(timestamp) or something similar to the markWatched() function I am using currently and inform them of the api endpoints they can use that way we can keep it consistent and use the python package api for everything plex related and not bring in http requests there too.

JChris246 commented 1 year ago

My bad, it seems I forgot that I had set an extra header value in my http client from a previous request when making the /Users/{userId}/PlayingItems/{itemId}?positionTicks={positionTicks} 😅 and that's probably why it wasn't working on your side.

Basically I had the X-Emby-Authorization header set similar to your code with an additional param; Token=. I am actually not sure how to obtain the value I used there programmatically, as I got it manually from looking at the network requests made when running jellyfin in the browser.

Yes, should definitely submit a feature request to the plex api package repo for consistency👍

luigi311 commented 1 year ago

Looks like your right. I pulled the token that is generated from the web client for jellyfin when im logged into the account and that makes the query work instantly. Going to need to figure out how to get that token. Ill see if i can find anything in the other jellyfin clients.

luigi311 commented 1 year ago

Looks like the plex api already had what we wanted lol. I went ahead and implemented it now in the partial_watch branch and it seems to be syncing partial progress for episodes and movies now.

Still need to see what we can do on the jellyfin side since it looks like that Token is a user access token which makes sense and i cant seem to find a way to generate that token for users without having to use the authenticate endpoints that require a username/user_id and a password unlike in the plex api where you can get a token for other users via the api_key/server host token. I asked the question in the jellyfin discord if there is a way to generate that token for other users via the api_key or if there is another way to update the watch status via a api_key only.

luigi311 commented 1 year ago

Can't seem to find anything for being able to do this on the jellyfin side without using a users token and there doesn't seem to be a way to grab a users token via an api key. I think I'm going to put this on hold for now and release what I have so far on the Plex side so people can at least use that.

luigi311 commented 1 year ago

I opened up a bug ticket on the jellyfin repo about not being able to update the position tick via an api_key so we will see if anything happens with that. Might need to just do a release with just the plex partial sync implemented for now until something comes up for this for jellyfin and/or emby. I added a feature list that shows what features are implemented in which servers

https://github.com/jellyfin/jellyfin/issues/9614

Inrego commented 1 year ago

If necessary, I could probably make a plugin for Jellyfin that will allow it. With that said, my knowledge about Jellyfin plugins is quite limited, so I might be wrong. I'm basing this off my experience of making a plugin for Emby 5 years ago. But if it's still somewhat similar, I guess it should be doable.

luigi311 commented 1 year ago

I would ideally like to not require an external plugin for this to work but im not sure how long we would have to wait for the jellyfin ticket to be answered so we might not have a choice.

If we do go down the plugin method we would hopefully be able to do a check to see if the plugin exists so it will only enable the partial sync feature for jellyfin servers and if not it will skip that entire section so people can run it without worrying about that.

Inrego commented 1 year ago

I totally understand that.

And yes, that would of course be doable.

arabcoders commented 8 months ago

@luigi311 my PR to jellyfin to support updating userData via apikey has been merged https://github.com/jellyfin/jellyfin/pull/10573

It will be available in 10.9.x version you should be able to target it i have a code in watchstate ready to go if you want to take look

luigi311 commented 8 months ago

Thank you so much for doing that, its so good to know that it wasnt something we were missing and instead was a jellyfin api limitation. Ill go ahead and see if i can get that implemented that way its ready for when 10.9.0+ releases

arabcoders commented 8 months ago

No worries, be aware that jellyfin PlaybackPositionTicks Uses higher precision value then plex milliseconds, so from jellyfin to plex it's (jellyfin.PlaybackPositionTicks/ 10_000) from plex to jellyfin (plex.offset * 10_000) roughly

luigi311 commented 3 months ago

This has finally been completely implemented for both jellyfin and emby! Thank you @arabcoders for bringing in the API for it in jellyfin