Add unicode replacements for illegal characters

This commit is contained in:
Michael Bucari-Tovo 2022-06-19 16:57:44 -06:00
parent 45c5efffbd
commit 490d121db3
12 changed files with 71 additions and 58 deletions

View File

@ -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>

View File

@ -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");

View File

@ -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
{ {

View File

@ -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)
{ {

View File

@ -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)

View File

@ -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)

View File

@ -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 };

View File

@ -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);
}
}
} }

View File

@ -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.
} }

View File

@ -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">

View File

@ -138,7 +138,7 @@ namespace LibationWinForms.ProcessQueue
return Result; return Result;
} }
public async Task Cancel() public async Task CancelAsync()
{ {
try try
{ {

View File

@ -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;
} }