浏览代码

Removed BaseSqliteRepository

JPVenson 8 月之前
父节点
当前提交
15bf43e3ad

+ 0 - 269
Emby.Server.Implementations/Data/BaseSqliteRepository.cs

@@ -1,269 +0,0 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using Jellyfin.Extensions;
-using Microsoft.Data.Sqlite;
-using Microsoft.Extensions.Logging;
-
-namespace Emby.Server.Implementations.Data
-{
-    public abstract class BaseSqliteRepository : IDisposable
-    {
-        private bool _disposed = false;
-        private SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1);
-        private SqliteConnection _writeConnection;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="BaseSqliteRepository"/> class.
-        /// </summary>
-        /// <param name="logger">The logger.</param>
-        protected BaseSqliteRepository(ILogger<BaseSqliteRepository> logger)
-        {
-            Logger = logger;
-        }
-
-        /// <summary>
-        /// Gets or sets the path to the DB file.
-        /// </summary>
-        protected string DbFilePath { get; set; }
-
-        /// <summary>
-        /// Gets the logger.
-        /// </summary>
-        /// <value>The logger.</value>
-        protected ILogger<BaseSqliteRepository> Logger { get; }
-
-        /// <summary>
-        /// Gets the cache size.
-        /// </summary>
-        /// <value>The cache size or null.</value>
-        protected virtual int? CacheSize => null;
-
-        /// <summary>
-        /// Gets the locking mode. <see href="https://www.sqlite.org/pragma.html#pragma_locking_mode" />.
-        /// </summary>
-        protected virtual string LockingMode => "NORMAL";
-
-        /// <summary>
-        /// Gets the journal mode. <see href="https://www.sqlite.org/pragma.html#pragma_journal_mode" />.
-        /// </summary>
-        /// <value>The journal mode.</value>
-        protected virtual string JournalMode => "WAL";
-
-        /// <summary>
-        /// Gets the journal size limit. <see href="https://www.sqlite.org/pragma.html#pragma_journal_size_limit" />.
-        /// The default (-1) is overridden to prevent unconstrained WAL size, as reported by users.
-        /// </summary>
-        /// <value>The journal size limit.</value>
-        protected virtual int? JournalSizeLimit => 134_217_728; // 128MiB
-
-        /// <summary>
-        /// Gets the page size.
-        /// </summary>
-        /// <value>The page size or null.</value>
-        protected virtual int? PageSize => null;
-
-        /// <summary>
-        /// Gets the temp store mode.
-        /// </summary>
-        /// <value>The temp store mode.</value>
-        /// <see cref="TempStoreMode"/>
-        protected virtual TempStoreMode TempStore => TempStoreMode.Memory;
-
-        /// <summary>
-        /// Gets the synchronous mode.
-        /// </summary>
-        /// <value>The synchronous mode or null.</value>
-        /// <see cref="SynchronousMode"/>
-        protected virtual SynchronousMode? Synchronous => SynchronousMode.Normal;
-
-        public virtual void Initialize()
-        {
-            // Configuration and pragmas can affect VACUUM so it needs to be last.
-            using (var connection = GetConnection())
-            {
-                connection.Execute("VACUUM");
-            }
-        }
-
-        protected ManagedConnection GetConnection(bool readOnly = false)
-        {
-            if (!readOnly)
-            {
-                _writeLock.Wait();
-                if (_writeConnection is not null)
-                {
-                    return new ManagedConnection(_writeConnection, _writeLock);
-                }
-
-                var writeConnection = new SqliteConnection($"Filename={DbFilePath};Pooling=False");
-                writeConnection.Open();
-
-                if (CacheSize.HasValue)
-                {
-                    writeConnection.Execute("PRAGMA cache_size=" + CacheSize.Value);
-                }
-
-                if (!string.IsNullOrWhiteSpace(LockingMode))
-                {
-                    writeConnection.Execute("PRAGMA locking_mode=" + LockingMode);
-                }
-
-                if (!string.IsNullOrWhiteSpace(JournalMode))
-                {
-                    writeConnection.Execute("PRAGMA journal_mode=" + JournalMode);
-                }
-
-                if (JournalSizeLimit.HasValue)
-                {
-                    writeConnection.Execute("PRAGMA journal_size_limit=" + JournalSizeLimit.Value);
-                }
-
-                if (Synchronous.HasValue)
-                {
-                    writeConnection.Execute("PRAGMA synchronous=" + (int)Synchronous.Value);
-                }
-
-                if (PageSize.HasValue)
-                {
-                    writeConnection.Execute("PRAGMA page_size=" + PageSize.Value);
-                }
-
-                writeConnection.Execute("PRAGMA temp_store=" + (int)TempStore);
-
-                return new ManagedConnection(_writeConnection = writeConnection, _writeLock);
-            }
-
-            var connection = new SqliteConnection($"Filename={DbFilePath};Mode=ReadOnly");
-            connection.Open();
-
-            if (CacheSize.HasValue)
-            {
-                connection.Execute("PRAGMA cache_size=" + CacheSize.Value);
-            }
-
-            if (!string.IsNullOrWhiteSpace(LockingMode))
-            {
-                connection.Execute("PRAGMA locking_mode=" + LockingMode);
-            }
-
-            if (!string.IsNullOrWhiteSpace(JournalMode))
-            {
-                connection.Execute("PRAGMA journal_mode=" + JournalMode);
-            }
-
-            if (JournalSizeLimit.HasValue)
-            {
-                connection.Execute("PRAGMA journal_size_limit=" + JournalSizeLimit.Value);
-            }
-
-            if (Synchronous.HasValue)
-            {
-                connection.Execute("PRAGMA synchronous=" + (int)Synchronous.Value);
-            }
-
-            if (PageSize.HasValue)
-            {
-                connection.Execute("PRAGMA page_size=" + PageSize.Value);
-            }
-
-            connection.Execute("PRAGMA temp_store=" + (int)TempStore);
-
-            return new ManagedConnection(connection, null);
-        }
-
-        public SqliteCommand PrepareStatement(ManagedConnection connection, string sql)
-        {
-            var command = connection.CreateCommand();
-            command.CommandText = sql;
-            return command;
-        }
-
-        protected bool TableExists(ManagedConnection connection, string name)
-        {
-            using var statement = PrepareStatement(connection, "select DISTINCT tbl_name from sqlite_master");
-            foreach (var row in statement.ExecuteQuery())
-            {
-                if (string.Equals(name, row.GetString(0), StringComparison.OrdinalIgnoreCase))
-                {
-                    return true;
-                }
-            }
-
-            return false;
-        }
-
-        protected List<string> GetColumnNames(ManagedConnection connection, string table)
-        {
-            var columnNames = new List<string>();
-
-            foreach (var row in connection.Query("PRAGMA table_info(" + table + ")"))
-            {
-                if (row.TryGetString(1, out var columnName))
-                {
-                    columnNames.Add(columnName);
-                }
-            }
-
-            return columnNames;
-        }
-
-        protected void AddColumn(ManagedConnection connection, string table, string columnName, string type, List<string> existingColumnNames)
-        {
-            if (existingColumnNames.Contains(columnName, StringComparison.OrdinalIgnoreCase))
-            {
-                return;
-            }
-
-            connection.Execute("alter table " + table + " add column " + columnName + " " + type + " NULL");
-        }
-
-        protected void CheckDisposed()
-        {
-            ObjectDisposedException.ThrowIf(_disposed, this);
-        }
-
-        /// <inheritdoc />
-        public void Dispose()
-        {
-            Dispose(true);
-            GC.SuppressFinalize(this);
-        }
-
-        /// <summary>
-        /// Releases unmanaged and - optionally - managed resources.
-        /// </summary>
-        /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
-        protected virtual void Dispose(bool dispose)
-        {
-            if (_disposed)
-            {
-                return;
-            }
-
-            if (dispose)
-            {
-                _writeLock.Wait();
-                try
-                {
-                    _writeConnection.Dispose();
-                }
-                finally
-                {
-                    _writeLock.Release();
-                }
-
-                _writeLock.Dispose();
-            }
-
-            _writeConnection = null;
-            _writeLock = null;
-
-            _disposed = true;
-        }
-    }
-}

