fix(jellyfin): fixes in jellyfin integration
This commit is contained in:
@@ -398,8 +398,14 @@
|
||||
if (!statusEl) return;
|
||||
try {
|
||||
const state = await ApiClient.getJSON(ApiClient.getUrl(`MovieNight/SyncState`));
|
||||
if (state && state.lastSyncAt) {
|
||||
statusEl.innerText = `Last sync: ${new Date(state.lastSyncAt).toLocaleString()}`;
|
||||
const states = Array.isArray(state) ? state : [];
|
||||
const latest = states
|
||||
.map(s => s.lastSuccessfulSyncAt || s.lastSyncedAt)
|
||||
.filter(Boolean)
|
||||
.sort()
|
||||
.pop();
|
||||
if (latest) {
|
||||
statusEl.innerText = `Last sync: ${new Date(latest).toLocaleString()}`;
|
||||
}
|
||||
} catch (err) { /* ignore */ }
|
||||
}
|
||||
|
||||
@@ -87,9 +87,10 @@ public class MovieNightController : ControllerBase
|
||||
/// <returns>Backend response.</returns>
|
||||
[HttpGet("SyncState")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<string>> SyncState(CancellationToken cancellationToken)
|
||||
public async Task<IActionResult> SyncState(CancellationToken cancellationToken)
|
||||
{
|
||||
return await _backendClient.GetSyncStateAsync(cancellationToken).ConfigureAwait(false);
|
||||
var body = await _backendClient.GetSyncStateAsync(cancellationToken).ConfigureAwait(false);
|
||||
return Content(body, "application/json");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -97,14 +98,15 @@ public class MovieNightController : ControllerBase
|
||||
/// </summary>
|
||||
[HttpGet("Users/{userId}/Recommendations")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<string>> GetRecommendations(
|
||||
public async Task<IActionResult> GetRecommendations(
|
||||
[FromRoute] string userId,
|
||||
[FromQuery] string? contentType,
|
||||
[FromQuery] string? mood,
|
||||
[FromQuery] int limit = 10,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _backendClient.GetRecommendationsAsync(userId, contentType, mood, limit, cancellationToken).ConfigureAwait(false);
|
||||
var body = await _backendClient.GetRecommendationsAsync(userId, contentType, mood, limit, cancellationToken).ConfigureAwait(false);
|
||||
return Content(body, "application/json");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -142,11 +144,12 @@ public class MovieNightController : ControllerBase
|
||||
/// </summary>
|
||||
[HttpGet("Users/{userId}/Preferences")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<string?>> GetPreferences(
|
||||
public async Task<IActionResult> GetPreferences(
|
||||
[FromRoute] string userId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return await _backendClient.GetPreferencesAsync(userId, cancellationToken).ConfigureAwait(false);
|
||||
var body = await _backendClient.GetPreferencesAsync(userId, cancellationToken).ConfigureAwait(false);
|
||||
return body is null ? NotFound() : Content(body, "application/json");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -40,7 +40,7 @@ public class MovieNightBackendClient
|
||||
{
|
||||
var payload = new MovieNightEventPayload(
|
||||
EventId: $"plugin-test:{Guid.NewGuid():N}",
|
||||
EventType: "playback.stopped",
|
||||
EventType: "plugin.test",
|
||||
OccurredAt: DateTimeOffset.UtcNow,
|
||||
JellyfinUserId: "movienight-plugin-test-user",
|
||||
ItemId: "movienight-plugin-test-item",
|
||||
@@ -95,11 +95,12 @@ public class MovieNightBackendClient
|
||||
/// </summary>
|
||||
public async Task<string> GetRecommendationsAsync(string userId, string? contentType, string? mood, int limit, CancellationToken cancellationToken)
|
||||
{
|
||||
userId = NormalizeJellyfinId(userId);
|
||||
var query = $"?limit={limit}";
|
||||
if (!string.IsNullOrEmpty(contentType)) query += $"&contentType={Uri.EscapeDataString(contentType)}";
|
||||
if (!string.IsNullOrEmpty(mood)) query += $"&mood={Uri.EscapeDataString(mood)}";
|
||||
|
||||
var request = CreateRequest(HttpMethod.Get, $"/api/users/{userId}/recommendations{query}");
|
||||
var request = CreateRequest(HttpMethod.Get, $"/api/integrations/jellyfin/users/{userId}/recommendations{query}");
|
||||
if (request is null) return "Plugin is not configured.";
|
||||
|
||||
using var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
||||
@@ -113,7 +114,9 @@ public class MovieNightBackendClient
|
||||
/// </summary>
|
||||
public async Task PostRatingAsync(string userId, string filmId, int score, string? note, CancellationToken cancellationToken)
|
||||
{
|
||||
var request = CreateRequest(HttpMethod.Post, $"/api/users/{userId}/ratings/films/{filmId}");
|
||||
userId = NormalizeJellyfinId(userId);
|
||||
filmId = NormalizeJellyfinId(filmId);
|
||||
var request = CreateRequest(HttpMethod.Post, $"/api/integrations/jellyfin/users/{userId}/ratings/items/{filmId}");
|
||||
if (request is null) return;
|
||||
|
||||
request.Content = JsonContent.Create(new { score, note }, options: JsonOptions);
|
||||
@@ -126,7 +129,8 @@ public class MovieNightBackendClient
|
||||
/// </summary>
|
||||
public async Task<string> GetRatingsAsync(string userId, CancellationToken cancellationToken)
|
||||
{
|
||||
var request = CreateRequest(HttpMethod.Get, $"/api/users/{userId}/ratings");
|
||||
userId = NormalizeJellyfinId(userId);
|
||||
var request = CreateRequest(HttpMethod.Get, $"/api/integrations/jellyfin/users/{userId}/ratings");
|
||||
if (request is null) return "[]";
|
||||
|
||||
using var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
||||
@@ -140,7 +144,9 @@ public class MovieNightBackendClient
|
||||
/// </summary>
|
||||
public async Task MarkViewedAsync(string userId, string filmId, DateTimeOffset? watchedAt, CancellationToken cancellationToken)
|
||||
{
|
||||
var request = CreateRequest(HttpMethod.Post, $"/api/users/{userId}/library/films/{filmId}/viewed");
|
||||
userId = NormalizeJellyfinId(userId);
|
||||
filmId = NormalizeJellyfinId(filmId);
|
||||
var request = CreateRequest(HttpMethod.Post, $"/api/integrations/jellyfin/users/{userId}/library/items/{filmId}/viewed");
|
||||
if (request is null) return;
|
||||
|
||||
request.Content = JsonContent.Create(new { watchedAt }, options: JsonOptions);
|
||||
@@ -172,13 +178,15 @@ public class MovieNightBackendClient
|
||||
/// </summary>
|
||||
public async Task<string?> GetPreferencesAsync(string userId, CancellationToken cancellationToken)
|
||||
{
|
||||
var request = CreateRequest(HttpMethod.Get, $"/api/users/{userId}/preferences");
|
||||
userId = NormalizeJellyfinId(userId);
|
||||
var request = CreateRequest(HttpMethod.Get, $"/api/integrations/jellyfin/users/{userId}/preferences");
|
||||
if (request is null) return null;
|
||||
|
||||
using var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
||||
if (response.StatusCode == System.Net.HttpStatusCode.NotFound) return null;
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
|
||||
response.EnsureSuccessStatusCode();
|
||||
return body;
|
||||
}
|
||||
|
||||
@@ -187,7 +195,8 @@ public class MovieNightBackendClient
|
||||
/// </summary>
|
||||
public async Task CompleteOnboardingAsync(string userId, object payload, CancellationToken cancellationToken)
|
||||
{
|
||||
var request = CreateRequest(HttpMethod.Post, $"/api/users/{userId}/recommendation-onboarding");
|
||||
userId = NormalizeJellyfinId(userId);
|
||||
var request = CreateRequest(HttpMethod.Post, $"/api/integrations/jellyfin/users/{userId}/recommendation-onboarding");
|
||||
if (request is null) return;
|
||||
|
||||
request.Content = JsonContent.Create(payload, options: JsonOptions);
|
||||
@@ -257,6 +266,11 @@ public class MovieNightBackendClient
|
||||
return string.IsNullOrWhiteSpace(value) ? null : value.TrimEnd('/');
|
||||
}
|
||||
|
||||
private static string NormalizeJellyfinId(string value)
|
||||
{
|
||||
return Guid.TryParse(value, out var guid) ? guid.ToString("N") : value;
|
||||
}
|
||||
|
||||
private static bool IsEnabled()
|
||||
{
|
||||
var configuration = Plugin.Instance?.Configuration;
|
||||
|
||||
@@ -64,6 +64,11 @@ public class MovieNightSyncService
|
||||
|
||||
var items = _libraryManager.GetItemList(query);
|
||||
var users = _userManager.Users;
|
||||
var syncUsers = users.Select(u => new
|
||||
{
|
||||
jellyfinUserId = u.Id.ToString("N"),
|
||||
name = u.Username
|
||||
}).ToList();
|
||||
var syncItems = new List<object>();
|
||||
|
||||
foreach (var item in items)
|
||||
@@ -71,11 +76,12 @@ public class MovieNightSyncService
|
||||
if (item is not Movie movie) continue;
|
||||
|
||||
var jellyfinItemId = movie.Id.ToString("N");
|
||||
var title = string.IsNullOrWhiteSpace(movie.Name) ? jellyfinItemId : movie.Name;
|
||||
|
||||
var itemData = new Dictionary<string, object?>
|
||||
{
|
||||
["jellyfinItemId"] = jellyfinItemId,
|
||||
["title"] = movie.Name,
|
||||
["title"] = title,
|
||||
["originalTitle"] = movie.OriginalTitle,
|
||||
["description"] = movie.Overview,
|
||||
["year"] = movie.ProductionYear,
|
||||
@@ -99,7 +105,7 @@ public class MovieNightSyncService
|
||||
syncItems.Add(itemData);
|
||||
}
|
||||
|
||||
await _backendClient.SyncAsync(new { items = syncItems }, cancellationToken).ConfigureAwait(false);
|
||||
await _backendClient.SyncAsync(new { users = syncUsers, items = syncItems }, cancellationToken).ConfigureAwait(false);
|
||||
_logger.LogInformation("MovieNight library sync completed");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user