mirror of
https://github.com/jellyfin/jellyfin-plugin-trakt.git
synced 2024-11-23 05:40:13 +00:00
Properly handle pause and resume events
This commit is contained in:
parent
caef90021b
commit
0985b9455c
21
Trakt/Helpers/MediaStatus.cs
Normal file
21
Trakt/Helpers/MediaStatus.cs
Normal file
@ -0,0 +1,21 @@
|
||||
namespace Trakt.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Enum MediaStatus.
|
||||
/// </summary>
|
||||
public enum MediaStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// The watching state.
|
||||
/// </summary>
|
||||
Watching,
|
||||
/// <summary>
|
||||
/// The paused state.
|
||||
/// </summary>
|
||||
Paused,
|
||||
/// <summary>
|
||||
/// The stopped state.
|
||||
/// </summary>
|
||||
Stop
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
namespace Trakt;
|
||||
|
||||
public enum MediaStatus
|
||||
{
|
||||
Watching,
|
||||
Paused,
|
||||
Stop
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
@ -15,263 +16,379 @@ using Microsoft.Extensions.Logging;
|
||||
using Trakt.Api;
|
||||
using Trakt.Helpers;
|
||||
|
||||
namespace Trakt;
|
||||
|
||||
/// <summary>
|
||||
/// All communication between the server and the plugins server instance should occur in this class.
|
||||
/// </summary>
|
||||
public class ServerMediator : IServerEntryPoint
|
||||
namespace Trakt
|
||||
{
|
||||
private readonly ISessionManager _sessionManager;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly ILogger<ServerMediator> _logger;
|
||||
private readonly UserDataManagerEventsHelper _userDataManagerEventsHelper;
|
||||
private readonly IUserDataManager _userDataManager;
|
||||
private TraktApi _traktApi;
|
||||
private LibraryManagerEventsHelper _libraryManagerEventsHelper;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// All communication between the server and the plugins server instance should occur in this class.
|
||||
/// </summary>
|
||||
/// <param name="sessionManager"> </param>
|
||||
/// <param name="userDataManager"></param>
|
||||
/// <param name="libraryManager"> </param>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="httpClient"></param>
|
||||
/// <param name="appHost"></param>
|
||||
/// <param name="fileSystem"></param>
|
||||
public ServerMediator(
|
||||
ISessionManager sessionManager,
|
||||
IUserDataManager userDataManager,
|
||||
ILibraryManager libraryManager,
|
||||
ILoggerFactory loggerFactory,
|
||||
IHttpClientFactory httpClientFactory,
|
||||
IServerApplicationHost appHost,
|
||||
IFileSystem fileSystem)
|
||||
public class ServerMediator : IServerEntryPoint, IDisposable
|
||||
{
|
||||
_sessionManager = sessionManager;
|
||||
_libraryManager = libraryManager;
|
||||
_userDataManager = userDataManager;
|
||||
private readonly ISessionManager _sessionManager;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly ILogger<ServerMediator> _logger;
|
||||
private readonly UserDataManagerEventsHelper _userDataManagerEventsHelper;
|
||||
private readonly IUserDataManager _userDataManager;
|
||||
private TraktApi _traktApi;
|
||||
private LibraryManagerEventsHelper _libraryManagerEventsHelper;
|
||||
|
||||
_logger = loggerFactory.CreateLogger<ServerMediator>();
|
||||
private Dictionary<string, bool> _playbackPause;
|
||||
|
||||
_traktApi = new TraktApi(loggerFactory.CreateLogger<TraktApi>(), httpClientFactory, appHost, userDataManager, fileSystem);
|
||||
_libraryManagerEventsHelper = new LibraryManagerEventsHelper(loggerFactory.CreateLogger<LibraryManagerEventsHelper>(), _traktApi);
|
||||
_userDataManagerEventsHelper = new UserDataManagerEventsHelper(loggerFactory.CreateLogger<UserDataManagerEventsHelper>(), _traktApi);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void OnUserDataSaved(object sender, UserDataSaveEventArgs e)
|
||||
{
|
||||
// ignore change events for any reason other than manually toggling played.
|
||||
if (e.SaveReason != UserDataSaveReason.TogglePlayed)
|
||||
/// <summary>
|
||||
/// Processes server events.
|
||||
/// </summary>
|
||||
/// <param name="sessionManager">The <see cref="ISessionManager"/>.</param>
|
||||
/// <param name="userDataManager">The <see cref="IUserDataManager"/>.</param>
|
||||
/// <param name="libraryManager">The <see cref="ILibraryManager"/>.</param>
|
||||
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
|
||||
/// <param name="httpClientFactory">The <see cref="IHttpClientFactory"/>.</param>
|
||||
/// <param name="appHost">The <see cref="IServerApplicationHost"/>.</param>
|
||||
/// <param name="fileSystem">The <see cref="IFileSystem"/>.</param>
|
||||
public ServerMediator(
|
||||
ISessionManager sessionManager,
|
||||
IUserDataManager userDataManager,
|
||||
ILibraryManager libraryManager,
|
||||
ILoggerFactory loggerFactory,
|
||||
IHttpClientFactory httpClientFactory,
|
||||
IServerApplicationHost appHost,
|
||||
IFileSystem fileSystem)
|
||||
{
|
||||
return;
|
||||
_sessionManager = sessionManager;
|
||||
_libraryManager = libraryManager;
|
||||
_userDataManager = userDataManager;
|
||||
|
||||
_logger = loggerFactory.CreateLogger<ServerMediator>();
|
||||
_playbackPause = new Dictionary<string, bool>();
|
||||
|
||||
_traktApi = new TraktApi(loggerFactory.CreateLogger<TraktApi>(), httpClientFactory, appHost, userDataManager, fileSystem);
|
||||
_libraryManagerEventsHelper = new LibraryManagerEventsHelper(loggerFactory.CreateLogger<LibraryManagerEventsHelper>(), _traktApi);
|
||||
_userDataManagerEventsHelper = new UserDataManagerEventsHelper(loggerFactory.CreateLogger<UserDataManagerEventsHelper>(), _traktApi);
|
||||
}
|
||||
|
||||
if (e.Item != null)
|
||||
/// <summary>
|
||||
/// User data was saved.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sending entity.</param>
|
||||
/// <param name="userDataSaveEventArgs">The <see cref="UserDataSaveEventArgs"/>.</param>
|
||||
private void OnUserDataSaved(object sender, UserDataSaveEventArgs userDataSaveEventArgs)
|
||||
{
|
||||
// determine if user has trakt credentials
|
||||
var traktUser = UserHelper.GetTraktUser(e.UserId);
|
||||
|
||||
// Can't progress
|
||||
if (traktUser == null || !_traktApi.CanSync(e.Item, traktUser))
|
||||
// Ignore change events for any reason other than manually toggling played.
|
||||
if (userDataSaveEventArgs.SaveReason != UserDataSaveReason.TogglePlayed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!traktUser.PostSetWatched && !traktUser.PostSetUnwatched)
|
||||
if (userDataSaveEventArgs.Item != null)
|
||||
{
|
||||
// Determine if user has trakt.tv credentials
|
||||
var traktUser = UserHelper.GetTraktUser(userDataSaveEventArgs.UserId);
|
||||
|
||||
// Can't progress if user has no trakt.tv credentials
|
||||
if (traktUser == null || !_traktApi.CanSync(userDataSaveEventArgs.Item, traktUser))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!traktUser.PostSetWatched && !traktUser.PostSetUnwatched)
|
||||
{
|
||||
// User doesn't want to post any status changes at all
|
||||
return;
|
||||
}
|
||||
|
||||
// We have a user who wants to post updates and the item is in a trakt.tv monitored location
|
||||
_userDataManagerEventsHelper.ProcessUserDataSaveEventArgs(userDataSaveEventArgs, traktUser);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Run observer tasks for observed events.
|
||||
/// </summary>
|
||||
public Task RunAsync()
|
||||
{
|
||||
_userDataManager.UserDataSaved += OnUserDataSaved;
|
||||
_sessionManager.PlaybackStart += KernelPlaybackStart;
|
||||
_sessionManager.PlaybackProgress += KernelPlaybackProgress;
|
||||
_sessionManager.PlaybackStopped += KernelPlaybackStopped;
|
||||
_libraryManager.ItemAdded += LibraryManagerItemAdded;
|
||||
_libraryManager.ItemRemoved += LibraryManagerItemRemoved;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Library item was removed.
|
||||
/// Let trakt.tv know which item was removed from the user's library.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sending entity.</param>
|
||||
/// <param name="itemChangeEventArgs">The <see cref="ItemChangeEventArgs"/>.</param>
|
||||
private void LibraryManagerItemRemoved(object sender, ItemChangeEventArgs itemChangeEventArgs)
|
||||
{
|
||||
if (!(itemChangeEventArgs.Item is Movie) && !(itemChangeEventArgs.Item is Episode) && !(itemChangeEventArgs.Item is Series))
|
||||
{
|
||||
// User doesn't want to post any status changes at all.
|
||||
return;
|
||||
}
|
||||
|
||||
// We have a user who wants to post updates and the item is in a trakt monitored location.
|
||||
_userDataManagerEventsHelper.ProcessUserDataSaveEventArgs(e, traktUser);
|
||||
}
|
||||
}
|
||||
if (itemChangeEventArgs.Item.LocationType == LocationType.Virtual)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task RunAsync()
|
||||
{
|
||||
_userDataManager.UserDataSaved += OnUserDataSaved;
|
||||
_sessionManager.PlaybackStart += KernelPlaybackStart;
|
||||
_sessionManager.PlaybackStopped += KernelPlaybackStopped;
|
||||
_libraryManager.ItemAdded += LibraryManagerItemAdded;
|
||||
_libraryManager.ItemRemoved += LibraryManagerItemRemoved;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void LibraryManagerItemRemoved(object sender, ItemChangeEventArgs e)
|
||||
{
|
||||
if (!(e.Item is Movie) && !(e.Item is Episode) && !(e.Item is Series))
|
||||
{
|
||||
return;
|
||||
_libraryManagerEventsHelper.QueueItem(itemChangeEventArgs.Item, EventType.Remove);
|
||||
}
|
||||
|
||||
if (e.Item.LocationType == LocationType.Virtual)
|
||||
/// <summary>
|
||||
/// Library item was added.
|
||||
/// Let trakt.tv know which item was added to the user's library.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sending entity.</param>
|
||||
/// <param name="itemChangeEventArgs">The <see cref="ItemChangeEventArgs"/>.</param>
|
||||
private void LibraryManagerItemAdded(object sender, ItemChangeEventArgs itemChangeEventArgs)
|
||||
{
|
||||
return;
|
||||
// Don't do anything if it's not a supported media type
|
||||
if (!(itemChangeEventArgs.Item is Movie) && !(itemChangeEventArgs.Item is Episode) && !(itemChangeEventArgs.Item is Series))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemChangeEventArgs.Item.LocationType == LocationType.Virtual)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_libraryManagerEventsHelper.QueueItem(itemChangeEventArgs.Item, EventType.Add);
|
||||
}
|
||||
|
||||
_libraryManagerEventsHelper.QueueItem(e.Item, EventType.Remove);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void LibraryManagerItemAdded(object sender, ItemChangeEventArgs e)
|
||||
{
|
||||
// Don't do anything if it's not a supported media type
|
||||
if (!(e.Item is Movie) && !(e.Item is Episode) && !(e.Item is Series))
|
||||
/// <summary>
|
||||
/// Media playback has startet.
|
||||
/// Let trakt.tv know that the user has started playback.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sending entity.</param>
|
||||
/// <param name="playbackProgressEventArgs">The <see cref="PlaybackProgressEventArgs"/>.</param>
|
||||
private async void KernelPlaybackStart(object sender, PlaybackProgressEventArgs playbackProgressEventArgs)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_logger.LogDebug("Playback started");
|
||||
|
||||
if (e.Item.LocationType == LocationType.Virtual)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_libraryManagerEventsHelper.QueueItem(e.Item, EventType.Add);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Let Trakt.tv know the user has started to watch something
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void KernelPlaybackStart(object sender, PlaybackProgressEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Playback Started");
|
||||
|
||||
if (e.Users == null || !e.Users.Any() || e.Item == null)
|
||||
if (playbackProgressEventArgs.Users == null || !playbackProgressEventArgs.Users.Any() || playbackProgressEventArgs.Item == null)
|
||||
{
|
||||
_logger.LogError("Event details incomplete. Cannot process current media");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var user in e.Users)
|
||||
foreach (var user in playbackProgressEventArgs.Users)
|
||||
{
|
||||
// Since Jellyfin supports user profiles we need to do a user lookup every time something starts
|
||||
var traktUser = UserHelper.GetTraktUser(user);
|
||||
|
||||
if (traktUser == null)
|
||||
{
|
||||
_logger.LogDebug("Could not match user {User} with any stored trakt.tv credentials.", user.Username);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!traktUser.Scrobble)
|
||||
{
|
||||
_logger.LogDebug("User {User} disabled scrobbling to trakt.", user.Username);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_traktApi.CanSync(playbackProgressEventArgs.Item, traktUser))
|
||||
{
|
||||
_logger.LogDebug("Syncing playback for {Item} is forbidden for user {User}.", playbackProgressEventArgs.Item.Name, user.Username);
|
||||
continue;
|
||||
}
|
||||
|
||||
var video = playbackProgressEventArgs.Item as Video;
|
||||
var progressPercent = video.RunTimeTicks.HasValue && video.RunTimeTicks != 0 ?
|
||||
(float)(playbackProgressEventArgs.PlaybackPositionTicks ?? 0) / video.RunTimeTicks.Value * 100.0f : 0.0f;
|
||||
|
||||
_logger.LogDebug("User {User} started watching item {Item}.", user.Username, playbackProgressEventArgs.Item.Name);
|
||||
|
||||
try
|
||||
{
|
||||
if (video is Movie movie)
|
||||
{
|
||||
_logger.LogDebug("Sending movie playback status update to trakt.tv for user {User}.", user.Username);
|
||||
await _traktApi.SendMovieStatusUpdateAsync(
|
||||
movie,
|
||||
MediaStatus.Watching,
|
||||
traktUser,
|
||||
progressPercent).ConfigureAwait(false);
|
||||
_playbackPause[traktUser.LinkedMbUserId] = false;
|
||||
}
|
||||
else if (video is Episode episode)
|
||||
{
|
||||
_logger.LogDebug("Sending episode playback status update to trakt.tv for user {User}.", user.Username);
|
||||
await _traktApi.SendEpisodeStatusUpdateAsync(
|
||||
episode,
|
||||
MediaStatus.Watching,
|
||||
traktUser,
|
||||
progressPercent).ConfigureAwait(false);
|
||||
_playbackPause[traktUser.LinkedMbUserId] = false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Exception occurred while sending a playback status update to trakt.tv for user {User}.", user.Username);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Media playback has progressed.
|
||||
/// Let trakt.tv know that the user has progressed in playback.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sending entity.</param>
|
||||
/// <param name="playbackProgressEventArgs">The <see cref="PlaybackProgressEventArgs"/>.</param>
|
||||
private async void KernelPlaybackProgress(object sender, PlaybackProgressEventArgs playbackProgressEventArgs)
|
||||
{
|
||||
_logger.LogDebug("Playback progressed");
|
||||
|
||||
if (playbackProgressEventArgs.Users == null || !playbackProgressEventArgs.Users.Any() || playbackProgressEventArgs.Item == null)
|
||||
{
|
||||
_logger.LogError("Event details incomplete. Cannot process current media");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var user in playbackProgressEventArgs.Users)
|
||||
{
|
||||
// Since Emby is user profile friendly, I'm going to need to do a user lookup every time something starts
|
||||
var traktUser = UserHelper.GetTraktUser(user);
|
||||
|
||||
if (traktUser == null)
|
||||
{
|
||||
_logger.LogInformation("Could not match user with any stored credentials");
|
||||
_logger.LogDebug("Could not match user {User} with any stored trakt.tv credentials.", user.Username);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!traktUser.Scrobble)
|
||||
{
|
||||
_logger.LogDebug("User {User} disabled scrobbling to trakt.", user.Username);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_traktApi.CanSync(e.Item, traktUser))
|
||||
if (!_traktApi.CanSync(playbackProgressEventArgs.Item, traktUser))
|
||||
{
|
||||
_logger.LogDebug("Syncing playback for {Item} is forbidden for user {User}.", playbackProgressEventArgs.Item.Name, user.Username);
|
||||
continue;
|
||||
}
|
||||
|
||||
_logger.LogDebug("{UseId} appears to be monitoring {Path}", traktUser.LinkedMbUserId, e.Item.Path);
|
||||
|
||||
var video = e.Item as Video;
|
||||
var video = playbackProgressEventArgs.Item as Video;
|
||||
var progressPercent = video.RunTimeTicks.HasValue && video.RunTimeTicks != 0 ?
|
||||
(float)(e.PlaybackPositionTicks ?? 0) / video.RunTimeTicks.Value * 100.0f : 0.0f;
|
||||
(float)(playbackProgressEventArgs.PlaybackPositionTicks ?? 0) / video.RunTimeTicks.Value * 100.0f : 0.0f;
|
||||
|
||||
_logger.LogDebug("User {User} progressed watching item {Item}.", user.Username, playbackProgressEventArgs.Item.Name);
|
||||
|
||||
try
|
||||
{
|
||||
if (video is Movie movie)
|
||||
if (playbackProgressEventArgs.IsPaused)
|
||||
{
|
||||
_logger.LogDebug("Send movie status update");
|
||||
await _traktApi.SendMovieStatusUpdateAsync(
|
||||
movie,
|
||||
MediaStatus.Watching,
|
||||
traktUser,
|
||||
progressPercent).ConfigureAwait(false);
|
||||
_logger.LogDebug("Playback paused");
|
||||
|
||||
if (video is Movie movie)
|
||||
{
|
||||
_logger.LogDebug("Sending movie playback status update to trakt.tv for user {User}.", user.Username);
|
||||
await _traktApi.SendMovieStatusUpdateAsync(
|
||||
movie,
|
||||
MediaStatus.Paused,
|
||||
traktUser,
|
||||
progressPercent).ConfigureAwait(false);
|
||||
_playbackPause[traktUser.LinkedMbUserId] = true;
|
||||
}
|
||||
else if (video is Episode episode)
|
||||
{
|
||||
_logger.LogDebug("Sending episode playback status update to trakt.tv for user {User}.", user.Username);
|
||||
await _traktApi.SendEpisodeStatusUpdateAsync(
|
||||
episode,
|
||||
MediaStatus.Paused,
|
||||
traktUser,
|
||||
progressPercent).ConfigureAwait(false);
|
||||
_playbackPause[traktUser.LinkedMbUserId] = true;
|
||||
}
|
||||
}
|
||||
else if (video is Episode episode)
|
||||
else
|
||||
{
|
||||
_logger.LogDebug("Send episode status update");
|
||||
await _traktApi.SendEpisodeStatusUpdateAsync(
|
||||
episode,
|
||||
MediaStatus.Watching,
|
||||
traktUser,
|
||||
progressPercent).ConfigureAwait(false);
|
||||
_playbackPause.TryGetValue(traktUser.LinkedMbUserId, out bool paused);
|
||||
if (paused)
|
||||
{
|
||||
_logger.LogDebug("Playback resumed");
|
||||
|
||||
if (video is Movie movie)
|
||||
{
|
||||
_logger.LogDebug("Sending movie playback status update to trakt.tv for user {User}.", user.Username);
|
||||
await _traktApi.SendMovieStatusUpdateAsync(
|
||||
movie,
|
||||
MediaStatus.Watching,
|
||||
traktUser,
|
||||
progressPercent).ConfigureAwait(false);
|
||||
}
|
||||
else if (video is Episode episode)
|
||||
{
|
||||
_logger.LogDebug("Sending episode playback status update to trakt.tv for user {User}.", user.Username);
|
||||
await _traktApi.SendEpisodeStatusUpdateAsync(
|
||||
episode,
|
||||
MediaStatus.Watching,
|
||||
traktUser,
|
||||
progressPercent).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
_playbackPause[traktUser.LinkedMbUserId] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Exception handled sending status update");
|
||||
_logger.LogError(ex, "Exception occurred while sending a playback status update to trakt.tv for user {User}.", user.Username);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error sending watching status update");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Media playback has stopped. Depending on playback progress, let Trakt.tv know the user has
|
||||
/// completed watching the item.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void KernelPlaybackStopped(object sender, PlaybackStopEventArgs e)
|
||||
{
|
||||
if (e.Users == null || !e.Users.Any() || e.Item == null)
|
||||
/// <summary>
|
||||
/// Media playback has stopped.
|
||||
/// Depending on playback progress, let trakt.tv know the user has completed watching the item.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="playbackStoppedEventArgs"></param>
|
||||
private async void KernelPlaybackStopped(object sender, PlaybackStopEventArgs playbackStoppedEventArgs)
|
||||
{
|
||||
_logger.LogError("Event details incomplete. Cannot process current media");
|
||||
return;
|
||||
}
|
||||
if (playbackStoppedEventArgs.Users == null || !playbackStoppedEventArgs.Users.Any() || playbackStoppedEventArgs.Item == null)
|
||||
{
|
||||
_logger.LogError("Event details incomplete. Cannot process current media");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Playback Stopped");
|
||||
_logger.LogInformation("Playback stopped");
|
||||
|
||||
foreach (var user in e.Users)
|
||||
foreach (var user in playbackStoppedEventArgs.Users)
|
||||
{
|
||||
var traktUser = UserHelper.GetTraktUser(user);
|
||||
|
||||
if (traktUser == null)
|
||||
{
|
||||
_logger.LogError("Could not match trakt user");
|
||||
_logger.LogDebug("Could not match user {User} with any stored trakt.tv credentials.", user.Username);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!traktUser.Scrobble)
|
||||
{
|
||||
_logger.LogDebug("User {User} disabled scrobbling to trakt.", user.Username);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_traktApi.CanSync(e.Item, traktUser))
|
||||
if (!_traktApi.CanSync(playbackStoppedEventArgs.Item, traktUser))
|
||||
{
|
||||
_logger.LogDebug("Syncing playback for {Item} is forbidden for user {User}.", playbackStoppedEventArgs.Item.Name, user.Username);
|
||||
continue;
|
||||
}
|
||||
|
||||
var video = e.Item as Video;
|
||||
var video = playbackStoppedEventArgs.Item as Video;
|
||||
|
||||
if (e.PlayedToCompletion)
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Item is played. Scrobble");
|
||||
|
||||
try
|
||||
if (playbackStoppedEventArgs.PlayedToCompletion)
|
||||
{
|
||||
_logger.LogDebug("User {User} completed watching item {Item}. Scrobbling.", user.Username, playbackStoppedEventArgs.Item.Name);
|
||||
|
||||
if (video is Movie movie)
|
||||
{
|
||||
_logger.LogDebug("Sending movie playback status update to trakt.tv for user {User}.", user.Username);
|
||||
await _traktApi.SendMovieStatusUpdateAsync(
|
||||
movie,
|
||||
MediaStatus.Stop,
|
||||
@ -280,6 +397,7 @@ public class ServerMediator : IServerEntryPoint
|
||||
}
|
||||
else if (video is Episode episode)
|
||||
{
|
||||
_logger.LogDebug("Sending episode playback status update to trakt.tv for user {User}.", user.Username);
|
||||
await _traktApi.SendEpisodeStatusUpdateAsync(
|
||||
episode,
|
||||
MediaStatus.Stop,
|
||||
@ -287,54 +405,57 @@ public class ServerMediator : IServerEntryPoint
|
||||
100).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Exception handled sending status update");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var progressPercent = video.RunTimeTicks.HasValue && video.RunTimeTicks != 0 ?
|
||||
(float)(e.PlaybackPositionTicks ?? 0) / video.RunTimeTicks.Value * 100.0f : 0.0f;
|
||||
_logger.LogInformation("Item Not fully played. Tell trakt.tv we are no longer watching but don't scrobble");
|
||||
|
||||
if (video is Movie movie)
|
||||
{
|
||||
await _traktApi.SendMovieStatusUpdateAsync(movie, MediaStatus.Stop, traktUser, progressPercent).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _traktApi.SendEpisodeStatusUpdateAsync(video as Episode, MediaStatus.Stop, traktUser, progressPercent).ConfigureAwait(false);
|
||||
var progressPercent = video.RunTimeTicks.HasValue && video.RunTimeTicks != 0 ?
|
||||
(float)(playbackStoppedEventArgs.PlaybackPositionTicks ?? 0) / video.RunTimeTicks.Value * 100.0f : 0.0f;
|
||||
|
||||
_logger.LogDebug("User {User} didn't watch item {Item} until the end. Not scrobbling but stopping playback and sending current playback position.", user.Username, playbackStoppedEventArgs.Item.Name);
|
||||
|
||||
if (video is Movie movie)
|
||||
{
|
||||
await _traktApi.SendMovieStatusUpdateAsync(movie, MediaStatus.Stop, traktUser, progressPercent).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _traktApi.SendEpisodeStatusUpdateAsync(video as Episode, MediaStatus.Stop, traktUser, progressPercent).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
_playbackPause[traktUser.LinkedMbUserId] = false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Exception occurred while sending a playback status update to trakt.tv for user {User}.", user.Username);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
_logger.LogError(ex, "Error sending scrobble");
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
/// <summary>
|
||||
/// Removes event subscriptions on dispose.
|
||||
/// </summary>
|
||||
/// <param name="disposing"><see cref="bool"/> indicating if object is currently disposed</param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
_userDataManager.UserDataSaved -= OnUserDataSaved;
|
||||
_sessionManager.PlaybackStart -= KernelPlaybackStart;
|
||||
_sessionManager.PlaybackStopped -= KernelPlaybackStopped;
|
||||
_libraryManager.ItemAdded -= LibraryManagerItemAdded;
|
||||
_libraryManager.ItemRemoved -= LibraryManagerItemRemoved;
|
||||
_traktApi = null;
|
||||
_libraryManagerEventsHelper.Dispose();
|
||||
_libraryManagerEventsHelper = null;
|
||||
_userDataManagerEventsHelper.Dispose();
|
||||
if (disposing)
|
||||
{
|
||||
_userDataManager.UserDataSaved -= OnUserDataSaved;
|
||||
_sessionManager.PlaybackStart -= KernelPlaybackStart;
|
||||
_sessionManager.PlaybackStopped -= KernelPlaybackStopped;
|
||||
_libraryManager.ItemAdded -= LibraryManagerItemAdded;
|
||||
_libraryManager.ItemRemoved -= LibraryManagerItemRemoved;
|
||||
_traktApi = null;
|
||||
_libraryManagerEventsHelper.Dispose();
|
||||
_libraryManagerEventsHelper = null;
|
||||
_userDataManagerEventsHelper.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user