Add unicode replacements for illegal characters
This commit is contained in:
parent
45c5efffbd
commit
490d121db3
@ -5,7 +5,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AAXClean.Codecs" Version="0.2.8" />
|
<PackageReference Include="AAXClean.Codecs" Version="0.2.9" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -42,10 +42,10 @@ namespace AaxDecrypter
|
|||||||
|
|
||||||
var outDir = Path.GetDirectoryName(OutputFileName);
|
var outDir = Path.GetDirectoryName(OutputFileName);
|
||||||
if (!Directory.Exists(outDir))
|
if (!Directory.Exists(outDir))
|
||||||
throw new DirectoryNotFoundException($"Directory does not exist: {nameof(outDir)}");
|
Directory.CreateDirectory(outDir);
|
||||||
|
|
||||||
if (!Directory.Exists(cacheDirectory))
|
if (!Directory.Exists(cacheDirectory))
|
||||||
throw new DirectoryNotFoundException($"Directory does not exist: {nameof(cacheDirectory)}");
|
Directory.CreateDirectory(cacheDirectory);
|
||||||
|
|
||||||
jsonDownloadState = Path.Combine(cacheDirectory, Path.GetFileName(Path.ChangeExtension(OutputFileName, ".json")));
|
jsonDownloadState = Path.Combine(cacheDirectory, Path.GetFileName(Path.ChangeExtension(OutputFileName, ".json")));
|
||||||
TempFilePath = Path.ChangeExtension(jsonDownloadState, ".aaxc");
|
TempFilePath = Path.ChangeExtension(jsonDownloadState, ".aaxc");
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AAXClean;
|
using AAXClean;
|
||||||
using AAXClean.Codecs;
|
using AAXClean.Codecs;
|
||||||
@ -19,12 +20,12 @@ namespace FileLiberator
|
|||||||
private long fileSize;
|
private long fileSize;
|
||||||
private static string Mp3FileName(string m4bPath) => Path.ChangeExtension(m4bPath ?? "", ".mp3");
|
private static string Mp3FileName(string m4bPath) => Path.ChangeExtension(m4bPath ?? "", ".mp3");
|
||||||
|
|
||||||
public override Task CancelAsync() => m4bBook?.CancelAsync();
|
public override Task CancelAsync() => m4bBook?.CancelAsync() ?? Task.CompletedTask;
|
||||||
|
|
||||||
public static bool ValidateMp3(LibraryBook libraryBook)
|
public static bool ValidateMp3(LibraryBook libraryBook)
|
||||||
{
|
{
|
||||||
var path = AudibleFileStorage.Audio.GetPath(libraryBook.Book.AudibleProductId);
|
var paths = AudibleFileStorage.Audio.GetPaths(libraryBook.Book.AudibleProductId);
|
||||||
return path?.ToString()?.ToLower()?.EndsWith(".m4b") == true && !File.Exists(Mp3FileName(path));
|
return paths.Any(path => 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);
|
||||||
@ -35,33 +36,38 @@ namespace FileLiberator
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var m4bPath = AudibleFileStorage.Audio.GetPath(libraryBook.Book.AudibleProductId);
|
var m4bPaths = AudibleFileStorage.Audio.GetPaths(libraryBook.Book.AudibleProductId);
|
||||||
m4bBook = new Mp4File(m4bPath, FileAccess.Read);
|
|
||||||
m4bBook.ConversionProgressUpdate += M4bBook_ConversionProgressUpdate;
|
|
||||||
|
|
||||||
fileSize = m4bBook.InputStream.Length;
|
foreach (var m4bPath in m4bPaths)
|
||||||
|
{
|
||||||
|
var proposedMp3Path = Mp3FileName(m4bPath);
|
||||||
|
if (File.Exists(proposedMp3Path) || !File.Exists(m4bPath)) continue;
|
||||||
|
|
||||||
OnTitleDiscovered(m4bBook.AppleTags.Title);
|
m4bBook = new Mp4File(m4bPath, FileAccess.Read);
|
||||||
OnAuthorsDiscovered(m4bBook.AppleTags.FirstAuthor);
|
m4bBook.ConversionProgressUpdate += M4bBook_ConversionProgressUpdate;
|
||||||
OnNarratorsDiscovered(m4bBook.AppleTags.Narrator);
|
|
||||||
OnCoverImageDiscovered(m4bBook.AppleTags.Cover);
|
|
||||||
|
|
||||||
using var mp3File = File.OpenWrite(Path.GetTempFileName());
|
fileSize = m4bBook.InputStream.Length;
|
||||||
var lameConfig = GetLameOptions(Configuration.Instance);
|
|
||||||
var result = await Task.Run(() => m4bBook.ConvertToMp3(mp3File, lameConfig));
|
|
||||||
m4bBook.InputStream.Close();
|
|
||||||
mp3File.Close();
|
|
||||||
|
|
||||||
var proposedMp3Path = Mp3FileName(m4bPath);
|
OnTitleDiscovered(m4bBook.AppleTags.Title);
|
||||||
var realMp3Path = FileUtility.SaferMoveToValidPath(mp3File.Name, proposedMp3Path);
|
OnAuthorsDiscovered(m4bBook.AppleTags.FirstAuthor);
|
||||||
OnFileCreated(libraryBook, realMp3Path);
|
OnNarratorsDiscovered(m4bBook.AppleTags.Narrator);
|
||||||
|
OnCoverImageDiscovered(m4bBook.AppleTags.Cover);
|
||||||
|
|
||||||
if (result == ConversionResult.Failed)
|
using var mp3File = File.OpenWrite(Path.GetTempFileName());
|
||||||
return new StatusHandler { "Conversion failed" };
|
var lameConfig = GetLameOptions(Configuration.Instance);
|
||||||
else if (result == ConversionResult.Cancelled)
|
var result = await Task.Run(() => m4bBook.ConvertToMp3(mp3File, lameConfig));
|
||||||
return new StatusHandler { "Cancelled" };
|
m4bBook.InputStream.Close();
|
||||||
else
|
mp3File.Close();
|
||||||
return new StatusHandler();
|
|
||||||
|
var realMp3Path = FileUtility.SaferMoveToValidPath(mp3File.Name, proposedMp3Path);
|
||||||
|
OnFileCreated(libraryBook, realMp3Path);
|
||||||
|
|
||||||
|
if (result == ConversionResult.Failed)
|
||||||
|
return new StatusHandler { "Conversion failed" };
|
||||||
|
else if (result == ConversionResult.Cancelled)
|
||||||
|
return new StatusHandler { "Cancelled" };
|
||||||
|
}
|
||||||
|
return new StatusHandler();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
@ -21,7 +21,7 @@ namespace FileLiberator
|
|||||||
|
|
||||||
public override bool Validate(LibraryBook libraryBook) => !libraryBook.Book.Audio_Exists();
|
public override bool Validate(LibraryBook libraryBook) => !libraryBook.Book.Audio_Exists();
|
||||||
|
|
||||||
public override Task CancelAsync() => abDownloader?.CancelAsync();
|
public override Task CancelAsync() => abDownloader?.CancelAsync() ?? Task.CompletedTask;
|
||||||
|
|
||||||
public override async Task<StatusHandler> ProcessAsync(LibraryBook libraryBook)
|
public override async Task<StatusHandler> ProcessAsync(LibraryBook libraryBook)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -32,12 +32,18 @@ namespace FileManager
|
|||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string FindFile(System.Text.RegularExpressions.Regex regex)
|
public LongPath FindFile(System.Text.RegularExpressions.Regex regex)
|
||||||
{
|
{
|
||||||
lock (fsCacheLocker)
|
lock (fsCacheLocker)
|
||||||
return fsCache.FirstOrDefault(s => regex.IsMatch(s));
|
return fsCache.FirstOrDefault(s => regex.IsMatch(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<LongPath> FindFiles(System.Text.RegularExpressions.Regex regex)
|
||||||
|
{
|
||||||
|
lock (fsCacheLocker)
|
||||||
|
return fsCache.Where(s => regex.IsMatch(s)).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
public void RefreshFiles()
|
public void RefreshFiles()
|
||||||
{
|
{
|
||||||
lock (fsCacheLocker)
|
lock (fsCacheLocker)
|
||||||
|
|||||||
@ -90,9 +90,9 @@ namespace FileManager
|
|||||||
|
|
||||||
var pathNoPrefix = path.PathWithoutPrefix;
|
var pathNoPrefix = path.PathWithoutPrefix;
|
||||||
|
|
||||||
|
pathNoPrefix = pathNoPrefix?.Replace(':', '꞉')?.Replace('?', '︖')?.Replace('*', '⁎');
|
||||||
|
|
||||||
pathNoPrefix = replaceInvalidChars(pathNoPrefix, illegalCharacterReplacements);
|
pathNoPrefix = replaceInvalidChars(pathNoPrefix, illegalCharacterReplacements);
|
||||||
pathNoPrefix = standardizeSlashes(pathNoPrefix);
|
|
||||||
pathNoPrefix = replaceColons(pathNoPrefix, illegalCharacterReplacements);
|
|
||||||
pathNoPrefix = removeDoubleSlashes(pathNoPrefix);
|
pathNoPrefix = removeDoubleSlashes(pathNoPrefix);
|
||||||
|
|
||||||
return pathNoPrefix;
|
return pathNoPrefix;
|
||||||
@ -107,24 +107,6 @@ namespace FileManager
|
|||||||
private static string replaceInvalidChars(string path, string illegalCharacterReplacements)
|
private static string replaceInvalidChars(string path, string illegalCharacterReplacements)
|
||||||
=> string.Join(illegalCharacterReplacements ?? "", path.Split(invalidChars));
|
=> string.Join(illegalCharacterReplacements ?? "", path.Split(invalidChars));
|
||||||
|
|
||||||
private static string standardizeSlashes(string path)
|
|
||||||
=> path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
|
||||||
|
|
||||||
private static string replaceColons(string path, string illegalCharacterReplacements)
|
|
||||||
{
|
|
||||||
// replace all colons except within the first 2 chars
|
|
||||||
var builder = new System.Text.StringBuilder();
|
|
||||||
for (var i = 0; i < path.Length; i++)
|
|
||||||
{
|
|
||||||
var c = path[i];
|
|
||||||
if (i >= 2 && c == ':')
|
|
||||||
builder.Append(illegalCharacterReplacements);
|
|
||||||
else
|
|
||||||
builder.Append(c);
|
|
||||||
}
|
|
||||||
return builder.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string removeDoubleSlashes(string path)
|
private static string removeDoubleSlashes(string path)
|
||||||
{
|
{
|
||||||
if (path.Length < 2)
|
if (path.Length < 2)
|
||||||
|
|||||||
@ -27,7 +27,7 @@ namespace FileManager
|
|||||||
|
|
||||||
//File I/O functions in the Windows API convert "/" to "\" as part of converting
|
//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
|
//the name to an NT-style name, except when using the "\\?\" prefix
|
||||||
path = path.Replace('/', '\\');
|
path = path.Replace(System.IO.Path.AltDirectorySeparatorChar, System.IO.Path.DirectorySeparatorChar);
|
||||||
|
|
||||||
if (path.StartsWith(LONG_PATH_PREFIX))
|
if (path.StartsWith(LONG_PATH_PREFIX))
|
||||||
return new LongPath { Path = path };
|
return new LongPath { Path = path };
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
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,6 +10,7 @@ namespace LibationFileManager
|
|||||||
public abstract class AudibleFileStorage
|
public abstract class AudibleFileStorage
|
||||||
{
|
{
|
||||||
protected abstract LongPath GetFilePathCustom(string productId);
|
protected abstract LongPath GetFilePathCustom(string productId);
|
||||||
|
protected abstract List<LongPath> GetFilePathsCustom(string productId);
|
||||||
|
|
||||||
#region static
|
#region static
|
||||||
public static LongPath DownloadsInProgressDirectory => Directory.CreateDirectory(Path.Combine(Configuration.Instance.InProgress, "DownloadsInProgress")).FullName;
|
public static LongPath DownloadsInProgressDirectory => Directory.CreateDirectory(Path.Combine(Configuration.Instance.InProgress, "DownloadsInProgress")).FullName;
|
||||||
@ -57,6 +59,9 @@ namespace LibationFileManager
|
|||||||
return firstOrNull;
|
return firstOrNull;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<LongPath> GetPaths(string productId)
|
||||||
|
=> GetFilePathsCustom(productId);
|
||||||
|
|
||||||
protected Regex GetBookSearchRegex(string productId)
|
protected Regex GetBookSearchRegex(string productId)
|
||||||
{
|
{
|
||||||
var pattern = string.Format(regexTemplate, productId);
|
var pattern = string.Format(regexTemplate, productId);
|
||||||
@ -70,11 +75,14 @@ namespace LibationFileManager
|
|||||||
internal AaxcFileStorage() : base(FileType.AAXC) { }
|
internal AaxcFileStorage() : base(FileType.AAXC) { }
|
||||||
|
|
||||||
protected override LongPath GetFilePathCustom(string productId)
|
protected override LongPath GetFilePathCustom(string productId)
|
||||||
|
=> GetFilePathsCustom(productId).FirstOrDefault();
|
||||||
|
|
||||||
|
protected override List<LongPath> GetFilePathsCustom(string productId)
|
||||||
{
|
{
|
||||||
var regex = GetBookSearchRegex(productId);
|
var regex = GetBookSearchRegex(productId);
|
||||||
return FileUtility
|
return FileUtility
|
||||||
.SaferEnumerateFiles(DownloadsInProgressDirectory, "*.*", SearchOption.AllDirectories)
|
.SaferEnumerateFiles(DownloadsInProgressDirectory, "*.*", SearchOption.AllDirectories)
|
||||||
.FirstOrDefault(s => regex.IsMatch(s));
|
.Where(s => regex.IsMatch(s)).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Exists(string productId) => GetFilePath(productId) is not null;
|
public bool Exists(string productId) => GetFilePath(productId) is not null;
|
||||||
@ -87,7 +95,11 @@ 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 LongPath GetFilePathCustom(string productId)
|
protected override LongPath GetFilePathCustom(string productId)
|
||||||
|
=> GetFilePathsCustom(productId).FirstOrDefault();
|
||||||
|
|
||||||
|
protected override List<LongPath> GetFilePathsCustom(string productId)
|
||||||
{
|
{
|
||||||
// If user changed the BooksDirectory: reinitialize
|
// If user changed the BooksDirectory: reinitialize
|
||||||
lock (bookDirectoryFilesLocker)
|
lock (bookDirectoryFilesLocker)
|
||||||
@ -95,11 +107,12 @@ namespace LibationFileManager
|
|||||||
BookDirectoryFiles = new BackgroundFileSystem(BooksDirectory, "*.*", SearchOption.AllDirectories);
|
BookDirectoryFiles = new BackgroundFileSystem(BooksDirectory, "*.*", SearchOption.AllDirectories);
|
||||||
|
|
||||||
var regex = GetBookSearchRegex(productId);
|
var regex = GetBookSearchRegex(productId);
|
||||||
return BookDirectoryFiles.FindFile(regex);
|
return BookDirectoryFiles.FindFiles(regex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Refresh() => BookDirectoryFiles.RefreshFiles();
|
public void Refresh() => BookDirectoryFiles.RefreshFiles();
|
||||||
|
|
||||||
public LongPath GetPath(string productId) => GetFilePath(productId);
|
public LongPath GetPath(string productId) => GetFilePath(productId);
|
||||||
}
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,7 +38,7 @@ namespace LibationWinForms
|
|||||||
{
|
{
|
||||||
SetQueueCollapseState(false);
|
SetQueueCollapseState(false);
|
||||||
await Task.Run(() => processBookQueue1.AddConvertMp3(ApplicationServices.DbContexts.GetLibrary_Flat_NoTracking()
|
await Task.Run(() => processBookQueue1.AddConvertMp3(ApplicationServices.DbContexts.GetLibrary_Flat_NoTracking()
|
||||||
.Where(lb => lb.Book.UserDefinedItem.BookStatus is DataLayer.LiberatedStatus.Liberated)));
|
.Where(lb => lb.Book.UserDefinedItem.BookStatus is DataLayer.LiberatedStatus.Liberated && lb.Book.ContentType is DataLayer.ContentType.Product)));
|
||||||
}
|
}
|
||||||
//Only Queue Liberated books for conversion. This isn't a perfect filter, but it's better than nothing.
|
//Only Queue Liberated books for conversion. This isn't a perfect filter, but it's better than nothing.
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,6 +43,12 @@
|
|||||||
<DependentUpon>Form1.cs</DependentUpon>
|
<DependentUpon>Form1.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Update="Dialogs\SettingsDialog.*.cs">
|
||||||
|
<DependentUpon>Dialogs\SettingsDialog.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Update="Properties\Resources.Designer.cs">
|
<Compile Update="Properties\Resources.Designer.cs">
|
||||||
|
|||||||
@ -138,7 +138,7 @@ namespace LibationWinForms.ProcessQueue
|
|||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Cancel()
|
public async Task CancelAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@ -212,7 +212,7 @@ namespace LibationWinForms.ProcessQueue
|
|||||||
private void cancelAllBtn_Click(object sender, EventArgs e)
|
private void cancelAllBtn_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
Queue.ClearQueue();
|
Queue.ClearQueue();
|
||||||
Queue.Current?.Cancel();
|
Queue.Current?.CancelAsync();
|
||||||
virtualFlowControl2.VirtualControlCount = Queue.Count;
|
virtualFlowControl2.VirtualControlCount = Queue.Count;
|
||||||
UpdateAllControls();
|
UpdateAllControls();
|
||||||
}
|
}
|
||||||
@ -331,7 +331,7 @@ namespace LibationWinForms.ProcessQueue
|
|||||||
ProcessBook item = Queue[queueIndex];
|
ProcessBook item = Queue[queueIndex];
|
||||||
if (buttonName == nameof(panelClicked.cancelBtn))
|
if (buttonName == nameof(panelClicked.cancelBtn))
|
||||||
{
|
{
|
||||||
await item.Cancel();
|
await item.CancelAsync();
|
||||||
Queue.RemoveQueued(item);
|
Queue.RemoveQueued(item);
|
||||||
virtualFlowControl2.VirtualControlCount = Queue.Count;
|
virtualFlowControl2.VirtualControlCount = Queue.Count;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user