+ 0 - 62
Emby.Server.Implementations/Data/ManagedConnection.cs

@@ -1,62 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using Microsoft.Data.Sqlite;
-
-namespace Emby.Server.Implementations.Data;
-
-public sealed class ManagedConnection : IDisposable
-{
-    private readonly SemaphoreSlim? _writeLock;
-
-    private SqliteConnection _db;
-
-    private bool _disposed = false;
-
-    public ManagedConnection(SqliteConnection db, SemaphoreSlim? writeLock)
-    {
-        _db = db;
-        _writeLock = writeLock;
-    }
-
-    public SqliteTransaction BeginTransaction()
-        => _db.BeginTransaction();
-
-    public SqliteCommand CreateCommand()
-        => _db.CreateCommand();
-
-    public void Execute(string commandText)
-        => _db.Execute(commandText);
-
-    public SqliteCommand PrepareStatement(string sql)
-        => _db.PrepareStatement(sql);
-
-    public IEnumerable<SqliteDataReader> Query(string commandText)
-        => _db.Query(commandText);
-
-    public void Dispose()
-    {
-        if (_disposed)
-        {
-            return;
-        }
-
-        if (_writeLock is null)
-        {
-            // Read connections are managed with an internal pool
-            _db.Dispose();
-        }
-        else
-        {
-            // Write lock is managed by BaseSqliteRepository
-            // Don't dispose here
-            _writeLock.Release();
-        }
-
-        _db = null!;
-
-        _disposed = true;
-    }
-}

+ 0 - 461
Emby.Server.Implementations/Data/SqliteItemRepository.cs

