Add long path support
This commit is contained in:
parent
b710075544
commit
a3844a3535
@ -27,7 +27,7 @@ namespace FileLiberator
|
|||||||
public static bool ValidateMp3(LibraryBook libraryBook)
|
public static bool ValidateMp3(LibraryBook libraryBook)
|
||||||
{
|
{
|
||||||
var path = AudibleFileStorage.Audio.GetPath(libraryBook.Book.AudibleProductId);
|
var path = AudibleFileStorage.Audio.GetPath(libraryBook.Book.AudibleProductId);
|
||||||
return path?.ToLower()?.EndsWith(".m4b") == true && !File.Exists(Mp3FileName(path));
|
return path?.ToString()?.ToLower()?.EndsWith(".m4b") == true && !File.Exists(Mp3FileName(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Validate(LibraryBook libraryBook) => ValidateMp3(libraryBook);
|
public override bool Validate(LibraryBook libraryBook) => ValidateMp3(libraryBook);
|
||||||
|
|||||||
@ -12,7 +12,7 @@ namespace FileManager
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class BackgroundFileSystem
|
public class BackgroundFileSystem
|
||||||
{
|
{
|
||||||
public string RootDirectory { get; private set; }
|
public LongPath RootDirectory { get; private set; }
|
||||||
public string SearchPattern { get; private set; }
|
public string SearchPattern { get; private set; }
|
||||||
public SearchOption SearchOption { get; private set; }
|
public SearchOption SearchOption { get; private set; }
|
||||||
|
|
||||||
@ -21,9 +21,9 @@ namespace FileManager
|
|||||||
private Task backgroundScanner { get; set; }
|
private Task backgroundScanner { get; set; }
|
||||||
|
|
||||||
private object fsCacheLocker { get; } = new();
|
private object fsCacheLocker { get; } = new();
|
||||||
private List<string> fsCache { get; } = new();
|
private List<LongPath> fsCache { get; } = new();
|
||||||
|
|
||||||
public BackgroundFileSystem(string rootDirectory, string searchPattern, SearchOption searchOptions)
|
public BackgroundFileSystem(LongPath rootDirectory, string searchPattern, SearchOption searchOptions)
|
||||||
{
|
{
|
||||||
RootDirectory = rootDirectory;
|
RootDirectory = rootDirectory;
|
||||||
SearchPattern = searchPattern;
|
SearchPattern = searchPattern;
|
||||||
@ -124,16 +124,18 @@ namespace FileManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RemovePath(string path)
|
private void RemovePath(LongPath path)
|
||||||
{
|
{
|
||||||
var pathsToRemove = fsCache.Where(p => p.StartsWith(path)).ToArray();
|
path = path.LongPathName;
|
||||||
|
var pathsToRemove = fsCache.Where(p => ((string)p).StartsWith(path)).ToArray();
|
||||||
|
|
||||||
foreach (var p in pathsToRemove)
|
foreach (var p in pathsToRemove)
|
||||||
fsCache.Remove(p);
|
fsCache.Remove(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddPath(string path)
|
private void AddPath(LongPath path)
|
||||||
{
|
{
|
||||||
|
path = path.LongPathName;
|
||||||
if (!File.Exists(path) && !Directory.Exists(path))
|
if (!File.Exists(path) && !Directory.Exists(path))
|
||||||
return;
|
return;
|
||||||
if (File.GetAttributes(path).HasFlag(FileAttributes.Directory))
|
if (File.GetAttributes(path).HasFlag(FileAttributes.Directory))
|
||||||
@ -141,12 +143,14 @@ namespace FileManager
|
|||||||
else
|
else
|
||||||
AddUniqueFile(path);
|
AddUniqueFile(path);
|
||||||
}
|
}
|
||||||
private void AddUniqueFiles(IEnumerable<string> newFiles)
|
|
||||||
|
private void AddUniqueFiles(IEnumerable<LongPath> newFiles)
|
||||||
{
|
{
|
||||||
foreach (var file in newFiles)
|
foreach (var file in newFiles)
|
||||||
AddUniqueFile(file);
|
AddUniqueFile(file);
|
||||||
}
|
}
|
||||||
private void AddUniqueFile(string newFile)
|
|
||||||
|
private void AddUniqueFile(LongPath newFile)
|
||||||
{
|
{
|
||||||
if (!fsCache.Contains(newFile))
|
if (!fsCache.Contains(newFile))
|
||||||
fsCache.Add(newFile);
|
fsCache.Add(newFile);
|
||||||
|
|||||||
@ -23,16 +23,16 @@ namespace FileManager
|
|||||||
=> ParameterReplacements.Add(key, value);
|
=> ParameterReplacements.Add(key, value);
|
||||||
|
|
||||||
/// <summary>If set, truncate each parameter replacement to this many characters. Default 50</summary>
|
/// <summary>If set, truncate each parameter replacement to this many characters. Default 50</summary>
|
||||||
public int? ParameterMaxSize { get; set; } = 50;
|
public int? ParameterMaxSize { get; set; } = null;
|
||||||
|
|
||||||
/// <summary>Optional step 2: Replace all illegal characters with this. Default=<see cref="string.Empty"/></summary>
|
/// <summary>Optional step 2: Replace all illegal characters with this. Default=<see cref="string.Empty"/></summary>
|
||||||
public string IllegalCharacterReplacements { get; set; }
|
public string IllegalCharacterReplacements { get; set; }
|
||||||
|
|
||||||
/// <summary>Generate a valid path for this file or directory</summary>
|
/// <summary>Generate a valid path for this file or directory</summary>
|
||||||
public string GetFilePath(bool returnFirstExisting = false)
|
public LongPath GetFilePath(bool returnFirstExisting = false)
|
||||||
{
|
{
|
||||||
var filename = Template;
|
var filename = Template;
|
||||||
|
|
||||||
foreach (var r in ParameterReplacements)
|
foreach (var r in ParameterReplacements)
|
||||||
filename = filename.Replace($"<{formatKey(r.Key)}>", formatValue(r.Value));
|
filename = filename.Replace($"<{formatKey(r.Key)}>", formatValue(r.Value));
|
||||||
|
|
||||||
|
|||||||
@ -39,8 +39,6 @@ namespace FileManager
|
|||||||
return position.ToString().PadLeft(total.ToString().Length, '0');
|
return position.ToString().PadLeft(total.ToString().Length, '0');
|
||||||
}
|
}
|
||||||
|
|
||||||
private const int MAX_FILENAME_LENGTH = 255;
|
|
||||||
private const int MAX_DIRECTORY_LENGTH = 247;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ensure valid file name path:
|
/// Ensure valid file name path:
|
||||||
@ -48,7 +46,7 @@ namespace FileManager
|
|||||||
/// <br/>- ensure uniqueness
|
/// <br/>- ensure uniqueness
|
||||||
/// <br/>- enforce max file length
|
/// <br/>- enforce max file length
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string GetValidFilename(string path, string illegalCharacterReplacements = "", bool returnFirstExisting = false)
|
public static LongPath GetValidFilename(LongPath path, string illegalCharacterReplacements = "", bool returnFirstExisting = false)
|
||||||
{
|
{
|
||||||
ArgumentValidator.EnsureNotNull(path, nameof(path));
|
ArgumentValidator.EnsureNotNull(path, nameof(path));
|
||||||
|
|
||||||
@ -57,14 +55,15 @@ namespace FileManager
|
|||||||
|
|
||||||
// ensure uniqueness and check lengths
|
// ensure uniqueness and check lengths
|
||||||
var dir = Path.GetDirectoryName(path);
|
var dir = Path.GetDirectoryName(path);
|
||||||
dir = dir.Truncate(MAX_DIRECTORY_LENGTH);
|
dir = dir?.Truncate(LongPath.MaxDirectoryLength) ?? string.Empty;
|
||||||
|
|
||||||
var filename = Path.GetFileNameWithoutExtension(path);
|
|
||||||
var fileStem = Path.Combine(dir, filename);
|
|
||||||
|
|
||||||
var extension = Path.GetExtension(path);
|
var extension = Path.GetExtension(path);
|
||||||
|
|
||||||
var fullfilename = fileStem.Truncate(MAX_FILENAME_LENGTH - extension.Length) + extension;
|
var filename = Path.GetFileNameWithoutExtension(path).Truncate(LongPath.MaxFilenameLength - extension.Length);
|
||||||
|
var fileStem = Path.Combine(dir, filename);
|
||||||
|
|
||||||
|
|
||||||
|
var fullfilename = fileStem.Truncate(LongPath.MaxPathLength - extension.Length) + extension;
|
||||||
|
|
||||||
fullfilename = removeInvalidWhitespace(fullfilename);
|
fullfilename = removeInvalidWhitespace(fullfilename);
|
||||||
|
|
||||||
@ -72,7 +71,7 @@ namespace FileManager
|
|||||||
while (File.Exists(fullfilename) && !returnFirstExisting)
|
while (File.Exists(fullfilename) && !returnFirstExisting)
|
||||||
{
|
{
|
||||||
var increm = $" ({++i})";
|
var increm = $" ({++i})";
|
||||||
fullfilename = fileStem.Truncate(MAX_FILENAME_LENGTH - increm.Length - extension.Length) + increm + extension;
|
fullfilename = fileStem.Truncate(LongPath.MaxPathLength - increm.Length - extension.Length) + increm + extension;
|
||||||
}
|
}
|
||||||
|
|
||||||
return fullfilename;
|
return fullfilename;
|
||||||
@ -85,16 +84,18 @@ namespace FileManager
|
|||||||
=> string.Join(illegalCharacterReplacements ?? "", str.Split(Path.GetInvalidFileNameChars()));
|
=> string.Join(illegalCharacterReplacements ?? "", str.Split(Path.GetInvalidFileNameChars()));
|
||||||
|
|
||||||
/// <summary>Use with full path, not file name. Valid path charaters which are invalid file name characters will be retained: '\\', '/'</summary>
|
/// <summary>Use with full path, not file name. Valid path charaters which are invalid file name characters will be retained: '\\', '/'</summary>
|
||||||
public static string GetSafePath(string path, string illegalCharacterReplacements = "")
|
public static LongPath GetSafePath(LongPath path, string illegalCharacterReplacements = "")
|
||||||
{
|
{
|
||||||
ArgumentValidator.EnsureNotNull(path, nameof(path));
|
ArgumentValidator.EnsureNotNull(path, nameof(path));
|
||||||
|
|
||||||
path = replaceInvalidChars(path, illegalCharacterReplacements);
|
var pathNoPrefix = path.PathWithoutPrefix;
|
||||||
path = standardizeSlashes(path);
|
|
||||||
path = replaceColons(path, illegalCharacterReplacements);
|
|
||||||
path = removeDoubleSlashes(path);
|
|
||||||
|
|
||||||
return path;
|
pathNoPrefix = replaceInvalidChars(pathNoPrefix, illegalCharacterReplacements);
|
||||||
|
pathNoPrefix = standardizeSlashes(pathNoPrefix);
|
||||||
|
pathNoPrefix = replaceColons(pathNoPrefix, illegalCharacterReplacements);
|
||||||
|
pathNoPrefix = removeDoubleSlashes(pathNoPrefix);
|
||||||
|
|
||||||
|
return pathNoPrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static char[] invalidChars { get; } = Path.GetInvalidPathChars().Union(new[] {
|
private static char[] invalidChars { get; } = Path.GetInvalidPathChars().Union(new[] {
|
||||||
@ -169,7 +170,7 @@ namespace FileManager
|
|||||||
/// <br/>- Perform <see cref="SaferMove"/>
|
/// <br/>- Perform <see cref="SaferMove"/>
|
||||||
/// <br/>- Return valid path
|
/// <br/>- Return valid path
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string SaferMoveToValidPath(string source, string destination)
|
public static string SaferMoveToValidPath(LongPath source, LongPath destination)
|
||||||
{
|
{
|
||||||
destination = GetValidFilename(destination);
|
destination = GetValidFilename(destination);
|
||||||
SaferMove(source, destination);
|
SaferMove(source, destination);
|
||||||
@ -184,7 +185,7 @@ namespace FileManager
|
|||||||
.WaitAndRetry(maxRetryAttempts, i => pauseBetweenFailures);
|
.WaitAndRetry(maxRetryAttempts, i => pauseBetweenFailures);
|
||||||
|
|
||||||
/// <summary>Delete file. No error when source does not exist. Retry up to 3 times before throwing exception.</summary>
|
/// <summary>Delete file. No error when source does not exist. Retry up to 3 times before throwing exception.</summary>
|
||||||
public static void SaferDelete(string source)
|
public static void SaferDelete(LongPath source)
|
||||||
=> retryPolicy.Execute(() =>
|
=> retryPolicy.Execute(() =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -207,7 +208,7 @@ namespace FileManager
|
|||||||
});
|
});
|
||||||
|
|
||||||
/// <summary>Move file. No error when source does not exist. Retry up to 3 times before throwing exception.</summary>
|
/// <summary>Move file. No error when source does not exist. Retry up to 3 times before throwing exception.</summary>
|
||||||
public static void SaferMove(string source, string destination)
|
public static void SaferMove(LongPath source, LongPath destination)
|
||||||
=> retryPolicy.Execute(() =>
|
=> retryPolicy.Execute(() =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -242,27 +243,32 @@ namespace FileManager
|
|||||||
/// <param name="patternMatch">Filename pattern match</param>
|
/// <param name="patternMatch">Filename pattern match</param>
|
||||||
/// <param name="searchOption">Search subdirectories or only top level directory for files</param>
|
/// <param name="searchOption">Search subdirectories or only top level directory for files</param>
|
||||||
/// <returns>List of files</returns>
|
/// <returns>List of files</returns>
|
||||||
public static IEnumerable<string> SaferEnumerateFiles(string path, string searchPattern = "*", SearchOption searchOption = SearchOption.TopDirectoryOnly)
|
public static IEnumerable<LongPath> SaferEnumerateFiles(LongPath path, string searchPattern = "*", SearchOption searchOption = SearchOption.TopDirectoryOnly)
|
||||||
{
|
{
|
||||||
var foundFiles = Enumerable.Empty<string>();
|
var foundFiles = Enumerable.Empty<LongPath>();
|
||||||
|
|
||||||
if (searchOption == SearchOption.AllDirectories)
|
if (searchOption == SearchOption.AllDirectories)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
IEnumerable<string> subDirs = Directory.EnumerateDirectories(path);
|
var list = Directory.EnumerateDirectories(path).ToList();
|
||||||
|
IEnumerable <LongPath> subDirs = Directory.EnumerateDirectories(path).Select(p => (LongPath)p);
|
||||||
// Add files in subdirectories recursively to the list
|
// Add files in subdirectories recursively to the list
|
||||||
foreach (string dir in subDirs)
|
foreach (string dir in subDirs)
|
||||||
foundFiles = foundFiles.Concat(SaferEnumerateFiles(dir, searchPattern, searchOption));
|
foundFiles = foundFiles.Concat(SaferEnumerateFiles(dir, searchPattern, searchOption));
|
||||||
}
|
}
|
||||||
catch (UnauthorizedAccessException) { }
|
catch (UnauthorizedAccessException) { }
|
||||||
catch (PathTooLongException) { }
|
catch (PathTooLongException) { }
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Add files from the current directory
|
// Add files from the current directory
|
||||||
foundFiles = foundFiles.Concat(Directory.EnumerateFiles(path, searchPattern));
|
foundFiles = foundFiles.Concat(Directory.EnumerateFiles(path, searchPattern).Select(f => (LongPath)f));
|
||||||
}
|
}
|
||||||
catch (UnauthorizedAccessException) { }
|
catch (UnauthorizedAccessException) { }
|
||||||
|
|
||||||
|
|||||||
92
Source/FileManager/LongPath.cs
Normal file
92
Source/FileManager/LongPath.cs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace FileManager
|
||||||
|
{
|
||||||
|
public class LongPath
|
||||||
|
{
|
||||||
|
//https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd
|
||||||
|
|
||||||
|
public const int MaxDirectoryLength = MaxPathLength - 13;
|
||||||
|
public const int MaxPathLength = short.MaxValue;
|
||||||
|
public const int MaxFilenameLength = 255;
|
||||||
|
|
||||||
|
private const int MAX_PATH = 260;
|
||||||
|
private const string LONG_PATH_PREFIX = "\\\\?\\";
|
||||||
|
private static readonly StringBuilder longPathBuffer = new(MaxPathLength);
|
||||||
|
|
||||||
|
public string Path { get; init; }
|
||||||
|
public override string ToString() => Path;
|
||||||
|
|
||||||
|
public static implicit operator LongPath(string path)
|
||||||
|
{
|
||||||
|
if (path is null) return null;
|
||||||
|
|
||||||
|
//File I/O functions in the Windows API convert "/" to "\" as part of converting
|
||||||
|
//the name to an NT-style name, except when using the "\\?\" prefix
|
||||||
|
path = path.Replace('/', '\\');
|
||||||
|
|
||||||
|
if (path.StartsWith(LONG_PATH_PREFIX))
|
||||||
|
return new LongPath { Path = path };
|
||||||
|
else if ((path.Length > 2 && path[1] == ':') || path.StartsWith("UNC\\"))
|
||||||
|
return new LongPath { Path = LONG_PATH_PREFIX + path };
|
||||||
|
else if (path.StartsWith("\\\\"))
|
||||||
|
//The "\\?\" prefix can also be used with paths constructed according to the
|
||||||
|
//universal naming convention (UNC). To specify such a path using UNC, use
|
||||||
|
//the "\\?\UNC\" prefix.
|
||||||
|
return new LongPath { Path = LONG_PATH_PREFIX + "UNC\\" + path.Substring(2) };
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//These prefixes are not used as part of the path itself. They indicate that
|
||||||
|
//the path should be passed to the system with minimal modification, which
|
||||||
|
//means that you cannot use forward slashes to represent path separators, or
|
||||||
|
//a period to represent the current directory, or double dots to represent the
|
||||||
|
//parent directory. Because you cannot use the "\\?\" prefix with a relative
|
||||||
|
//path, relative paths are always limited to a total of MAX_PATH characters.
|
||||||
|
if (path.Length > MAX_PATH)
|
||||||
|
throw new System.IO.PathTooLongException();
|
||||||
|
return new LongPath { Path = path };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator string(LongPath path) => path?.Path ?? null;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public string ShortPathName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Path is null) return null;
|
||||||
|
GetShortPathName(Path, longPathBuffer, MAX_PATH);
|
||||||
|
return longPathBuffer.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public string LongPathName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Path is null) return null;
|
||||||
|
GetLongPathName(Path, longPathBuffer, MaxPathLength);
|
||||||
|
return longPathBuffer.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public string PathWithoutPrefix
|
||||||
|
=> Path?.StartsWith(LONG_PATH_PREFIX) == true ?
|
||||||
|
Path.Remove(0, LONG_PATH_PREFIX.Length) :
|
||||||
|
Path;
|
||||||
|
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
||||||
|
private static extern int GetShortPathName(string path, StringBuilder shortPath, int shortPathLength);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
||||||
|
private static extern int GetLongPathName(string lpszShortPath, StringBuilder lpszLongPath, int cchBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
@ -9,18 +8,18 @@ namespace LibationFileManager
|
|||||||
{
|
{
|
||||||
public abstract class AudibleFileStorage
|
public abstract class AudibleFileStorage
|
||||||
{
|
{
|
||||||
protected abstract string GetFilePathCustom(string productId);
|
protected abstract LongPath GetFilePathCustom(string productId);
|
||||||
|
|
||||||
#region static
|
#region static
|
||||||
public static string DownloadsInProgressDirectory => Directory.CreateDirectory(Path.Combine(Configuration.Instance.InProgress, "DownloadsInProgress")).FullName;
|
public static LongPath DownloadsInProgressDirectory => Directory.CreateDirectory(Path.Combine(Configuration.Instance.InProgress, "DownloadsInProgress")).FullName;
|
||||||
public static string DecryptInProgressDirectory => Directory.CreateDirectory(Path.Combine(Configuration.Instance.InProgress, "DecryptInProgress")).FullName;
|
public static LongPath DecryptInProgressDirectory => Directory.CreateDirectory(Path.Combine(Configuration.Instance.InProgress, "DecryptInProgress")).FullName;
|
||||||
|
|
||||||
private static AaxcFileStorage AAXC { get; } = new AaxcFileStorage();
|
private static AaxcFileStorage AAXC { get; } = new AaxcFileStorage();
|
||||||
public static bool AaxcExists(string productId) => AAXC.Exists(productId);
|
public static bool AaxcExists(string productId) => AAXC.Exists(productId);
|
||||||
|
|
||||||
public static AudioFileStorage Audio { get; } = new AudioFileStorage();
|
public static AudioFileStorage Audio { get; } = new AudioFileStorage();
|
||||||
|
|
||||||
public static string BooksDirectory
|
public static LongPath BooksDirectory
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@ -43,7 +42,7 @@ namespace LibationFileManager
|
|||||||
regexTemplate = $@"{{0}}.*?\.({extAggr})$";
|
regexTemplate = $@"{{0}}.*?\.({extAggr})$";
|
||||||
}
|
}
|
||||||
|
|
||||||
protected string GetFilePath(string productId)
|
protected LongPath GetFilePath(string productId)
|
||||||
{
|
{
|
||||||
// primary lookup
|
// primary lookup
|
||||||
var cachedFile = FilePathCache.GetFirstPath(productId, FileType);
|
var cachedFile = FilePathCache.GetFirstPath(productId, FileType);
|
||||||
@ -70,7 +69,7 @@ namespace LibationFileManager
|
|||||||
{
|
{
|
||||||
internal AaxcFileStorage() : base(FileType.AAXC) { }
|
internal AaxcFileStorage() : base(FileType.AAXC) { }
|
||||||
|
|
||||||
protected override string GetFilePathCustom(string productId)
|
protected override LongPath GetFilePathCustom(string productId)
|
||||||
{
|
{
|
||||||
var regex = GetBookSearchRegex(productId);
|
var regex = GetBookSearchRegex(productId);
|
||||||
return FileUtility
|
return FileUtility
|
||||||
@ -88,7 +87,7 @@ namespace LibationFileManager
|
|||||||
|
|
||||||
private static BackgroundFileSystem BookDirectoryFiles { get; set; }
|
private static BackgroundFileSystem BookDirectoryFiles { get; set; }
|
||||||
private static object bookDirectoryFilesLocker { get; } = new();
|
private static object bookDirectoryFilesLocker { get; } = new();
|
||||||
protected override string GetFilePathCustom(string productId)
|
protected override LongPath GetFilePathCustom(string productId)
|
||||||
{
|
{
|
||||||
// If user changed the BooksDirectory: reinitialize
|
// If user changed the BooksDirectory: reinitialize
|
||||||
lock (bookDirectoryFilesLocker)
|
lock (bookDirectoryFilesLocker)
|
||||||
@ -101,6 +100,6 @@ namespace LibationFileManager
|
|||||||
|
|
||||||
public void Refresh() => BookDirectoryFiles.RefreshFiles();
|
public void Refresh() => BookDirectoryFiles.RefreshFiles();
|
||||||
|
|
||||||
public string GetPath(string productId) => GetFilePath(productId);
|
public LongPath GetPath(string productId) => GetFilePath(productId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -455,7 +455,7 @@ namespace LibationFileManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string libationFilesPathCache;
|
private static string libationFilesPathCache { get; set; }
|
||||||
|
|
||||||
private string getLibationFilesSettingFromJson()
|
private string getLibationFilesSettingFromJson()
|
||||||
{
|
{
|
||||||
@ -478,7 +478,7 @@ namespace LibationFileManager
|
|||||||
catch { }
|
catch { }
|
||||||
|
|
||||||
// not found. write to file. read from file
|
// not found. write to file. read from file
|
||||||
var endingContents = new JObject { { LIBATION_FILES_KEY, UserProfile } }.ToString(Formatting.Indented);
|
var endingContents = new JObject { { LIBATION_FILES_KEY, UserProfile.ToString() } }.ToString(Formatting.Indented);
|
||||||
if (startingContents != endingContents)
|
if (startingContents != endingContents)
|
||||||
{
|
{
|
||||||
File.WriteAllText(APPSETTINGS_JSON, endingContents);
|
File.WriteAllText(APPSETTINGS_JSON, endingContents);
|
||||||
|
|||||||
@ -3,13 +3,14 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Dinah.Core.Collections.Immutable;
|
using Dinah.Core.Collections.Immutable;
|
||||||
|
using FileManager;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace LibationFileManager
|
namespace LibationFileManager
|
||||||
{
|
{
|
||||||
public static class FilePathCache
|
public static class FilePathCache
|
||||||
{
|
{
|
||||||
public record CacheEntry(string Id, FileType FileType, string Path);
|
public record CacheEntry(string Id, FileType FileType, LongPath Path);
|
||||||
|
|
||||||
private const string FILENAME = "FileLocations.json";
|
private const string FILENAME = "FileLocations.json";
|
||||||
|
|
||||||
@ -18,7 +19,7 @@ namespace LibationFileManager
|
|||||||
|
|
||||||
private static Cache<CacheEntry> cache { get; } = new Cache<CacheEntry>();
|
private static Cache<CacheEntry> cache { get; } = new Cache<CacheEntry>();
|
||||||
|
|
||||||
private static string jsonFile => Path.Combine(Configuration.Instance.LibationFiles, FILENAME);
|
private static LongPath jsonFile => Path.Combine(Configuration.Instance.LibationFiles, FILENAME);
|
||||||
|
|
||||||
static FilePathCache()
|
static FilePathCache()
|
||||||
{
|
{
|
||||||
@ -44,12 +45,12 @@ namespace LibationFileManager
|
|||||||
|
|
||||||
public static bool Exists(string id, FileType type) => GetFirstPath(id, type) is not null;
|
public static bool Exists(string id, FileType type) => GetFirstPath(id, type) is not null;
|
||||||
|
|
||||||
public static List<(FileType fileType, string path)> GetFiles(string id)
|
public static List<(FileType fileType, LongPath path)> GetFiles(string id)
|
||||||
=> getEntries(entry => entry.Id == id)
|
=> getEntries(entry => entry.Id == id)
|
||||||
.Select(entry => (entry.FileType, entry.Path))
|
.Select(entry => (entry.FileType, entry.Path))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
public static string GetFirstPath(string id, FileType type)
|
public static LongPath GetFirstPath(string id, FileType type)
|
||||||
=> getEntries(entry => entry.Id == id && entry.FileType == type)
|
=> getEntries(entry => entry.Id == id && entry.FileType == type)
|
||||||
?.FirstOrDefault()
|
?.FirstOrDefault()
|
||||||
?.Path;
|
?.Path;
|
||||||
@ -62,7 +63,7 @@ namespace LibationFileManager
|
|||||||
|
|
||||||
remove(entries.Where(e => !File.Exists(e.Path)).ToList());
|
remove(entries.Where(e => !File.Exists(e.Path)).ToList());
|
||||||
|
|
||||||
return entries;
|
return cache.Where(predicate).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void remove(List<CacheEntry> entries)
|
private static void remove(List<CacheEntry> entries)
|
||||||
|
|||||||
@ -105,7 +105,7 @@ namespace LibationFileManager
|
|||||||
=> string.IsNullOrWhiteSpace(template)
|
=> string.IsNullOrWhiteSpace(template)
|
||||||
? ""
|
? ""
|
||||||
: getFileNamingTemplate(libraryBookDto, template, null, null)
|
: getFileNamingTemplate(libraryBookDto, template, null, null)
|
||||||
.GetFilePath();
|
.GetFilePath().PathWithoutPrefix;
|
||||||
|
|
||||||
private static Regex ifSeriesRegex { get; } = new Regex("<if series->(.*?)<-if series>", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
private static Regex ifSeriesRegex { get; } = new Regex("<if series->(.*?)<-if series>", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
@ -276,7 +276,7 @@ namespace LibationFileManager
|
|||||||
fileNamingTemplate.AddParameterReplacement(TemplateTags.ChNumber0, FileUtility.GetSequenceFormatted(props.PartsPosition, props.PartsTotal));
|
fileNamingTemplate.AddParameterReplacement(TemplateTags.ChNumber0, FileUtility.GetSequenceFormatted(props.PartsPosition, props.PartsTotal));
|
||||||
fileNamingTemplate.AddParameterReplacement(TemplateTags.ChTitle, props.Title ?? "");
|
fileNamingTemplate.AddParameterReplacement(TemplateTags.ChTitle, props.Title ?? "");
|
||||||
|
|
||||||
return fileNamingTemplate.GetFilePath();
|
return fileNamingTemplate.GetFilePath().PathWithoutPrefix;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ using System;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using FileManager;
|
||||||
|
|
||||||
namespace LibationWinForms.Dialogs
|
namespace LibationWinForms.Dialogs
|
||||||
{
|
{
|
||||||
@ -47,7 +48,7 @@ namespace LibationWinForms.Dialogs
|
|||||||
|
|
||||||
private void logsLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
|
private void logsLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
|
||||||
{
|
{
|
||||||
string dir = "";
|
LongPath dir = "";
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
dir = LibationFileManager.Configuration.Instance.LibationFiles;
|
dir = LibationFileManager.Configuration.Instance.LibationFiles;
|
||||||
@ -56,7 +57,7 @@ namespace LibationWinForms.Dialogs
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Go.To.Folder(dir);
|
Go.To.Folder(dir.ShortPathName);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
|
using FileManager;
|
||||||
|
|
||||||
namespace LibationWinForms.Dialogs
|
namespace LibationWinForms.Dialogs
|
||||||
{
|
{
|
||||||
@ -124,7 +124,7 @@ namespace LibationWinForms.Dialogs
|
|||||||
chapterFileTemplateTb.Text = config.ChapterFileTemplate;
|
chapterFileTemplateTb.Text = config.ChapterFileTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void logsBtn_Click(object sender, EventArgs e) => Go.To.Folder(Configuration.Instance.LibationFiles);
|
private void logsBtn_Click(object sender, EventArgs e) => Go.To.Folder(((LongPath)Configuration.Instance.LibationFiles).ShortPathName);
|
||||||
|
|
||||||
private void folderTemplateBtn_Click(object sender, EventArgs e) => editTemplate(Templates.Folder, folderTemplateTb);
|
private void folderTemplateBtn_Click(object sender, EventArgs e) => editTemplate(Templates.Folder, folderTemplateTb);
|
||||||
private void fileTemplateBtn_Click(object sender, EventArgs e) => editTemplate(Templates.File, fileTemplateTb);
|
private void fileTemplateBtn_Click(object sender, EventArgs e) => editTemplate(Templates.File, fileTemplateTb);
|
||||||
|
|||||||
@ -38,7 +38,7 @@ namespace LibationWinForms
|
|||||||
{
|
{
|
||||||
// liberated: open explorer to file
|
// liberated: open explorer to file
|
||||||
var filePath = AudibleFileStorage.Audio.GetPath(e.Book.AudibleProductId);
|
var filePath = AudibleFileStorage.Audio.GetPath(e.Book.AudibleProductId);
|
||||||
if (!Go.To.File(filePath))
|
if (!Go.To.File(filePath?.ShortPathName))
|
||||||
{
|
{
|
||||||
var suffix = string.IsNullOrWhiteSpace(filePath) ? "" : $":\r\n{filePath}";
|
var suffix = string.IsNullOrWhiteSpace(filePath) ? "" : $":\r\n{filePath}";
|
||||||
MessageBox.Show($"File not found" + suffix);
|
MessageBox.Show($"File not found" + suffix);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user