mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-05-31 20:24:21 -04:00
Remove all DB data on item removal, delete internal trickplay files (#13753)
This commit is contained in:
parent
8b6aec7ce5
commit
8db6a39e92
@ -5,80 +5,80 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Jellyfin.Server.Implementations;
|
using Jellyfin.Server.Implementations;
|
||||||
|
using MediaBrowser.Controller;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
|
using MediaBrowser.Controller.Trickplay;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Data
|
namespace Emby.Server.Implementations.Data;
|
||||||
|
|
||||||
|
public class CleanDatabaseScheduledTask : ILibraryPostScanTask
|
||||||
{
|
{
|
||||||
public class CleanDatabaseScheduledTask : ILibraryPostScanTask
|
private readonly ILibraryManager _libraryManager;
|
||||||
|
private readonly ILogger<CleanDatabaseScheduledTask> _logger;
|
||||||
|
private readonly IDbContextFactory<JellyfinDbContext> _dbProvider;
|
||||||
|
|
||||||
|
public CleanDatabaseScheduledTask(
|
||||||
|
ILibraryManager libraryManager,
|
||||||
|
ILogger<CleanDatabaseScheduledTask> logger,
|
||||||
|
IDbContextFactory<JellyfinDbContext> dbProvider)
|
||||||
{
|
{
|
||||||
private readonly ILibraryManager _libraryManager;
|
_libraryManager = libraryManager;
|
||||||
private readonly ILogger<CleanDatabaseScheduledTask> _logger;
|
_logger = logger;
|
||||||
private readonly IDbContextFactory<JellyfinDbContext> _dbProvider;
|
_dbProvider = dbProvider;
|
||||||
|
}
|
||||||
|
|
||||||
public CleanDatabaseScheduledTask(
|
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
|
||||||
ILibraryManager libraryManager,
|
{
|
||||||
ILogger<CleanDatabaseScheduledTask> logger,
|
await CleanDeadItems(cancellationToken, progress).ConfigureAwait(false);
|
||||||
IDbContextFactory<JellyfinDbContext> dbProvider)
|
}
|
||||||
{
|
|
||||||
_libraryManager = libraryManager;
|
|
||||||
_logger = logger;
|
|
||||||
_dbProvider = dbProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
|
private async Task CleanDeadItems(CancellationToken cancellationToken, IProgress<double> progress)
|
||||||
|
{
|
||||||
|
var itemIds = _libraryManager.GetItemIds(new InternalItemsQuery
|
||||||
{
|
{
|
||||||
await CleanDeadItems(cancellationToken, progress).ConfigureAwait(false);
|
HasDeadParentId = true
|
||||||
}
|
});
|
||||||
|
|
||||||
private async Task CleanDeadItems(CancellationToken cancellationToken, IProgress<double> progress)
|
var numComplete = 0;
|
||||||
|
var numItems = itemIds.Count + 1;
|
||||||
|
|
||||||
|
_logger.LogDebug("Cleaning {Number} items with dead parent links", numItems);
|
||||||
|
|
||||||
|
foreach (var itemId in itemIds)
|
||||||
{
|
{
|
||||||
var itemIds = _libraryManager.GetItemIds(new InternalItemsQuery
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
var item = _libraryManager.GetItemById(itemId);
|
||||||
|
if (item is not null)
|
||||||
{
|
{
|
||||||
HasDeadParentId = true
|
_logger.LogInformation("Cleaning item {Item} type: {Type} path: {Path}", item.Name, item.GetType().Name, item.Path ?? string.Empty);
|
||||||
});
|
|
||||||
|
|
||||||
var numComplete = 0;
|
_libraryManager.DeleteItem(item, new DeleteOptions
|
||||||
var numItems = itemIds.Count + 1;
|
|
||||||
|
|
||||||
_logger.LogDebug("Cleaning {0} items with dead parent links", numItems);
|
|
||||||
|
|
||||||
foreach (var itemId in itemIds)
|
|
||||||
{
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
|
|
||||||
var item = _libraryManager.GetItemById(itemId);
|
|
||||||
|
|
||||||
if (item is not null)
|
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Cleaning item {0} type: {1} path: {2}", item.Name, item.GetType().Name, item.Path ?? string.Empty);
|
DeleteFileLocation = false
|
||||||
|
});
|
||||||
_libraryManager.DeleteItem(item, new DeleteOptions
|
|
||||||
{
|
|
||||||
DeleteFileLocation = false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
numComplete++;
|
|
||||||
double percent = numComplete;
|
|
||||||
percent /= numItems;
|
|
||||||
progress.Report(percent * 100);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var context = await _dbProvider.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
|
numComplete++;
|
||||||
await using (context.ConfigureAwait(false))
|
double percent = numComplete;
|
||||||
{
|
percent /= numItems;
|
||||||
var transaction = await context.Database.BeginTransactionAsync(cancellationToken).ConfigureAwait(false);
|
progress.Report(percent * 100);
|
||||||
await using (transaction.ConfigureAwait(false))
|
|
||||||
{
|
|
||||||
await context.ItemValues.Where(e => e.BaseItemsMap!.Count == 0).ExecuteDeleteAsync(cancellationToken).ConfigureAwait(false);
|
|
||||||
await transaction.CommitAsync(cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
progress.Report(100);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var context = await _dbProvider.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
await using (context.ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
var transaction = await context.Database.BeginTransactionAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
await using (transaction.ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
await context.ItemValues.Where(e => e.BaseItemsMap!.Count == 0).ExecuteDeleteAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
await transaction.CommitAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
progress.Report(100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,6 +78,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
private readonly NamingOptions _namingOptions;
|
private readonly NamingOptions _namingOptions;
|
||||||
private readonly IPeopleRepository _peopleRepository;
|
private readonly IPeopleRepository _peopleRepository;
|
||||||
private readonly ExtraResolver _extraResolver;
|
private readonly ExtraResolver _extraResolver;
|
||||||
|
private readonly IPathManager _pathManager;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The _root folder sync lock.
|
/// The _root folder sync lock.
|
||||||
@ -113,7 +114,8 @@ namespace Emby.Server.Implementations.Library
|
|||||||
/// <param name="imageProcessor">The image processor.</param>
|
/// <param name="imageProcessor">The image processor.</param>
|
||||||
/// <param name="namingOptions">The naming options.</param>
|
/// <param name="namingOptions">The naming options.</param>
|
||||||
/// <param name="directoryService">The directory service.</param>
|
/// <param name="directoryService">The directory service.</param>
|
||||||
/// <param name="peopleRepository">The People Repository.</param>
|
/// <param name="peopleRepository">The people repository.</param>
|
||||||
|
/// <param name="pathManager">The path manager.</param>
|
||||||
public LibraryManager(
|
public LibraryManager(
|
||||||
IServerApplicationHost appHost,
|
IServerApplicationHost appHost,
|
||||||
ILoggerFactory loggerFactory,
|
ILoggerFactory loggerFactory,
|
||||||
@ -130,7 +132,8 @@ namespace Emby.Server.Implementations.Library
|
|||||||
IImageProcessor imageProcessor,
|
IImageProcessor imageProcessor,
|
||||||
NamingOptions namingOptions,
|
NamingOptions namingOptions,
|
||||||
IDirectoryService directoryService,
|
IDirectoryService directoryService,
|
||||||
IPeopleRepository peopleRepository)
|
IPeopleRepository peopleRepository,
|
||||||
|
IPathManager pathManager)
|
||||||
{
|
{
|
||||||
_appHost = appHost;
|
_appHost = appHost;
|
||||||
_logger = loggerFactory.CreateLogger<LibraryManager>();
|
_logger = loggerFactory.CreateLogger<LibraryManager>();
|
||||||
@ -148,6 +151,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
_cache = new ConcurrentDictionary<Guid, BaseItem>();
|
_cache = new ConcurrentDictionary<Guid, BaseItem>();
|
||||||
_namingOptions = namingOptions;
|
_namingOptions = namingOptions;
|
||||||
_peopleRepository = peopleRepository;
|
_peopleRepository = peopleRepository;
|
||||||
|
_pathManager = pathManager;
|
||||||
_extraResolver = new ExtraResolver(loggerFactory.CreateLogger<ExtraResolver>(), namingOptions, directoryService);
|
_extraResolver = new ExtraResolver(loggerFactory.CreateLogger<ExtraResolver>(), namingOptions, directoryService);
|
||||||
|
|
||||||
_configurationManager.ConfigurationUpdated += ConfigurationUpdated;
|
_configurationManager.ConfigurationUpdated += ConfigurationUpdated;
|
||||||
@ -200,33 +204,33 @@ namespace Emby.Server.Implementations.Library
|
|||||||
/// Gets or sets the postscan tasks.
|
/// Gets or sets the postscan tasks.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The postscan tasks.</value>
|
/// <value>The postscan tasks.</value>
|
||||||
private ILibraryPostScanTask[] PostscanTasks { get; set; } = Array.Empty<ILibraryPostScanTask>();
|
private ILibraryPostScanTask[] PostscanTasks { get; set; } = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the intro providers.
|
/// Gets or sets the intro providers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The intro providers.</value>
|
/// <value>The intro providers.</value>
|
||||||
private IIntroProvider[] IntroProviders { get; set; } = Array.Empty<IIntroProvider>();
|
private IIntroProvider[] IntroProviders { get; set; } = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the list of entity resolution ignore rules.
|
/// Gets or sets the list of entity resolution ignore rules.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The entity resolution ignore rules.</value>
|
/// <value>The entity resolution ignore rules.</value>
|
||||||
private IResolverIgnoreRule[] EntityResolutionIgnoreRules { get; set; } = Array.Empty<IResolverIgnoreRule>();
|
private IResolverIgnoreRule[] EntityResolutionIgnoreRules { get; set; } = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the list of currently registered entity resolvers.
|
/// Gets or sets the list of currently registered entity resolvers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The entity resolvers enumerable.</value>
|
/// <value>The entity resolvers enumerable.</value>
|
||||||
private IItemResolver[] EntityResolvers { get; set; } = Array.Empty<IItemResolver>();
|
private IItemResolver[] EntityResolvers { get; set; } = [];
|
||||||
|
|
||||||
private IMultiItemResolver[] MultiItemResolvers { get; set; } = Array.Empty<IMultiItemResolver>();
|
private IMultiItemResolver[] MultiItemResolvers { get; set; } = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the comparers.
|
/// Gets or sets the comparers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The comparers.</value>
|
/// <value>The comparers.</value>
|
||||||
private IBaseItemComparer[] Comparers { get; set; } = Array.Empty<IBaseItemComparer>();
|
private IBaseItemComparer[] Comparers { get; set; } = [];
|
||||||
|
|
||||||
public bool IsScanRunning { get; private set; }
|
public bool IsScanRunning { get; private set; }
|
||||||
|
|
||||||
@ -359,7 +363,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
|
|
||||||
var children = item.IsFolder
|
var children = item.IsFolder
|
||||||
? ((Folder)item).GetRecursiveChildren(false)
|
? ((Folder)item).GetRecursiveChildren(false)
|
||||||
: Array.Empty<BaseItem>();
|
: [];
|
||||||
|
|
||||||
foreach (var metadataPath in GetMetadataPaths(item, children))
|
foreach (var metadataPath in GetMetadataPaths(item, children))
|
||||||
{
|
{
|
||||||
@ -465,14 +469,28 @@ namespace Emby.Server.Implementations.Library
|
|||||||
ReportItemRemoved(item, parent);
|
ReportItemRemoved(item, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<string> GetMetadataPaths(BaseItem item, IEnumerable<BaseItem> children)
|
private List<string> GetMetadataPaths(BaseItem item, IEnumerable<BaseItem> children)
|
||||||
|
{
|
||||||
|
var list = GetInternalMetadataPaths(item);
|
||||||
|
foreach (var child in children)
|
||||||
|
{
|
||||||
|
list.AddRange(GetInternalMetadataPaths(child));
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<string> GetInternalMetadataPaths(BaseItem item)
|
||||||
{
|
{
|
||||||
var list = new List<string>
|
var list = new List<string>
|
||||||
{
|
{
|
||||||
item.GetInternalMetadataPath()
|
item.GetInternalMetadataPath()
|
||||||
};
|
};
|
||||||
|
|
||||||
list.AddRange(children.Select(i => i.GetInternalMetadataPath()));
|
if (item is Video video)
|
||||||
|
{
|
||||||
|
list.Add(_pathManager.GetTrickplayDirectory(video));
|
||||||
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
@ -593,7 +611,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error in GetFilteredFileSystemEntries isPhysicalRoot: {0} IsVf: {1}", isPhysicalRoot, isVf);
|
_logger.LogError(ex, "Error in GetFilteredFileSystemEntries isPhysicalRoot: {0} IsVf: {1}", isPhysicalRoot, isVf);
|
||||||
|
|
||||||
files = Array.Empty<FileSystemMetadata>();
|
files = [];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1463,7 +1481,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
|
|
||||||
// Optimize by querying against top level views
|
// Optimize by querying against top level views
|
||||||
query.TopParentIds = parents.SelectMany(i => GetTopParentIdsForQuery(i, query.User)).ToArray();
|
query.TopParentIds = parents.SelectMany(i => GetTopParentIdsForQuery(i, query.User)).ToArray();
|
||||||
query.AncestorIds = Array.Empty<Guid>();
|
query.AncestorIds = [];
|
||||||
|
|
||||||
// Prevent searching in all libraries due to empty filter
|
// Prevent searching in all libraries due to empty filter
|
||||||
if (query.TopParentIds.Length == 0)
|
if (query.TopParentIds.Length == 0)
|
||||||
@ -1583,7 +1601,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
return GetTopParentIdsForQuery(displayParent, user);
|
return GetTopParentIdsForQuery(displayParent, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Array.Empty<Guid>();
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!view.ParentId.IsEmpty())
|
if (!view.ParentId.IsEmpty())
|
||||||
@ -1594,7 +1612,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
return GetTopParentIdsForQuery(displayParent, user);
|
return GetTopParentIdsForQuery(displayParent, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Array.Empty<Guid>();
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle grouping
|
// Handle grouping
|
||||||
@ -1609,7 +1627,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
.SelectMany(i => GetTopParentIdsForQuery(i, user));
|
.SelectMany(i => GetTopParentIdsForQuery(i, user));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Array.Empty<Guid>();
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item is CollectionFolder collectionFolder)
|
if (item is CollectionFolder collectionFolder)
|
||||||
@ -1623,7 +1641,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
return new[] { topParent.Id };
|
return new[] { topParent.Id };
|
||||||
}
|
}
|
||||||
|
|
||||||
return Array.Empty<Guid>();
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -1667,7 +1685,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error getting intros");
|
_logger.LogError(ex, "Error getting intros");
|
||||||
|
|
||||||
return Enumerable.Empty<IntroInfo>();
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2894,7 +2912,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
{
|
{
|
||||||
var path = Path.Combine(virtualFolderPath, collectionType.ToString()!.ToLowerInvariant() + ".collection"); // Can't be null with legal values?
|
var path = Path.Combine(virtualFolderPath, collectionType.ToString()!.ToLowerInvariant() + ".collection"); // Can't be null with legal values?
|
||||||
|
|
||||||
await File.WriteAllBytesAsync(path, Array.Empty<byte>()).ConfigureAwait(false);
|
await File.WriteAllBytesAsync(path, []).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
CollectionFolder.SaveLibraryOptions(virtualFolderPath, options);
|
CollectionFolder.SaveLibraryOptions(virtualFolderPath, options);
|
||||||
|
36
Emby.Server.Implementations/Library/PathManager.cs
Normal file
36
Emby.Server.Implementations/Library/PathManager.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using MediaBrowser.Controller.Configuration;
|
||||||
|
using MediaBrowser.Controller.Entities;
|
||||||
|
using MediaBrowser.Controller.IO;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.Library;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// IPathManager implementation.
|
||||||
|
/// </summary>
|
||||||
|
public class PathManager : IPathManager
|
||||||
|
{
|
||||||
|
private readonly IServerConfigurationManager _config;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="PathManager"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The server configuration manager.</param>
|
||||||
|
public PathManager(
|
||||||
|
IServerConfigurationManager config)
|
||||||
|
{
|
||||||
|
_config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string GetTrickplayDirectory(BaseItem item, bool saveWithMedia = false)
|
||||||
|
{
|
||||||
|
var basePath = _config.ApplicationPaths.TrickplayPath;
|
||||||
|
var idString = item.Id.ToString("N", CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
|
return saveWithMedia
|
||||||
|
? Path.Combine(item.ContainingFolderPath, Path.ChangeExtension(item.Path, ".trickplay"))
|
||||||
|
: Path.Combine(basePath, idString);
|
||||||
|
}
|
||||||
|
}
|
@ -101,16 +101,23 @@ public sealed class BaseItemRepository
|
|||||||
|
|
||||||
using var context = _dbProvider.CreateDbContext();
|
using var context = _dbProvider.CreateDbContext();
|
||||||
using var transaction = context.Database.BeginTransaction();
|
using var transaction = context.Database.BeginTransaction();
|
||||||
|
context.AncestorIds.Where(e => e.ItemId == id || e.ParentItemId == id).ExecuteDelete();
|
||||||
|
context.AttachmentStreamInfos.Where(e => e.ItemId == id).ExecuteDelete();
|
||||||
|
context.BaseItemImageInfos.Where(e => e.ItemId == id).ExecuteDelete();
|
||||||
|
context.BaseItemMetadataFields.Where(e => e.ItemId == id).ExecuteDelete();
|
||||||
|
context.BaseItemProviders.Where(e => e.ItemId == id).ExecuteDelete();
|
||||||
|
context.BaseItemTrailerTypes.Where(e => e.ItemId == id).ExecuteDelete();
|
||||||
|
context.BaseItems.Where(e => e.Id == id).ExecuteDelete();
|
||||||
|
context.Chapters.Where(e => e.ItemId == id).ExecuteDelete();
|
||||||
|
context.CustomItemDisplayPreferences.Where(e => e.ItemId == id).ExecuteDelete();
|
||||||
|
context.ItemDisplayPreferences.Where(e => e.ItemId == id).ExecuteDelete();
|
||||||
|
context.ItemValues.Where(e => e.BaseItemsMap!.Count == 0).ExecuteDelete();
|
||||||
|
context.ItemValuesMap.Where(e => e.ItemId == id).ExecuteDelete();
|
||||||
|
context.MediaSegments.Where(e => e.ItemId == id).ExecuteDelete();
|
||||||
|
context.MediaStreamInfos.Where(e => e.ItemId == id).ExecuteDelete();
|
||||||
context.PeopleBaseItemMap.Where(e => e.ItemId == id).ExecuteDelete();
|
context.PeopleBaseItemMap.Where(e => e.ItemId == id).ExecuteDelete();
|
||||||
context.Peoples.Where(e => e.BaseItems!.Count == 0).ExecuteDelete();
|
context.Peoples.Where(e => e.BaseItems!.Count == 0).ExecuteDelete();
|
||||||
context.Chapters.Where(e => e.ItemId == id).ExecuteDelete();
|
context.TrickplayInfos.Where(e => e.ItemId == id).ExecuteDelete();
|
||||||
context.MediaStreamInfos.Where(e => e.ItemId == id).ExecuteDelete();
|
|
||||||
context.AncestorIds.Where(e => e.ItemId == id || e.ParentItemId == id).ExecuteDelete();
|
|
||||||
context.ItemValuesMap.Where(e => e.ItemId == id).ExecuteDelete();
|
|
||||||
context.ItemValues.Where(e => e.BaseItemsMap!.Count == 0).ExecuteDelete();
|
|
||||||
context.BaseItemImageInfos.Where(e => e.ItemId == id).ExecuteDelete();
|
|
||||||
context.BaseItemProviders.Where(e => e.ItemId == id).ExecuteDelete();
|
|
||||||
context.BaseItems.Where(e => e.Id == id).ExecuteDelete();
|
|
||||||
context.SaveChanges();
|
context.SaveChanges();
|
||||||
transaction.Commit();
|
transaction.Commit();
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ using MediaBrowser.Common.Configuration;
|
|||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Drawing;
|
using MediaBrowser.Controller.Drawing;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
|
using MediaBrowser.Controller.IO;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.MediaEncoding;
|
using MediaBrowser.Controller.MediaEncoding;
|
||||||
using MediaBrowser.Controller.Trickplay;
|
using MediaBrowser.Controller.Trickplay;
|
||||||
@ -37,9 +38,10 @@ public class TrickplayManager : ITrickplayManager
|
|||||||
private readonly IImageEncoder _imageEncoder;
|
private readonly IImageEncoder _imageEncoder;
|
||||||
private readonly IDbContextFactory<JellyfinDbContext> _dbProvider;
|
private readonly IDbContextFactory<JellyfinDbContext> _dbProvider;
|
||||||
private readonly IApplicationPaths _appPaths;
|
private readonly IApplicationPaths _appPaths;
|
||||||
|
private readonly IPathManager _pathManager;
|
||||||
|
|
||||||
private static readonly AsyncNonKeyedLocker _resourcePool = new(1);
|
private static readonly AsyncNonKeyedLocker _resourcePool = new(1);
|
||||||
private static readonly string[] _trickplayImgExtensions = { ".jpg" };
|
private static readonly string[] _trickplayImgExtensions = [".jpg"];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="TrickplayManager"/> class.
|
/// Initializes a new instance of the <see cref="TrickplayManager"/> class.
|
||||||
@ -53,6 +55,7 @@ public class TrickplayManager : ITrickplayManager
|
|||||||
/// <param name="imageEncoder">The image encoder.</param>
|
/// <param name="imageEncoder">The image encoder.</param>
|
||||||
/// <param name="dbProvider">The database provider.</param>
|
/// <param name="dbProvider">The database provider.</param>
|
||||||
/// <param name="appPaths">The application paths.</param>
|
/// <param name="appPaths">The application paths.</param>
|
||||||
|
/// <param name="pathManager">The path manager.</param>
|
||||||
public TrickplayManager(
|
public TrickplayManager(
|
||||||
ILogger<TrickplayManager> logger,
|
ILogger<TrickplayManager> logger,
|
||||||
IMediaEncoder mediaEncoder,
|
IMediaEncoder mediaEncoder,
|
||||||
@ -62,7 +65,8 @@ public class TrickplayManager : ITrickplayManager
|
|||||||
IServerConfigurationManager config,
|
IServerConfigurationManager config,
|
||||||
IImageEncoder imageEncoder,
|
IImageEncoder imageEncoder,
|
||||||
IDbContextFactory<JellyfinDbContext> dbProvider,
|
IDbContextFactory<JellyfinDbContext> dbProvider,
|
||||||
IApplicationPaths appPaths)
|
IApplicationPaths appPaths,
|
||||||
|
IPathManager pathManager)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_mediaEncoder = mediaEncoder;
|
_mediaEncoder = mediaEncoder;
|
||||||
@ -73,6 +77,7 @@ public class TrickplayManager : ITrickplayManager
|
|||||||
_imageEncoder = imageEncoder;
|
_imageEncoder = imageEncoder;
|
||||||
_dbProvider = dbProvider;
|
_dbProvider = dbProvider;
|
||||||
_appPaths = appPaths;
|
_appPaths = appPaths;
|
||||||
|
_pathManager = pathManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -610,12 +615,7 @@ public class TrickplayManager : ITrickplayManager
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string GetTrickplayDirectory(BaseItem item, int tileWidth, int tileHeight, int width, bool saveWithMedia = false)
|
public string GetTrickplayDirectory(BaseItem item, int tileWidth, int tileHeight, int width, bool saveWithMedia = false)
|
||||||
{
|
{
|
||||||
var basePath = _config.ApplicationPaths.TrickplayPath;
|
var path = _pathManager.GetTrickplayDirectory(item, saveWithMedia);
|
||||||
var idString = item.Id.ToString("N", CultureInfo.InvariantCulture);
|
|
||||||
var path = saveWithMedia
|
|
||||||
? Path.Combine(item.ContainingFolderPath, Path.ChangeExtension(item.Path, ".trickplay"))
|
|
||||||
: Path.Combine(basePath, idString);
|
|
||||||
|
|
||||||
var subdirectory = string.Format(
|
var subdirectory = string.Format(
|
||||||
CultureInfo.InvariantCulture,
|
CultureInfo.InvariantCulture,
|
||||||
"{0} - {1}x{2}",
|
"{0} - {1}x{2}",
|
||||||
|
17
MediaBrowser.Controller/IO/IPathManager.cs
Normal file
17
MediaBrowser.Controller/IO/IPathManager.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using MediaBrowser.Controller.Entities;
|
||||||
|
|
||||||
|
namespace MediaBrowser.Controller.IO;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interface ITrickplayManager.
|
||||||
|
/// </summary>
|
||||||
|
public interface IPathManager
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the path to the trickplay image base folder.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">The item.</param>
|
||||||
|
/// <param name="saveWithMedia">Whether or not the tile should be saved next to the media file.</param>
|
||||||
|
/// <returns>The absolute path.</returns>
|
||||||
|
public string GetTrickplayDirectory(BaseItem item, bool saveWithMedia = false);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user