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:
parent
ac114b27a9
commit
6734450d40
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
64
MediaBrowser.Controller/IO/FileSystemHelper.cs
Normal file
64
MediaBrowser.Controller/IO/FileSystemHelper.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user