Backport pull request #12025 from jellyfin/release-10.9.z

Fix empty image folder removal for legacy locations

Original-merge: 476dc01f4d

Merged-by: Bond-009 <bond.009@outlook.com>

Backported-by: Joshua M. Boniface <joshua@boniface.me>
This commit is contained in:
Shadowghost 2024-06-24 20:28:58 -04:00 committed by Joshua M. Boniface
parent ac114b27a9
commit 6734450d40
9 changed files with 118 additions and 118 deletions

View File

@ -389,7 +389,7 @@ namespace Emby.Server.Implementations.IO
var info = new FileInfo(path); var info = new FileInfo(path);
if (info.Exists && if (info.Exists &&
((info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) != isHidden) (info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden != isHidden)
{ {
if (isHidden) if (isHidden)
{ {
@ -417,8 +417,8 @@ namespace Emby.Server.Implementations.IO
return; return;
} }
if (((info.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) == readOnly if ((info.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly == readOnly
&& ((info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) == isHidden) && (info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden == isHidden)
{ {
return; return;
} }

View File

@ -1884,7 +1884,7 @@ namespace Emby.Server.Implementations.Library
try try
{ {
var index = item.GetImageIndex(img); var index = item.GetImageIndex(img);
image = await ConvertImageToLocal(item, img, index, removeOnFailure: true).ConfigureAwait(false); image = await ConvertImageToLocal(item, img, index, true).ConfigureAwait(false);
} }
catch (ArgumentException) catch (ArgumentException)
{ {

View File

@ -5,6 +5,7 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Tasks;
@ -133,53 +134,14 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
DeleteFile(file.FullName); FileSystemHelper.DeleteFile(_fileSystem, file.FullName, _logger);
index++; index++;
} }
DeleteEmptyFolders(directory); FileSystemHelper.DeleteEmptyFolders(_fileSystem, directory, _logger);
progress.Report(100); progress.Report(100);
} }
private void DeleteEmptyFolders(string parent)
{
foreach (var directory in _fileSystem.GetDirectoryPaths(parent))
{
DeleteEmptyFolders(directory);
if (!_fileSystem.GetFileSystemEntryPaths(directory).Any())
{
try
{
Directory.Delete(directory, false);
}
catch (UnauthorizedAccessException ex)
{
_logger.LogError(ex, "Error deleting directory {Path}", directory);
}
catch (IOException ex)
{
_logger.LogError(ex, "Error deleting directory {Path}", directory);
}
}
}
}
private void DeleteFile(string path)
{
try
{
_fileSystem.DeleteFile(path);
}
catch (UnauthorizedAccessException ex)
{
_logger.LogError(ex, "Error deleting file {Path}", path);
}
catch (IOException ex)
{
_logger.LogError(ex, "Error deleting file {Path}", path);
}
}
} }
} }

View File

@ -1,10 +1,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Tasks;
@ -113,53 +113,14 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
DeleteFile(file.FullName); FileSystemHelper.DeleteFile(_fileSystem, file.FullName, _logger);
index++; index++;
} }
DeleteEmptyFolders(directory); FileSystemHelper.DeleteEmptyFolders(_fileSystem, directory, _logger);
progress.Report(100); progress.Report(100);
} }
private void DeleteEmptyFolders(string parent)
{
foreach (var directory in _fileSystem.GetDirectoryPaths(parent))
{
DeleteEmptyFolders(directory);
if (!_fileSystem.GetFileSystemEntryPaths(directory).Any())
{
try
{
Directory.Delete(directory, false);
}
catch (UnauthorizedAccessException ex)
{
_logger.LogError(ex, "Error deleting directory {Path}", directory);
}
catch (IOException ex)
{
_logger.LogError(ex, "Error deleting directory {Path}", directory);
}
}
}
}
private void DeleteFile(string path)
{
try
{
_fileSystem.DeleteFile(path);
}
catch (UnauthorizedAccessException ex)
{
_logger.LogError(ex, "Error deleting file {Path}", path);
}
catch (IOException ex)
{
_logger.LogError(ex, "Error deleting file {Path}", path);
}
}
} }
} }

View File

@ -1949,14 +1949,15 @@ namespace MediaBrowser.Controller.Entities
return; return;
} }
// Remove it from the item // Remove from file system
RemoveImage(info);
if (info.IsLocalFile) if (info.IsLocalFile)
{ {
FileSystem.DeleteFile(info.Path); FileSystem.DeleteFile(info.Path);
} }
// Remove from item
RemoveImage(info);
await UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false); await UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
} }

View File

@ -0,0 +1,64 @@
using System;
using System.IO;
using System.Linq;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Controller.IO;
/// <summary>
/// Helper methods for file system management.
/// </summary>
public static class FileSystemHelper
{
/// <summary>
/// Deletes the file.
/// </summary>
/// <param name="fileSystem">The fileSystem.</param>
/// <param name="path">The path.</param>
/// <param name="logger">The logger.</param>
public static void DeleteFile(IFileSystem fileSystem, string path, ILogger logger)
{
try
{
fileSystem.DeleteFile(path);
}
catch (UnauthorizedAccessException ex)
{
logger.LogError(ex, "Error deleting file {Path}", path);
}
catch (IOException ex)
{
logger.LogError(ex, "Error deleting file {Path}", path);
}
}
/// <summary>
/// Recursively delete empty folders.
/// </summary>
/// <param name="fileSystem">The fileSystem.</param>
/// <param name="path">The path.</param>
/// <param name="logger">The logger.</param>
public static void DeleteEmptyFolders(IFileSystem fileSystem, string path, ILogger logger)
{
foreach (var directory in fileSystem.GetDirectoryPaths(path))
{
DeleteEmptyFolders(fileSystem, directory, logger);
if (!fileSystem.GetFileSystemEntryPaths(directory).Any())
{
try
{
Directory.Delete(directory, false);
}
catch (UnauthorizedAccessException ex)
{
logger.LogError(ex, "Error deleting directory {Path}", directory);
}
catch (IOException ex)
{
logger.LogError(ex, "Error deleting directory {Path}", directory);
}
}
}
}
}

