Files
movienight-backend/plugins/jellyfin/Jellyfin.Plugin.MovieNight/Services/MovieNightSyncService.cs
T
skettiks 43be102d4e refactor: обновить use case слои и интеграцию Jellyfin
Основные изменения: укрупнены use case-интерфейсы, контроллеры переведены на цельные зависимости, логика доступных фильмов перенесена в FilmLibraryService, добавлены проверки существования фильма и улучшена обработка ошибок API.

Метрики: FilmService больше не зависит напрямую от Micrometer для counters, используется BusinessMetricsPort; добавлены TimedAspect и duration-метрики через @Timed для create/edit/delete фильмов.

Jellyfin: event handling переведен на транзакционную модель, sync учитывает runtime-ошибки, добавлены plugin-token/web-url настройки, webhook и sync endpoints защищены X-MovieNight-Plugin-Token.

Плагин: добавлен Jellyfin plugin в plugins/jellyfin, backend принимает push-sync payload, sync-state доступен плагину, pull-sync вынесен в /api/integrations/jellyfin/pull-sync, README описывает фактический контракт.

API и DTO: добавлены RecommendationResponse, ContentTypeParser, validation annotations, обработка validation errors и ResponseStatusException, endpoint /api/users/me.

БД: добавлена V7 cleanup-миграция для legacy ratings/jellyfin_id объектов; V4/V5 в этот коммит не включались.

Проверка: .\gradlew.bat check --stacktrace проходит полностью.
2026-05-22 12:32:16 +03:00

106 lines
3.7 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
using Jellyfin.Data.Enums;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Plugin.MovieNight.Services;
/// <summary>
/// Service for synchronizing the Jellyfin library with MovieNight.
/// </summary>
public class MovieNightSyncService
{
private readonly MovieNightBackendClient _backendClient;
private readonly ILibraryManager _libraryManager;
private readonly IUserManager _userManager;
private readonly IUserDataManager _userDataManager;
private readonly ILogger<MovieNightSyncService> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="MovieNightSyncService"/> class.
/// </summary>
public MovieNightSyncService(
MovieNightBackendClient backendClient,
ILibraryManager libraryManager,
IUserManager userManager,
IUserDataManager userDataManager,
ILogger<MovieNightSyncService> logger)
{
_backendClient = backendClient;
_libraryManager = libraryManager;
_userManager = userManager;
_userDataManager = userDataManager;
_logger = logger;
}
/// <summary>
/// Performs a full library sync.
/// </summary>
public async Task PerformSyncAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Starting MovieNight library sync");
var config = Plugin.Instance?.Configuration;
var enabledLibraryIds = config?.EnabledLibraryIds ?? new List<string>();
var query = new InternalItemsQuery
{
IncludeItemTypes = new[] { BaseItemKind.Movie },
Recursive = true
};
if (enabledLibraryIds.Count > 0)
{
query.AncestorIds = enabledLibraryIds.Select(Guid.Parse).ToArray();
}
var items = _libraryManager.GetItemList(query);
var users = _userManager.Users;
var syncItems = new List<object>();
foreach (var item in items)
{
if (item is not Movie movie) continue;
var jellyfinItemId = movie.Id.ToString("N");
var itemData = new Dictionary<string, object?>
{
["jellyfinItemId"] = jellyfinItemId,
["title"] = movie.Name,
["originalTitle"] = movie.OriginalTitle,
["description"] = movie.Overview,
["year"] = movie.ProductionYear,
["duration"] = movie.RunTimeTicks,
["genres"] = movie.Genres,
["posterUrl"] = $"/Items/{jellyfinItemId}/Images/Primary",
["imdbId"] = movie.GetProviderId(MetadataProvider.Imdb),
["tmdbId"] = movie.GetProviderId(MetadataProvider.Tmdb),
["userStates"] = users.Select(u => {
var userData = _userDataManager.GetUserData(u, movie);
return new {
jellyfinUserId = u.Id.ToString("N"),
isViewed = userData?.Played ?? false,
playCount = userData?.PlayCount ?? 0,
lastPlayedAt = userData?.LastPlayedDate,
userRating = userData?.Rating
};
}).ToList()
};
syncItems.Add(itemData);
}
await _backendClient.SyncAsync(new { items = syncItems }, cancellationToken).ConfigureAwait(false);
_logger.LogInformation("MovieNight library sync completed");
}
}