-
Notifications
You must be signed in to change notification settings - Fork 74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Plex webhook episode detection for non-episode videos #1126
base: master
Are you sure you want to change the base?
Changes from all commits
a0789b3
56ffc8b
ef7c281
9df5c1b
e57fb93
d122259
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,12 +15,15 @@ | |
using Shoko.Models.Plex.Collection; | ||
using Shoko.Models.Plex.Connections; | ||
using Shoko.Models.Plex.Libraries; | ||
using Shoko.Models.Plex.TVShow; | ||
using Shoko.Models.Server; | ||
using Shoko.Server.API.v2.Models.core; | ||
using Shoko.Server.Extensions; | ||
using Shoko.Server.Models; | ||
using Shoko.Server.Plex; | ||
using Shoko.Server.Plex.Collection; | ||
using Shoko.Server.Plex.Libraries; | ||
using Shoko.Server.Plex.TVShow; | ||
using Shoko.Server.Providers.TraktTV; | ||
using Shoko.Server.Repositories; | ||
using Shoko.Server.Scheduling; | ||
|
@@ -60,20 +63,29 @@ public ActionResult WebhookPost([FromForm] [ModelBinder(BinderType = typeof(Plex | |
} | ||
|
||
_logger.LogTrace($"{payload.Event}: {payload.Metadata?.Guid}"); | ||
|
||
var user = User; | ||
user ??= RepoFactory.JMMUser.GetAll().FirstOrDefault(u => payload.Account.Title.FindIn(u.GetPlexUsers())); | ||
if (user == null) | ||
{ | ||
_logger.LogInformation("Unable to determine who \"{AccountTitle}\" is in Shoko, make sure this is set under user settings in Desktop", payload.Account.Title); | ||
return Ok(); //At this point in time, we don't want to scrobble for unknown users | ||
} | ||
|
||
switch (payload.Event) | ||
{ | ||
case "media.scrobble": | ||
Scrobble(payload, User); | ||
Scrobble(payload, user); | ||
break; | ||
case "media.resume": | ||
case "media.play": | ||
TraktScrobble(payload, ScrobblePlayingStatus.Start); | ||
TraktScrobble(payload, ScrobblePlayingStatus.Start, user); | ||
break; | ||
case "media.pause": | ||
TraktScrobble(payload, ScrobblePlayingStatus.Pause); | ||
TraktScrobble(payload, ScrobblePlayingStatus.Pause, user); | ||
break; | ||
case "media.stop": | ||
TraktScrobble(payload, ScrobblePlayingStatus.Stop); | ||
TraktScrobble(payload, ScrobblePlayingStatus.Stop, user); | ||
break; | ||
} | ||
|
||
|
@@ -84,15 +96,15 @@ public ActionResult WebhookPost([FromForm] [ModelBinder(BinderType = typeof(Plex | |
#region Plex events | ||
|
||
[NonAction] | ||
private void TraktScrobble(PlexEvent evt, ScrobblePlayingStatus type) | ||
private void TraktScrobble(PlexEvent evt, ScrobblePlayingStatus type, JMMUser user) | ||
{ | ||
var metadata = evt.Metadata; | ||
var (episode, _) = GetEpisode(metadata); | ||
var (episode, _) = GetEpisodeForUser(metadata, user); | ||
|
||
if (episode == null) return; | ||
|
||
var vl = RepoFactory.VideoLocal.GetByAniDBEpisodeID(episode.AniDB_EpisodeID).FirstOrDefault(); | ||
if (vl == null || vl.Duration == 0) return; | ||
if (vl == null || vl.Duration == 0) return; | ||
|
||
var per = 100 * | ||
(metadata.ViewOffset / | ||
|
@@ -109,7 +121,7 @@ private void TraktScrobble(PlexEvent evt, ScrobblePlayingStatus type) | |
private void Scrobble(PlexEvent data, SVR_JMMUser user) | ||
{ | ||
var metadata = data.Metadata; | ||
var (episode, anime) = GetEpisode(metadata); | ||
var (episode, anime) = GetEpisodeForUser(metadata, user); | ||
if (episode == null) | ||
{ | ||
_logger.LogInformation( | ||
|
@@ -119,12 +131,6 @@ private void Scrobble(PlexEvent data, SVR_JMMUser user) | |
|
||
_logger.LogTrace("Got anime: {Anime}, ep: {EpisodeNumber}", anime, episode.AniDB_Episode.EpisodeNumber); | ||
|
||
user ??= RepoFactory.JMMUser.GetAll().FirstOrDefault(u => data.Account.Title.FindIn(u.GetPlexUsers())); | ||
if (user == null) | ||
{ | ||
_logger.LogInformation("Unable to determine who \"{AccountTitle}\" is in Shoko, make sure this is set under user settings in Desktop", data.Account.Title); | ||
return; //At this point in time, we don't want to scrobble for unknown users | ||
} | ||
|
||
episode.ToggleWatchedStatus(true, true, FromUnixTime(metadata.LastViewedAt), false, user.JMMUserID, | ||
true); | ||
|
@@ -135,7 +141,7 @@ private void Scrobble(PlexEvent data, SVR_JMMUser user) | |
#endregion | ||
|
||
[NonAction] | ||
private (SVR_AnimeEpisode, SVR_AnimeSeries) GetEpisode(PlexEvent.PlexMetadata metadata) | ||
private (SVR_AnimeEpisode, SVR_AnimeSeries) GetEpisodeForUser(PlexEvent.PlexMetadata metadata, JMMUser user) | ||
{ | ||
var guid = new Uri(metadata.Guid); | ||
if (guid.Scheme != "com.plexapp.agents.shoko" && guid.Scheme != "com.plexapp.agents.shokorelay") | ||
|
@@ -179,10 +185,28 @@ private void Scrobble(PlexEvent data, SVR_JMMUser user) | |
break; | ||
} | ||
|
||
var key = metadata.Key.Split("/").LastOrDefault(); // '/library/metadata/{key}' | ||
|
||
var plexEpisode = (SVR_Episode)GetPlexEpisodeData(key, user); | ||
var episode = plexEpisode?.AnimeEpisode; | ||
Comment on lines
+188
to
+191
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we add this with a guard against people not having a valid plex connection? The original design was that the webhook did not require user plex login, so it was entirely working off it's own data. Happy to add this in, but I think it should be at least guarded to allow for users to not have their own plex config. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you clarify the scenario you're describing? The user has plex webhooks enabled, but hasn't linked their plex account to Shoko? If that's the case, I suppose I could fall back original behavior of only matching episodes. |
||
|
||
if (episodeType != EpisodeType.Episode || | ||
metadata.Index == 0) //metadata.index = 0 when it's something else. | ||
{ | ||
return (null, anime); //right now no clean way to detect the episode. I could do by title. | ||
if (episode == null) | ||
{ | ||
_logger.LogInformation($"Failed to get anime episode from plex using key {metadata.Key}."); | ||
return (null, anime); | ||
} | ||
|
||
if (episode.EpisodeTypeEnum == episodeType && anime.GetAnimeEpisodes().Contains(episode)) | ||
{ | ||
return (episode, anime); | ||
} | ||
|
||
_logger.LogInformation($"Unable to work out the metadata for {metadata.Guid}."); | ||
|
||
return (null, anime); | ||
} | ||
|
||
|
||
|
@@ -204,7 +228,7 @@ private void Scrobble(PlexEvent data, SVR_JMMUser user) | |
|
||
//catch all | ||
_logger.LogInformation( | ||
$"Unable to work out the metadata for {metadata.Guid}, this might be a clash of multipl episodes linked, but no tvdb link."); | ||
$"Unable to work out the metadata for {metadata.Guid}, this might be a clash of multiple episodes linked, but no tvdb link."); | ||
return (null, anime); | ||
} | ||
|
||
|
@@ -335,6 +359,13 @@ private T CallPlexHelper<T>(Func<PlexHelper, T> act) | |
return act(PlexHelper.GetForUser(user)); | ||
} | ||
|
||
[NonAction] | ||
private Episode GetPlexEpisodeData(string ratingKey, JMMUser user) | ||
{ | ||
var helper = PlexHelper.GetForUser(user); | ||
return new SVR_PlexLibrary(helper).GetEpisode(ratingKey).FirstOrDefault(); | ||
} | ||
|
||
#region plexapi | ||
|
||
#pragma warning disable 0649 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of passing the user around and having additional parameters, would it be possibly better to have a cached
JMMUser
property on the payload?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How would that be done? (Sorry, I haven't worked with C# before)