using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Jellyfin.Plugin.JellyLMS.Models;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Plugin.JellyLMS.Services;
///
/// Manages LMS player discovery and state tracking.
///
public class LmsPlayerManager
{
private readonly ILogger _logger;
private readonly ILmsApiClient _lmsClient;
private readonly ConcurrentDictionary _players = new();
private DateTime _lastRefresh = DateTime.MinValue;
private readonly TimeSpan _cacheExpiry = TimeSpan.FromSeconds(30);
///
/// Initializes a new instance of the class.
///
/// The logger instance.
/// The LMS API client.
public LmsPlayerManager(ILogger logger, ILmsApiClient lmsClient)
{
_logger = logger;
_lmsClient = lmsClient;
}
///
/// Gets all known LMS players, refreshing if cache is stale.
///
/// Force a refresh from LMS.
/// List of LMS players.
public async Task> GetPlayersAsync(bool forceRefresh = false)
{
if (!forceRefresh && DateTime.UtcNow - _lastRefresh < _cacheExpiry && _players.Count > 0)
{
return _players.Values.ToList();
}
await RefreshPlayersAsync().ConfigureAwait(false);
return _players.Values.ToList();
}
///
/// Gets a specific player by MAC address.
///
/// The player's MAC address.
/// The player, or null if not found.
public async Task GetPlayerAsync(string macAddress)
{
if (_players.TryGetValue(macAddress, out var player))
{
return player;
}
await RefreshPlayersAsync().ConfigureAwait(false);
return _players.GetValueOrDefault(macAddress);
}
///
/// Refreshes the player list from LMS.
///
/// A task representing the operation.
public async Task RefreshPlayersAsync()
{
try
{
var players = await _lmsClient.GetPlayersAsync().ConfigureAwait(false);
_players.Clear();
foreach (var player in players)
{
_players[player.MacAddress] = player;
}
_lastRefresh = DateTime.UtcNow;
_logger.LogDebug("Refreshed {Count} players from LMS", players.Count);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to refresh players from LMS");
}
}
///
/// Gets all current sync groups.
///
/// List of sync groups.
public async Task> GetSyncGroupsAsync()
{
return await _lmsClient.GetSyncGroupsAsync().ConfigureAwait(false);
}
///
/// Creates a sync group with the specified players.
///
/// The master player's MAC address.
/// The slave players' MAC addresses.
/// True if successful.
public async Task CreateSyncGroupAsync(string masterMac, IEnumerable slaveMacs)
{
var success = true;
foreach (var slaveMac in slaveMacs)
{
if (!await _lmsClient.SyncPlayerAsync(masterMac, slaveMac).ConfigureAwait(false))
{
_logger.LogWarning("Failed to sync player {Slave} to master {Master}", slaveMac, masterMac);
success = false;
}
}
// Refresh player state to update sync info
await RefreshPlayersAsync().ConfigureAwait(false);
return success;
}
///
/// Removes a player from its sync group.
///
/// The player's MAC address.
/// True if successful.
public async Task UnsyncPlayerAsync(string playerMac)
{
var result = await _lmsClient.UnsyncPlayerAsync(playerMac).ConfigureAwait(false);
await RefreshPlayersAsync().ConfigureAwait(false);
return result;
}
///
/// Dissolves an entire sync group (unsyncs all members).
///
/// The master player's MAC address.
/// True if successful.
public async Task DissolveSyncGroupAsync(string masterMac)
{
var player = await GetPlayerAsync(masterMac).ConfigureAwait(false);
if (player == null)
{
return false;
}
var success = true;
// Unsync all slaves
foreach (var slaveMac in player.SyncSlaves)
{
if (!await _lmsClient.UnsyncPlayerAsync(slaveMac).ConfigureAwait(false))
{
success = false;
}
}
await RefreshPlayersAsync().ConfigureAwait(false);
return success;
}
}