// // Copyright (c) PlaceholderCompany. All rights reserved. // namespace Jellyfin.Server.Implementations.Item; using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; using Jellyfin.Database.Implementations; using Jellyfin.Database.Implementations.Entities; using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Entities; using Microsoft.EntityFrameworkCore; /// /// Manager for handling Media Attachments. /// /// Efcore Factory. public class MediaAttachmentRepository(IDbContextFactory dbProvider) : IMediaAttachmentRepository { /// public async Task SaveMediaAttachmentsAsync( Guid id, IReadOnlyList attachments, CancellationToken cancellationToken = default) { await using var context = dbProvider.CreateDbContext(); await using var transaction = await context.Database.BeginTransactionAsync(cancellationToken).ConfigureAwait(false); // Users may replace a media with a version that includes attachments to one without them. // So when saving attachments is triggered by a library scan, we always unconditionally // clear the old ones, and then add the new ones if given. await context.AttachmentStreamInfos .Where(e => e.ItemId.Equals(id)) .ExecuteDeleteAsync(cancellationToken) .ConfigureAwait(false); if (attachments.Any()) { await context.AttachmentStreamInfos .AddRangeAsync(attachments.Select(e => Map(e, id)), cancellationToken) .ConfigureAwait(false); } await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); await transaction.CommitAsync(cancellationToken).ConfigureAwait(false); } /// public async Task> GetMediaAttachmentsAsync( MediaAttachmentQuery filter, CancellationToken cancellationToken = default) { await using var context = dbProvider.CreateDbContext(); var query = context.AttachmentStreamInfos .AsNoTracking() .Where(e => e.ItemId.Equals(filter.ItemId)); if (filter.Index.HasValue) { query = query.Where(e => e.Index == filter.Index); } var attachments = await query .ToArrayAsync(cancellationToken) .ConfigureAwait(false); return attachments.Select(Map).ToArray(); } private MediaAttachment Map(AttachmentStreamInfo attachment) { return new MediaAttachment() { Codec = attachment.Codec, CodecTag = attachment.CodecTag, Comment = attachment.Comment, FileName = attachment.Filename, Index = attachment.Index, MimeType = attachment.MimeType, }; } private AttachmentStreamInfo Map(MediaAttachment attachment, Guid id) { return new AttachmentStreamInfo() { Codec = attachment.Codec, CodecTag = attachment.CodecTag, Comment = attachment.Comment, Filename = attachment.FileName, Index = attachment.Index, MimeType = attachment.MimeType, ItemId = id, Item = null! }; } }