diff --git a/plugins/jellyfin/Jellyfin.Plugin.MovieNight/Configuration/ui.js b/plugins/jellyfin/Jellyfin.Plugin.MovieNight/Configuration/ui.js
index e02f661..022ad33 100644
--- a/plugins/jellyfin/Jellyfin.Plugin.MovieNight/Configuration/ui.js
+++ b/plugins/jellyfin/Jellyfin.Plugin.MovieNight/Configuration/ui.js
@@ -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 */ }
}
diff --git a/plugins/jellyfin/Jellyfin.Plugin.MovieNight/Controllers/MovieNightController.cs b/plugins/jellyfin/Jellyfin.Plugin.MovieNight/Controllers/MovieNightController.cs
index 8c9ff73..7d94e7c 100644
--- a/plugins/jellyfin/Jellyfin.Plugin.MovieNight/Controllers/MovieNightController.cs
+++ b/plugins/jellyfin/Jellyfin.Plugin.MovieNight/Controllers/MovieNightController.cs
@@ -87,9 +87,10 @@ public class MovieNightController : ControllerBase
/// Backend response.
[HttpGet("SyncState")]
[Authorize]
- public async Task> SyncState(CancellationToken cancellationToken)
+ public async Task SyncState(CancellationToken cancellationToken)
{
- return await _backendClient.GetSyncStateAsync(cancellationToken).ConfigureAwait(false);
+ var body = await _backendClient.GetSyncStateAsync(cancellationToken).ConfigureAwait(false);
+ return Content(body, "application/json");
}
///
@@ -97,14 +98,15 @@ public class MovieNightController : ControllerBase
///
[HttpGet("Users/{userId}/Recommendations")]
[Authorize]
- public async Task> GetRecommendations(
+ public async Task 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");
}
///
@@ -142,11 +144,12 @@ public class MovieNightController : ControllerBase
///
[HttpGet("Users/{userId}/Preferences")]
[Authorize]
- public async Task> GetPreferences(
+ public async Task 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");
}
///
diff --git a/plugins/jellyfin/Jellyfin.Plugin.MovieNight/Services/MovieNightBackendClient.cs b/plugins/jellyfin/Jellyfin.Plugin.MovieNight/Services/MovieNightBackendClient.cs
index deca123..90381b9 100644
--- a/plugins/jellyfin/Jellyfin.Plugin.MovieNight/Services/MovieNightBackendClient.cs
+++ b/plugins/jellyfin/Jellyfin.Plugin.MovieNight/Services/MovieNightBackendClient.cs
@@ -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
///
public async Task 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
///
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
///
public async Task 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
///
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
///
public async Task 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
///
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;
diff --git a/plugins/jellyfin/Jellyfin.Plugin.MovieNight/Services/MovieNightSyncService.cs b/plugins/jellyfin/Jellyfin.Plugin.MovieNight/Services/MovieNightSyncService.cs
index 4c1ec05..93da778 100644
--- a/plugins/jellyfin/Jellyfin.Plugin.MovieNight/Services/MovieNightSyncService.cs
+++ b/plugins/jellyfin/Jellyfin.Plugin.MovieNight/Services/MovieNightSyncService.cs
@@ -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