using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Threading.Tasks;
using Jellyfin.Plugin.JellyLMS.Configuration;
using Jellyfin.Plugin.JellyLMS.Models;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Plugin.JellyLMS.Services;
///
/// HTTP client for LMS JSON-RPC API communication.
///
public class LmsApiClient : ILmsApiClient, IDisposable
{
private readonly ILogger _logger;
private readonly HttpClient _httpClient;
private bool _disposed;
///
/// Initializes a new instance of the class.
///
/// The logger instance.
public LmsApiClient(ILogger logger)
{
_logger = logger;
_httpClient = new HttpClient();
}
private PluginConfiguration Config => Plugin.Instance?.Configuration ?? new PluginConfiguration();
private string JsonRpcEndpoint => $"{Config.LmsServerUrl.TrimEnd('/')}/jsonrpc.js";
///
public async Task TestConnectionAsync()
{
var status = new LmsServerStatus();
try
{
var result = await SendCommandAsync("-", ["players", "0", "1"]).ConfigureAwait(false);
status.IsConnected = result != null;
status.PlayerCount = result?.Count ?? 0;
status.Version = "Connected";
}
catch (Exception ex)
{
status.IsConnected = false;
status.LastError = ex.Message;
_logger.LogError(ex, "Failed to connect to LMS at {Endpoint}", JsonRpcEndpoint);
}
return status;
}
///
public async Task> GetPlayersAsync()
{
var players = new List();
try
{
// First get player count
var countResult = await SendCommandAsync("-", ["player", "count", "?"]).ConfigureAwait(false);
var count = countResult?.Count ?? 0;
if (count == 0)
{
return players;
}
// Then get player list
var listResult = await SendCommandAsync("-", ["players", "0", count.ToString(CultureInfo.InvariantCulture)]).ConfigureAwait(false);
if (listResult?.Players == null)
{
return players;
}
foreach (var p in listResult.Players)
{
var player = new LmsPlayer
{
Name = p.Name,
MacAddress = p.PlayerId,
IpAddress = p.Ip.Split(':')[0], // Remove port if present
IsConnected = p.Connected == 1,
IsPoweredOn = p.Power == 1,
Model = p.ModelName
};
// Get additional status for sync info
var status = await GetPlayerStatusAsync(p.PlayerId).ConfigureAwait(false);
if (status != null)
{
player.Volume = status.Volume;
player.SyncMaster = status.SyncMaster;
if (!string.IsNullOrEmpty(status.SyncSlaves))
{
player.SyncSlaves = status.SyncSlaves.Split(',').ToList();
}
}
players.Add(player);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to get players from LMS");
}
return players;
}
///
public async Task GetPlayerStatusAsync(string playerMac)
{
try
{
return await SendCommandAsync(playerMac, ["status", "-", "1", "tags:"])
.ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to get status for player {Mac}", playerMac);
return null;
}
}
///
public async Task PlayUrlAsync(string playerMac, string url, string? title = null)
{
try
{
// First clear the playlist and add the URL
await SendCommandAsync