View File

@ -14,6 +14,7 @@ using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
@ -188,11 +189,27 @@ namespace MediaBrowser.Providers.Manager
{ {
_fileSystem.DeleteFile(currentPath); _fileSystem.DeleteFile(currentPath);
// Remove containing directory if empty // Remove local episode metadata directory if it exists and is empty
var folder = Path.GetDirectoryName(currentPath); var directory = Path.GetDirectoryName(currentPath);
if (!_fileSystem.GetFiles(folder).Any()) if (item is Episode && directory.Equals("metadata", StringComparison.Ordinal))
{ {
Directory.Delete(folder); var parentDirectoryPath = Directory.GetParent(currentPath).FullName;
if (_fileSystem.DirectoryExists(parentDirectoryPath) && !_fileSystem.GetFiles(parentDirectoryPath).Any())
{
try
{
_logger.LogInformation("Deleting empty local metadata folder {Folder}", parentDirectoryPath);
Directory.Delete(parentDirectoryPath);
}
catch (UnauthorizedAccessException ex)
{
_logger.LogError(ex, "Error deleting directory {Path}", parentDirectoryPath);
}
catch (IOException ex)
{
_logger.LogError(ex, "Error deleting directory {Path}", parentDirectoryPath);
}
}
} }
} }
catch (FileNotFoundException) catch (FileNotFoundException)

View File

@ -10,6 +10,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
@ -96,7 +97,7 @@ namespace MediaBrowser.Providers.Manager
public bool ValidateImages(BaseItem item, IEnumerable<IImageProvider> providers, ImageRefreshOptions refreshOptions) public bool ValidateImages(BaseItem item, IEnumerable<IImageProvider> providers, ImageRefreshOptions refreshOptions)
{ {
var hasChanges = false; var hasChanges = false;
IDirectoryService directoryService = refreshOptions?.DirectoryService; var directoryService = refreshOptions?.DirectoryService;
if (item is not Photo) if (item is not Photo)
{ {
@ -359,10 +360,8 @@ namespace MediaBrowser.Providers.Manager
private void PruneImages(BaseItem item, IReadOnlyList<ItemImageInfo> images) private void PruneImages(BaseItem item, IReadOnlyList<ItemImageInfo> images)
{ {
for (var i = 0; i < images.Count; i++) foreach (var image in images)
{ {
var image = images[i];
if (image.IsLocalFile) if (image.IsLocalFile)
{ {
try try
@ -377,19 +376,20 @@ namespace MediaBrowser.Providers.Manager
{ {
_logger.LogWarning(ex, "Unable to delete {Image}", image.Path); _logger.LogWarning(ex, "Unable to delete {Image}", image.Path);
} }
finally
{
// Always remove empty parent folder
var folder = Path.GetDirectoryName(image.Path);
if (Directory.Exists(folder) && !_fileSystem.GetFiles(folder).Any())
{
Directory.Delete(folder);
}
}
} }
} }
item.RemoveImages(images); item.RemoveImages(images);
// Cleanup old metadata directory for episodes if empty
if (item is Episode)
{
var oldLocalMetadataDirectory = Path.Combine(item.ContainingFolderPath, "metadata");
if (_fileSystem.DirectoryExists(oldLocalMetadataDirectory) && !_fileSystem.GetFiles(oldLocalMetadataDirectory).Any())
{
Directory.Delete(oldLocalMetadataDirectory);
}
}
} }
/// <summary> /// <summary>

View File

@ -92,10 +92,6 @@ namespace MediaBrowser.Providers.Manager
} }
} }
var localImagesFailed = false;
var allImageProviders = ProviderManager.GetImageProviders(item, refreshOptions).ToList();
if (refreshOptions.RemoveOldMetadata && refreshOptions.ReplaceAllImages) if (refreshOptions.RemoveOldMetadata && refreshOptions.ReplaceAllImages)
{ {
if (ImageProvider.RemoveImages(item)) if (ImageProvider.RemoveImages(item))
@ -105,6 +101,8 @@ namespace MediaBrowser.Providers.Manager
} }
// Start by validating images // Start by validating images
var localImagesFailed = false;
var allImageProviders = ProviderManager.GetImageProviders(item, refreshOptions).ToList();
try try
{ {
// Always validate images and check for new locally stored ones. // Always validate images and check for new locally stored ones.
@ -813,19 +811,16 @@ namespace MediaBrowser.Providers.Manager
{ {
var refreshResult = new RefreshResult(); var refreshResult = new RefreshResult();
var tmpDataMerged = false; if (id is not null)
{
MergeNewData(temp.Item, id);
}
foreach (var provider in providers) foreach (var provider in providers)
{ {
var providerName = provider.GetType().Name; var providerName = provider.GetType().Name;
Logger.LogDebug("Running {Provider} for {Item}", providerName, logName); Logger.LogDebug("Running {Provider} for {Item}", providerName, logName);
if (id is not null && !tmpDataMerged)
{
MergeNewData(temp.Item, id);
tmpDataMerged = true;
}
try try
{ {
var result = await provider.GetMetadata(id, cancellationToken).ConfigureAwait(false); var result = await provider.GetMetadata(id, cancellationToken).ConfigureAwait(false);