repo creation with initial code after cloning public repo
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.IO;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MediaBrowser.Controller.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides low level File access that is much faster than the File/Directory api's.
|
||||
/// </summary>
|
||||
public static class FileData
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the filtered file system entries.
|
||||
/// </summary>
|
||||
/// <param name="directoryService">The directory service.</param>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="fileSystem">The file system.</param>
|
||||
/// <param name="appHost">The application host.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <param name="args">The args.</param>
|
||||
/// <param name="flattenFolderDepth">The flatten folder depth.</param>
|
||||
/// <param name="resolveShortcuts">if set to <c>true</c> [resolve shortcuts].</param>
|
||||
/// <returns>Dictionary{System.StringFileSystemInfo}.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="path" /> is <c>null</c> or empty.</exception>
|
||||
public static FileSystemMetadata[] GetFilteredFileSystemEntries(
|
||||
IDirectoryService directoryService,
|
||||
string path,
|
||||
IFileSystem fileSystem,
|
||||
IServerApplicationHost appHost,
|
||||
ILogger logger,
|
||||
ItemResolveArgs args,
|
||||
int flattenFolderDepth = 0,
|
||||
bool resolveShortcuts = true)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrEmpty(path);
|
||||
|
||||
ArgumentNullException.ThrowIfNull(args);
|
||||
|
||||
var entries = directoryService.GetFileSystemEntries(path);
|
||||
|
||||
if (!resolveShortcuts && flattenFolderDepth == 0)
|
||||
{
|
||||
return entries;
|
||||
}
|
||||
|
||||
var dict = new Dictionary<string, FileSystemMetadata>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
var isDirectory = entry.IsDirectory;
|
||||
|
||||
var fullName = entry.FullName;
|
||||
|
||||
if (resolveShortcuts && fileSystem.IsShortcut(fullName))
|
||||
{
|
||||
try
|
||||
{
|
||||
var newPath = appHost.ExpandVirtualPath(fileSystem.ResolveShortcut(fullName));
|
||||
|
||||
if (string.IsNullOrEmpty(newPath))
|
||||
{
|
||||
// invalid shortcut - could be old or target could just be unavailable
|
||||
logger.LogWarning("Encountered invalid shortcut: {Path}", fullName);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't check if it exists here because that could return false for network shares.
|
||||
var data = fileSystem.GetDirectoryInfo(newPath);
|
||||
|
||||
// add to our physical locations
|
||||
args.AddAdditionalLocation(newPath);
|
||||
|
||||
dict[newPath] = data;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Error resolving shortcut from {Path}", fullName);
|
||||
}
|
||||
}
|
||||
else if (flattenFolderDepth > 0 && isDirectory)
|
||||
{
|
||||
foreach (var child in GetFilteredFileSystemEntries(directoryService, fullName, fileSystem, appHost, logger, args, flattenFolderDepth: flattenFolderDepth - 1, resolveShortcuts: resolveShortcuts))
|
||||
{
|
||||
dict[child.FullName] = child;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dict[fullName] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
var returnResult = new FileSystemMetadata[dict.Count];
|
||||
var index = 0;
|
||||
var values = dict.Values;
|
||||
foreach (var value in values)
|
||||
{
|
||||
returnResult[index] = value;
|
||||
index++;
|
||||
}
|
||||
|
||||
return returnResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves a single link hop for the specified path.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Returns <c>null</c> if the path is not a symbolic link or the filesystem does not support link resolution (e.g., exFAT).
|
||||
/// </remarks>
|
||||
/// <param name="path">The file path to resolve.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="FileInfo"/> representing the next link target if the path is a link; otherwise, <c>null</c>.
|
||||
/// </returns>
|
||||
private static FileInfo? Resolve(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
return File.ResolveLinkTarget(path, returnFinalTarget: false) as FileInfo;
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
// Filesystem doesn't support links (e.g., exFAT).
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target of the specified file link.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This helper exists because of this upstream runtime issue; https://github.com/dotnet/runtime/issues/92128.
|
||||
/// </remarks>
|
||||
/// <param name="linkPath">The path of the file link.</param>
|
||||
/// <param name="returnFinalTarget">true to follow links to the final target; false to return the immediate next link.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="FileInfo"/> if the <paramref name="linkPath"/> is a link, regardless of if the target exists; otherwise, <c>null</c>.
|
||||
/// </returns>
|
||||
public static FileInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget = false)
|
||||
{
|
||||
// Check if the file exists so the native resolve handler won't throw at us.
|
||||
if (!File.Exists(linkPath))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!returnFinalTarget)
|
||||
{
|
||||
return Resolve(linkPath);
|
||||
}
|
||||
|
||||
var targetInfo = Resolve(linkPath);
|
||||
if (targetInfo is null || !targetInfo.Exists)
|
||||
{
|
||||
return targetInfo;
|
||||
}
|
||||
|
||||
var currentPath = targetInfo.FullName;
|
||||
var visited = new HashSet<string>(StringComparer.Ordinal) { linkPath, currentPath };
|
||||
|
||||
while (true)
|
||||
{
|
||||
var linkInfo = Resolve(currentPath);
|
||||
if (linkInfo is null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var targetPath = linkInfo.FullName;
|
||||
|
||||
// If an infinite loop is detected, return the file info for the
|
||||
// first link in the loop we encountered.
|
||||
if (!visited.Add(targetPath))
|
||||
{
|
||||
return new FileInfo(targetPath);
|
||||
}
|
||||
|
||||
targetInfo = linkInfo;
|
||||
currentPath = targetPath;
|
||||
|
||||
// Exit if the target doesn't exist, so the native resolve handler won't throw at us.
|
||||
if (!targetInfo.Exists)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return targetInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target of the specified file link.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This helper exists because of this upstream runtime issue; https://github.com/dotnet/runtime/issues/92128.
|
||||
/// </remarks>
|
||||
/// <param name="fileInfo">The file info of the file link.</param>
|
||||
/// <param name="returnFinalTarget">true to follow links to the final target; false to return the immediate next link.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="FileInfo"/> if the <paramref name="fileInfo"/> is a link, regardless of if the target exists; otherwise, <c>null</c>.
|
||||
/// </returns>
|
||||
public static FileInfo? ResolveLinkTarget(FileInfo fileInfo, bool returnFinalTarget = false)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(fileInfo);
|
||||
|
||||
return ResolveLinkTarget(fileInfo.FullName, returnFinalTarget);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
|
||||
namespace MediaBrowser.Controller.IO;
|
||||
|
||||
/// <summary>
|
||||
/// Interface IPathManager.
|
||||
/// </summary>
|
||||
public interface IExternalDataManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Deletes all external item data.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task DeleteExternalItemDataAsync(BaseItem item, CancellationToken cancellationToken);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
|
||||
namespace MediaBrowser.Controller.IO;
|
||||
|
||||
/// <summary>
|
||||
/// Interface IPathManager.
|
||||
/// </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);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the subtitle file.
|
||||
/// </summary>
|
||||
/// <param name="mediaSourceId">The media source id.</param>
|
||||
/// <param name="streamIndex">The stream index.</param>
|
||||
/// <param name="extension">The subtitle file extension.</param>
|
||||
/// <returns>The absolute path.</returns>
|
||||
public string GetSubtitlePath(string mediaSourceId, int streamIndex, string extension);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the subtitle file.
|
||||
/// </summary>
|
||||
/// <param name="mediaSourceId">The media source id.</param>
|
||||
/// <returns>The absolute path.</returns>
|
||||
public string GetSubtitleFolderPath(string mediaSourceId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the attachment file.
|
||||
/// </summary>
|
||||
/// <param name="mediaSourceId">The media source id.</param>
|
||||
/// <param name="fileName">The attachmentFileName index.</param>
|
||||
/// <returns>The absolute path.</returns>
|
||||
public string GetAttachmentPath(string mediaSourceId, string fileName);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the attachment folder.
|
||||
/// </summary>
|
||||
/// <param name="mediaSourceId">The media source id.</param>
|
||||
/// <returns>The absolute path.</returns>
|
||||
public string GetAttachmentFolderPath(string mediaSourceId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the chapter images data path.
|
||||
/// </summary>
|
||||
/// <param name="item">The base item.</param>
|
||||
/// <returns>The chapter images data path.</returns>
|
||||
public string GetChapterImageFolderPath(BaseItem item);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the chapter images path.
|
||||
/// </summary>
|
||||
/// <param name="item">The base item.</param>
|
||||
/// <param name="chapterPositionTicks">The chapter position.</param>
|
||||
/// <returns>The chapter images data path.</returns>
|
||||
public string GetChapterImagePath(BaseItem item, long chapterPositionTicks);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the paths of extracted data folders.
|
||||
/// </summary>
|
||||
/// <param name="item">The base item.</param>
|
||||
/// <returns>The absolute paths.</returns>
|
||||
public IReadOnlyList<string> GetExtractedDataPaths(BaseItem item);
|
||||
}
|
||||
Reference in New Issue
Block a user