@@ -1,461 +0,0 @@
-#nullable disable
-
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Runtime.CompilerServices;
-using System.Text;
-using System.Text.Json;
-using System.Threading;
-using Emby.Server.Implementations.Playlists;
-using Jellyfin.Data.Enums;
-using Jellyfin.Extensions;
-using Jellyfin.Extensions.Json;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Channels;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Extensions;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Controller.Playlists;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Globalization;
-using MediaBrowser.Model.LiveTv;
-using MediaBrowser.Model.Querying;
-using Microsoft.Data.Sqlite;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.Logging;
-
-namespace Emby.Server.Implementations.Data
-{
-    /// <summary>
-    /// Class SQLiteItemRepository.
-    /// </summary>
-    public class SqliteItemRepository : BaseSqliteRepository, IItemRepository
-    {
-        private const string FromText = " from TypedBaseItems A";
-        private const string ChaptersTableName = "Chapters2";
-
-        private const string SaveItemCommandText =
-            @"replace into TypedBaseItems
-            (guid,type,data,Path,StartDate,EndDate,ChannelId,IsMovie,IsSeries,EpisodeTitle,IsRepeat,CommunityRating,CustomRating,IndexNumber,IsLocked,Name,OfficialRating,MediaType,Overview,ParentIndexNumber,PremiereDate,ProductionYear,ParentId,Genres,InheritedParentalRatingValue,SortName,ForcedSortName,RunTimeTicks,Size,DateCreated,DateModified,PreferredMetadataLanguage,PreferredMetadataCountryCode,Width,Height,DateLastRefreshed,DateLastSaved,IsInMixedFolder,LockedFields,Studios,Audio,ExternalServiceId,Tags,IsFolder,UnratedType,TopParentId,TrailerTypes,CriticRating,CleanName,PresentationUniqueKey,OriginalTitle,PrimaryVersionId,DateLastMediaAdded,Album,LUFS,NormalizationGain,IsVirtualItem,SeriesName,UserDataKey,SeasonName,SeasonId,SeriesId,ExternalSeriesId,Tagline,ProviderIds,Images,ProductionLocations,ExtraIds,TotalBitrate,ExtraType,Artists,AlbumArtists,ExternalId,SeriesPresentationUniqueKey,ShowId,OwnerId)
-            values (@guid,@type,@data,@Path,@StartDate,@EndDate,@ChannelId,@IsMovie,@IsSeries,@EpisodeTitle,@IsRepeat,@CommunityRating,@CustomRating,@IndexNumber,@IsLocked,@Name,@OfficialRating,@MediaType,@Overview,@ParentIndexNumber,@PremiereDate,@ProductionYear,@ParentId,@Genres,@InheritedParentalRatingValue,@SortName,@ForcedSortName,@RunTimeTicks,@Size,@DateCreated,@DateModified,@PreferredMetadataLanguage,@PreferredMetadataCountryCode,@Width,@Height,@DateLastRefreshed,@DateLastSaved,@IsInMixedFolder,@LockedFields,@Studios,@Audio,@ExternalServiceId,@Tags,@IsFolder,@UnratedType,@TopParentId,@TrailerTypes,@CriticRating,@CleanName,@PresentationUniqueKey,@OriginalTitle,@PrimaryVersionId,@DateLastMediaAdded,@Album,@LUFS,@NormalizationGain,@IsVirtualItem,@SeriesName,@UserDataKey,@SeasonName,@SeasonId,@SeriesId,@ExternalSeriesId,@Tagline,@ProviderIds,@Images,@ProductionLocations,@ExtraIds,@TotalBitrate,@ExtraType,@Artists,@AlbumArtists,@ExternalId,@SeriesPresentationUniqueKey,@ShowId,@OwnerId)";
-
-        private readonly IServerConfigurationManager _config;
-        private readonly IServerApplicationHost _appHost;
-        private readonly ILocalizationManager _localization;
-        // TODO: Remove this dependency. GetImageCacheTag() is the only method used and it can be converted to a static helper method
-        private readonly IImageProcessor _imageProcessor;
-
-        private readonly TypeMapper _typeMapper;
-        private readonly JsonSerializerOptions _jsonOptions;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
-        /// </summary>
-        /// <param name="config">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
-        /// <param name="appHost">Instance of the <see cref="IServerApplicationHost"/> interface.</param>
-        /// <param name="logger">Instance of the <see cref="ILogger{SqliteItemRepository}"/> interface.</param>
-        /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
-        /// <param name="imageProcessor">Instance of the <see cref="IImageProcessor"/> interface.</param>
-        /// <param name="configuration">Instance of the <see cref="IConfiguration"/> interface.</param>
-        /// <exception cref="ArgumentNullException">config is null.</exception>
-        public SqliteItemRepository(
-            IServerConfigurationManager config,
-            IServerApplicationHost appHost,
-            ILogger<SqliteItemRepository> logger,
-            ILocalizationManager localization,
-            IImageProcessor imageProcessor,
-            IConfiguration configuration)
-            : base(logger)
-        {
-            _config = config;
-            _appHost = appHost;
-            _localization = localization;
-            _imageProcessor = imageProcessor;
-
-            _typeMapper = new TypeMapper();
-            _jsonOptions = JsonDefaults.Options;
-
-            DbFilePath = Path.Combine(_config.ApplicationPaths.DataPath, "library.db");
-
-            CacheSize = configuration.GetSqliteCacheSize();
-        }
-
-        /// <inheritdoc />
-        protected override int? CacheSize { get; }
-
-        /// <inheritdoc />
-        protected override TempStoreMode TempStore => TempStoreMode.Memory;
-
-
-        private bool TypeRequiresDeserialization(Type type)
-        {
-            if (_config.Configuration.SkipDeserializationForBasicTypes)
-            {
-                if (type == typeof(Channel)
-                    || type == typeof(UserRootFolder))
-                {
-                    return false;
-                }
-            }
-
-            return type != typeof(Season)
-                && type != typeof(MusicArtist)
-                && type != typeof(Person)
-                && type != typeof(MusicGenre)
-                && type != typeof(Genre)
-                && type != typeof(Studio)
-                && type != typeof(PlaylistsFolder)
-                && type != typeof(PhotoAlbum)
-                && type != typeof(Year)
-                && type != typeof(Book)
-                && type != typeof(LiveTvProgram)
-                && type != typeof(AudioBook)
-                && type != typeof(MusicAlbum);
-        }
-
-        private static bool EnableJoinUserData(InternalItemsQuery query)
-        {
-            if (query.User is null)
-            {
-                return false;
-            }
-
-            var sortingFields = new HashSet<ItemSortBy>(query.OrderBy.Select(i => i.OrderBy));
-
-            return sortingFields.Contains(ItemSortBy.IsFavoriteOrLiked)
-                    || sortingFields.Contains(ItemSortBy.IsPlayed)
-                    || sortingFields.Contains(ItemSortBy.IsUnplayed)
-                    || sortingFields.Contains(ItemSortBy.PlayCount)
-                    || sortingFields.Contains(ItemSortBy.DatePlayed)
-                    || sortingFields.Contains(ItemSortBy.SeriesDatePlayed)
-                    || query.IsFavoriteOrLiked.HasValue
-                    || query.IsFavorite.HasValue
-                    || query.IsResumable.HasValue
-                    || query.IsPlayed.HasValue
-                    || query.IsLiked.HasValue;
-        }
-
-        private string GetJoinUserDataText(InternalItemsQuery query)
-        {
-            if (!EnableJoinUserData(query))
-            {
-                return string.Empty;
-            }
-
-            return " left join UserDatas on UserDataKey=UserDatas.Key And (UserId=@UserId)";
-        }
-
-        /// <inheritdoc />
-        public List<string> GetStudioNames()
-        {
-            return GetItemValueNames(new[] { 3 }, Array.Empty<string>(), Array.Empty<string>());
-        }
-
-        /// <inheritdoc />
-        public List<string> GetAllArtistNames()
-        {
-            return GetItemValueNames(new[] { 0, 1 }, Array.Empty<string>(), Array.Empty<string>());
-        }
-
-        /// <inheritdoc />
-        public List<string> GetMusicGenreNames()
-        {
-            return GetItemValueNames(
-                new[] { 2 },
-                new string[]
-                {
-                    typeof(Audio).FullName,
-                    typeof(MusicVideo).FullName,
-                    typeof(MusicAlbum).FullName,
-                    typeof(MusicArtist).FullName
-                },
-                Array.Empty<string>());
-        }
-
-        /// <inheritdoc />
-        public List<string> GetGenreNames()
-        {
-            return GetItemValueNames(
-                new[] { 2 },
-                Array.Empty<string>(),
-                new string[]
-                {
-                    typeof(Audio).FullName,
-                    typeof(MusicVideo).FullName,
-                    typeof(MusicAlbum).FullName,
-                    typeof(MusicArtist).FullName
-                });
-        }
-
-        private List<string> GetItemValueNames(int[] itemValueTypes, IReadOnlyList<string> withItemTypes, IReadOnlyList<string> excludeItemTypes)
-        {
-            CheckDisposed();
-
-            var stringBuilder = new StringBuilder("Select Value From ItemValues where Type", 128);
-            if (itemValueTypes.Length == 1)
-            {
-                stringBuilder.Append('=')
-                    .Append(itemValueTypes[0]);
-            }
-            else
-            {
-                stringBuilder.Append(" in (")
-                    .AppendJoin(',', itemValueTypes)
-                    .Append(')');
-            }
-
-            if (withItemTypes.Count > 0)
-            {
-                stringBuilder.Append(" AND ItemId In (select guid from typedbaseitems where type in (")
-                    .AppendJoinInSingleQuotes(',', withItemTypes)
-                    .Append("))");
-            }
-
-            if (excludeItemTypes.Count > 0)
-            {
-                stringBuilder.Append(" AND ItemId not In (select guid from typedbaseitems where type in (")
-                    .AppendJoinInSingleQuotes(',', excludeItemTypes)
-                    .Append("))");
-            }
-
-            stringBuilder.Append(" Group By CleanValue");
-            var commandText = stringBuilder.ToString();
-
-            var list = new List<string>();
-            using (new QueryTimeLogger(Logger, commandText))
-            using (var connection = GetConnection(true))
-            using (var statement = PrepareStatement(connection, commandText))
-            {
-                foreach (var row in statement.ExecuteQuery())
-                {
-                    if (row.TryGetString(0, out var result))
-                    {
-                        list.Add(result);
-                    }
-                }
-            }
-
-            return list;
-        }
-
-
-
-        /// <inheritdoc />
-        public List<MediaAttachment> GetMediaAttachments(MediaAttachmentQuery query)
-        {
-            CheckDisposed();
-
-            ArgumentNullException.ThrowIfNull(query);
-
-            var cmdText = _mediaAttachmentSaveColumnsSelectQuery;
-
-            if (query.Index.HasValue)
-            {
-                cmdText += " AND AttachmentIndex=@AttachmentIndex";
-            }
-
-            cmdText += " order by AttachmentIndex ASC";
-
-            var list = new List<MediaAttachment>();
-            using (var connection = GetConnection(true))
-            using (var statement = PrepareStatement(connection, cmdText))
-            {
-                statement.TryBind("@ItemId", query.ItemId);
-
-                if (query.Index.HasValue)
-                {
-                    statement.TryBind("@AttachmentIndex", query.Index.Value);
-                }
-
-                foreach (var row in statement.ExecuteQuery())
-                {
-                    list.Add(GetMediaAttachment(row));
-                }
-            }
-
-            return list;
-        }
-
-        /// <inheritdoc />
-        public void SaveMediaAttachments(
-            Guid id,
-            IReadOnlyList<MediaAttachment> attachments,
-            CancellationToken cancellationToken)
-        {
-            CheckDisposed();
-            if (id.IsEmpty())
-            {
-                throw new ArgumentException("Guid can't be empty.", nameof(id));
-            }
-
-            ArgumentNullException.ThrowIfNull(attachments);
-
-            cancellationToken.ThrowIfCancellationRequested();
-
-            using (var connection = GetConnection())
-            using (var transaction = connection.BeginTransaction())
-            using (var command = connection.PrepareStatement("delete from mediaattachments where ItemId=@ItemId"))
-            {
-                command.TryBind("@ItemId", id);
-                command.ExecuteNonQuery();
-
-                InsertMediaAttachments(id, attachments, connection, cancellationToken);
-
-                transaction.Commit();
-            }
-        }
-
-        private void InsertMediaAttachments(
-            Guid id,
-            IReadOnlyList<MediaAttachment> attachments,
-            ManagedConnection db,
-            CancellationToken cancellationToken)
-        {
-            const int InsertAtOnce = 10;
-
-            var insertText = new StringBuilder(_mediaAttachmentInsertPrefix);
-            for (var startIndex = 0; startIndex < attachments.Count; startIndex += InsertAtOnce)
-            {
-                var endIndex = Math.Min(attachments.Count, startIndex + InsertAtOnce);
-
-                for (var i = startIndex; i < endIndex; i++)
-                {
-                    insertText.Append("(@ItemId, ");
-
-                    foreach (var column in _mediaAttachmentSaveColumns.Skip(1))
-                    {
-                        insertText.Append('@')
-                            .Append(column)
-                            .Append(i)
-                            .Append(',');
-                    }
-
-                    insertText.Length -= 1;
-
-                    insertText.Append("),");
-                }
-
-                insertText.Length--;
-
-                cancellationToken.ThrowIfCancellationRequested();
-
-                using (var statement = PrepareStatement(db, insertText.ToString()))
-                {
-                    statement.TryBind("@ItemId", id);
-
-                    for (var i = startIndex; i < endIndex; i++)
-                    {
-                        var index = i.ToString(CultureInfo.InvariantCulture);
-
-                        var attachment = attachments[i];
-
-                        statement.TryBind("@AttachmentIndex" + index, attachment.Index);
-                        statement.TryBind("@Codec" + index, attachment.Codec);
-                        statement.TryBind("@CodecTag" + index, attachment.CodecTag);
-                        statement.TryBind("@Comment" + index, attachment.Comment);
-                        statement.TryBind("@Filename" + index, attachment.FileName);
-                        statement.TryBind("@MIMEType" + index, attachment.MimeType);
-                    }
-
-                    statement.ExecuteNonQuery();
-                }
-
-                insertText.Length = _mediaAttachmentInsertPrefix.Length;
-            }
-        }
-
-        /// <summary>
-        /// Gets the attachment.
-        /// </summary>
-        /// <param name="reader">The reader.</param>
-        /// <returns>MediaAttachment.</returns>
-        private MediaAttachment GetMediaAttachment(SqliteDataReader reader)
-        {
-            var item = new MediaAttachment
-            {
-                Index = reader.GetInt32(1)
-            };
-
-            if (reader.TryGetString(2, out var codec))
-            {
-                item.Codec = codec;
-            }
-
-            if (reader.TryGetString(3, out var codecTag))
-            {
-                item.CodecTag = codecTag;
-            }
-
-            if (reader.TryGetString(4, out var comment))
-            {
-                item.Comment = comment;
-            }
-
-            if (reader.TryGetString(5, out var fileName))
-            {
-                item.FileName = fileName;
-            }
-
-            if (reader.TryGetString(6, out var mimeType))
-            {
-                item.MimeType = mimeType;
-            }
-
-            return item;
-        }
-
-#nullable enable
-
-        private readonly struct QueryTimeLogger : IDisposable
-        {
-            private readonly ILogger _logger;
-            private readonly string _commandText;
-            private readonly string _methodName;
-            private readonly long _startTimestamp;
-
-            public QueryTimeLogger(ILogger logger, string commandText, [CallerMemberName] string methodName = "")
-            {
-                _logger = logger;
-                _commandText = commandText;
-                _methodName = methodName;
-                _startTimestamp = logger.IsEnabled(LogLevel.Debug) ? Stopwatch.GetTimestamp() : -1;
-            }
-
-            public void Dispose()
-            {
-                if (_startTimestamp == -1)
-                {
-                    return;
-                }
-
-                var elapsedMs = Stopwatch.GetElapsedTime(_startTimestamp).TotalMilliseconds;
-
-#if DEBUG
-                const int SlowThreshold = 100;
-#else
-                const int SlowThreshold = 10;
-#endif
-
-                if (elapsedMs >= SlowThreshold)
-                {
-                    _logger.LogDebug(
-                        "{Method} query time (slow): {ElapsedMs}ms. Query: {Query}",
-                        _methodName,
-                        elapsedMs,
-                        _commandText);
-                }
-            }
-        }
-    }
-}

