diff options
Diffstat (limited to 'Emby.Server.Implementations/Library')
5 files changed, 175 insertions, 96 deletions
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 2d1af82b3..93ee47fe8 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -76,6 +76,7 @@ namespace Emby.Server.Implementations.Library private readonly IItemRepository _itemRepository; private readonly IImageProcessor _imageProcessor; private readonly NamingOptions _namingOptions; + private readonly IPeopleRepository _peopleRepository; private readonly ExtraResolver _extraResolver; /// <summary> @@ -112,6 +113,7 @@ namespace Emby.Server.Implementations.Library /// <param name="imageProcessor">The image processor.</param> /// <param name="namingOptions">The naming options.</param> /// <param name="directoryService">The directory service.</param> + /// <param name="peopleRepository">The People Repository.</param> public LibraryManager( IServerApplicationHost appHost, ILoggerFactory loggerFactory, @@ -127,7 +129,8 @@ namespace Emby.Server.Implementations.Library IItemRepository itemRepository, IImageProcessor imageProcessor, NamingOptions namingOptions, - IDirectoryService directoryService) + IDirectoryService directoryService, + IPeopleRepository peopleRepository) { _appHost = appHost; _logger = loggerFactory.CreateLogger<LibraryManager>(); @@ -144,7 +147,7 @@ namespace Emby.Server.Implementations.Library _imageProcessor = imageProcessor; _cache = new ConcurrentDictionary<Guid, BaseItem>(); _namingOptions = namingOptions; - + _peopleRepository = peopleRepository; _extraResolver = new ExtraResolver(loggerFactory.CreateLogger<ExtraResolver>(), namingOptions, directoryService); _configurationManager.ConfigurationUpdated += ConfigurationUpdated; @@ -1274,7 +1277,7 @@ namespace Emby.Server.Implementations.Library return ItemIsVisible(item, user) ? item : null; } - public List<BaseItem> GetItemList(InternalItemsQuery query, bool allowExternalContent) + public IReadOnlyList<BaseItem> GetItemList(InternalItemsQuery query, bool allowExternalContent) { if (query.Recursive && !query.ParentId.IsEmpty()) { @@ -1300,7 +1303,7 @@ namespace Emby.Server.Implementations.Library return itemList; } - public List<BaseItem> GetItemList(InternalItemsQuery query) + public IReadOnlyList<BaseItem> GetItemList(InternalItemsQuery query) { return GetItemList(query, true); } @@ -1324,7 +1327,7 @@ namespace Emby.Server.Implementations.Library return _itemRepository.GetCount(query); } - public List<BaseItem> GetItemList(InternalItemsQuery query, List<BaseItem> parents) + public IReadOnlyList<BaseItem> GetItemList(InternalItemsQuery query, List<BaseItem> parents) { SetTopParentIdsOrAncestors(query, parents); @@ -1357,7 +1360,7 @@ namespace Emby.Server.Implementations.Library _itemRepository.GetItemList(query)); } - public List<Guid> GetItemIds(InternalItemsQuery query) + public IReadOnlyList<Guid> GetItemIds(InternalItemsQuery query) { if (query.User is not null) { @@ -1807,11 +1810,11 @@ namespace Emby.Server.Implementations.Library /// <inheritdoc /> public void CreateItem(BaseItem item, BaseItem? parent) { - CreateItems(new[] { item }, parent, CancellationToken.None); + CreateOrUpdateItems(new[] { item }, parent, CancellationToken.None); } /// <inheritdoc /> - public void CreateItems(IReadOnlyList<BaseItem> items, BaseItem? parent, CancellationToken cancellationToken) + public void CreateOrUpdateItems(IReadOnlyList<BaseItem> items, BaseItem? parent, CancellationToken cancellationToken) { _itemRepository.SaveItems(items, cancellationToken); @@ -1955,13 +1958,13 @@ namespace Emby.Server.Implementations.Library /// <inheritdoc /> public async Task UpdateItemsAsync(IReadOnlyList<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken) { + _itemRepository.SaveItems(items, cancellationToken); + foreach (var item in items) { await RunMetadataSavers(item, updateReason).ConfigureAwait(false); } - _itemRepository.SaveItems(items, cancellationToken); - if (ItemUpdated is not null) { foreach (var item in items) @@ -2736,12 +2739,12 @@ namespace Emby.Server.Implementations.Library return path; } - public List<PersonInfo> GetPeople(InternalPeopleQuery query) + public IReadOnlyList<PersonInfo> GetPeople(InternalPeopleQuery query) { - return _itemRepository.GetPeople(query); + return _peopleRepository.GetPeople(query); } - public List<PersonInfo> GetPeople(BaseItem item) + public IReadOnlyList<PersonInfo> GetPeople(BaseItem item) { if (item.SupportsPeople) { @@ -2756,12 +2759,12 @@ namespace Emby.Server.Implementations.Library } } - return new List<PersonInfo>(); + return []; } - public List<Person> GetPeopleItems(InternalPeopleQuery query) + public IReadOnlyList<Person> GetPeopleItems(InternalPeopleQuery query) { - return _itemRepository.GetPeopleNames(query) + return _peopleRepository.GetPeopleNames(query) .Select(i => { try @@ -2779,9 +2782,9 @@ namespace Emby.Server.Implementations.Library .ToList()!; // null values are filtered out } - public List<string> GetPeopleNames(InternalPeopleQuery query) + public IReadOnlyList<string> GetPeopleNames(InternalPeopleQuery query) { - return _itemRepository.GetPeopleNames(query); + return _peopleRepository.GetPeopleNames(query); } public void UpdatePeople(BaseItem item, List<PersonInfo> people) @@ -2790,16 +2793,17 @@ namespace Emby.Server.Implementations.Library } /// <inheritdoc /> - public async Task UpdatePeopleAsync(BaseItem item, List<PersonInfo> people, CancellationToken cancellationToken) + public async Task UpdatePeopleAsync(BaseItem item, IReadOnlyList<PersonInfo> people, CancellationToken cancellationToken) { if (!item.SupportsPeople) { return; } - _itemRepository.UpdatePeople(item.Id, people); if (people is not null) { + people = people.Where(e => e is not null).ToArray(); + _peopleRepository.UpdatePeople(item.Id, people); await SavePeopleMetadataAsync(people, cancellationToken).ConfigureAwait(false); } } @@ -2914,14 +2918,13 @@ namespace Emby.Server.Implementations.Library private async Task SavePeopleMetadataAsync(IEnumerable<PersonInfo> people, CancellationToken cancellationToken) { - List<BaseItem>? personsToSave = null; - foreach (var person in people) { cancellationToken.ThrowIfCancellationRequested(); var itemUpdateType = ItemUpdateType.MetadataDownload; var saveEntity = false; + var createEntity = false; var personEntity = GetPerson(person.Name); if (personEntity is null) @@ -2938,6 +2941,7 @@ namespace Emby.Server.Implementations.Library personEntity.PresentationUniqueKey = personEntity.CreatePresentationUniqueKey(); saveEntity = true; + createEntity = true; } foreach (var id in person.ProviderIds) @@ -2965,15 +2969,15 @@ namespace Emby.Server.Implementations.Library if (saveEntity) { - (personsToSave ??= new()).Add(personEntity); + if (createEntity) + { + CreateOrUpdateItems([personEntity], null, CancellationToken.None); + } + await RunMetadataSavers(personEntity, itemUpdateType).ConfigureAwait(false); + CreateOrUpdateItems([personEntity], null, CancellationToken.None); } } - - if (personsToSave is not null) - { - CreateItems(personsToSave, null, CancellationToken.None); - } } private void StartScanInBackground() @@ -3027,7 +3031,7 @@ namespace Emby.Server.Implementations.Library { var libraryOptions = CollectionFolder.GetLibraryOptions(virtualFolderPath); - libraryOptions.PathInfos = [..libraryOptions.PathInfos, pathInfo]; + libraryOptions.PathInfos = [.. libraryOptions.PathInfos, pathInfo]; SyncLibraryOptionsToLocations(virtualFolderPath, libraryOptions); diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 90a01c052..d0f5e60f7 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Collections.Immutable; using System.Globalization; using System.IO; using System.Linq; @@ -51,7 +52,8 @@ namespace Emby.Server.Implementations.Library private readonly ILocalizationManager _localizationManager; private readonly IApplicationPaths _appPaths; private readonly IDirectoryService _directoryService; - + private readonly IMediaStreamRepository _mediaStreamRepository; + private readonly IMediaAttachmentRepository _mediaAttachmentRepository; private readonly ConcurrentDictionary<string, ILiveStream> _openStreams = new ConcurrentDictionary<string, ILiveStream>(StringComparer.OrdinalIgnoreCase); private readonly AsyncNonKeyedLocker _liveStreamLocker = new(1); private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; @@ -69,7 +71,9 @@ namespace Emby.Server.Implementations.Library IFileSystem fileSystem, IUserDataManager userDataManager, IMediaEncoder mediaEncoder, - IDirectoryService directoryService) + IDirectoryService directoryService, + IMediaStreamRepository mediaStreamRepository, + IMediaAttachmentRepository mediaAttachmentRepository) { _appHost = appHost; _itemRepo = itemRepo; @@ -82,6 +86,8 @@ namespace Emby.Server.Implementations.Library _localizationManager = localizationManager; _appPaths = applicationPaths; _directoryService = directoryService; + _mediaStreamRepository = mediaStreamRepository; + _mediaAttachmentRepository = mediaAttachmentRepository; } public void AddParts(IEnumerable<IMediaSourceProvider> providers) @@ -89,9 +95,9 @@ namespace Emby.Server.Implementations.Library _providers = providers.ToArray(); } - public List<MediaStream> GetMediaStreams(MediaStreamQuery query) + public IReadOnlyList<MediaStream> GetMediaStreams(MediaStreamQuery query) { - var list = _itemRepo.GetMediaStreams(query); + var list = _mediaStreamRepository.GetMediaStreams(query); foreach (var stream in list) { @@ -121,7 +127,7 @@ namespace Emby.Server.Implementations.Library return false; } - public List<MediaStream> GetMediaStreams(Guid itemId) + public IReadOnlyList<MediaStream> GetMediaStreams(Guid itemId) { var list = GetMediaStreams(new MediaStreamQuery { @@ -131,7 +137,7 @@ namespace Emby.Server.Implementations.Library return GetMediaStreamsForItem(list); } - private List<MediaStream> GetMediaStreamsForItem(List<MediaStream> streams) + private IReadOnlyList<MediaStream> GetMediaStreamsForItem(IReadOnlyList<MediaStream> streams) { foreach (var stream in streams) { @@ -145,13 +151,13 @@ namespace Emby.Server.Implementations.Library } /// <inheritdoc /> - public List<MediaAttachment> GetMediaAttachments(MediaAttachmentQuery query) + public IReadOnlyList<MediaAttachment> GetMediaAttachments(MediaAttachmentQuery query) { - return _itemRepo.GetMediaAttachments(query); + return _mediaAttachmentRepository.GetMediaAttachments(query); } /// <inheritdoc /> - public List<MediaAttachment> GetMediaAttachments(Guid itemId) + public IReadOnlyList<MediaAttachment> GetMediaAttachments(Guid itemId) { return GetMediaAttachments(new MediaAttachmentQuery { @@ -159,7 +165,7 @@ namespace Emby.Server.Implementations.Library }); } - public async Task<List<MediaSourceInfo>> GetPlaybackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken) + public async Task<IReadOnlyList<MediaSourceInfo>> GetPlaybackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken) { var mediaSources = GetStaticMediaSources(item, enablePathSubstitution, user); @@ -212,7 +218,7 @@ namespace Emby.Server.Implementations.Library list.Add(source); } - return SortMediaSources(list); + return SortMediaSources(list).ToArray(); } /// <inheritdoc />> @@ -332,7 +338,7 @@ namespace Emby.Server.Implementations.Library return sources.FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); } - public List<MediaSourceInfo> GetStaticMediaSources(BaseItem item, bool enablePathSubstitution, User user = null) + public IReadOnlyList<MediaSourceInfo> GetStaticMediaSources(BaseItem item, bool enablePathSubstitution, User user = null) { ArgumentNullException.ThrowIfNull(item); @@ -453,7 +459,7 @@ namespace Emby.Server.Implementations.Library } } - private static List<MediaSourceInfo> SortMediaSources(IEnumerable<MediaSourceInfo> sources) + private static IEnumerable<MediaSourceInfo> SortMediaSources(IEnumerable<MediaSourceInfo> sources) { return sources.OrderBy(i => { @@ -470,8 +476,7 @@ namespace Emby.Server.Implementations.Library return stream?.Width ?? 0; }) - .Where(i => i.Type != MediaSourceType.Placeholder) - .ToList(); + .Where(i => i.Type != MediaSourceType.Placeholder); } public async Task<Tuple<LiveStreamResponse, IDirectStreamProvider>> OpenLiveStreamInternal(LiveStreamRequest request, CancellationToken cancellationToken) @@ -806,7 +811,7 @@ namespace Emby.Server.Implementations.Library return result.Item1; } - public async Task<List<MediaSourceInfo>> GetRecordingStreamMediaSources(ActiveRecordingInfo info, CancellationToken cancellationToken) + public async Task<IReadOnlyList<MediaSourceInfo>> GetRecordingStreamMediaSources(ActiveRecordingInfo info, CancellationToken cancellationToken) { var stream = new MediaSourceInfo { @@ -829,10 +834,7 @@ namespace Emby.Server.Implementations.Library await new LiveStreamHelper(_mediaEncoder, _logger, _appPaths) .AddMediaInfoWithProbe(stream, false, false, cancellationToken).ConfigureAwait(false); - return new List<MediaSourceInfo> - { - stream - }; + return [stream]; } public async Task CloseLiveStream(string id) diff --git a/Emby.Server.Implementations/Library/MusicManager.cs b/Emby.Server.Implementations/Library/MusicManager.cs index a69a0f33f..71c69ec50 100644 --- a/Emby.Server.Implementations/Library/MusicManager.cs +++ b/Emby.Server.Implementations/Library/MusicManager.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; @@ -24,30 +25,23 @@ namespace Emby.Server.Implementations.Library _libraryManager = libraryManager; } - public List<BaseItem> GetInstantMixFromSong(Audio item, User? user, DtoOptions dtoOptions) + public IReadOnlyList<BaseItem> GetInstantMixFromSong(Audio item, User? user, DtoOptions dtoOptions) { - var list = new List<BaseItem> - { - item - }; - - list.AddRange(GetInstantMixFromGenres(item.Genres, user, dtoOptions)); - - return list; + return GetInstantMixFromGenres(item.Genres, user, dtoOptions); } /// <inheritdoc /> - public List<BaseItem> GetInstantMixFromArtist(MusicArtist artist, User? user, DtoOptions dtoOptions) + public IReadOnlyList<BaseItem> GetInstantMixFromArtist(MusicArtist artist, User? user, DtoOptions dtoOptions) { return GetInstantMixFromGenres(artist.Genres, user, dtoOptions); } - public List<BaseItem> GetInstantMixFromAlbum(MusicAlbum item, User? user, DtoOptions dtoOptions) + public IReadOnlyList<BaseItem> GetInstantMixFromAlbum(MusicAlbum item, User? user, DtoOptions dtoOptions) { return GetInstantMixFromGenres(item.Genres, user, dtoOptions); } - public List<BaseItem> GetInstantMixFromFolder(Folder item, User? user, DtoOptions dtoOptions) + public IReadOnlyList<BaseItem> GetInstantMixFromFolder(Folder item, User? user, DtoOptions dtoOptions) { var genres = item .GetRecursiveChildren(user, new InternalItemsQuery(user) @@ -63,12 +57,12 @@ namespace Emby.Server.Implementations.Library return GetInstantMixFromGenres(genres, user, dtoOptions); } - public List<BaseItem> GetInstantMixFromPlaylist(Playlist item, User? user, DtoOptions dtoOptions) + public IReadOnlyList<BaseItem> GetInstantMixFromPlaylist(Playlist item, User? user, DtoOptions dtoOptions) { return GetInstantMixFromGenres(item.Genres, user, dtoOptions); } - public List<BaseItem> GetInstantMixFromGenres(IEnumerable<string> genres, User? user, DtoOptions dtoOptions) + public IReadOnlyList<BaseItem> GetInstantMixFromGenres(IEnumerable<string> genres, User? user, DtoOptions dtoOptions) { var genreIds = genres.DistinctNames().Select(i => { @@ -85,7 +79,7 @@ namespace Emby.Server.Implementations.Library return GetInstantMixFromGenreIds(genreIds, user, dtoOptions); } - public List<BaseItem> GetInstantMixFromGenreIds(Guid[] genreIds, User? user, DtoOptions dtoOptions) + public IReadOnlyList<BaseItem> GetInstantMixFromGenreIds(Guid[] genreIds, User? user, DtoOptions dtoOptions) { return _libraryManager.GetItemList(new InternalItemsQuery(user) { @@ -97,7 +91,7 @@ namespace Emby.Server.Implementations.Library }); } - public List<BaseItem> GetInstantMixFromItem(BaseItem item, User? user, DtoOptions dtoOptions) + public IReadOnlyList<BaseItem> GetInstantMixFromItem(BaseItem item, User? user, DtoOptions dtoOptions) { if (item is MusicGenre) { diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs index 7f3f8615e..3ac1d0219 100644 --- a/Emby.Server.Implementations/Library/SearchEngine.cs +++ b/Emby.Server.Implementations/Library/SearchEngine.cs @@ -171,7 +171,7 @@ namespace Emby.Server.Implementations.Library } }; - List<BaseItem> mediaItems; + IReadOnlyList<BaseItem> mediaItems; if (searchQuery.IncludeItemTypes.Length == 1 && searchQuery.IncludeItemTypes[0] == BaseItemKind.MusicArtist) { diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs index ceb3d65a4..a41ef888b 100644 --- a/Emby.Server.Implementations/Library/UserDataManager.cs +++ b/Emby.Server.Implementations/Library/UserDataManager.cs @@ -1,17 +1,21 @@ +#pragma warning disable RS0030 // Do not use banned APIs + using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Diagnostics; using System.Globalization; +using System.Linq; using System.Threading; using Jellyfin.Data.Entities; +using Jellyfin.Extensions; +using Jellyfin.Server.Implementations; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; +using Microsoft.EntityFrameworkCore; using AudioBook = MediaBrowser.Controller.Entities.AudioBook; using Book = MediaBrowser.Controller.Entities.Book; @@ -26,22 +30,18 @@ namespace Emby.Server.Implementations.Library new ConcurrentDictionary<string, UserItemData>(StringComparer.OrdinalIgnoreCase); private readonly IServerConfigurationManager _config; - private readonly IUserManager _userManager; - private readonly IUserDataRepository _repository; + private readonly IDbContextFactory<JellyfinDbContext> _repository; /// <summary> /// Initializes a new instance of the <see cref="UserDataManager"/> class. /// </summary> /// <param name="config">Instance of the <see cref="IServerConfigurationManager"/> interface.</param> - /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param> - /// <param name="repository">Instance of the <see cref="IUserDataRepository"/> interface.</param> + /// <param name="repository">Instance of the <see cref="IDbContextFactory{JellyfinDbContext}"/> interface.</param> public UserDataManager( IServerConfigurationManager config, - IUserManager userManager, - IUserDataRepository repository) + IDbContextFactory<JellyfinDbContext> repository) { _config = config; - _userManager = userManager; _repository = repository; } @@ -59,13 +59,27 @@ namespace Emby.Server.Implementations.Library var keys = item.GetUserDataKeys(); - var userId = user.InternalId; + using var dbContext = _repository.CreateDbContext(); + using var transaction = dbContext.Database.BeginTransaction(); foreach (var key in keys) { - _repository.SaveUserData(userId, key, userData, cancellationToken); + userData.Key = key; + var userDataEntry = Map(userData, user.Id, item.Id); + if (dbContext.UserData.Any(f => f.ItemId == userDataEntry.ItemId && f.UserId == userDataEntry.UserId && f.CustomDataKey == userDataEntry.CustomDataKey)) + { + dbContext.UserData.Attach(userDataEntry).State = EntityState.Modified; + } + else + { + dbContext.UserData.Add(userDataEntry); + } } + dbContext.SaveChanges(); + transaction.Commit(); + + var userId = user.InternalId; var cacheKey = GetCacheKey(userId, item.Id); _userData.AddOrUpdate(cacheKey, userData, (_, _) => userData); @@ -86,7 +100,7 @@ namespace Emby.Server.Implementations.Library ArgumentNullException.ThrowIfNull(item); ArgumentNullException.ThrowIfNull(userDataDto); - var userData = GetUserData(user, item); + var userData = GetUserData(user, item) ?? throw new InvalidOperationException("UserData should not be null."); if (userDataDto.PlaybackPositionTicks.HasValue) { @@ -126,33 +140,91 @@ namespace Emby.Server.Implementations.Library SaveUserData(user, item, userData, reason, CancellationToken.None); } - private UserItemData GetUserData(User user, Guid itemId, List<string> keys) + private UserData Map(UserItemData dto, Guid userId, Guid itemId) { - var userId = user.InternalId; - - var cacheKey = GetCacheKey(userId, itemId); + return new UserData() + { + ItemId = itemId, + CustomDataKey = dto.Key, + Item = null, + User = null, + AudioStreamIndex = dto.AudioStreamIndex, + IsFavorite = dto.IsFavorite, + LastPlayedDate = dto.LastPlayedDate, + Likes = dto.Likes, + PlaybackPositionTicks = dto.PlaybackPositionTicks, + PlayCount = dto.PlayCount, + Played = dto.Played, + Rating = dto.Rating, + UserId = userId, + SubtitleStreamIndex = dto.SubtitleStreamIndex, + }; + } - return _userData.GetOrAdd(cacheKey, _ => GetUserDataInternal(userId, keys)); + private UserItemData Map(UserData dto) + { + return new UserItemData() + { + Key = dto.CustomDataKey!, + AudioStreamIndex = dto.AudioStreamIndex, + IsFavorite = dto.IsFavorite, + LastPlayedDate = dto.LastPlayedDate, + Likes = dto.Likes, + PlaybackPositionTicks = dto.PlaybackPositionTicks, + PlayCount = dto.PlayCount, + Played = dto.Played, + Rating = dto.Rating, + SubtitleStreamIndex = dto.SubtitleStreamIndex, + }; } - private UserItemData GetUserDataInternal(long internalUserId, List<string> keys) + private UserItemData? GetUserData(User user, Guid itemId, List<string> keys) { - var userData = _repository.GetUserData(internalUserId, keys); + var cacheKey = GetCacheKey(user.InternalId, itemId); - if (userData is not null) + if (_userData.TryGetValue(cacheKey, out var data)) { - return userData; + return data; } - if (keys.Count > 0) + data = GetUserDataInternal(user.Id, itemId, keys); + + if (data is null) { - return new UserItemData + return new UserItemData() { - Key = keys[0] + Key = keys[0], }; } - throw new UnreachableException(); + return _userData.GetOrAdd(cacheKey, data); + } + + private UserItemData? GetUserDataInternal(Guid userId, Guid itemId, List<string> keys) + { + if (keys.Count == 0) + { + return null; + } + + using var context = _repository.CreateDbContext(); + var userData = context.UserData.AsNoTracking().Where(e => e.ItemId == itemId && keys.Contains(e.CustomDataKey) && e.UserId.Equals(userId)).ToArray(); + + if (userData.Length > 0) + { + var directDataReference = userData.FirstOrDefault(e => e.CustomDataKey == itemId.ToString("N")); + if (directDataReference is not null) + { + return Map(directDataReference); + } + + return Map(userData.First()); + } + + return new UserItemData + { + Key = keys.Last()! + }; } /// <summary> @@ -165,20 +237,25 @@ namespace Emby.Server.Implementations.Library } /// <inheritdoc /> - public UserItemData GetUserData(User user, BaseItem item) + public UserItemData? GetUserData(User user, BaseItem item) { return GetUserData(user, item.Id, item.GetUserDataKeys()); } /// <inheritdoc /> - public UserItemDataDto GetUserDataDto(BaseItem item, User user) + public UserItemDataDto? GetUserDataDto(BaseItem item, User user) => GetUserDataDto(item, null, user, new DtoOptions()); /// <inheritdoc /> - public UserItemDataDto GetUserDataDto(BaseItem item, BaseItemDto? itemDto, User user, DtoOptions options) + public UserItemDataDto? GetUserDataDto(BaseItem item, BaseItemDto? itemDto, User user, DtoOptions options) { var userData = GetUserData(user, item); - var dto = GetUserItemDataDto(userData); + if (userData is null) + { + return null; + } + + var dto = GetUserItemDataDto(userData, item.Id); item.FillUserDataDtoValues(dto, userData, itemDto, user, options); return dto; @@ -188,9 +265,10 @@ namespace Emby.Server.Implementations.Library /// Converts a UserItemData to a DTOUserItemData. /// </summary> /// <param name="data">The data.</param> + /// <param name="itemId">The reference key to an Item.</param> /// <returns>DtoUserItemData.</returns> /// <exception cref="ArgumentNullException"><paramref name="data"/> is <c>null</c>.</exception> - private UserItemDataDto GetUserItemDataDto(UserItemData data) + private UserItemDataDto GetUserItemDataDto(UserItemData data, Guid itemId) { ArgumentNullException.ThrowIfNull(data); @@ -203,6 +281,7 @@ namespace Emby.Server.Implementations.Library Rating = data.Rating, Played = data.Played, LastPlayedDate = data.LastPlayedDate, + ItemId = itemId, Key = data.Key }; } |
