diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
deleted file mode 100644
index 8ed72c2082..0000000000
--- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
+++ /dev/null
@@ -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;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The logger.
- protected BaseSqliteRepository(ILogger logger)
- {
- Logger = logger;
- }
-
- ///
- /// Gets or sets the path to the DB file.
- ///
- protected string DbFilePath { get; set; }
-
- ///
- /// Gets the logger.
- ///
- /// The logger.
- protected ILogger Logger { get; }
-
- ///
- /// Gets the cache size.
- ///
- /// The cache size or null.
- protected virtual int? CacheSize => null;
-
- ///
- /// Gets the locking mode. .
- ///
- protected virtual string LockingMode => "NORMAL";
-
- ///
- /// Gets the journal mode. .
- ///
- /// The journal mode.
- protected virtual string JournalMode => "WAL";
-
- ///
- /// Gets the journal size limit. .
- /// The default (-1) is overridden to prevent unconstrained WAL size, as reported by users.
- ///
- /// The journal size limit.
- protected virtual int? JournalSizeLimit => 134_217_728; // 128MiB
-
- ///
- /// Gets the page size.
- ///
- /// The page size or null.
- protected virtual int? PageSize => null;
-
- ///
- /// Gets the temp store mode.
- ///
- /// The temp store mode.
- ///
- protected virtual TempStoreMode TempStore => TempStoreMode.Memory;
-
- ///
- /// Gets the synchronous mode.
- ///
- /// The synchronous mode or null.
- ///
- 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 GetColumnNames(ManagedConnection connection, string table)
- {
- var columnNames = new List();
-
- 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 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);
- }
-
- ///
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- ///
- /// Releases unmanaged and - optionally - managed resources.
- ///
- /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
- 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;
- }
- }
-}
diff --git a/Emby.Server.Implementations/Data/ManagedConnection.cs b/Emby.Server.Implementations/Data/ManagedConnection.cs
deleted file mode 100644
index 860950b303..0000000000
--- a/Emby.Server.Implementations/Data/ManagedConnection.cs
+++ /dev/null
@@ -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 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;
- }
-}
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
deleted file mode 100644
index a650f95556..0000000000
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ /dev/null
@@ -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
-{
- ///
- /// Class SQLiteItemRepository.
- ///
- 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;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// Instance of the interface.
- /// Instance of the interface.
- /// Instance of the interface.
- /// Instance of the interface.
- /// Instance of the interface.
- /// Instance of the interface.
- /// config is null.
- public SqliteItemRepository(
- IServerConfigurationManager config,
- IServerApplicationHost appHost,
- ILogger 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();
- }
-
- ///
- protected override int? CacheSize { get; }
-
- ///
- 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(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)";
- }
-
- ///
- public List GetStudioNames()
- {
- return GetItemValueNames(new[] { 3 }, Array.Empty(), Array.Empty());
- }
-
- ///
- public List GetAllArtistNames()
- {
- return GetItemValueNames(new[] { 0, 1 }, Array.Empty(), Array.Empty());
- }
-
- ///
- public List GetMusicGenreNames()
- {
- return GetItemValueNames(
- new[] { 2 },
- new string[]
- {
- typeof(Audio).FullName,
- typeof(MusicVideo).FullName,
- typeof(MusicAlbum).FullName,
- typeof(MusicArtist).FullName
- },
- Array.Empty());
- }
-
- ///
- public List GetGenreNames()
- {
- return GetItemValueNames(
- new[] { 2 },
- Array.Empty(),
- new string[]
- {
- typeof(Audio).FullName,
- typeof(MusicVideo).FullName,
- typeof(MusicAlbum).FullName,
- typeof(MusicArtist).FullName
- });
- }
-
- private List GetItemValueNames(int[] itemValueTypes, IReadOnlyList withItemTypes, IReadOnlyList 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();
- 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;
- }
-
-
-
- ///
- public List 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();
- 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;
- }
-
- ///
- public void SaveMediaAttachments(
- Guid id,
- IReadOnlyList 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 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;
- }
- }
-
- ///
- /// Gets the attachment.
- ///
- /// The reader.
- /// MediaAttachment.
- 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);
- }
- }
- }
- }
-}
diff --git a/Emby.Server.Implementations/Data/SynchronousMode.cs b/Emby.Server.Implementations/Data/SynchronousMode.cs
deleted file mode 100644
index cde524e2e0..0000000000
--- a/Emby.Server.Implementations/Data/SynchronousMode.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-namespace Emby.Server.Implementations.Data;
-
-///
-/// The disk synchronization mode, controls how aggressively SQLite will write data
-/// all the way out to physical storage.
-///
-public enum SynchronousMode
-{
- ///
- /// SQLite continues without syncing as soon as it has handed data off to the operating system.
- ///
- Off = 0,
-
- ///
- /// SQLite database engine will still sync at the most critical moments.
- ///
- Normal = 1,
-
- ///
- /// 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.
- ///
- Full = 2,
-
- ///
- /// 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.
- ///
- Extra = 3
-}
diff --git a/Emby.Server.Implementations/Data/TempStoreMode.cs b/Emby.Server.Implementations/Data/TempStoreMode.cs
deleted file mode 100644
index d2427ce478..0000000000
--- a/Emby.Server.Implementations/Data/TempStoreMode.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-namespace Emby.Server.Implementations.Data;
-
-///
-/// Storage mode used by temporary database files.
-///
-public enum TempStoreMode
-{
- ///
- /// The compile-time C preprocessor macro SQLITE_TEMP_STORE
- /// is used to determine where temporary tables and indices are stored.
- ///
- Default = 0,
-
- ///
- /// Temporary tables and indices are stored in a file.
- ///
- File = 1,
-
- ///
- /// Temporary tables and indices are kept in as if they were pure in-memory databases memory.
- ///
- Memory = 2
-}
diff --git a/Jellyfin.Data/Entities/AttachmentStreamInfo.cs b/Jellyfin.Data/Entities/AttachmentStreamInfo.cs
index d2483548b8..858465424b 100644
--- a/Jellyfin.Data/Entities/AttachmentStreamInfo.cs
+++ b/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; }
diff --git a/Jellyfin.Server.Implementations/Item/BaseItemManager.cs b/Jellyfin.Server.Implementations/Item/BaseItemManager.cs
index 022f26cd72..66cc765f35 100644
--- a/Jellyfin.Server.Implementations/Item/BaseItemManager.cs
+++ b/Jellyfin.Server.Implementations/Item/BaseItemManager.cs
@@ -34,7 +34,7 @@ namespace Jellyfin.Server.Implementations.Item;
///
/// Handles all storage logic for BaseItems.
///
-public class BaseItemManager : IItemRepository
+public sealed class BaseItemManager : IItemRepository, IDisposable
{
private readonly IDbContextFactory _dbProvider;
private readonly IServerApplicationHost _appHost;
@@ -135,6 +135,7 @@ public class BaseItemManager : IItemRepository
/// so that we can de-serialize properly when we don't have strong types.
///
private static readonly ConcurrentDictionary _typeMap = new ConcurrentDictionary();
+ private bool _disposed;
///
/// Initializes a new instance of the class.
@@ -147,6 +148,17 @@ public class BaseItemManager : IItemRepository
_appHost = appHost;
}
+ ///
+ public void Dispose()
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ _disposed = true;
+ }
+
private QueryResult<(BaseItemDto Item, ItemCounts ItemCounts)> GetItemValues(InternalItemsQuery filter, int[] itemValueTypes, string returnType)
{
ArgumentNullException.ThrowIfNull(filter);
@@ -338,106 +350,148 @@ public class BaseItemManager : IItemRepository
}
///
- public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetAllArtists(InternalItemsQuery query)
+ public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetAllArtists(InternalItemsQuery filter)
{
- return GetItemValues(query, new[] { 0, 1 }, typeof(MusicArtist).FullName);
+ return GetItemValues(filter, new[] { 0, 1 }, typeof(MusicArtist).FullName!);
}
///
- public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetArtists(InternalItemsQuery query)
+ public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetArtists(InternalItemsQuery filter)
{
- return GetItemValues(query, new[] { 0 }, typeof(MusicArtist).FullName);
+ return GetItemValues(filter, new[] { 0 }, typeof(MusicArtist).FullName!);
}
///
- public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetAlbumArtists(InternalItemsQuery query)
+ public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetAlbumArtists(InternalItemsQuery filter)
{
- return GetItemValues(query, new[] { 1 }, typeof(MusicArtist).FullName);
+ return GetItemValues(filter, new[] { 1 }, typeof(MusicArtist).FullName!);
}
///
- public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetStudios(InternalItemsQuery query)
+ public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetStudios(InternalItemsQuery filter)
{
- return GetItemValues(query, new[] { 3 }, typeof(Studio).FullName);
+ return GetItemValues(filter, new[] { 3 }, typeof(Studio).FullName!);
}
///
- public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetGenres(InternalItemsQuery query)
+ public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetGenres(InternalItemsQuery filter)
{
- return GetItemValues(query, new[] { 2 }, typeof(Genre).FullName);
+ return GetItemValues(filter, new[] { 2 }, typeof(Genre).FullName!);
}
///
- public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetMusicGenres(InternalItemsQuery query)
+ public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetMusicGenres(InternalItemsQuery filter)
{
- return GetItemValues(query, new[] { 2 }, typeof(MusicGenre).FullName);
+ return GetItemValues(filter, new[] { 2 }, typeof(MusicGenre).FullName!);
+ }
+
+ ///
+ public IReadOnlyList GetStudioNames()
+ {
+ return GetItemValueNames(new[] { 3 }, Array.Empty(), Array.Empty());
+ }
+
+ ///
+ public IReadOnlyList GetAllArtistNames()
+ {
+ return GetItemValueNames(new[] { 0, 1 }, Array.Empty(), Array.Empty());
+ }
+
+ ///
+ public IReadOnlyList GetMusicGenreNames()
+ {
+ return GetItemValueNames(
+ new[] { 2 },
+ new string[]
+ {
+ typeof(Audio).FullName!,
+ typeof(MusicVideo).FullName!,
+ typeof(MusicAlbum).FullName!,
+ typeof(MusicArtist).FullName!
+ },
+ Array.Empty());
+ }
+
+ ///
+ public IReadOnlyList GetGenreNames()
+ {
+ return GetItemValueNames(
+ new[] { 2 },
+ Array.Empty(),
+ new string[]
+ {
+ typeof(Audio).FullName!,
+ typeof(MusicVideo).FullName!,
+ typeof(MusicAlbum).FullName!,
+ typeof(MusicArtist).FullName!
+ });
}
///
- public QueryResult GetItems(InternalItemsQuery query)
+ public QueryResult GetItems(InternalItemsQuery filter)
{
- ArgumentNullException.ThrowIfNull(query);
- if (!query.EnableTotalRecordCount || (!query.Limit.HasValue && (query.StartIndex ?? 0) == 0))
+ ArgumentNullException.ThrowIfNull(filter);
+ if (!filter.EnableTotalRecordCount || (!filter.Limit.HasValue && (filter.StartIndex ?? 0) == 0))
{
- var returnList = GetItemList(query);
+ var returnList = GetItemList(filter);
return new QueryResult(
- query.StartIndex,
+ filter.StartIndex,
returnList.Count,
returnList);
}
- PrepareFilterQuery(query);
+ PrepareFilterQuery(filter);
var result = new QueryResult();
using var context = _dbProvider.CreateDbContext();
- var dbQuery = TranslateQuery(context.BaseItems, context, query)
+ var dbQuery = TranslateQuery(context.BaseItems, context, filter)
.DistinctBy(e => e.Id);
- if (query.EnableTotalRecordCount)
+ if (filter.EnableTotalRecordCount)
{
result.TotalRecordCount = dbQuery.Count();
}
- if (query.Limit.HasValue || query.StartIndex.HasValue)
+ if (filter.Limit.HasValue || filter.StartIndex.HasValue)
{
- var offset = query.StartIndex ?? 0;
+ var offset = filter.StartIndex ?? 0;
if (offset > 0)
{
dbQuery = dbQuery.Skip(offset);
}
- if (query.Limit.HasValue)
+ if (filter.Limit.HasValue)
{
- dbQuery = dbQuery.Take(query.Limit.Value);
+ dbQuery = dbQuery.Take(filter.Limit.Value);
}
}
result.Items = dbQuery.ToList().Select(DeserialiseBaseItem).ToImmutableArray();
- result.StartIndex = query.StartIndex ?? 0;
+ result.StartIndex = filter.StartIndex ?? 0;
return result;
}
///
- public IReadOnlyList GetItemList(InternalItemsQuery query)
+ public IReadOnlyList GetItemList(InternalItemsQuery filter)
{
- ArgumentNullException.ThrowIfNull(query);
- PrepareFilterQuery(query);
+ ArgumentNullException.ThrowIfNull(filter);
+ PrepareFilterQuery(filter);
using var context = _dbProvider.CreateDbContext();
- var dbQuery = TranslateQuery(context.BaseItems, context, query)
+ var dbQuery = TranslateQuery(context.BaseItems, context, filter)
.DistinctBy(e => e.Id);
- if (query.Limit.HasValue || query.StartIndex.HasValue)
+ if (filter.Limit.HasValue || filter.StartIndex.HasValue)
{
- var offset = query.StartIndex ?? 0;
+ var offset = filter.StartIndex ?? 0;
if (offset > 0)
{
dbQuery = dbQuery.Skip(offset);
}
- if (query.Limit.HasValue)
+ if (filter.Limit.HasValue)
{
- dbQuery = dbQuery.Take(query.Limit.Value);
+ dbQuery = dbQuery.Take(filter.Limit.Value);
}
}
@@ -445,14 +499,14 @@ public class BaseItemManager : IItemRepository
}
///
- public int GetCount(InternalItemsQuery query)
+ public int GetCount(InternalItemsQuery filter)
{
- ArgumentNullException.ThrowIfNull(query);
+ ArgumentNullException.ThrowIfNull(filter);
// Hack for right now since we currently don't support filtering out these duplicates within a query
- PrepareFilterQuery(query);
+ PrepareFilterQuery(filter);
using var context = _dbProvider.CreateDbContext();
- var dbQuery = TranslateQuery(context.BaseItems, context, query);
+ var dbQuery = TranslateQuery(context.BaseItems, context, filter);
return dbQuery.Count();
}
@@ -460,16 +514,16 @@ public class BaseItemManager : IItemRepository
private IQueryable TranslateQuery(
IQueryable baseQuery,
JellyfinDbContext context,
- InternalItemsQuery query)
+ InternalItemsQuery filter)
{
- var minWidth = query.MinWidth;
- var maxWidth = query.MaxWidth;
+ var minWidth = filter.MinWidth;
+ var maxWidth = filter.MaxWidth;
var now = DateTime.UtcNow;
- if (query.IsHD.HasValue)
+ if (filter.IsHD.HasValue)
{
const int Threshold = 1200;
- if (query.IsHD.Value)
+ if (filter.IsHD.Value)
{
minWidth = Threshold;
}
@@ -479,10 +533,10 @@ public class BaseItemManager : IItemRepository
}
}
- if (query.Is4K.HasValue)
+ if (filter.Is4K.HasValue)
{
const int Threshold = 3800;
- if (query.Is4K.Value)
+ if (filter.Is4K.Value)
{
minWidth = Threshold;
}
@@ -497,9 +551,9 @@ public class BaseItemManager : IItemRepository
baseQuery = baseQuery.Where(e => e.Width >= minWidth);
}
- if (query.MinHeight.HasValue)
+ if (filter.MinHeight.HasValue)
{
- baseQuery = baseQuery.Where(e => e.Height >= query.MinHeight);
+ baseQuery = baseQuery.Where(e => e.Height >= filter.MinHeight);
}
if (maxWidth.HasValue)
@@ -507,41 +561,41 @@ public class BaseItemManager : IItemRepository
baseQuery = baseQuery.Where(e => e.Width >= maxWidth);
}
- if (query.MaxHeight.HasValue)
+ if (filter.MaxHeight.HasValue)
{
- baseQuery = baseQuery.Where(e => e.Height <= query.MaxHeight);
+ baseQuery = baseQuery.Where(e => e.Height <= filter.MaxHeight);
}
- if (query.IsLocked.HasValue)
+ if (filter.IsLocked.HasValue)
{
- baseQuery = baseQuery.Where(e => e.IsLocked == query.IsLocked);
+ baseQuery = baseQuery.Where(e => e.IsLocked == filter.IsLocked);
}
- var tags = query.Tags.ToList();
- var excludeTags = query.ExcludeTags.ToList();
+ var tags = filter.Tags.ToList();
+ var excludeTags = filter.ExcludeTags.ToList();
- if (query.IsMovie == true)
+ if (filter.IsMovie == true)
{
- if (query.IncludeItemTypes.Length == 0
- || query.IncludeItemTypes.Contains(BaseItemKind.Movie)
- || query.IncludeItemTypes.Contains(BaseItemKind.Trailer))
+ if (filter.IncludeItemTypes.Length == 0
+ || filter.IncludeItemTypes.Contains(BaseItemKind.Movie)
+ || filter.IncludeItemTypes.Contains(BaseItemKind.Trailer))
{
baseQuery = baseQuery.Where(e => e.IsMovie);
}
}
- else if (query.IsMovie.HasValue)
+ else if (filter.IsMovie.HasValue)
{
- baseQuery = baseQuery.Where(e => e.IsMovie == query.IsMovie);
+ baseQuery = baseQuery.Where(e => e.IsMovie == filter.IsMovie);
}
- if (query.IsSeries.HasValue)
+ if (filter.IsSeries.HasValue)
{
- baseQuery = baseQuery.Where(e => e.IsSeries == query.IsSeries);
+ baseQuery = baseQuery.Where(e => e.IsSeries == filter.IsSeries);
}
- if (query.IsSports.HasValue)
+ if (filter.IsSports.HasValue)
{
- if (query.IsSports.Value)
+ if (filter.IsSports.Value)
{
tags.Add("Sports");
}
@@ -551,9 +605,9 @@ public class BaseItemManager : IItemRepository
}
}
- if (query.IsNews.HasValue)
+ if (filter.IsNews.HasValue)
{
- if (query.IsNews.Value)
+ if (filter.IsNews.Value)
{
tags.Add("News");
}
@@ -563,9 +617,9 @@ public class BaseItemManager : IItemRepository
}
}
- if (query.IsKids.HasValue)
+ if (filter.IsKids.HasValue)
{
- if (query.IsKids.Value)
+ if (filter.IsKids.Value)
{
tags.Add("Kids");
}
@@ -575,21 +629,21 @@ public class BaseItemManager : IItemRepository
}
}
- if (!string.IsNullOrEmpty(query.SearchTerm))
+ if (!string.IsNullOrEmpty(filter.SearchTerm))
{
- baseQuery = baseQuery.Where(e => e.CleanName!.Contains(query.SearchTerm, StringComparison.InvariantCultureIgnoreCase) || (e.OriginalTitle != null && e.OriginalTitle.Contains(query.SearchTerm, StringComparison.InvariantCultureIgnoreCase)));
+ baseQuery = baseQuery.Where(e => e.CleanName!.Contains(filter.SearchTerm, StringComparison.InvariantCultureIgnoreCase) || (e.OriginalTitle != null && e.OriginalTitle.Contains(filter.SearchTerm, StringComparison.InvariantCultureIgnoreCase)));
}
- if (query.IsFolder.HasValue)
+ if (filter.IsFolder.HasValue)
{
- baseQuery = baseQuery.Where(e => e.IsFolder == query.IsFolder);
+ baseQuery = baseQuery.Where(e => e.IsFolder == filter.IsFolder);
}
- var includeTypes = query.IncludeItemTypes;
+ var includeTypes = filter.IncludeItemTypes;
// Only specify excluded types if no included types are specified
- if (query.IncludeItemTypes.Length == 0)
+ if (filter.IncludeItemTypes.Length == 0)
{
- var excludeTypes = query.ExcludeItemTypes;
+ var excludeTypes = filter.ExcludeItemTypes;
if (excludeTypes.Length == 1)
{
if (_baseItemKindNames.TryGetValue(excludeTypes[0], out var excludeTypeName))
@@ -632,82 +686,82 @@ public class BaseItemManager : IItemRepository
baseQuery = baseQuery.Where(e => includeTypeName.Contains(e.Type));
}
- if (query.ChannelIds.Count == 1)
+ if (filter.ChannelIds.Count == 1)
{
- baseQuery = baseQuery.Where(e => e.ChannelId == query.ChannelIds[0].ToString("N", CultureInfo.InvariantCulture));
+ baseQuery = baseQuery.Where(e => e.ChannelId == filter.ChannelIds[0].ToString("N", CultureInfo.InvariantCulture));
}
- else if (query.ChannelIds.Count > 1)
+ else if (filter.ChannelIds.Count > 1)
{
- baseQuery = baseQuery.Where(e => query.ChannelIds.Select(f => f.ToString("N", CultureInfo.InvariantCulture)).Contains(e.ChannelId));
+ baseQuery = baseQuery.Where(e => filter.ChannelIds.Select(f => f.ToString("N", CultureInfo.InvariantCulture)).Contains(e.ChannelId));
}
- if (!query.ParentId.IsEmpty())
+ if (!filter.ParentId.IsEmpty())
{
- baseQuery = baseQuery.Where(e => e.ParentId.Equals(query.ParentId));
+ baseQuery = baseQuery.Where(e => e.ParentId.Equals(filter.ParentId));
}
- if (!string.IsNullOrWhiteSpace(query.Path))
+ if (!string.IsNullOrWhiteSpace(filter.Path))
{
- baseQuery = baseQuery.Where(e => e.Path == query.Path);
+ baseQuery = baseQuery.Where(e => e.Path == filter.Path);
}
- if (!string.IsNullOrWhiteSpace(query.PresentationUniqueKey))
+ if (!string.IsNullOrWhiteSpace(filter.PresentationUniqueKey))
{
- baseQuery = baseQuery.Where(e => e.PresentationUniqueKey == query.PresentationUniqueKey);
+ baseQuery = baseQuery.Where(e => e.PresentationUniqueKey == filter.PresentationUniqueKey);
}
- if (query.MinCommunityRating.HasValue)
+ if (filter.MinCommunityRating.HasValue)
{
- baseQuery = baseQuery.Where(e => e.CommunityRating >= query.MinCommunityRating);
+ baseQuery = baseQuery.Where(e => e.CommunityRating >= filter.MinCommunityRating);
}
- if (query.MinIndexNumber.HasValue)
+ if (filter.MinIndexNumber.HasValue)
{
- baseQuery = baseQuery.Where(e => e.IndexNumber >= query.MinIndexNumber);
+ baseQuery = baseQuery.Where(e => e.IndexNumber >= filter.MinIndexNumber);
}
- if (query.MinParentAndIndexNumber.HasValue)
+ if (filter.MinParentAndIndexNumber.HasValue)
{
baseQuery = baseQuery
- .Where(e => (e.ParentIndexNumber == query.MinParentAndIndexNumber.Value.ParentIndexNumber && e.IndexNumber >= query.MinParentAndIndexNumber.Value.IndexNumber) || e.ParentIndexNumber > query.MinParentAndIndexNumber.Value.ParentIndexNumber);
+ .Where(e => (e.ParentIndexNumber == filter.MinParentAndIndexNumber.Value.ParentIndexNumber && e.IndexNumber >= filter.MinParentAndIndexNumber.Value.IndexNumber) || e.ParentIndexNumber > filter.MinParentAndIndexNumber.Value.ParentIndexNumber);
}
- if (query.MinDateCreated.HasValue)
+ if (filter.MinDateCreated.HasValue)
{
- baseQuery = baseQuery.Where(e => e.DateCreated >= query.MinDateCreated);
+ baseQuery = baseQuery.Where(e => e.DateCreated >= filter.MinDateCreated);
}
- if (query.MinDateLastSaved.HasValue)
+ if (filter.MinDateLastSaved.HasValue)
{
- baseQuery = baseQuery.Where(e => e.DateLastSaved != null && e.DateLastSaved >= query.MinDateLastSaved.Value);
+ baseQuery = baseQuery.Where(e => e.DateLastSaved != null && e.DateLastSaved >= filter.MinDateLastSaved.Value);
}
- if (query.MinDateLastSavedForUser.HasValue)
+ if (filter.MinDateLastSavedForUser.HasValue)
{
- baseQuery = baseQuery.Where(e => e.DateLastSaved != null && e.DateLastSaved >= query.MinDateLastSavedForUser.Value);
+ baseQuery = baseQuery.Where(e => e.DateLastSaved != null && e.DateLastSaved >= filter.MinDateLastSavedForUser.Value);
}
- if (query.IndexNumber.HasValue)
+ if (filter.IndexNumber.HasValue)
{
- baseQuery = baseQuery.Where(e => e.IndexNumber == query.IndexNumber.Value);
+ baseQuery = baseQuery.Where(e => e.IndexNumber == filter.IndexNumber.Value);
}
- if (query.ParentIndexNumber.HasValue)
+ if (filter.ParentIndexNumber.HasValue)
{
- baseQuery = baseQuery.Where(e => e.ParentIndexNumber == query.ParentIndexNumber.Value);
+ baseQuery = baseQuery.Where(e => e.ParentIndexNumber == filter.ParentIndexNumber.Value);
}
- if (query.ParentIndexNumberNotEquals.HasValue)
+ if (filter.ParentIndexNumberNotEquals.HasValue)
{
- baseQuery = baseQuery.Where(e => e.ParentIndexNumber != query.ParentIndexNumberNotEquals.Value || e.ParentIndexNumber == null);
+ baseQuery = baseQuery.Where(e => e.ParentIndexNumber != filter.ParentIndexNumberNotEquals.Value || e.ParentIndexNumber == null);
}
- var minEndDate = query.MinEndDate;
- var maxEndDate = query.MaxEndDate;
+ var minEndDate = filter.MinEndDate;
+ var maxEndDate = filter.MaxEndDate;
- if (query.HasAired.HasValue)
+ if (filter.HasAired.HasValue)
{
- if (query.HasAired.Value)
+ if (filter.HasAired.Value)
{
maxEndDate = DateTime.UtcNow;
}
@@ -727,34 +781,34 @@ public class BaseItemManager : IItemRepository
baseQuery = baseQuery.Where(e => e.EndDate <= maxEndDate);
}
- if (query.MinStartDate.HasValue)
+ if (filter.MinStartDate.HasValue)
{
- baseQuery = baseQuery.Where(e => e.StartDate >= query.MinStartDate.Value);
+ baseQuery = baseQuery.Where(e => e.StartDate >= filter.MinStartDate.Value);
}
- if (query.MaxStartDate.HasValue)
+ if (filter.MaxStartDate.HasValue)
{
- baseQuery = baseQuery.Where(e => e.StartDate <= query.MaxStartDate.Value);
+ baseQuery = baseQuery.Where(e => e.StartDate <= filter.MaxStartDate.Value);
}
- if (query.MinPremiereDate.HasValue)
+ if (filter.MinPremiereDate.HasValue)
{
- baseQuery = baseQuery.Where(e => e.PremiereDate <= query.MinPremiereDate.Value);
+ baseQuery = baseQuery.Where(e => e.PremiereDate <= filter.MinPremiereDate.Value);
}
- if (query.MaxPremiereDate.HasValue)
+ if (filter.MaxPremiereDate.HasValue)
{
- baseQuery = baseQuery.Where(e => e.PremiereDate <= query.MaxPremiereDate.Value);
+ baseQuery = baseQuery.Where(e => e.PremiereDate <= filter.MaxPremiereDate.Value);
}
- if (query.TrailerTypes.Length > 0)
+ if (filter.TrailerTypes.Length > 0)
{
- baseQuery = baseQuery.Where(e => query.TrailerTypes.Any(f => e.TrailerTypes!.Contains(f.ToString(), StringComparison.OrdinalIgnoreCase)));
+ baseQuery = baseQuery.Where(e => filter.TrailerTypes.Any(f => e.TrailerTypes!.Contains(f.ToString(), StringComparison.OrdinalIgnoreCase)));
}
- if (query.IsAiring.HasValue)
+ if (filter.IsAiring.HasValue)
{
- if (query.IsAiring.Value)
+ if (filter.IsAiring.Value)
{
baseQuery = baseQuery.Where(e => e.StartDate <= now && e.EndDate >= now);
}
@@ -764,20 +818,20 @@ public class BaseItemManager : IItemRepository
}
}
- if (query.PersonIds.Length > 0)
+ if (filter.PersonIds.Length > 0)
{
baseQuery = baseQuery
.Where(e =>
- context.Peoples.Where(w => context.BaseItems.Where(w => query.PersonIds.Contains(w.Id)).Any(f => f.Name == w.Name))
+ context.Peoples.Where(w => context.BaseItems.Where(w => filter.PersonIds.Contains(w.Id)).Any(f => f.Name == w.Name))
.Any(f => f.ItemId.Equals(e.Id)));
}
- if (!string.IsNullOrWhiteSpace(query.Person))
+ if (!string.IsNullOrWhiteSpace(filter.Person))
{
- baseQuery = baseQuery.Where(e => e.Peoples!.Any(f => f.Name == query.Person));
+ baseQuery = baseQuery.Where(e => e.Peoples!.Any(f => f.Name == filter.Person));
}
- if (!string.IsNullOrWhiteSpace(query.MinSortName))
+ if (!string.IsNullOrWhiteSpace(filter.MinSortName))
{
// this does not makes sense.
// baseQuery = baseQuery.Where(e => e.SortName >= query.MinSortName);
@@ -785,132 +839,132 @@ public class BaseItemManager : IItemRepository
// statement?.TryBind("@MinSortName", query.MinSortName);
}
- if (!string.IsNullOrWhiteSpace(query.ExternalSeriesId))
+ if (!string.IsNullOrWhiteSpace(filter.ExternalSeriesId))
{
- baseQuery = baseQuery.Where(e => e.ExternalSeriesId == query.ExternalSeriesId);
+ baseQuery = baseQuery.Where(e => e.ExternalSeriesId == filter.ExternalSeriesId);
}
- if (!string.IsNullOrWhiteSpace(query.ExternalId))
+ if (!string.IsNullOrWhiteSpace(filter.ExternalId))
{
- baseQuery = baseQuery.Where(e => e.ExternalId == query.ExternalId);
+ baseQuery = baseQuery.Where(e => e.ExternalId == filter.ExternalId);
}
- if (!string.IsNullOrWhiteSpace(query.Name))
+ if (!string.IsNullOrWhiteSpace(filter.Name))
{
- var cleanName = GetCleanValue(query.Name);
+ var cleanName = GetCleanValue(filter.Name);
baseQuery = baseQuery.Where(e => e.CleanName == cleanName);
}
// These are the same, for now
- var nameContains = query.NameContains;
+ var nameContains = filter.NameContains;
if (!string.IsNullOrWhiteSpace(nameContains))
{
baseQuery = baseQuery.Where(e =>
- e.CleanName == query.NameContains
- || e.OriginalTitle!.Contains(query.NameContains!, StringComparison.Ordinal));
+ e.CleanName == filter.NameContains
+ || e.OriginalTitle!.Contains(filter.NameContains!, StringComparison.Ordinal));
}
- if (!string.IsNullOrWhiteSpace(query.NameStartsWith))
+ if (!string.IsNullOrWhiteSpace(filter.NameStartsWith))
{
- baseQuery = baseQuery.Where(e => e.SortName!.Contains(query.NameStartsWith, StringComparison.OrdinalIgnoreCase));
+ baseQuery = baseQuery.Where(e => e.SortName!.Contains(filter.NameStartsWith, StringComparison.OrdinalIgnoreCase));
}
- if (!string.IsNullOrWhiteSpace(query.NameStartsWithOrGreater))
+ if (!string.IsNullOrWhiteSpace(filter.NameStartsWithOrGreater))
{
// i hate this
- baseQuery = baseQuery.Where(e => e.SortName![0] > query.NameStartsWithOrGreater[0]);
+ baseQuery = baseQuery.Where(e => e.SortName![0] > filter.NameStartsWithOrGreater[0]);
}
- if (!string.IsNullOrWhiteSpace(query.NameLessThan))
+ if (!string.IsNullOrWhiteSpace(filter.NameLessThan))
{
// i hate this
- baseQuery = baseQuery.Where(e => e.SortName![0] < query.NameLessThan[0]);
+ baseQuery = baseQuery.Where(e => e.SortName![0] < filter.NameLessThan[0]);
}
- if (query.ImageTypes.Length > 0)
+ if (filter.ImageTypes.Length > 0)
{
- baseQuery = baseQuery.Where(e => query.ImageTypes.Any(f => e.Images!.Contains(f.ToString(), StringComparison.InvariantCulture)));
+ baseQuery = baseQuery.Where(e => filter.ImageTypes.Any(f => e.Images!.Contains(f.ToString(), StringComparison.InvariantCulture)));
}
- if (query.IsLiked.HasValue)
+ if (filter.IsLiked.HasValue)
{
baseQuery = baseQuery
- .Where(e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(query.User!.Id) && f.Key == e.UserDataKey)!.Rating >= UserItemData.MinLikeValue);
+ .Where(e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(filter.User!.Id) && f.Key == e.UserDataKey)!.Rating >= UserItemData.MinLikeValue);
}
- if (query.IsFavoriteOrLiked.HasValue)
+ if (filter.IsFavoriteOrLiked.HasValue)
{
baseQuery = baseQuery
- .Where(e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(query.User!.Id) && f.Key == e.UserDataKey)!.IsFavorite == query.IsFavoriteOrLiked);
+ .Where(e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(filter.User!.Id) && f.Key == e.UserDataKey)!.IsFavorite == filter.IsFavoriteOrLiked);
}
- if (query.IsFavorite.HasValue)
+ if (filter.IsFavorite.HasValue)
{
baseQuery = baseQuery
- .Where(e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(query.User!.Id) && f.Key == e.UserDataKey)!.IsFavorite == query.IsFavorite);
+ .Where(e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(filter.User!.Id) && f.Key == e.UserDataKey)!.IsFavorite == filter.IsFavorite);
}
- if (query.IsPlayed.HasValue)
+ if (filter.IsPlayed.HasValue)
{
baseQuery = baseQuery
- .Where(e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(query.User!.Id) && f.Key == e.UserDataKey)!.Played == query.IsPlayed.Value);
+ .Where(e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(filter.User!.Id) && f.Key == e.UserDataKey)!.Played == filter.IsPlayed.Value);
}
- if (query.IsResumable.HasValue)
+ if (filter.IsResumable.HasValue)
{
- if (query.IsResumable.Value)
+ if (filter.IsResumable.Value)
{
baseQuery = baseQuery
- .Where(e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(query.User!.Id) && f.Key == e.UserDataKey)!.PlaybackPositionTicks > 0);
+ .Where(e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(filter.User!.Id) && f.Key == e.UserDataKey)!.PlaybackPositionTicks > 0);
}
else
{
baseQuery = baseQuery
- .Where(e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(query.User!.Id) && f.Key == e.UserDataKey)!.PlaybackPositionTicks == 0);
+ .Where(e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(filter.User!.Id) && f.Key == e.UserDataKey)!.PlaybackPositionTicks == 0);
}
}
- var artistQuery = context.BaseItems.Where(w => query.ArtistIds.Contains(w.Id));
+ var artistQuery = context.BaseItems.Where(w => filter.ArtistIds.Contains(w.Id));
- if (query.ArtistIds.Length > 0)
+ if (filter.ArtistIds.Length > 0)
{
baseQuery = baseQuery
.Where(e => e.ItemValues!.Any(f => f.Type <= 1 && artistQuery.Any(w => w.CleanName == f.CleanValue)));
}
- if (query.AlbumArtistIds.Length > 0)
+ if (filter.AlbumArtistIds.Length > 0)
{
baseQuery = baseQuery
.Where(e => e.ItemValues!.Any(f => f.Type == 1 && artistQuery.Any(w => w.CleanName == f.CleanValue)));
}
- if (query.ContributingArtistIds.Length > 0)
+ if (filter.ContributingArtistIds.Length > 0)
{
- var contributingArtists = context.BaseItems.Where(e => query.ContributingArtistIds.Contains(e.Id));
+ var contributingArtists = context.BaseItems.Where(e => filter.ContributingArtistIds.Contains(e.Id));
baseQuery = baseQuery.Where(e => e.ItemValues!.Any(f => f.Type == 0 && contributingArtists.Any(w => w.CleanName == f.CleanValue)));
}
- if (query.AlbumIds.Length > 0)
+ if (filter.AlbumIds.Length > 0)
{
- baseQuery = baseQuery.Where(e => context.BaseItems.Where(e => query.AlbumIds.Contains(e.Id)).Any(f => f.Name == e.Album));
+ baseQuery = baseQuery.Where(e => context.BaseItems.Where(e => filter.AlbumIds.Contains(e.Id)).Any(f => f.Name == e.Album));
}
- if (query.ExcludeArtistIds.Length > 0)
+ if (filter.ExcludeArtistIds.Length > 0)
{
- var excludeArtistQuery = context.BaseItems.Where(w => query.ExcludeArtistIds.Contains(w.Id));
+ var excludeArtistQuery = context.BaseItems.Where(w => filter.ExcludeArtistIds.Contains(w.Id));
baseQuery = baseQuery
.Where(e => !e.ItemValues!.Any(f => f.Type <= 1 && artistQuery.Any(w => w.CleanName == f.CleanValue)));
}
- if (query.GenreIds.Count > 0)
+ if (filter.GenreIds.Count > 0)
{
baseQuery = baseQuery
- .Where(e => e.ItemValues!.Any(f => f.Type == 2 && context.BaseItems.Where(w => query.GenreIds.Contains(w.Id)).Any(w => w.CleanName == f.CleanValue)));
+ .Where(e => e.ItemValues!.Any(f => f.Type == 2 && context.BaseItems.Where(w => filter.GenreIds.Contains(w.Id)).Any(w => w.CleanName == f.CleanValue)));
}
- if (query.Genres.Count > 0)
+ if (filter.Genres.Count > 0)
{
- var cleanGenres = query.Genres.Select(e => GetCleanValue(e)).ToArray();
+ var cleanGenres = filter.Genres.Select(e => GetCleanValue(e)).ToArray();
baseQuery = baseQuery
.Where(e => e.ItemValues!.Any(f => f.Type == 2 && cleanGenres.Contains(f.CleanValue)));
}
@@ -929,82 +983,82 @@ public class BaseItemManager : IItemRepository
.Where(e => !e.ItemValues!.Any(f => f.Type == 4 && cleanValues.Contains(f.CleanValue)));
}
- if (query.StudioIds.Length > 0)
+ if (filter.StudioIds.Length > 0)
{
baseQuery = baseQuery
- .Where(e => e.ItemValues!.Any(f => f.Type == 3 && context.BaseItems.Where(w => query.StudioIds.Contains(w.Id)).Any(w => w.CleanName == f.CleanValue)));
+ .Where(e => e.ItemValues!.Any(f => f.Type == 3 && context.BaseItems.Where(w => filter.StudioIds.Contains(w.Id)).Any(w => w.CleanName == f.CleanValue)));
}
- if (query.OfficialRatings.Length > 0)
+ if (filter.OfficialRatings.Length > 0)
{
baseQuery = baseQuery
- .Where(e => query.OfficialRatings.Contains(e.OfficialRating));
+ .Where(e => filter.OfficialRatings.Contains(e.OfficialRating));
}
- if (query.HasParentalRating ?? false)
+ if (filter.HasParentalRating ?? false)
{
- if (query.MinParentalRating.HasValue)
+ if (filter.MinParentalRating.HasValue)
{
baseQuery = baseQuery
- .Where(e => e.InheritedParentalRatingValue >= query.MinParentalRating.Value);
+ .Where(e => e.InheritedParentalRatingValue >= filter.MinParentalRating.Value);
}
- if (query.MaxParentalRating.HasValue)
+ if (filter.MaxParentalRating.HasValue)
{
baseQuery = baseQuery
- .Where(e => e.InheritedParentalRatingValue < query.MaxParentalRating.Value);
+ .Where(e => e.InheritedParentalRatingValue < filter.MaxParentalRating.Value);
}
}
- else if (query.BlockUnratedItems.Length > 0)
+ else if (filter.BlockUnratedItems.Length > 0)
{
- if (query.MinParentalRating.HasValue)
+ if (filter.MinParentalRating.HasValue)
{
- if (query.MaxParentalRating.HasValue)
+ if (filter.MaxParentalRating.HasValue)
{
baseQuery = baseQuery
- .Where(e => (e.InheritedParentalRatingValue == null && !query.BlockUnratedItems.Select(e => e.ToString()).Contains(e.UnratedType))
- || (e.InheritedParentalRatingValue >= query.MinParentalRating && e.InheritedParentalRatingValue <= query.MaxParentalRating));
+ .Where(e => (e.InheritedParentalRatingValue == null && !filter.BlockUnratedItems.Select(e => e.ToString()).Contains(e.UnratedType))
+ || (e.InheritedParentalRatingValue >= filter.MinParentalRating && e.InheritedParentalRatingValue <= filter.MaxParentalRating));
}
else
{
baseQuery = baseQuery
- .Where(e => (e.InheritedParentalRatingValue == null && !query.BlockUnratedItems.Select(e => e.ToString()).Contains(e.UnratedType))
- || e.InheritedParentalRatingValue >= query.MinParentalRating);
+ .Where(e => (e.InheritedParentalRatingValue == null && !filter.BlockUnratedItems.Select(e => e.ToString()).Contains(e.UnratedType))
+ || e.InheritedParentalRatingValue >= filter.MinParentalRating);
}
}
else
{
baseQuery = baseQuery
- .Where(e => e.InheritedParentalRatingValue != null && !query.BlockUnratedItems.Select(e => e.ToString()).Contains(e.UnratedType));
+ .Where(e => e.InheritedParentalRatingValue != null && !filter.BlockUnratedItems.Select(e => e.ToString()).Contains(e.UnratedType));
}
}
- else if (query.MinParentalRating.HasValue)
+ else if (filter.MinParentalRating.HasValue)
{
- if (query.MaxParentalRating.HasValue)
+ if (filter.MaxParentalRating.HasValue)
{
baseQuery = baseQuery
- .Where(e => e.InheritedParentalRatingValue != null && e.InheritedParentalRatingValue >= query.MinParentalRating.Value && e.InheritedParentalRatingValue <= query.MaxParentalRating.Value);
+ .Where(e => e.InheritedParentalRatingValue != null && e.InheritedParentalRatingValue >= filter.MinParentalRating.Value && e.InheritedParentalRatingValue <= filter.MaxParentalRating.Value);
}
else
{
baseQuery = baseQuery
- .Where(e => e.InheritedParentalRatingValue != null && e.InheritedParentalRatingValue >= query.MinParentalRating.Value);
+ .Where(e => e.InheritedParentalRatingValue != null && e.InheritedParentalRatingValue >= filter.MinParentalRating.Value);
}
}
- else if (query.MaxParentalRating.HasValue)
+ else if (filter.MaxParentalRating.HasValue)
{
baseQuery = baseQuery
- .Where(e => e.InheritedParentalRatingValue != null && e.InheritedParentalRatingValue >= query.MaxParentalRating.Value);
+ .Where(e => e.InheritedParentalRatingValue != null && e.InheritedParentalRatingValue >= filter.MaxParentalRating.Value);
}
- else if (!query.HasParentalRating ?? false)
+ else if (!filter.HasParentalRating ?? false)
{
baseQuery = baseQuery
.Where(e => e.InheritedParentalRatingValue == null);
}
- if (query.HasOfficialRating.HasValue)
+ if (filter.HasOfficialRating.HasValue)
{
- if (query.HasOfficialRating.Value)
+ if (filter.HasOfficialRating.Value)
{
baseQuery = baseQuery
.Where(e => e.OfficialRating != null && e.OfficialRating != string.Empty);
@@ -1016,9 +1070,9 @@ public class BaseItemManager : IItemRepository
}
}
- if (query.HasOverview.HasValue)
+ if (filter.HasOverview.HasValue)
{
- if (query.HasOverview.Value)
+ if (filter.HasOverview.Value)
{
baseQuery = baseQuery
.Where(e => e.Overview != null && e.Overview != string.Empty);
@@ -1030,9 +1084,9 @@ public class BaseItemManager : IItemRepository
}
}
- if (query.HasOwnerId.HasValue)
+ if (filter.HasOwnerId.HasValue)
{
- if (query.HasOwnerId.Value)
+ if (filter.HasOwnerId.Value)
{
baseQuery = baseQuery
.Where(e => e.OwnerId != null);
@@ -1044,87 +1098,87 @@ public class BaseItemManager : IItemRepository
}
}
- if (!string.IsNullOrWhiteSpace(query.HasNoAudioTrackWithLanguage))
+ if (!string.IsNullOrWhiteSpace(filter.HasNoAudioTrackWithLanguage))
{
baseQuery = baseQuery
- .Where(e => !e.MediaStreams!.Any(e => e.StreamType == "Audio" && e.Language == query.HasNoAudioTrackWithLanguage));
+ .Where(e => !e.MediaStreams!.Any(e => e.StreamType == "Audio" && e.Language == filter.HasNoAudioTrackWithLanguage));
}
- if (!string.IsNullOrWhiteSpace(query.HasNoInternalSubtitleTrackWithLanguage))
+ if (!string.IsNullOrWhiteSpace(filter.HasNoInternalSubtitleTrackWithLanguage))
{
baseQuery = baseQuery
- .Where(e => !e.MediaStreams!.Any(e => e.StreamType == "Subtitle" && !e.IsExternal && e.Language == query.HasNoInternalSubtitleTrackWithLanguage));
+ .Where(e => !e.MediaStreams!.Any(e => e.StreamType == "Subtitle" && !e.IsExternal && e.Language == filter.HasNoInternalSubtitleTrackWithLanguage));
}
- if (!string.IsNullOrWhiteSpace(query.HasNoExternalSubtitleTrackWithLanguage))
+ if (!string.IsNullOrWhiteSpace(filter.HasNoExternalSubtitleTrackWithLanguage))
{
baseQuery = baseQuery
- .Where(e => !e.MediaStreams!.Any(e => e.StreamType == "Subtitle" && e.IsExternal && e.Language == query.HasNoExternalSubtitleTrackWithLanguage));
+ .Where(e => !e.MediaStreams!.Any(e => e.StreamType == "Subtitle" && e.IsExternal && e.Language == filter.HasNoExternalSubtitleTrackWithLanguage));
}
- if (!string.IsNullOrWhiteSpace(query.HasNoSubtitleTrackWithLanguage))
+ if (!string.IsNullOrWhiteSpace(filter.HasNoSubtitleTrackWithLanguage))
{
baseQuery = baseQuery
- .Where(e => !e.MediaStreams!.Any(e => e.StreamType == "Subtitle" && e.Language == query.HasNoSubtitleTrackWithLanguage));
+ .Where(e => !e.MediaStreams!.Any(e => e.StreamType == "Subtitle" && e.Language == filter.HasNoSubtitleTrackWithLanguage));
}
- if (query.HasSubtitles.HasValue)
+ if (filter.HasSubtitles.HasValue)
{
baseQuery = baseQuery
- .Where(e => e.MediaStreams!.Any(e => e.StreamType == "Subtitle") == query.HasSubtitles.Value);
+ .Where(e => e.MediaStreams!.Any(e => e.StreamType == "Subtitle") == filter.HasSubtitles.Value);
}
- if (query.HasChapterImages.HasValue)
+ if (filter.HasChapterImages.HasValue)
{
baseQuery = baseQuery
- .Where(e => e.Chapters!.Any(e => e.ImagePath != null) == query.HasChapterImages.Value);
+ .Where(e => e.Chapters!.Any(e => e.ImagePath != null) == filter.HasChapterImages.Value);
}
- if (query.HasDeadParentId.HasValue && query.HasDeadParentId.Value)
+ if (filter.HasDeadParentId.HasValue && filter.HasDeadParentId.Value)
{
baseQuery = baseQuery
.Where(e => e.ParentId.HasValue && context.BaseItems.Any(f => f.Id.Equals(e.ParentId.Value)));
}
- if (query.IsDeadArtist.HasValue && query.IsDeadArtist.Value)
+ if (filter.IsDeadArtist.HasValue && filter.IsDeadArtist.Value)
{
baseQuery = baseQuery
.Where(e => e.ItemValues!.Any(f => (f.Type == 0 || f.Type == 1) && f.CleanValue == e.CleanName));
}
- if (query.IsDeadStudio.HasValue && query.IsDeadStudio.Value)
+ if (filter.IsDeadStudio.HasValue && filter.IsDeadStudio.Value)
{
baseQuery = baseQuery
.Where(e => e.ItemValues!.Any(f => f.Type == 3 && f.CleanValue == e.CleanName));
}
- if (query.IsDeadPerson.HasValue && query.IsDeadPerson.Value)
+ if (filter.IsDeadPerson.HasValue && filter.IsDeadPerson.Value)
{
baseQuery = baseQuery
.Where(e => !e.Peoples!.Any(f => f.Name == e.Name));
}
- if (query.Years.Length == 1)
+ if (filter.Years.Length == 1)
{
baseQuery = baseQuery
- .Where(e => e.ProductionYear == query.Years[0]);
+ .Where(e => e.ProductionYear == filter.Years[0]);
}
- else if (query.Years.Length > 1)
+ else if (filter.Years.Length > 1)
{
baseQuery = baseQuery
- .Where(e => query.Years.Any(f => f == e.ProductionYear));
+ .Where(e => filter.Years.Any(f => f == e.ProductionYear));
}
- var isVirtualItem = query.IsVirtualItem ?? query.IsMissing;
+ var isVirtualItem = filter.IsVirtualItem ?? filter.IsMissing;
if (isVirtualItem.HasValue)
{
baseQuery = baseQuery
.Where(e => e.IsVirtualItem == isVirtualItem.Value);
}
- if (query.IsSpecialSeason.HasValue)
+ if (filter.IsSpecialSeason.HasValue)
{
- if (query.IsSpecialSeason.Value)
+ if (filter.IsSpecialSeason.Value)
{
baseQuery = baseQuery
.Where(e => e.IndexNumber == 0);
@@ -1136,9 +1190,9 @@ public class BaseItemManager : IItemRepository
}
}
- if (query.IsUnaired.HasValue)
+ if (filter.IsUnaired.HasValue)
{
- if (query.IsUnaired.Value)
+ if (filter.IsUnaired.Value)
{
baseQuery = baseQuery
.Where(e => e.PremiereDate >= now);
@@ -1150,60 +1204,60 @@ public class BaseItemManager : IItemRepository
}
}
- if (query.MediaTypes.Length == 1)
+ if (filter.MediaTypes.Length == 1)
{
baseQuery = baseQuery
- .Where(e => e.MediaType == query.MediaTypes[0].ToString());
+ .Where(e => e.MediaType == filter.MediaTypes[0].ToString());
}
- else if (query.MediaTypes.Length > 1)
+ else if (filter.MediaTypes.Length > 1)
{
baseQuery = baseQuery
- .Where(e => query.MediaTypes.Select(f => f.ToString()).Contains(e.MediaType));
+ .Where(e => filter.MediaTypes.Select(f => f.ToString()).Contains(e.MediaType));
}
- if (query.ItemIds.Length > 0)
+ if (filter.ItemIds.Length > 0)
{
baseQuery = baseQuery
- .Where(e => query.ItemIds.Contains(e.Id));
+ .Where(e => filter.ItemIds.Contains(e.Id));
}
- if (query.ExcludeItemIds.Length > 0)
+ if (filter.ExcludeItemIds.Length > 0)
{
baseQuery = baseQuery
- .Where(e => !query.ItemIds.Contains(e.Id));
+ .Where(e => !filter.ItemIds.Contains(e.Id));
}
- if (query.ExcludeProviderIds is not null && query.ExcludeProviderIds.Count > 0)
+ if (filter.ExcludeProviderIds is not null && filter.ExcludeProviderIds.Count > 0)
{
- baseQuery = baseQuery.Where(e => !e.Provider!.All(f => !query.ExcludeProviderIds.All(w => f.ProviderId == w.Key && f.ProviderValue == w.Value)));
+ baseQuery = baseQuery.Where(e => !e.Provider!.All(f => !filter.ExcludeProviderIds.All(w => f.ProviderId == w.Key && f.ProviderValue == w.Value)));
}
- if (query.HasAnyProviderId is not null && query.HasAnyProviderId.Count > 0)
+ if (filter.HasAnyProviderId is not null && filter.HasAnyProviderId.Count > 0)
{
- baseQuery = baseQuery.Where(e => e.Provider!.Any(f => !query.HasAnyProviderId.Any(w => f.ProviderId == w.Key && f.ProviderValue == w.Value)));
+ baseQuery = baseQuery.Where(e => e.Provider!.Any(f => !filter.HasAnyProviderId.Any(w => f.ProviderId == w.Key && f.ProviderValue == w.Value)));
}
- if (query.HasImdbId.HasValue)
+ if (filter.HasImdbId.HasValue)
{
baseQuery = baseQuery.Where(e => e.Provider!.Any(f => f.ProviderId == "imdb"));
}
- if (query.HasTmdbId.HasValue)
+ if (filter.HasTmdbId.HasValue)
{
baseQuery = baseQuery.Where(e => e.Provider!.Any(f => f.ProviderId == "tmdb"));
}
- if (query.HasTvdbId.HasValue)
+ if (filter.HasTvdbId.HasValue)
{
baseQuery = baseQuery.Where(e => e.Provider!.Any(f => f.ProviderId == "tvdb"));
}
- var queryTopParentIds = query.TopParentIds;
+ var queryTopParentIds = filter.TopParentIds;
if (queryTopParentIds.Length > 0)
{
- var includedItemByNameTypes = GetItemByNameTypesInQuery(query);
- var enableItemsByName = (query.IncludeItemsByName ?? false) && includedItemByNameTypes.Count > 0;
+ var includedItemByNameTypes = GetItemByNameTypesInQuery(filter);
+ var enableItemsByName = (filter.IncludeItemsByName ?? false) && includedItemByNameTypes.Count > 0;
if (enableItemsByName && includedItemByNameTypes.Count > 0)
{
baseQuery = baseQuery.Where(e => includedItemByNameTypes.Contains(e.Type) || queryTopParentIds.Any(w => w.Equals(e.TopParentId!.Value)));
@@ -1214,31 +1268,31 @@ public class BaseItemManager : IItemRepository
}
}
- if (query.AncestorIds.Length > 0)
+ if (filter.AncestorIds.Length > 0)
{
- baseQuery = baseQuery.Where(e => e.AncestorIds!.Any(f => query.AncestorIds.Contains(f.Id)));
+ baseQuery = baseQuery.Where(e => e.AncestorIds!.Any(f => filter.AncestorIds.Contains(f.Id)));
}
- if (!string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey))
+ if (!string.IsNullOrWhiteSpace(filter.AncestorWithPresentationUniqueKey))
{
baseQuery = baseQuery
- .Where(e => context.BaseItems.Where(f => f.PresentationUniqueKey == query.AncestorWithPresentationUniqueKey).Any(f => f.AncestorIds!.Any(w => w.ItemId.Equals(f.Id))));
+ .Where(e => context.BaseItems.Where(f => f.PresentationUniqueKey == filter.AncestorWithPresentationUniqueKey).Any(f => f.AncestorIds!.Any(w => w.ItemId.Equals(f.Id))));
}
- if (!string.IsNullOrWhiteSpace(query.SeriesPresentationUniqueKey))
+ if (!string.IsNullOrWhiteSpace(filter.SeriesPresentationUniqueKey))
{
baseQuery = baseQuery
- .Where(e => e.SeriesPresentationUniqueKey == query.SeriesPresentationUniqueKey);
+ .Where(e => e.SeriesPresentationUniqueKey == filter.SeriesPresentationUniqueKey);
}
- if (query.ExcludeInheritedTags.Length > 0)
+ if (filter.ExcludeInheritedTags.Length > 0)
{
baseQuery = baseQuery
.Where(e => !e.ItemValues!.Where(e => e.Type == 6)
- .Any(f => query.ExcludeInheritedTags.Contains(f.CleanValue)));
+ .Any(f => filter.ExcludeInheritedTags.Contains(f.CleanValue)));
}
- if (query.IncludeInheritedTags.Length > 0)
+ if (filter.IncludeInheritedTags.Length > 0)
{
// Episodes do not store inherit tags from their parents in the database, and the tag may be still required by the client.
// In addtion to the tags for the episodes themselves, we need to manually query its parent (the season)'s tags as well.
@@ -1246,10 +1300,10 @@ public class BaseItemManager : IItemRepository
{
baseQuery = baseQuery
.Where(e => e.ItemValues!.Where(e => e.Type == 6)
- .Any(f => query.IncludeInheritedTags.Contains(f.CleanValue))
+ .Any(f => filter.IncludeInheritedTags.Contains(f.CleanValue))
||
(e.ParentId.HasValue && context.ItemValues.Where(w => w.ItemId.Equals(e.ParentId.Value))!.Where(e => e.Type == 6)
- .Any(f => query.IncludeInheritedTags.Contains(f.CleanValue))));
+ .Any(f => filter.IncludeInheritedTags.Contains(f.CleanValue))));
}
// A playlist should be accessible to its owner regardless of allowed tags.
@@ -1257,39 +1311,39 @@ public class BaseItemManager : IItemRepository
{
baseQuery = baseQuery
.Where(e => e.ItemValues!.Where(e => e.Type == 6)
- .Any(f => query.IncludeInheritedTags.Contains(f.CleanValue)) || e.Data!.Contains($"OwnerUserId\":\"{query.User!.Id:N}\""));
+ .Any(f => filter.IncludeInheritedTags.Contains(f.CleanValue)) || e.Data!.Contains($"OwnerUserId\":\"{filter.User!.Id:N}\""));
// d ^^ this is stupid it hate this.
}
else
{
baseQuery = baseQuery
.Where(e => e.ItemValues!.Where(e => e.Type == 6)
- .Any(f => query.IncludeInheritedTags.Contains(f.CleanValue)));
+ .Any(f => filter.IncludeInheritedTags.Contains(f.CleanValue)));
}
}
- if (query.SeriesStatuses.Length > 0)
+ if (filter.SeriesStatuses.Length > 0)
{
baseQuery = baseQuery
- .Where(e => query.SeriesStatuses.Any(f => e.Data!.Contains(f.ToString(), StringComparison.InvariantCultureIgnoreCase)));
+ .Where(e => filter.SeriesStatuses.Any(f => e.Data!.Contains(f.ToString(), StringComparison.InvariantCultureIgnoreCase)));
}
- if (query.BoxSetLibraryFolders.Length > 0)
+ if (filter.BoxSetLibraryFolders.Length > 0)
{
baseQuery = baseQuery
- .Where(e => query.BoxSetLibraryFolders.Any(f => e.Data!.Contains(f.ToString("N", CultureInfo.InvariantCulture), StringComparison.InvariantCultureIgnoreCase)));
+ .Where(e => filter.BoxSetLibraryFolders.Any(f => e.Data!.Contains(f.ToString("N", CultureInfo.InvariantCulture), StringComparison.InvariantCultureIgnoreCase)));
}
- if (query.VideoTypes.Length > 0)
+ if (filter.VideoTypes.Length > 0)
{
- var videoTypeBs = query.VideoTypes.Select(e => $"\"VideoType\":\"" + e + "\"");
+ var videoTypeBs = filter.VideoTypes.Select(e => $"\"VideoType\":\"" + e + "\"");
baseQuery = baseQuery
.Where(e => videoTypeBs.Any(f => e.Data!.Contains(f, StringComparison.InvariantCultureIgnoreCase)));
}
- if (query.Is3D.HasValue)
+ if (filter.Is3D.HasValue)
{
- if (query.Is3D.Value)
+ if (filter.Is3D.Value)
{
baseQuery = baseQuery
.Where(e => e.Data!.Contains("Video3DFormat", StringComparison.InvariantCultureIgnoreCase));
@@ -1301,9 +1355,9 @@ public class BaseItemManager : IItemRepository
}
}
- if (query.IsPlaceHolder.HasValue)
+ if (filter.IsPlaceHolder.HasValue)
{
- if (query.IsPlaceHolder.Value)
+ if (filter.IsPlaceHolder.Value)
{
baseQuery = baseQuery
.Where(e => e.Data!.Contains("IsPlaceHolder\":true", StringComparison.InvariantCultureIgnoreCase));
@@ -1315,9 +1369,9 @@ public class BaseItemManager : IItemRepository
}
}
- if (query.HasSpecialFeature.HasValue)
+ if (filter.HasSpecialFeature.HasValue)
{
- if (query.HasSpecialFeature.Value)
+ if (filter.HasSpecialFeature.Value)
{
baseQuery = baseQuery
.Where(e => e.ExtraIds != null);
@@ -1329,9 +1383,9 @@ public class BaseItemManager : IItemRepository
}
}
- if (query.HasTrailer.HasValue || query.HasThemeSong.HasValue || query.HasThemeVideo.HasValue)
+ if (filter.HasTrailer.HasValue || filter.HasThemeSong.HasValue || filter.HasThemeVideo.HasValue)
{
- if (query.HasTrailer.GetValueOrDefault() || query.HasThemeSong.GetValueOrDefault() || query.HasThemeVideo.GetValueOrDefault())
+ if (filter.HasTrailer.GetValueOrDefault() || filter.HasThemeSong.GetValueOrDefault() || filter.HasThemeVideo.GetValueOrDefault())
{
baseQuery = baseQuery
.Where(e => e.ExtraIds != null);
@@ -1776,6 +1830,26 @@ public class BaseItemManager : IItemRepository
return entity;
}
+ private IReadOnlyList GetItemValueNames(int[] itemValueTypes, IReadOnlyList withItemTypes, IReadOnlyList excludeItemTypes)
+ {
+ using var context = _dbProvider.CreateDbContext();
+
+ var query = context.ItemValues
+ .Where(e => itemValueTypes.Contains(e.Type));
+ if (withItemTypes.Count > 0)
+ {
+ query = query.Where(e => context.BaseItems.Where(e => withItemTypes.Contains(e.Type)).Any(f => f.ItemValues!.Any(w => w.ItemId.Equals(e.ItemId))));
+ }
+
+ if (excludeItemTypes.Count > 0)
+ {
+ query = query.Where(e => !context.BaseItems.Where(e => withItemTypes.Contains(e.Type)).Any(f => f.ItemValues!.Any(w => w.ItemId.Equals(e.ItemId))));
+ }
+
+ query = query.DistinctBy(e => e.CleanValue);
+ return query.Select(e => e.CleanValue).ToImmutableArray();
+ }
+
private BaseItemDto DeserialiseBaseItem(BaseItemEntity baseItemEntity)
{
var type = GetType(baseItemEntity.Type) ?? throw new InvalidOperationException("Cannot deserialise unkown type.");
diff --git a/Jellyfin.Server.Implementations/Item/MediaAttachmentManager.cs b/Jellyfin.Server.Implementations/Item/MediaAttachmentManager.cs
new file mode 100644
index 0000000000..288b1943e7
--- /dev/null
+++ b/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;
+
+///
+/// Manager for handling Media Attachments.
+///
+/// Efcore Factory.
+public class MediaAttachmentManager(IDbContextFactory dbProvider) : IMediaAttachmentManager
+{
+ ///
+ public void SaveMediaAttachments(
+ Guid id,
+ IReadOnlyList 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();
+ }
+
+ ///
+ public IReadOnlyList 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!
+ };
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Item/MediaStreamManager.cs b/Jellyfin.Server.Implementations/Item/MediaStreamManager.cs
index e609cdc1ec..b7124283a4 100644
--- a/Jellyfin.Server.Implementations/Item/MediaStreamManager.cs
+++ b/Jellyfin.Server.Implementations/Item/MediaStreamManager.cs
@@ -18,7 +18,7 @@ namespace Jellyfin.Server.Implementations.Item;
///
///
///
-public class MediaStreamManager(IDbContextFactory dbProvider, IServerApplicationHost serverApplicationHost, ILocalizationManager localization)
+public class MediaStreamManager(IDbContextFactory dbProvider, IServerApplicationHost serverApplicationHost, ILocalizationManager localization) : IMediaStreamManager
{
///
public void SaveMediaStreams(Guid id, IReadOnlyList streams, CancellationToken cancellationToken)
diff --git a/Jellyfin.Server.Implementations/Item/PeopleManager.cs b/Jellyfin.Server.Implementations/Item/PeopleManager.cs
index 0f1760cbdc..d29d8b143e 100644
--- a/Jellyfin.Server.Implementations/Item/PeopleManager.cs
+++ b/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
+///
+/// Manager for handling people.
+///
+/// Efcore Factory.
+///
+/// Initializes a new instance of the class.
+///
+/// The EFCore Context factory.
+public class PeopleManager(IDbContextFactory dbProvider) : IPeopleManager
{
- private readonly IDbContextFactory _dbProvider;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The EFCore Context factory.
- public PeopleManager(IDbContextFactory dbProvider)
- {
- _dbProvider = dbProvider;
- }
+ private readonly IDbContextFactory _dbProvider = dbProvider;
public IReadOnlyList GetPeople(InternalPeopleQuery filter)
{
diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs
index 21b9ee4b7e..313b1459ab 100644
--- a/MediaBrowser.Controller/Persistence/IItemRepository.cs
+++ b/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;
+
+///
+/// Provides an interface to implement an Item repository.
+///
+public interface IItemRepository : IDisposable
{
///
- /// Provides an interface to implement an Item repository.
+ /// Deletes the item.
///
- public interface IItemRepository : IDisposable
- {
- ///
- /// Deletes the item.
- ///
- /// The identifier.
- void DeleteItem(Guid id);
+ /// The identifier.
+ void DeleteItem(Guid id);
- ///
- /// Saves the items.
- ///
- /// The items.
- /// The cancellation token.
- void SaveItems(IReadOnlyList items, CancellationToken cancellationToken);
+ ///
+ /// Saves the items.
+ ///
+ /// The items.
+ /// The cancellation token.
+ void SaveItems(IReadOnlyList items, CancellationToken cancellationToken);
- void SaveImages(BaseItem item);
+ void SaveImages(BaseItem item);
- ///
- /// Retrieves the item.
- ///
- /// The id.
- /// BaseItem.
- BaseItem RetrieveItem(Guid id);
+ ///
+ /// Retrieves the item.
+ ///
+ /// The id.
+ /// BaseItem.
+ BaseItem RetrieveItem(Guid id);
- ///
- /// Gets the media streams.
- ///
- /// The query.
- /// IEnumerable{MediaStream}.
- List GetMediaStreams(MediaStreamQuery query);
+ ///
+ /// Gets the items.
+ ///
+ /// The query.
+ /// QueryResult<BaseItem>.
+ QueryResult GetItems(InternalItemsQuery filter);
- ///
- /// Saves the media streams.
- ///
- /// The identifier.
- /// The streams.
- /// The cancellation token.
- void SaveMediaStreams(Guid id, IReadOnlyList streams, CancellationToken cancellationToken);
+ ///
+ /// Gets the item ids list.
+ ///
+ /// The query.
+ /// List<Guid>.
+ IReadOnlyList GetItemIdsList(InternalItemsQuery filter);
- ///
- /// Gets the media attachments.
- ///
- /// The query.
- /// IEnumerable{MediaAttachment}.
- List GetMediaAttachments(MediaAttachmentQuery query);
- ///
- /// Saves the media attachments.
- ///
- /// The identifier.
- /// The attachments.
- /// The cancellation token.
- void SaveMediaAttachments(Guid id, IReadOnlyList attachments, CancellationToken cancellationToken);
+ ///
+ /// Gets the item list.
+ ///
+ /// The query.
+ /// List<BaseItem>.
+ IReadOnlyList GetItemList(InternalItemsQuery filter);
- ///
- /// Gets the items.
- ///
- /// The query.
- /// QueryResult<BaseItem>.
- QueryResult GetItems(InternalItemsQuery query);
+ ///
+ /// Updates the inherited values.
+ ///
+ void UpdateInheritedValues();
- ///
- /// Gets the item ids list.
- ///
- /// The query.
- /// List<Guid>.
- List GetItemIdsList(InternalItemsQuery query);
+ int GetCount(InternalItemsQuery filter);
- ///
- /// Gets the people.
- ///
- /// The query.
- /// List<PersonInfo>.
- List GetPeople(InternalPeopleQuery query);
+ QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetGenres(InternalItemsQuery filter);
- ///
- /// Updates the people.
- ///
- /// The item identifier.
- /// The people.
- void UpdatePeople(Guid itemId, List people);
+ QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetMusicGenres(InternalItemsQuery filter);
- ///
- /// Gets the people names.
- ///
- /// The query.
- /// List<System.String>.
- List GetPeopleNames(InternalPeopleQuery query);
+ QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetStudios(InternalItemsQuery filter);
- ///
- /// Gets the item list.
- ///
- /// The query.
- /// List<BaseItem>.
- List GetItemList(InternalItemsQuery query);
+ QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetArtists(InternalItemsQuery filter);
- ///
- /// Updates the inherited values.
- ///
- void UpdateInheritedValues();
+ QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetAlbumArtists(InternalItemsQuery filter);
- int GetCount(InternalItemsQuery query);
+ QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetAllArtists(InternalItemsQuery filter);
- QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetGenres(InternalItemsQuery query);
+ IReadOnlyList GetMusicGenreNames();
- QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetMusicGenres(InternalItemsQuery query);
+ IReadOnlyList GetStudioNames();
- QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetStudios(InternalItemsQuery query);
+ IReadOnlyList GetGenreNames();
- 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 GetMusicGenreNames();
-
- List GetStudioNames();
-
- List GetGenreNames();
-
- List GetAllArtistNames();
- }
+ IReadOnlyList GetAllArtistNames();
}
diff --git a/MediaBrowser.Controller/Persistence/IMediaAttachmentManager.cs b/MediaBrowser.Controller/Persistence/IMediaAttachmentManager.cs
new file mode 100644
index 0000000000..210d80afa2
--- /dev/null
+++ b/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
+{
+
+ ///
+ /// Gets the media attachments.
+ ///
+ /// The query.
+ /// IEnumerable{MediaAttachment}.
+ IReadOnlyList GetMediaAttachments(MediaAttachmentQuery filter);
+
+ ///
+ /// Saves the media attachments.
+ ///
+ /// The identifier.
+ /// The attachments.
+ /// The cancellation token.
+ void SaveMediaAttachments(Guid id, IReadOnlyList attachments, CancellationToken cancellationToken);
+}
diff --git a/MediaBrowser.Controller/Persistence/IMediaStreamManager.cs b/MediaBrowser.Controller/Persistence/IMediaStreamManager.cs
new file mode 100644
index 0000000000..ec7c72935b
--- /dev/null
+++ b/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
+{
+ ///
+ /// Gets the media streams.
+ ///
+ /// The query.
+ /// IEnumerable{MediaStream}.
+ List GetMediaStreams(MediaStreamQuery filter);
+
+ ///
+ /// Saves the media streams.
+ ///
+ /// The identifier.
+ /// The streams.
+ /// The cancellation token.
+ void SaveMediaStreams(Guid id, IReadOnlyList streams, CancellationToken cancellationToken);
+}
diff --git a/MediaBrowser.Controller/Persistence/IPeopleManager.cs b/MediaBrowser.Controller/Persistence/IPeopleManager.cs
new file mode 100644
index 0000000000..84e503fefb
--- /dev/null
+++ b/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
+{
+ ///
+ /// Gets the people.
+ ///
+ /// The query.
+ /// List<PersonInfo>.
+ IReadOnlyList GetPeople(InternalPeopleQuery filter);
+
+ ///
+ /// Updates the people.
+ ///
+ /// The item identifier.
+ /// The people.
+ void UpdatePeople(Guid itemId, IReadOnlyList people);
+
+ ///
+ /// Gets the people names.
+ ///
+ /// The query.
+ /// List<System.String>.
+ IReadOnlyList GetPeopleNames(InternalPeopleQuery filter);
+
+}