+ 0 - 30
Emby.Server.Implementations/Data/SynchronousMode.cs

@@ -1,30 +0,0 @@
-namespace Emby.Server.Implementations.Data;
-
-/// <summary>
-/// The disk synchronization mode, controls how aggressively SQLite will write data
-/// all the way out to physical storage.
-/// </summary>
-public enum SynchronousMode
-{
-    /// <summary>
-    /// SQLite continues without syncing as soon as it has handed data off to the operating system.
-    /// </summary>
-    Off = 0,
-
-    /// <summary>
-    /// SQLite database engine will still sync at the most critical moments.
-    /// </summary>
-    Normal = 1,
-
-    /// <summary>
-    /// SQLite database engine will use the xSync method of the VFS
-    /// to ensure that all content is safely written to the disk surface prior to continuing.
-    /// </summary>
-    Full = 2,
-
-    /// <summary>
-    /// EXTRA synchronous is like FULL with the addition that the directory containing a rollback journal
-    /// is synced after that journal is unlinked to commit a transaction in DELETE mode.
-    /// </summary>
-    Extra = 3
-}

+ 0 - 23
Emby.Server.Implementations/Data/TempStoreMode.cs

@@ -1,23 +0,0 @@
-namespace Emby.Server.Implementations.Data;
-
-/// <summary>
-/// Storage mode used by temporary database files.
-/// </summary>
-public enum TempStoreMode
-{
-    /// <summary>
-    /// The compile-time C preprocessor macro SQLITE_TEMP_STORE
-    /// is used to determine where temporary tables and indices are stored.
-    /// </summary>
-    Default = 0,
-
-    /// <summary>
-    /// Temporary tables and indices are stored in a file.
-    /// </summary>
-    File = 1,
-
-    /// <summary>
-    /// Temporary tables and indices are kept in as if they were pure in-memory databases memory.
-    /// </summary>
-    Memory = 2
-}

