using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Session;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Plugin.MovieNight.Services;
///
/// Subscribes to Jellyfin playback events and forwards thin payloads.
///
public sealed class MovieNightPlaybackEventService : IHostedService
{
private readonly ISessionManager _sessionManager;
private readonly MovieNightBackendClient _backendClient;
private readonly ILogger _logger;
///
/// Initializes a new instance of the class.
///
/// Jellyfin session manager.
/// Backend client.
/// Logger.
public MovieNightPlaybackEventService(
ISessionManager sessionManager,
MovieNightBackendClient backendClient,
ILogger logger)
{
_sessionManager = sessionManager;
_backendClient = backendClient;
_logger = logger;
}
///
public Task StartAsync(CancellationToken cancellationToken)
{
_sessionManager.PlaybackStopped += OnPlaybackStopped;
return Task.CompletedTask;
}
///
public Task StopAsync(CancellationToken cancellationToken)
{
_sessionManager.PlaybackStopped -= OnPlaybackStopped;
return Task.CompletedTask;
}
private void OnPlaybackStopped(object? sender, PlaybackStopEventArgs e)
{
if (Plugin.Instance?.Configuration is not { Enabled: true, EnablePlaybackEvents: true })
{
return;
}
if (!e.PlayedToCompletion)
{
return;
}
var userId = e.Users?.FirstOrDefault()?.Id.ToString("N");
var itemId = e.Item?.Id.ToString("N");
if (string.IsNullOrWhiteSpace(userId) || string.IsNullOrWhiteSpace(itemId))
{
return;
}
var occurredAt = DateTimeOffset.UtcNow;
var eventId = string.IsNullOrWhiteSpace(e.PlaySessionId)
? $"playback-stopped:{userId}:{itemId}:{occurredAt.ToUnixTimeMilliseconds()}"
: $"playback-stopped:{userId}:{itemId}:{e.PlaySessionId}";
var payload = new MovieNightEventPayload(
EventId: eventId,
EventType: "playback.stopped",
OccurredAt: occurredAt,
JellyfinUserId: userId,
ItemId: itemId,
PayloadVersion: 1,
Payload: new Dictionary
{
["itemName"] = e.Item?.Name,
["playSessionId"] = e.PlaySessionId,
["positionTicks"] = e.PlaybackPositionTicks,
["playedToCompletion"] = e.PlayedToCompletion
});
_ = Task.Run(
async () =>
{
try
{
await _backendClient.PushEventAsync(payload, CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.LogDebug(ex, "MovieNight playback event push failed");
}
});
}
}