+ 2 - 0
Jellyfin.Data/Entities/AttachmentStreamInfo.cs

@@ -7,6 +7,8 @@ public class AttachmentStreamInfo
 {
     public required Guid ItemId { get; set; }
 
+    public required BaseItem Item { get; set; }
+
     public required int Index { get; set; }
 
     public required string Codec { get; set; }

文件差异内容过多而无法显示
+ 272 - 218
Jellyfin.Server.Implementations/Item/BaseItemManager.cs


+ 73 - 0
Jellyfin.Server.Implementations/Item/MediaAttachmentManager.cs

@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Threading;
+using Jellyfin.Data.Entities;
+using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Model.Entities;
+using Microsoft.EntityFrameworkCore;
+
+namespace Jellyfin.Server.Implementations.Item;
+
+/// <summary>
+/// Manager for handling Media Attachments.
+/// </summary>
+/// <param name="dbProvider">Efcore Factory.</param>
+public class MediaAttachmentManager(IDbContextFactory<JellyfinDbContext> dbProvider) : IMediaAttachmentManager
+{
+    /// <inheritdoc />
+    public void SaveMediaAttachments(
+        Guid id,
+        IReadOnlyList<MediaAttachment> attachments,
+        CancellationToken cancellationToken)
+    {
+        using var context = dbProvider.CreateDbContext();
+        using var transaction = context.Database.BeginTransaction();
+        context.AttachmentStreamInfos.Where(e => e.ItemId.Equals(id)).ExecuteDelete();
+        context.AttachmentStreamInfos.AddRange(attachments.Select(e => Map(e, id)));
+        context.SaveChanges();
+        transaction.Commit();
+    }
+
+    /// <inheritdoc />
+    public IReadOnlyList<MediaAttachment> GetMediaAttachments(MediaAttachmentQuery filter)
+    {
+        using var context = dbProvider.CreateDbContext();
+        var query = context.AttachmentStreamInfos.Where(e => e.ItemId.Equals(filter.ItemId));
+        if (filter.Index.HasValue)
+        {
+            query = query.Where(e => e.Index == filter.Index);
+        }
+
+        return query.ToList().Select(Map).ToImmutableArray();
+    }
+
+    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!
+        };
+    }
+}

+ 1 - 1
Jellyfin.Server.Implementations/Item/MediaStreamManager.cs

@@ -18,7 +18,7 @@ namespace Jellyfin.Server.Implementations.Item;
 /// <param name="dbProvider"></param>
 /// <param name="serverApplicationHost"></param>
 /// <param name="localization"></param>
-public class MediaStreamManager(IDbContextFactory<JellyfinDbContext> dbProvider, IServerApplicationHost serverApplicationHost, ILocalizationManager localization)
+public class MediaStreamManager(IDbContextFactory<JellyfinDbContext> dbProvider, IServerApplicationHost serverApplicationHost, ILocalizationManager localization) : IMediaStreamManager
 {
     /// <inheritdoc />
     public void SaveMediaStreams(Guid id, IReadOnlyList<MediaStream> streams, CancellationToken cancellationToken)

+ 11 - 11
Jellyfin.Server.Implementations/Item/PeopleManager.cs

@@ -6,22 +6,22 @@ using Jellyfin.Data.Entities;
 using Jellyfin.Data.Enums;
 using Jellyfin.Extensions;
 using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Persistence;
 using Microsoft.EntityFrameworkCore;
 
 namespace Jellyfin.Server.Implementations.Item;
 
-public class PeopleManager
+/// <summary>
+/// Manager for handling people.
+/// </summary>
+/// <param name="dbProvider">Efcore Factory.</param>
+/// <remarks>
+/// Initializes a new instance of the <see cref="PeopleManager"/> class.
+/// </remarks>
+/// <param name="dbProvider">The EFCore Context factory.</param>
+public class PeopleManager(IDbContextFactory<JellyfinDbContext> dbProvider) : IPeopleManager
 {
-    private readonly IDbContextFactory<JellyfinDbContext> _dbProvider;
-
-    /// <summary>
-    /// Initializes a new instance of the <see cref="PeopleManager"/> class.
-    /// </summary>
-    /// <param name="dbProvider">The EFCore Context factory.</param>
-    public PeopleManager(IDbContextFactory<JellyfinDbContext> dbProvider)
-    {
-        _dbProvider = dbProvider;
-    }
+    private readonly IDbContextFactory<JellyfinDbContext> _dbProvider = dbProvider;
 
     public IReadOnlyList<PersonInfo> GetPeople(InternalPeopleQuery filter)
     {

+ 74 - 126
MediaBrowser.Controller/Persistence/IItemRepository.cs

@@ -7,135 +7,83 @@ using System.Collections.Generic;
 using System.Threading;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Querying;
 
-namespace MediaBrowser.Controller.Persistence
+namespace MediaBrowser.Controller.Persistence;
+
+/// <summary>
+/// Provides an interface to implement an Item repository.
+/// </summary>
+public interface IItemRepository : IDisposable
 {
     /// <summary>
-    /// Provides an interface to implement an Item repository.
+    /// Deletes the item.
+    /// </summary>
+    /// <param name="id">The identifier.</param>
+    void DeleteItem(Guid id);
+
+    /// <summary>
+    /// Saves the items.
+    /// </summary>
+    /// <param name="items">The items.</param>
+    /// <param name="cancellationToken">The cancellation token.</param>
+    void SaveItems(IReadOnlyList<BaseItem> items, CancellationToken cancellationToken);
+
+    void SaveImages(BaseItem item);
+
+    /// <summary>
+    /// Retrieves the item.
+    /// </summary>
+    /// <param name="id">The id.</param>
+    /// <returns>BaseItem.</returns>
+    BaseItem RetrieveItem(Guid id);
+
+    /// <summary>
+    /// Gets the items.
+    /// </summary>
+    /// <param name="filter">The query.</param>
+    /// <returns>QueryResult&lt;BaseItem&gt;.</returns>
+    QueryResult<BaseItem> GetItems(InternalItemsQuery filter);
+
+    /// <summary>
+    /// Gets the item ids list.
+    /// </summary>
+    /// <param name="filter">The query.</param>
+    /// <returns>List&lt;Guid&gt;.</returns>
+    IReadOnlyList<Guid> GetItemIdsList(InternalItemsQuery filter);
+
+
+    /// <summary>
+    /// Gets the item list.
+    /// </summary>
+    /// <param name="filter">The query.</param>
+    /// <returns>List&lt;BaseItem&gt;.</returns>
+    IReadOnlyList<BaseItem> GetItemList(InternalItemsQuery filter);
+
+    /// <summary>
+    /// Updates the inherited values.
     /// </summary>
-    public interface IItemRepository : IDisposable
-    {
-        /// <summary>
-        /// Deletes the item.
-        /// </summary>
-        /// <param name="id">The identifier.</param>
-        void DeleteItem(Guid id);
-
-        /// <summary>
-        /// Saves the items.
-        /// </summary>
-        /// <param name="items">The items.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        void SaveItems(IReadOnlyList<BaseItem> items, CancellationToken cancellationToken);
-
-        void SaveImages(BaseItem item);
-
-        /// <summary>
-        /// Retrieves the item.
-        /// </summary>
-        /// <param name="id">The id.</param>
-        /// <returns>BaseItem.</returns>
-        BaseItem RetrieveItem(Guid id);
-
-        /// <summary>
-        /// Gets the media streams.
-        /// </summary>
-        /// <param name="query">The query.</param>
-        /// <returns>IEnumerable{MediaStream}.</returns>
-        List<MediaStream> GetMediaStreams(MediaStreamQuery query);
-
-        /// <summary>
-        /// Saves the media streams.
-        /// </summary>
-        /// <param name="id">The identifier.</param>
-        /// <param name="streams">The streams.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        void SaveMediaStreams(Guid id, IReadOnlyList<MediaStream> streams, CancellationToken cancellationToken);
-
-        /// <summary>
-        /// Gets the media attachments.
-        /// </summary>
-        /// <param name="query">The query.</param>
-        /// <returns>IEnumerable{MediaAttachment}.</returns>
-        List<MediaAttachment> GetMediaAttachments(MediaAttachmentQuery query);
-
-        /// <summary>
-        /// Saves the media attachments.
-        /// </summary>
-        /// <param name="id">The identifier.</param>
-        /// <param name="attachments">The attachments.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        void SaveMediaAttachments(Guid id, IReadOnlyList<MediaAttachment> attachments, CancellationToken cancellationToken);
-
-        /// <summary>
-        /// Gets the items.
-        /// </summary>
-        /// <param name="query">The query.</param>
-        /// <returns>QueryResult&lt;BaseItem&gt;.</returns>
-        QueryResult<BaseItem> GetItems(InternalItemsQuery query);
-
-        /// <summary>
-        /// Gets the item ids list.
-        /// </summary>
-        /// <param name="query">The query.</param>
-        /// <returns>List&lt;Guid&gt;.</returns>
-        List<Guid> GetItemIdsList(InternalItemsQuery query);
-
-        /// <summary>
-        /// Gets the people.
-        /// </summary>
-        /// <param name="query">The query.</param>
-        /// <returns>List&lt;PersonInfo&gt;.</returns>
-        List<PersonInfo> GetPeople(InternalPeopleQuery query);
-
-        /// <summary>
-        /// Updates the people.
-        /// </summary>
-        /// <param name="itemId">The item identifier.</param>
-        /// <param name="people">The people.</param>
-        void UpdatePeople(Guid itemId, List<PersonInfo> people);
-
-        /// <summary>
-        /// Gets the people names.
-        /// </summary>
-        /// <param name="query">The query.</param>
-        /// <returns>List&lt;System.String&gt;.</returns>
-        List<string> GetPeopleNames(InternalPeopleQuery query);
-
-        /// <summary>
-        /// Gets the item list.
-        /// </summary>
-        /// <param name="query">The query.</param>
-        /// <returns>List&lt;BaseItem&gt;.</returns>
-        List<BaseItem> GetItemList(InternalItemsQuery query);
-
-        /// <summary>
-        /// Updates the inherited values.
-        /// </summary>
-        void UpdateInheritedValues();
-
-        int GetCount(InternalItemsQuery query);
-
-        QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetGenres(InternalItemsQuery query);
-
-        QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetMusicGenres(InternalItemsQuery query);
-
-        QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetStudios(InternalItemsQuery query);
-
-        QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetArtists(InternalItemsQuery query);
-
-        QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetAlbumArtists(InternalItemsQuery query);
-
-        QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetAllArtists(InternalItemsQuery query);
-
-        List<string> GetMusicGenreNames();
-
-        List<string> GetStudioNames();
-
-        List<string> GetGenreNames();
-
-        List<string> GetAllArtistNames();
-    }
+    void UpdateInheritedValues();
+
+    int GetCount(InternalItemsQuery filter);
+
+    QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetGenres(InternalItemsQuery filter);
+
+    QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetMusicGenres(InternalItemsQuery filter);
+
+    QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetStudios(InternalItemsQuery filter);
+
+    QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetArtists(InternalItemsQuery filter);
+
+    QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetAlbumArtists(InternalItemsQuery filter);
+
+    QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetAllArtists(InternalItemsQuery filter);
+
+    IReadOnlyList<string> GetMusicGenreNames();
+
+    IReadOnlyList<string> GetStudioNames();
+
+    IReadOnlyList<string> GetGenreNames();
+
+    IReadOnlyList<string> GetAllArtistNames();
 }

+ 29 - 0
MediaBrowser.Controller/Persistence/IMediaAttachmentManager.cs

@@ -0,0 +1,29 @@
+#nullable disable
+
+#pragma warning disable CS1591
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using MediaBrowser.Model.Entities;
+
+namespace MediaBrowser.Controller.Persistence;
+
+public interface IMediaAttachmentManager
+{
+
+    /// <summary>
+    /// Gets the media attachments.
+    /// </summary>
+    /// <param name="filter">The query.</param>
+    /// <returns>IEnumerable{MediaAttachment}.</returns>
+    IReadOnlyList<MediaAttachment> GetMediaAttachments(MediaAttachmentQuery filter);
+
+    /// <summary>
+    /// Saves the media attachments.
+    /// </summary>
+    /// <param name="id">The identifier.</param>
+    /// <param name="attachments">The attachments.</param>
+    /// <param name="cancellationToken">The cancellation token.</param>
+    void SaveMediaAttachments(Guid id, IReadOnlyList<MediaAttachment> attachments, CancellationToken cancellationToken);
+}

+ 28 - 0
MediaBrowser.Controller/Persistence/IMediaStreamManager.cs

@@ -0,0 +1,28 @@
+#nullable disable
+
+#pragma warning disable CS1591
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using MediaBrowser.Model.Entities;
+
+namespace MediaBrowser.Controller.Persistence;
+
+public interface IMediaStreamManager
+{
+    /// <summary>
+    /// Gets the media streams.
+    /// </summary>
+    /// <param name="filter">The query.</param>
+    /// <returns>IEnumerable{MediaStream}.</returns>
+    List<MediaStream> GetMediaStreams(MediaStreamQuery filter);
+
+    /// <summary>
+    /// Saves the media streams.
+    /// </summary>
+    /// <param name="id">The identifier.</param>
+    /// <param name="streams">The streams.</param>
+    /// <param name="cancellationToken">The cancellation token.</param>
+    void SaveMediaStreams(Guid id, IReadOnlyList<MediaStream> streams, CancellationToken cancellationToken);
+}

+ 34 - 0
MediaBrowser.Controller/Persistence/IPeopleManager.cs

@@ -0,0 +1,34 @@
+#nullable disable
+
+#pragma warning disable CS1591
+
+using System;
+using System.Collections.Generic;
+using MediaBrowser.Controller.Entities;
+
+namespace MediaBrowser.Controller.Persistence;
+
+public interface IPeopleManager
+{
+    /// <summary>
+    /// Gets the people.
+    /// </summary>
+    /// <param name="filter">The query.</param>
+    /// <returns>List&lt;PersonInfo&gt;.</returns>
+    IReadOnlyList<PersonInfo> GetPeople(InternalPeopleQuery filter);
+
+    /// <summary>
+    /// Updates the people.
+    /// </summary>
+    /// <param name="itemId">The item identifier.</param>
+    /// <param name="people">The people.</param>
+    void UpdatePeople(Guid itemId, IReadOnlyList<PersonInfo> people);
+
+    /// <summary>
+    /// Gets the people names.
+    /// </summary>
+    /// <param name="filter">The query.</param>
+    /// <returns>List&lt;System.String&gt;.</returns>
+    IReadOnlyList<string> GetPeopleNames(InternalPeopleQuery filter);
+
+}

部分文件因为文件数量过多而无法显示