* Feature #307 : New windows setting to use cover art as folder's icon. Incomplete. Need to add to avalonia settings
* Interop refactor
This commit is contained in:
parent
aea8c11dc4
commit
1524d558a4
@ -4,7 +4,6 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using ApplicationServices;
|
using ApplicationServices;
|
||||||
using AppScaffolding.OSInterop;
|
|
||||||
using AudibleUtilities;
|
using AudibleUtilities;
|
||||||
using Dinah.Core.Collections.Generic;
|
using Dinah.Core.Collections.Generic;
|
||||||
using Dinah.Core.IO;
|
using Dinah.Core.IO;
|
||||||
@ -30,10 +29,6 @@ namespace AppScaffolding
|
|||||||
|
|
||||||
public static class LibationScaffolding
|
public static class LibationScaffolding
|
||||||
{
|
{
|
||||||
public static bool IsWindows { get; } = OperatingSystem.IsWindows();
|
|
||||||
public static bool IsLinux { get; } = OperatingSystem.IsLinux();
|
|
||||||
public static bool IsMacOs { get; } = OperatingSystem.IsMacOS();
|
|
||||||
|
|
||||||
public static ReleaseIdentifier ReleaseIdentifier { get; private set; }
|
public static ReleaseIdentifier ReleaseIdentifier { get; private set; }
|
||||||
public static VarietyType Variety
|
public static VarietyType Variety
|
||||||
=> ReleaseIdentifier == ReleaseIdentifier.WindowsClassic ? VarietyType.Classic
|
=> ReleaseIdentifier == ReleaseIdentifier.WindowsClassic ? VarietyType.Classic
|
||||||
@ -60,8 +55,6 @@ namespace AppScaffolding
|
|||||||
??= new[] { ExecutingAssembly.GetName(), EntryAssembly.GetName() }
|
??= new[] { ExecutingAssembly.GetName(), EntryAssembly.GetName() }
|
||||||
.Max(a => a.Version);
|
.Max(a => a.Version);
|
||||||
|
|
||||||
public static OSInteropProxy InteropInstance { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>Run migrations before loading Configuration for the first time. Then load and return Configuration</summary>
|
/// <summary>Run migrations before loading Configuration for the first time. Then load and return Configuration</summary>
|
||||||
public static Configuration RunPreConfigMigrations()
|
public static Configuration RunPreConfigMigrations()
|
||||||
{
|
{
|
||||||
@ -95,6 +88,9 @@ namespace AppScaffolding
|
|||||||
{
|
{
|
||||||
config.InProgress ??= Configuration.WinTemp;
|
config.InProgress ??= Configuration.WinTemp;
|
||||||
|
|
||||||
|
if (!config.Exists(nameof(config.UseCoverAsFolderIcon)))
|
||||||
|
config.UseCoverAsFolderIcon = false;
|
||||||
|
|
||||||
if (!config.Exists(nameof(config.BetaOptIn)))
|
if (!config.Exists(nameof(config.BetaOptIn)))
|
||||||
config.BetaOptIn = false;
|
config.BetaOptIn = false;
|
||||||
|
|
||||||
@ -192,7 +188,6 @@ namespace AppScaffolding
|
|||||||
|
|
||||||
// all else should occur after logging
|
// all else should occur after logging
|
||||||
|
|
||||||
loadOSInterop(config);
|
|
||||||
wireUpSystemEvents(config);
|
wireUpSystemEvents(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,12 +302,6 @@ namespace AppScaffolding
|
|||||||
if (System.Diagnostics.Debugger.IsAttached)
|
if (System.Diagnostics.Debugger.IsAttached)
|
||||||
mode += " (Debugger attached)";
|
mode += " (Debugger attached)";
|
||||||
|
|
||||||
string OS
|
|
||||||
= IsLinux ? "Linux"
|
|
||||||
: IsMacOs ? "MacOS"
|
|
||||||
: IsWindows ? "Windows"
|
|
||||||
: "UNKNOWN_OS";
|
|
||||||
|
|
||||||
// begin logging session with a form feed
|
// begin logging session with a form feed
|
||||||
Log.Logger.Information("\r\n\f");
|
Log.Logger.Information("\r\n\f");
|
||||||
Log.Logger.Information("Begin. {@DebugInfo}", new
|
Log.Logger.Information("Begin. {@DebugInfo}", new
|
||||||
@ -320,7 +309,8 @@ namespace AppScaffolding
|
|||||||
AppName = EntryAssembly.GetName().Name,
|
AppName = EntryAssembly.GetName().Name,
|
||||||
Version = BuildVersion.ToString(),
|
Version = BuildVersion.ToString(),
|
||||||
ReleaseIdentifier,
|
ReleaseIdentifier,
|
||||||
OS,
|
Configuration.OS,
|
||||||
|
InteropFactory.InteropFunctionsType,
|
||||||
Mode = mode,
|
Mode = mode,
|
||||||
LogLevel_Verbose_Enabled = Log.Logger.IsVerboseEnabled(),
|
LogLevel_Verbose_Enabled = Log.Logger.IsVerboseEnabled(),
|
||||||
LogLevel_Debug_Enabled = Log.Logger.IsDebugEnabled(),
|
LogLevel_Debug_Enabled = Log.Logger.IsDebugEnabled(),
|
||||||
@ -330,6 +320,7 @@ namespace AppScaffolding
|
|||||||
LogLevel_Fatal_Enabled = Log.Logger.IsFatalEnabled(),
|
LogLevel_Fatal_Enabled = Log.Logger.IsFatalEnabled(),
|
||||||
|
|
||||||
config.BetaOptIn,
|
config.BetaOptIn,
|
||||||
|
config.UseCoverAsFolderIcon,
|
||||||
config.LibationFiles,
|
config.LibationFiles,
|
||||||
AudibleFileStorage.BooksDirectory,
|
AudibleFileStorage.BooksDirectory,
|
||||||
|
|
||||||
@ -341,18 +332,8 @@ namespace AppScaffolding
|
|||||||
AudibleFileStorage.DecryptInProgressDirectory,
|
AudibleFileStorage.DecryptInProgressDirectory,
|
||||||
DecryptInProgressFiles = FileManager.FileUtility.SaferEnumerateFiles(AudibleFileStorage.DecryptInProgressDirectory).Count(),
|
DecryptInProgressFiles = FileManager.FileUtility.SaferEnumerateFiles(AudibleFileStorage.DecryptInProgressDirectory).Count(),
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
private static void loadOSInterop(Configuration configuration)
|
if (InteropFactory.InteropFunctionsType is null)
|
||||||
{
|
|
||||||
InteropInstance = new OSInteropProxy();
|
|
||||||
Serilog.Log.Logger.Information("InteropInstance:{@DebugInfo}", new
|
|
||||||
{
|
|
||||||
type = OSInteropProxy.InteropFunctionsType,
|
|
||||||
instance = InteropInstance.InteropFunctions
|
|
||||||
});
|
|
||||||
|
|
||||||
if (OSInteropProxy.InteropFunctionsType is null)
|
|
||||||
Serilog.Log.Logger.Warning("WARNING: OSInteropProxy.InteropFunctionsType is null");
|
Serilog.Log.Logger.Warning("WARNING: OSInteropProxy.InteropFunctionsType is null");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace AppScaffolding.OSInterop
|
namespace AppScaffolding
|
||||||
{
|
{
|
||||||
public abstract class OSConfigBase
|
public abstract class OSConfigBase
|
||||||
{
|
{
|
||||||
@ -1,15 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace AppScaffolding.OSInterop
|
|
||||||
{
|
|
||||||
public interface IInteropFunctions
|
|
||||||
{
|
|
||||||
// examples until the real interface is filled out
|
|
||||||
/*
|
|
||||||
public string TransformInit1();
|
|
||||||
public int TransformInit2();
|
|
||||||
public void CopyTextToClipboard(string text);
|
|
||||||
public void ShowForm();
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace AppScaffolding.OSInterop
|
|
||||||
{
|
|
||||||
internal class NullInteropFunctions : IInteropFunctions
|
|
||||||
{
|
|
||||||
public NullInteropFunctions() { }
|
|
||||||
public NullInteropFunctions(params object[] values) { }
|
|
||||||
|
|
||||||
// examples until the real interface is filled out
|
|
||||||
public string TransformInit1() => throw new PlatformNotSupportedException();
|
|
||||||
public int TransformInit2() => throw new PlatformNotSupportedException();
|
|
||||||
public void CopyTextToClipboard(string text) => throw new PlatformNotSupportedException();
|
|
||||||
public void ShowForm() => throw new PlatformNotSupportedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -74,14 +74,17 @@ namespace FileLiberator
|
|||||||
|
|
||||||
// moves new files from temp dir to final dest.
|
// moves new files from temp dir to final dest.
|
||||||
// This could take a few seconds if moving hundreds of files.
|
// This could take a few seconds if moving hundreds of files.
|
||||||
var movedAudioFile = await Task.Run(() => moveFilesToBooksDir(libraryBook, entries));
|
var finalStorageDir = await Task.Run(() => moveFilesToBooksDir(libraryBook, entries));
|
||||||
|
|
||||||
// decrypt failed
|
// decrypt failed
|
||||||
if (!movedAudioFile)
|
if (finalStorageDir is null)
|
||||||
return new StatusHandler { "Cannot find final audio file after decryption" };
|
return new StatusHandler { "Cannot find final audio file after decryption" };
|
||||||
|
|
||||||
if (Configuration.Instance.DownloadCoverArt)
|
if (Configuration.Instance.DownloadCoverArt)
|
||||||
DownloadCoverArt(libraryBook);
|
downloadCoverArt(libraryBook);
|
||||||
|
|
||||||
|
// contains logic to check for config setting and OS
|
||||||
|
WindowsDirectory.SetCoverAsFolderIcon(pictureId: libraryBook.Book.PictureId, directory: finalStorageDir);
|
||||||
|
|
||||||
libraryBook.Book.UpdateBookStatus(LiberatedStatus.Liberated);
|
libraryBook.Book.UpdateBookStatus(LiberatedStatus.Liberated);
|
||||||
|
|
||||||
@ -345,8 +348,8 @@ namespace FileLiberator
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Move new files to 'Books' directory</summary>
|
/// <summary>Move new files to 'Books' directory</summary>
|
||||||
/// <returns>True if audiobook file(s) were successfully created and can be located on disk. Else false.</returns>
|
/// <returns>Return directory if audiobook file(s) were successfully created and can be located on disk. Else null.</returns>
|
||||||
private static bool moveFilesToBooksDir(LibraryBook libraryBook, List<FilePathCache.CacheEntry> entries)
|
private static string moveFilesToBooksDir(LibraryBook libraryBook, List<FilePathCache.CacheEntry> entries)
|
||||||
{
|
{
|
||||||
// create final directory. move each file into it
|
// create final directory. move each file into it
|
||||||
var destinationDir = AudibleFileStorage.Audio.GetDestinationDirectory(libraryBook);
|
var destinationDir = AudibleFileStorage.Audio.GetDestinationDirectory(libraryBook);
|
||||||
@ -355,7 +358,7 @@ namespace FileLiberator
|
|||||||
FilePathCache.CacheEntry getFirstAudio() => entries.FirstOrDefault(f => f.FileType == FileType.Audio);
|
FilePathCache.CacheEntry getFirstAudio() => entries.FirstOrDefault(f => f.FileType == FileType.Audio);
|
||||||
|
|
||||||
if (getFirstAudio() == default)
|
if (getFirstAudio() == default)
|
||||||
return false;
|
return null;
|
||||||
|
|
||||||
for (var i = 0; i < entries.Count; i++)
|
for (var i = 0; i < entries.Count; i++)
|
||||||
{
|
{
|
||||||
@ -374,36 +377,31 @@ namespace FileLiberator
|
|||||||
|
|
||||||
AudibleFileStorage.Audio.Refresh();
|
AudibleFileStorage.Audio.Refresh();
|
||||||
|
|
||||||
return true;
|
return destinationDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DownloadCoverArt(LibraryBook libraryBook)
|
private static void downloadCoverArt(LibraryBook libraryBook)
|
||||||
{
|
{
|
||||||
var destinationDir = AudibleFileStorage.Audio.GetDestinationDirectory(libraryBook);
|
var coverPath = "[null]";
|
||||||
var coverPath = AudibleFileStorage.Audio.GetBooksDirectoryFilename(libraryBook, ".jpg");
|
|
||||||
coverPath = Path.Combine(destinationDir, Path.GetFileName(coverPath));
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var destinationDir = AudibleFileStorage.Audio.GetDestinationDirectory(libraryBook);
|
||||||
|
coverPath = AudibleFileStorage.Audio.GetBooksDirectoryFilename(libraryBook, ".jpg");
|
||||||
|
coverPath = Path.Combine(destinationDir, Path.GetFileName(coverPath));
|
||||||
|
|
||||||
if (File.Exists(coverPath))
|
if (File.Exists(coverPath))
|
||||||
FileUtility.SaferDelete(coverPath);
|
FileUtility.SaferDelete(coverPath);
|
||||||
|
|
||||||
(string picId, PictureSize size) = libraryBook.Book.PictureLarge is null ?
|
var picBytes = PictureStorage.GetPictureSynchronously(new(libraryBook.Book.PictureLarge ?? libraryBook.Book.PictureId, PictureSize.Native));
|
||||||
(libraryBook.Book.PictureId, PictureSize.Native) :
|
|
||||||
(libraryBook.Book.PictureLarge, PictureSize.Native);
|
|
||||||
|
|
||||||
var picBytes = PictureStorage.GetPictureSynchronously(new PictureDefinition(picId, size));
|
|
||||||
|
|
||||||
if (picBytes.Length > 0)
|
if (picBytes.Length > 0)
|
||||||
File.WriteAllBytes(coverPath, picBytes);
|
File.WriteAllBytes(coverPath, picBytes);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
//Failure to download cover art should not be
|
//Failure to download cover art should not be considered a failure to download the book
|
||||||
//considered a failure to download the book
|
|
||||||
Serilog.Log.Logger.Error(ex, $"Error downloading cover art of {libraryBook.Book.AudibleProductId} to {coverPath} catalog product.");
|
Serilog.Log.Logger.Error(ex, $"Error downloading cover art of {libraryBook.Book.AudibleProductId} to {coverPath} catalog product.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,7 +51,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
saveFileDialog.Filters.Add(new FileDialogFilter { Name = "Jpeg", Extensions = new System.Collections.Generic.List<string>() { "jpg" } });
|
saveFileDialog.Filters.Add(new FileDialogFilter { Name = "Jpeg", Extensions = new System.Collections.Generic.List<string>() { "jpg" } });
|
||||||
saveFileDialog.InitialFileName = PictureFileName;
|
saveFileDialog.InitialFileName = PictureFileName;
|
||||||
saveFileDialog.Directory
|
saveFileDialog.Directory
|
||||||
= !AppScaffolding.LibationScaffolding.IsWindows ? null
|
= !LibationFileManager.Configuration.IsWindows ? null
|
||||||
: Directory.Exists(BookSaveDirectory) ? BookSaveDirectory
|
: Directory.Exists(BookSaveDirectory) ? BookSaveDirectory
|
||||||
: Path.GetDirectoryName(BookSaveDirectory);
|
: Path.GetDirectoryName(BookSaveDirectory);
|
||||||
|
|
||||||
|
|||||||
@ -381,7 +381,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
private int _lameVBRQuality;
|
private int _lameVBRQuality;
|
||||||
private string _chapterTitleTemplate;
|
private string _chapterTitleTemplate;
|
||||||
|
|
||||||
public bool IsMp3Supported => AppScaffolding.LibationScaffolding.IsLinux || AppScaffolding.LibationScaffolding.IsWindows;
|
public bool IsMp3Supported => Configuration.IsLinux || Configuration.IsWindows;
|
||||||
|
|
||||||
public AudioSettings(Configuration config)
|
public AudioSettings(Configuration config)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -113,7 +113,7 @@ namespace LibationAvalonia
|
|||||||
|
|
||||||
public static void HideMinMaxBtns(this Window form)
|
public static void HideMinMaxBtns(this Window form)
|
||||||
{
|
{
|
||||||
if (Design.IsDesignMode || !AppScaffolding.LibationScaffolding.IsWindows)
|
if (Design.IsDesignMode || !Configuration.IsWindows)
|
||||||
return;
|
return;
|
||||||
var handle = form.PlatformImpl.Handle.Handle;
|
var handle = form.PlatformImpl.Handle.Handle;
|
||||||
var currentStyle = GetWindowLong(handle, GWL_STYLE);
|
var currentStyle = GetWindowLong(handle, GWL_STYLE);
|
||||||
|
|||||||
@ -30,11 +30,11 @@ namespace LibationAvalonia
|
|||||||
var classicLifetimeTask = Task.Run(() => new ClassicDesktopStyleApplicationLifetime());
|
var classicLifetimeTask = Task.Run(() => new ClassicDesktopStyleApplicationLifetime());
|
||||||
var appBuilderTask = Task.Run(BuildAvaloniaApp);
|
var appBuilderTask = Task.Run(BuildAvaloniaApp);
|
||||||
|
|
||||||
if (AppScaffolding.LibationScaffolding.IsWindows)
|
if (Configuration.IsWindows)
|
||||||
AppScaffolding.LibationScaffolding.SetReleaseIdentifier(AppScaffolding.ReleaseIdentifier.WindowsAvalonia);
|
AppScaffolding.LibationScaffolding.SetReleaseIdentifier(AppScaffolding.ReleaseIdentifier.WindowsAvalonia);
|
||||||
else if (AppScaffolding.LibationScaffolding.IsLinux)
|
else if (Configuration.IsLinux)
|
||||||
AppScaffolding.LibationScaffolding.SetReleaseIdentifier(AppScaffolding.ReleaseIdentifier.LinuxAvalonia);
|
AppScaffolding.LibationScaffolding.SetReleaseIdentifier(AppScaffolding.ReleaseIdentifier.LinuxAvalonia);
|
||||||
else if (AppScaffolding.LibationScaffolding.IsMacOs)
|
else if (Configuration.IsMacOs)
|
||||||
AppScaffolding.LibationScaffolding.SetReleaseIdentifier(AppScaffolding.ReleaseIdentifier.MacOSAvalonia);
|
AppScaffolding.LibationScaffolding.SetReleaseIdentifier(AppScaffolding.ReleaseIdentifier.MacOSAvalonia);
|
||||||
else return;
|
else return;
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,7 @@ namespace LibationAvalonia.ViewModels
|
|||||||
private int _visibleCount = 1;
|
private int _visibleCount = 1;
|
||||||
private LibraryCommands.LibraryStats _libraryStats;
|
private LibraryCommands.LibraryStats _libraryStats;
|
||||||
private int _visibleNotLiberated = 1;
|
private int _visibleNotLiberated = 1;
|
||||||
public bool IsMp3Supported => AppScaffolding.LibationScaffolding.IsLinux || AppScaffolding.LibationScaffolding.IsWindows;
|
public bool IsMp3Supported => Configuration.IsLinux || Configuration.IsWindows;
|
||||||
|
|
||||||
/// <summary> The Process Queue's viewmodel </summary>
|
/// <summary> The Process Queue's viewmodel </summary>
|
||||||
public ProcessQueueViewModel ProcessQueue { get; } = new ProcessQueueViewModel();
|
public ProcessQueueViewModel ProcessQueue { get; } = new ProcessQueueViewModel();
|
||||||
|
|||||||
21
Source/LibationFileManager/Configuration.Environment.cs
Normal file
21
Source/LibationFileManager/Configuration.Environment.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace LibationFileManager
|
||||||
|
{
|
||||||
|
public partial class Configuration
|
||||||
|
{
|
||||||
|
public static bool IsWindows { get; } = OperatingSystem.IsWindows();
|
||||||
|
public static bool IsLinux { get; } = OperatingSystem.IsLinux();
|
||||||
|
public static bool IsMacOs { get; } = OperatingSystem.IsMacOS();
|
||||||
|
|
||||||
|
public static string OS { get; }
|
||||||
|
= IsLinux ? "Linux"
|
||||||
|
: IsMacOs ? "MacOS"
|
||||||
|
: IsWindows ? "Windows"
|
||||||
|
: "UNKNOWN_OS";
|
||||||
|
}
|
||||||
|
}
|
||||||
66
Source/LibationFileManager/Configuration.KnownDirectories.cs
Normal file
66
Source/LibationFileManager/Configuration.KnownDirectories.cs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Dinah.Core;
|
||||||
|
|
||||||
|
namespace LibationFileManager
|
||||||
|
{
|
||||||
|
public partial class Configuration
|
||||||
|
{
|
||||||
|
public static string AppDir_Relative => $@".{Path.PathSeparator}{LIBATION_FILES_KEY}";
|
||||||
|
public static string AppDir_Absolute => Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Exe.FileLocationOnDisk), LIBATION_FILES_KEY));
|
||||||
|
public static string MyDocs => Path.GetFullPath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Libation"));
|
||||||
|
public static string WinTemp => Path.GetFullPath(Path.Combine(Path.GetTempPath(), "Libation"));
|
||||||
|
public static string UserProfile => Path.GetFullPath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Libation"));
|
||||||
|
|
||||||
|
public enum KnownDirectories
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
[Description("My Users folder")]
|
||||||
|
UserProfile = 1,
|
||||||
|
|
||||||
|
[Description("The same folder that Libation is running from")]
|
||||||
|
AppDir = 2,
|
||||||
|
|
||||||
|
[Description("Windows temporary folder")]
|
||||||
|
WinTemp = 3,
|
||||||
|
|
||||||
|
[Description("My Documents")]
|
||||||
|
MyDocs = 4,
|
||||||
|
|
||||||
|
[Description("Your settings folder (aka: Libation Files)")]
|
||||||
|
LibationFiles = 5
|
||||||
|
}
|
||||||
|
// use func calls so we always get the latest value of LibationFiles
|
||||||
|
private static List<(KnownDirectories directory, Func<string> getPathFunc)> directoryOptionsPaths { get; } = new()
|
||||||
|
{
|
||||||
|
(KnownDirectories.None, () => null),
|
||||||
|
(KnownDirectories.UserProfile, () => UserProfile),
|
||||||
|
(KnownDirectories.AppDir, () => AppDir_Relative),
|
||||||
|
(KnownDirectories.WinTemp, () => WinTemp),
|
||||||
|
(KnownDirectories.MyDocs, () => MyDocs),
|
||||||
|
// this is important to not let very early calls try to accidentally load LibationFiles too early.
|
||||||
|
// also, keep this at bottom of this list
|
||||||
|
(KnownDirectories.LibationFiles, () => libationFilesPathCache)
|
||||||
|
};
|
||||||
|
public static string GetKnownDirectoryPath(KnownDirectories directory)
|
||||||
|
{
|
||||||
|
var dirFunc = directoryOptionsPaths.SingleOrDefault(dirFunc => dirFunc.directory == directory);
|
||||||
|
return dirFunc == default ? null : dirFunc.getPathFunc();
|
||||||
|
}
|
||||||
|
public static KnownDirectories GetKnownDirectory(string directory)
|
||||||
|
{
|
||||||
|
// especially important so a very early call doesn't match null => LibationFiles
|
||||||
|
if (string.IsNullOrWhiteSpace(directory))
|
||||||
|
return KnownDirectories.None;
|
||||||
|
|
||||||
|
// 'First' instead of 'Single' because LibationFiles could match other directories. eg: default value of LibationFiles == UserProfile.
|
||||||
|
// since it's a list, order matters and non-LibationFiles will be returned first
|
||||||
|
var dirFunc = directoryOptionsPaths.FirstOrDefault(dirFunc => dirFunc.getPathFunc() == directory);
|
||||||
|
return dirFunc == default ? KnownDirectories.None : dirFunc.directory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
113
Source/LibationFileManager/Configuration.LibationFiles.cs
Normal file
113
Source/LibationFileManager/Configuration.LibationFiles.cs
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.IO;
|
||||||
|
using FileManager;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
|
namespace LibationFileManager
|
||||||
|
{
|
||||||
|
public partial class Configuration
|
||||||
|
{
|
||||||
|
private static string APPSETTINGS_JSON { get; } = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "appsettings.json");
|
||||||
|
private const string LIBATION_FILES_KEY = "LibationFiles";
|
||||||
|
|
||||||
|
[Description("Location for storage of program-created files")]
|
||||||
|
public string LibationFiles
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (libationFilesPathCache is not null)
|
||||||
|
return libationFilesPathCache;
|
||||||
|
|
||||||
|
// FIRST: must write here before SettingsFilePath in next step reads cache
|
||||||
|
libationFilesPathCache = getLibationFilesSettingFromJson();
|
||||||
|
|
||||||
|
// SECOND. before setting to json file with SetWithJsonPath, PersistentDictionary must exist
|
||||||
|
persistentDictionary = new PersistentDictionary(SettingsFilePath);
|
||||||
|
|
||||||
|
// Config init in ensureSerilogConfig() only happens when serilog setting is first created (prob on 1st run).
|
||||||
|
// This Set() enforces current LibationFiles every time we restart Libation or redirect LibationFiles
|
||||||
|
var logPath = Path.Combine(LibationFiles, "Log.log");
|
||||||
|
|
||||||
|
// BAD: Serilog.WriteTo[1].Args
|
||||||
|
// "[1]" assumes ordinal position
|
||||||
|
// GOOD: Serilog.WriteTo[?(@.Name=='File')].Args
|
||||||
|
var jsonpath = "Serilog.WriteTo[?(@.Name=='File')].Args";
|
||||||
|
|
||||||
|
SetWithJsonPath(jsonpath, "path", logPath, true);
|
||||||
|
|
||||||
|
return libationFilesPathCache;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string libationFilesPathCache { get; set; }
|
||||||
|
|
||||||
|
private string getLibationFilesSettingFromJson()
|
||||||
|
{
|
||||||
|
string startingContents = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (File.Exists(APPSETTINGS_JSON))
|
||||||
|
{
|
||||||
|
startingContents = File.ReadAllText(APPSETTINGS_JSON);
|
||||||
|
var startingJObj = JObject.Parse(startingContents);
|
||||||
|
|
||||||
|
if (startingJObj.ContainsKey(LIBATION_FILES_KEY))
|
||||||
|
{
|
||||||
|
var startingValue = startingJObj[LIBATION_FILES_KEY].Value<string>();
|
||||||
|
if (!string.IsNullOrWhiteSpace(startingValue))
|
||||||
|
return startingValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
// not found. write to file. read from file
|
||||||
|
var endingContents = new JObject { { LIBATION_FILES_KEY, UserProfile.ToString() } }.ToString(Formatting.Indented);
|
||||||
|
if (startingContents != endingContents)
|
||||||
|
{
|
||||||
|
File.WriteAllText(APPSETTINGS_JSON, endingContents);
|
||||||
|
System.Threading.Thread.Sleep(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// do not check whether directory exists. special/meta directory (eg: AppDir) is valid
|
||||||
|
// verify from live file. no try/catch. want failures to be visible
|
||||||
|
var jObjFinal = JObject.Parse(File.ReadAllText(APPSETTINGS_JSON));
|
||||||
|
var valueFinal = jObjFinal[LIBATION_FILES_KEY].Value<string>();
|
||||||
|
return valueFinal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetLibationFiles(string directory)
|
||||||
|
{
|
||||||
|
// ensure exists
|
||||||
|
if (!File.Exists(APPSETTINGS_JSON))
|
||||||
|
{
|
||||||
|
// getter creates new file, loads PersistentDictionary
|
||||||
|
var _ = LibationFiles;
|
||||||
|
System.Threading.Thread.Sleep(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
libationFilesPathCache = null;
|
||||||
|
|
||||||
|
var startingContents = File.ReadAllText(APPSETTINGS_JSON);
|
||||||
|
var jObj = JObject.Parse(startingContents);
|
||||||
|
|
||||||
|
jObj[LIBATION_FILES_KEY] = directory;
|
||||||
|
|
||||||
|
var endingContents = JsonConvert.SerializeObject(jObj, Formatting.Indented);
|
||||||
|
if (startingContents == endingContents)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// now it's set in the file again but no settings have moved yet
|
||||||
|
File.WriteAllText(APPSETTINGS_JSON, endingContents);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Log.Logger.Information("Libation files changed {@DebugInfo}", new { APPSETTINGS_JSON, LIBATION_FILES_KEY, directory });
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
58
Source/LibationFileManager/Configuration.Logging.cs
Normal file
58
Source/LibationFileManager/Configuration.Logging.cs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using Dinah.Core;
|
||||||
|
using Dinah.Core.Logging;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Serilog;
|
||||||
|
using Serilog.Events;
|
||||||
|
|
||||||
|
namespace LibationFileManager
|
||||||
|
{
|
||||||
|
public partial class Configuration
|
||||||
|
{
|
||||||
|
private IConfigurationRoot configuration;
|
||||||
|
|
||||||
|
public void ConfigureLogging()
|
||||||
|
{
|
||||||
|
configuration = new ConfigurationBuilder()
|
||||||
|
.AddJsonFile(SettingsFilePath, optional: false, reloadOnChange: true)
|
||||||
|
.Build();
|
||||||
|
Log.Logger = new LoggerConfiguration()
|
||||||
|
.ReadFrom.Configuration(configuration)
|
||||||
|
.CreateLogger();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("The importance of a log event")]
|
||||||
|
public LogEventLevel LogLevel
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var logLevelStr = persistentDictionary.GetStringFromJsonPath("Serilog", "MinimumLevel");
|
||||||
|
return Enum.TryParse<LogEventLevel>(logLevelStr, out var logLevelEnum) ? logLevelEnum : LogEventLevel.Information;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
var valueWasChanged = persistentDictionary.SetWithJsonPath("Serilog", "MinimumLevel", value.ToString());
|
||||||
|
if (!valueWasChanged)
|
||||||
|
{
|
||||||
|
Log.Logger.Debug("LogLevel.set attempt. No change");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
configuration.Reload();
|
||||||
|
|
||||||
|
Log.Logger.Information("Updated LogLevel MinimumLevel. {@DebugInfo}", new
|
||||||
|
{
|
||||||
|
LogLevel_Verbose_Enabled = Log.Logger.IsVerboseEnabled(),
|
||||||
|
LogLevel_Debug_Enabled = Log.Logger.IsDebugEnabled(),
|
||||||
|
LogLevel_Information_Enabled = Log.Logger.IsInformationEnabled(),
|
||||||
|
LogLevel_Warning_Enabled = Log.Logger.IsWarningEnabled(),
|
||||||
|
LogLevel_Error_Enabled = Log.Logger.IsErrorEnabled(),
|
||||||
|
LogLevel_Fatal_Enabled = Log.Logger.IsFatalEnabled()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
322
Source/LibationFileManager/Configuration.PersistentSettings.cs
Normal file
322
Source/LibationFileManager/Configuration.PersistentSettings.cs
Normal file
@ -0,0 +1,322 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using FileManager;
|
||||||
|
|
||||||
|
namespace LibationFileManager
|
||||||
|
{
|
||||||
|
public partial class Configuration
|
||||||
|
{
|
||||||
|
// note: any potential file manager static ctors can't compensate if storage dir is changed at run time via settings. this is partly bad architecture. but the side effect is desirable. if changing LibationFiles location: restart app
|
||||||
|
|
||||||
|
// default setting and directory creation occur in class responsible for files.
|
||||||
|
// config class is only responsible for path. not responsible for setting defaults, dir validation, or dir creation
|
||||||
|
// exceptions: appsettings.json, LibationFiles dir, Settings.json
|
||||||
|
|
||||||
|
private PersistentDictionary persistentDictionary;
|
||||||
|
|
||||||
|
public T GetNonString<T>(string propertyName) => persistentDictionary.GetNonString<T>(propertyName);
|
||||||
|
public object GetObject(string propertyName) => persistentDictionary.GetObject(propertyName);
|
||||||
|
public void SetObject(string propertyName, object newValue) => persistentDictionary.SetNonString(propertyName, newValue);
|
||||||
|
|
||||||
|
/// <summary>WILL ONLY set if already present. WILL NOT create new</summary>
|
||||||
|
public void SetWithJsonPath(string jsonPath, string propertyName, string newValue, bool suppressLogging = false)
|
||||||
|
{
|
||||||
|
var settingWasChanged = persistentDictionary.SetWithJsonPath(jsonPath, propertyName, newValue, suppressLogging);
|
||||||
|
if (settingWasChanged)
|
||||||
|
configuration?.Reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string SettingsFilePath => Path.Combine(LibationFiles, "Settings.json");
|
||||||
|
|
||||||
|
public static string GetDescription(string propertyName)
|
||||||
|
{
|
||||||
|
var attribute = typeof(Configuration)
|
||||||
|
.GetProperty(propertyName)
|
||||||
|
?.GetCustomAttributes(typeof(DescriptionAttribute), true)
|
||||||
|
.SingleOrDefault()
|
||||||
|
as DescriptionAttribute;
|
||||||
|
|
||||||
|
return attribute?.Description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Exists(string propertyName) => persistentDictionary.Exists(propertyName);
|
||||||
|
|
||||||
|
[Description("Set cover art as the folder's icon. (Windows only)")]
|
||||||
|
public bool UseCoverAsFolderIcon
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<bool>(nameof(UseCoverAsFolderIcon));
|
||||||
|
set => persistentDictionary.SetNonString(nameof(UseCoverAsFolderIcon), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("Use the beta version of Libation\r\nNew and experimental features, but probably buggy.\r\n(requires restart to take effect)")]
|
||||||
|
public bool BetaOptIn
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<bool>(nameof(BetaOptIn));
|
||||||
|
set => persistentDictionary.SetNonString(nameof(BetaOptIn), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("Location for book storage. Includes destination of newly liberated books")]
|
||||||
|
public string Books
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetString(nameof(Books));
|
||||||
|
set => persistentDictionary.SetString(nameof(Books), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// temp/working dir(s) should be outside of dropbox
|
||||||
|
[Description("Temporary location of files while they're in process of being downloaded and decrypted.\r\nWhen decryption is complete, the final file will be in Books location\r\nRecommend not using a folder which is backed up real time. Eg: Dropbox, iCloud, Google Drive")]
|
||||||
|
public string InProgress
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetString(nameof(InProgress));
|
||||||
|
set => persistentDictionary.SetString(nameof(InProgress), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("Allow Libation to fix up audiobook metadata")]
|
||||||
|
public bool AllowLibationFixup
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<bool>(nameof(AllowLibationFixup));
|
||||||
|
set => persistentDictionary.SetNonString(nameof(AllowLibationFixup), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("Create a cue sheet (.cue)")]
|
||||||
|
public bool CreateCueSheet
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<bool>(nameof(CreateCueSheet));
|
||||||
|
set => persistentDictionary.SetNonString(nameof(CreateCueSheet), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("Retain the Aax file after successfully decrypting")]
|
||||||
|
public bool RetainAaxFile
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<bool>(nameof(RetainAaxFile));
|
||||||
|
set => persistentDictionary.SetNonString(nameof(RetainAaxFile), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("Split my books into multiple files by chapter")]
|
||||||
|
public bool SplitFilesByChapter
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<bool>(nameof(SplitFilesByChapter));
|
||||||
|
set => persistentDictionary.SetNonString(nameof(SplitFilesByChapter), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("Merge Opening/End Credits into the following/preceding chapters")]
|
||||||
|
public bool MergeOpeningAndEndCredits
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<bool>(nameof(MergeOpeningAndEndCredits));
|
||||||
|
set => persistentDictionary.SetNonString(nameof(MergeOpeningAndEndCredits), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("Strip \"(Unabridged)\" from audiobook metadata tags")]
|
||||||
|
public bool StripUnabridged
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<bool>(nameof(StripUnabridged));
|
||||||
|
set => persistentDictionary.SetNonString(nameof(StripUnabridged), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("Strip audible branding from the start and end of audiobooks.\r\n(e.g. \"This is Audible\")")]
|
||||||
|
public bool StripAudibleBrandAudio
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<bool>(nameof(StripAudibleBrandAudio));
|
||||||
|
set => persistentDictionary.SetNonString(nameof(StripAudibleBrandAudio), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("Decrypt to lossy format?")]
|
||||||
|
public bool DecryptToLossy
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<bool>(nameof(DecryptToLossy));
|
||||||
|
set => persistentDictionary.SetNonString(nameof(DecryptToLossy), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("Lame encoder target. true = Bitrate, false = Quality")]
|
||||||
|
public bool LameTargetBitrate
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<bool>(nameof(LameTargetBitrate));
|
||||||
|
set => persistentDictionary.SetNonString(nameof(LameTargetBitrate), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("Lame encoder downsamples to mono")]
|
||||||
|
public bool LameDownsampleMono
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<bool>(nameof(LameDownsampleMono));
|
||||||
|
set => persistentDictionary.SetNonString(nameof(LameDownsampleMono), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("Lame target bitrate [16,320]")]
|
||||||
|
public int LameBitrate
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<int>(nameof(LameBitrate));
|
||||||
|
set => persistentDictionary.SetNonString(nameof(LameBitrate), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("Restrict encoder to constant bitrate?")]
|
||||||
|
public bool LameConstantBitrate
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<bool>(nameof(LameConstantBitrate));
|
||||||
|
set => persistentDictionary.SetNonString(nameof(LameConstantBitrate), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("Match the source bitrate?")]
|
||||||
|
public bool LameMatchSourceBR
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<bool>(nameof(LameMatchSourceBR));
|
||||||
|
set => persistentDictionary.SetNonString(nameof(LameMatchSourceBR), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("Lame target VBR quality [10,100]")]
|
||||||
|
public int LameVBRQuality
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<int>(nameof(LameVBRQuality));
|
||||||
|
set => persistentDictionary.SetNonString(nameof(LameVBRQuality), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("A Dictionary of GridView data property names and bool indicating its column's visibility in ProductsGrid")]
|
||||||
|
public Dictionary<string, bool> GridColumnsVisibilities
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<Dictionary<string, bool>>(nameof(GridColumnsVisibilities));
|
||||||
|
set => persistentDictionary.SetNonString(nameof(GridColumnsVisibilities), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("A Dictionary of GridView data property names and int indicating its column's display index in ProductsGrid")]
|
||||||
|
public Dictionary<string, int> GridColumnsDisplayIndices
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<Dictionary<string, int>>(nameof(GridColumnsDisplayIndices));
|
||||||
|
set => persistentDictionary.SetNonString(nameof(GridColumnsDisplayIndices), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("A Dictionary of GridView data property names and int indicating its column's width in ProductsGrid")]
|
||||||
|
public Dictionary<string, int> GridColumnsWidths
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<Dictionary<string, int>>(nameof(GridColumnsWidths));
|
||||||
|
set => persistentDictionary.SetNonString(nameof(GridColumnsWidths), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("Save cover image alongside audiobook?")]
|
||||||
|
public bool DownloadCoverArt
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<bool>(nameof(DownloadCoverArt));
|
||||||
|
set => persistentDictionary.SetNonString(nameof(DownloadCoverArt), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum BadBookAction
|
||||||
|
{
|
||||||
|
[Description("Ask each time what action to take.")]
|
||||||
|
Ask = 0,
|
||||||
|
[Description("Stop processing books.")]
|
||||||
|
Abort = 1,
|
||||||
|
[Description("Retry book later. Skip for now. Continue processing books.")]
|
||||||
|
Retry = 2,
|
||||||
|
[Description("Permanently ignore book. Continue processing books. Do not try book again.")]
|
||||||
|
Ignore = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("When liberating books and there is an error, Libation should:")]
|
||||||
|
public BadBookAction BadBook
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var badBookStr = persistentDictionary.GetString(nameof(BadBook));
|
||||||
|
return Enum.TryParse<BadBookAction>(badBookStr, out var badBookEnum) ? badBookEnum : BadBookAction.Ask;
|
||||||
|
}
|
||||||
|
set => persistentDictionary.SetString(nameof(BadBook), value.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("Show number of newly imported titles? When unchecked, no pop-up will appear after library scan.")]
|
||||||
|
public bool ShowImportedStats
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<bool>(nameof(ShowImportedStats));
|
||||||
|
set => persistentDictionary.SetNonString(nameof(ShowImportedStats), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("Import episodes? (eg: podcasts) When unchecked, episodes will not be imported into Libation.")]
|
||||||
|
public bool ImportEpisodes
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<bool>(nameof(ImportEpisodes));
|
||||||
|
set => persistentDictionary.SetNonString(nameof(ImportEpisodes), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("Download episodes? (eg: podcasts). When unchecked, episodes already in Libation will not be downloaded.")]
|
||||||
|
public bool DownloadEpisodes
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<bool>(nameof(DownloadEpisodes));
|
||||||
|
set => persistentDictionary.SetNonString(nameof(DownloadEpisodes), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler AutoScanChanged;
|
||||||
|
|
||||||
|
[Description("Automatically run periodic scans in the background?")]
|
||||||
|
public bool AutoScan
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<bool>(nameof(AutoScan));
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (AutoScan != value)
|
||||||
|
{
|
||||||
|
persistentDictionary.SetNonString(nameof(AutoScan), value);
|
||||||
|
AutoScanChanged?.Invoke(null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("Auto download episodes? After scan, download new books in 'checked' accounts.")]
|
||||||
|
public bool AutoDownloadEpisodes
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<bool>(nameof(AutoDownloadEpisodes));
|
||||||
|
set => persistentDictionary.SetNonString(nameof(AutoDownloadEpisodes), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("Save all podcast episodes in a series to the series parent folder?")]
|
||||||
|
public bool SavePodcastsToParentFolder
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<bool>(nameof(SavePodcastsToParentFolder));
|
||||||
|
set => persistentDictionary.SetNonString(nameof(SavePodcastsToParentFolder), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region templates: custom file naming
|
||||||
|
|
||||||
|
[Description("Edit how illegal filename characters are replaced")]
|
||||||
|
public ReplacementCharacters ReplacementCharacters
|
||||||
|
{
|
||||||
|
get => persistentDictionary.GetNonString<ReplacementCharacters>(nameof(ReplacementCharacters));
|
||||||
|
set => persistentDictionary.SetNonString(nameof(ReplacementCharacters), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("How to format the folders in which files will be saved")]
|
||||||
|
public string FolderTemplate
|
||||||
|
{
|
||||||
|
get => getTemplate(nameof(FolderTemplate), Templates.Folder);
|
||||||
|
set => setTemplate(nameof(FolderTemplate), Templates.Folder, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("How to format the saved pdf and audio files")]
|
||||||
|
public string FileTemplate
|
||||||
|
{
|
||||||
|
get => getTemplate(nameof(FileTemplate), Templates.File);
|
||||||
|
set => setTemplate(nameof(FileTemplate), Templates.File, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("How to format the saved audio files when split by chapters")]
|
||||||
|
public string ChapterFileTemplate
|
||||||
|
{
|
||||||
|
get => getTemplate(nameof(ChapterFileTemplate), Templates.ChapterFile);
|
||||||
|
set => setTemplate(nameof(ChapterFileTemplate), Templates.ChapterFile, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("How to format the file's Tile stored in metadata")]
|
||||||
|
public string ChapterTitleTemplate
|
||||||
|
{
|
||||||
|
get => getTemplate(nameof(ChapterTitleTemplate), Templates.ChapterTitle);
|
||||||
|
set => setTemplate(nameof(ChapterTitleTemplate), Templates.ChapterTitle, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string getTemplate(string settingName, Templates templ) => templ.GetValid(persistentDictionary.GetString(settingName));
|
||||||
|
private void setTemplate(string settingName, Templates templ, string newValue)
|
||||||
|
{
|
||||||
|
var template = newValue?.Trim();
|
||||||
|
if (templ.IsValid(template))
|
||||||
|
persistentDictionary.SetString(settingName, template);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,20 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
using Dinah.Core.Logging;
|
|
||||||
using FileManager;
|
using FileManager;
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using Serilog;
|
|
||||||
using Serilog.Events;
|
|
||||||
|
|
||||||
namespace LibationFileManager
|
namespace LibationFileManager
|
||||||
{
|
{
|
||||||
public class Configuration
|
public partial class Configuration
|
||||||
{
|
{
|
||||||
public bool LibationSettingsAreValid
|
public bool LibationSettingsAreValid
|
||||||
=> File.Exists(APPSETTINGS_JSON)
|
=> File.Exists(APPSETTINGS_JSON)
|
||||||
@ -37,519 +31,9 @@ namespace LibationFileManager
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#region persistent configuration settings/values
|
|
||||||
|
|
||||||
// note: any potential file manager static ctors can't compensate if storage dir is changed at run time via settings. this is partly bad architecture. but the side effect is desirable. if changing LibationFiles location: restart app
|
|
||||||
|
|
||||||
// default setting and directory creation occur in class responsible for files.
|
|
||||||
// config class is only responsible for path. not responsible for setting defaults, dir validation, or dir creation
|
|
||||||
// exceptions: appsettings.json, LibationFiles dir, Settings.json
|
|
||||||
|
|
||||||
private PersistentDictionary persistentDictionary;
|
|
||||||
|
|
||||||
public T GetNonString<T>(string propertyName) => persistentDictionary.GetNonString<T>(propertyName);
|
|
||||||
public object GetObject(string propertyName) => persistentDictionary.GetObject(propertyName);
|
|
||||||
public void SetObject(string propertyName, object newValue) => persistentDictionary.SetNonString(propertyName, newValue);
|
|
||||||
|
|
||||||
/// <summary>WILL ONLY set if already present. WILL NOT create new</summary>
|
|
||||||
public void SetWithJsonPath(string jsonPath, string propertyName, string newValue, bool suppressLogging = false)
|
|
||||||
{
|
|
||||||
var settingWasChanged = persistentDictionary.SetWithJsonPath(jsonPath, propertyName, newValue, suppressLogging);
|
|
||||||
if (settingWasChanged)
|
|
||||||
configuration?.Reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string SettingsFilePath => Path.Combine(LibationFiles, "Settings.json");
|
|
||||||
|
|
||||||
public static string GetDescription(string propertyName)
|
|
||||||
{
|
|
||||||
var attribute = typeof(Configuration)
|
|
||||||
.GetProperty(propertyName)
|
|
||||||
?.GetCustomAttributes(typeof(DescriptionAttribute), true)
|
|
||||||
.SingleOrDefault()
|
|
||||||
as DescriptionAttribute;
|
|
||||||
|
|
||||||
return attribute?.Description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Exists(string propertyName) => persistentDictionary.Exists(propertyName);
|
|
||||||
|
|
||||||
[Description("Use the beta version of Libation\r\nNew and experimental features, but probably buggy.\r\n(requires restart to take effect)")]
|
|
||||||
public bool BetaOptIn
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetNonString<bool>(nameof(BetaOptIn));
|
|
||||||
set => persistentDictionary.SetNonString(nameof(BetaOptIn), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("Location for book storage. Includes destination of newly liberated books")]
|
|
||||||
public string Books
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetString(nameof(Books));
|
|
||||||
set => persistentDictionary.SetString(nameof(Books), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// temp/working dir(s) should be outside of dropbox
|
|
||||||
[Description("Temporary location of files while they're in process of being downloaded and decrypted.\r\nWhen decryption is complete, the final file will be in Books location\r\nRecommend not using a folder which is backed up real time. Eg: Dropbox, iCloud, Google Drive")]
|
|
||||||
public string InProgress
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetString(nameof(InProgress));
|
|
||||||
set => persistentDictionary.SetString(nameof(InProgress), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("Allow Libation to fix up audiobook metadata")]
|
|
||||||
public bool AllowLibationFixup
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetNonString<bool>(nameof(AllowLibationFixup));
|
|
||||||
set => persistentDictionary.SetNonString(nameof(AllowLibationFixup), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("Create a cue sheet (.cue)")]
|
|
||||||
public bool CreateCueSheet
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetNonString<bool>(nameof(CreateCueSheet));
|
|
||||||
set => persistentDictionary.SetNonString(nameof(CreateCueSheet), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("Retain the Aax file after successfully decrypting")]
|
|
||||||
public bool RetainAaxFile
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetNonString<bool>(nameof(RetainAaxFile));
|
|
||||||
set => persistentDictionary.SetNonString(nameof(RetainAaxFile), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("Split my books into multiple files by chapter")]
|
|
||||||
public bool SplitFilesByChapter
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetNonString<bool>(nameof(SplitFilesByChapter));
|
|
||||||
set => persistentDictionary.SetNonString(nameof(SplitFilesByChapter), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("Merge Opening/End Credits into the following/preceding chapters")]
|
|
||||||
public bool MergeOpeningAndEndCredits
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetNonString<bool>(nameof(MergeOpeningAndEndCredits));
|
|
||||||
set => persistentDictionary.SetNonString(nameof(MergeOpeningAndEndCredits), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("Strip \"(Unabridged)\" from audiobook metadata tags")]
|
|
||||||
public bool StripUnabridged
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetNonString<bool>(nameof(StripUnabridged));
|
|
||||||
set => persistentDictionary.SetNonString(nameof(StripUnabridged), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("Strip audible branding from the start and end of audiobooks.\r\n(e.g. \"This is Audible\")")]
|
|
||||||
public bool StripAudibleBrandAudio
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetNonString<bool>(nameof(StripAudibleBrandAudio));
|
|
||||||
set => persistentDictionary.SetNonString(nameof(StripAudibleBrandAudio), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("Decrypt to lossy format?")]
|
|
||||||
public bool DecryptToLossy
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetNonString<bool>(nameof(DecryptToLossy));
|
|
||||||
set => persistentDictionary.SetNonString(nameof(DecryptToLossy), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("Lame encoder target. true = Bitrate, false = Quality")]
|
|
||||||
public bool LameTargetBitrate
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetNonString<bool>(nameof(LameTargetBitrate));
|
|
||||||
set => persistentDictionary.SetNonString(nameof(LameTargetBitrate), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("Lame encoder downsamples to mono")]
|
|
||||||
public bool LameDownsampleMono
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetNonString<bool>(nameof(LameDownsampleMono));
|
|
||||||
set => persistentDictionary.SetNonString(nameof(LameDownsampleMono), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("Lame target bitrate [16,320]")]
|
|
||||||
public int LameBitrate
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetNonString<int>(nameof(LameBitrate));
|
|
||||||
set => persistentDictionary.SetNonString(nameof(LameBitrate), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("Restrict encoder to constant bitrate?")]
|
|
||||||
public bool LameConstantBitrate
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetNonString<bool>(nameof(LameConstantBitrate));
|
|
||||||
set => persistentDictionary.SetNonString(nameof(LameConstantBitrate), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("Match the source bitrate?")]
|
|
||||||
public bool LameMatchSourceBR
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetNonString<bool>(nameof(LameMatchSourceBR));
|
|
||||||
set => persistentDictionary.SetNonString(nameof(LameMatchSourceBR), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("Lame target VBR quality [10,100]")]
|
|
||||||
public int LameVBRQuality
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetNonString<int>(nameof(LameVBRQuality));
|
|
||||||
set => persistentDictionary.SetNonString(nameof(LameVBRQuality), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("A Dictionary of GridView data property names and bool indicating its column's visibility in ProductsGrid")]
|
|
||||||
public Dictionary<string, bool> GridColumnsVisibilities
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetNonString<Dictionary<string, bool>>(nameof(GridColumnsVisibilities));
|
|
||||||
set => persistentDictionary.SetNonString(nameof(GridColumnsVisibilities), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("A Dictionary of GridView data property names and int indicating its column's display index in ProductsGrid")]
|
|
||||||
public Dictionary<string, int> GridColumnsDisplayIndices
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetNonString<Dictionary<string,int>>(nameof(GridColumnsDisplayIndices));
|
|
||||||
set => persistentDictionary.SetNonString(nameof(GridColumnsDisplayIndices), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("A Dictionary of GridView data property names and int indicating its column's width in ProductsGrid")]
|
|
||||||
public Dictionary<string, int> GridColumnsWidths
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetNonString<Dictionary<string,int>>(nameof(GridColumnsWidths));
|
|
||||||
set => persistentDictionary.SetNonString(nameof(GridColumnsWidths), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("Save cover image alongside audiobook?")]
|
|
||||||
public bool DownloadCoverArt
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetNonString<bool>(nameof(DownloadCoverArt));
|
|
||||||
set => persistentDictionary.SetNonString(nameof(DownloadCoverArt), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum BadBookAction
|
|
||||||
{
|
|
||||||
[Description("Ask each time what action to take.")]
|
|
||||||
Ask = 0,
|
|
||||||
[Description("Stop processing books.")]
|
|
||||||
Abort = 1,
|
|
||||||
[Description("Retry book later. Skip for now. Continue processing books.")]
|
|
||||||
Retry = 2,
|
|
||||||
[Description("Permanently ignore book. Continue processing books. Do not try book again.")]
|
|
||||||
Ignore = 3
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("When liberating books and there is an error, Libation should:")]
|
|
||||||
public BadBookAction BadBook
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var badBookStr = persistentDictionary.GetString(nameof(BadBook));
|
|
||||||
return Enum.TryParse<BadBookAction>(badBookStr, out var badBookEnum) ? badBookEnum : BadBookAction.Ask;
|
|
||||||
}
|
|
||||||
set => persistentDictionary.SetString(nameof(BadBook), value.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("Show number of newly imported titles? When unchecked, no pop-up will appear after library scan.")]
|
|
||||||
public bool ShowImportedStats
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetNonString<bool>(nameof(ShowImportedStats));
|
|
||||||
set => persistentDictionary.SetNonString(nameof(ShowImportedStats), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("Import episodes? (eg: podcasts) When unchecked, episodes will not be imported into Libation.")]
|
|
||||||
public bool ImportEpisodes
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetNonString<bool>(nameof(ImportEpisodes));
|
|
||||||
set => persistentDictionary.SetNonString(nameof(ImportEpisodes), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("Download episodes? (eg: podcasts). When unchecked, episodes already in Libation will not be downloaded.")]
|
|
||||||
public bool DownloadEpisodes
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetNonString<bool>(nameof(DownloadEpisodes));
|
|
||||||
set => persistentDictionary.SetNonString(nameof(DownloadEpisodes), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public event EventHandler AutoScanChanged;
|
|
||||||
|
|
||||||
[Description("Automatically run periodic scans in the background?")]
|
|
||||||
public bool AutoScan
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetNonString<bool>(nameof(AutoScan));
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (AutoScan != value)
|
|
||||||
{
|
|
||||||
persistentDictionary.SetNonString(nameof(AutoScan), value);
|
|
||||||
AutoScanChanged?.Invoke(null, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("Auto download episodes? After scan, download new books in 'checked' accounts.")]
|
|
||||||
public bool AutoDownloadEpisodes
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetNonString<bool>(nameof(AutoDownloadEpisodes));
|
|
||||||
set => persistentDictionary.SetNonString(nameof(AutoDownloadEpisodes), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("Save all podcast episodes in a series to the series parent folder?")]
|
|
||||||
public bool SavePodcastsToParentFolder
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetNonString<bool>(nameof(SavePodcastsToParentFolder));
|
|
||||||
set => persistentDictionary.SetNonString(nameof(SavePodcastsToParentFolder), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
#region templates: custom file naming
|
|
||||||
|
|
||||||
[Description("Edit how illegal filename characters are replaced")]
|
|
||||||
public ReplacementCharacters ReplacementCharacters
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetNonString<ReplacementCharacters>(nameof(ReplacementCharacters));
|
|
||||||
set => persistentDictionary.SetNonString(nameof(ReplacementCharacters), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("How to format the folders in which files will be saved")]
|
|
||||||
public string FolderTemplate
|
|
||||||
{
|
|
||||||
get => getTemplate(nameof(FolderTemplate), Templates.Folder);
|
|
||||||
set => setTemplate(nameof(FolderTemplate), Templates.Folder, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("How to format the saved pdf and audio files")]
|
|
||||||
public string FileTemplate
|
|
||||||
{
|
|
||||||
get => getTemplate(nameof(FileTemplate), Templates.File);
|
|
||||||
set => setTemplate(nameof(FileTemplate), Templates.File, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("How to format the saved audio files when split by chapters")]
|
|
||||||
public string ChapterFileTemplate
|
|
||||||
{
|
|
||||||
get => getTemplate(nameof(ChapterFileTemplate), Templates.ChapterFile);
|
|
||||||
set => setTemplate(nameof(ChapterFileTemplate), Templates.ChapterFile, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("How to format the file's Tile stored in metadata")]
|
|
||||||
public string ChapterTitleTemplate
|
|
||||||
{
|
|
||||||
get => getTemplate(nameof(ChapterTitleTemplate), Templates.ChapterTitle);
|
|
||||||
set => setTemplate(nameof(ChapterTitleTemplate), Templates.ChapterTitle, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string getTemplate(string settingName, Templates templ) => templ.GetValid(persistentDictionary.GetString(settingName));
|
|
||||||
private void setTemplate(string settingName, Templates templ, string newValue)
|
|
||||||
{
|
|
||||||
var template = newValue?.Trim();
|
|
||||||
if (templ.IsValid(template))
|
|
||||||
persistentDictionary.SetString(settingName, template);
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region known directories
|
|
||||||
public static string AppDir_Relative => $@".{Path.PathSeparator}{LIBATION_FILES_KEY}";
|
|
||||||
public static string AppDir_Absolute => Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Exe.FileLocationOnDisk), LIBATION_FILES_KEY));
|
|
||||||
public static string MyDocs => Path.GetFullPath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Libation"));
|
|
||||||
public static string WinTemp => Path.GetFullPath(Path.Combine(Path.GetTempPath(), "Libation"));
|
|
||||||
public static string UserProfile => Path.GetFullPath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Libation"));
|
|
||||||
|
|
||||||
public enum KnownDirectories
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
|
|
||||||
[Description("My Users folder")]
|
|
||||||
UserProfile = 1,
|
|
||||||
|
|
||||||
[Description("The same folder that Libation is running from")]
|
|
||||||
AppDir = 2,
|
|
||||||
|
|
||||||
[Description("Windows temporary folder")]
|
|
||||||
WinTemp = 3,
|
|
||||||
|
|
||||||
[Description("My Documents")]
|
|
||||||
MyDocs = 4,
|
|
||||||
|
|
||||||
[Description("Your settings folder (aka: Libation Files)")]
|
|
||||||
LibationFiles = 5
|
|
||||||
}
|
|
||||||
// use func calls so we always get the latest value of LibationFiles
|
|
||||||
private static List<(KnownDirectories directory, Func<string> getPathFunc)> directoryOptionsPaths { get; } = new()
|
|
||||||
{
|
|
||||||
(KnownDirectories.None, () => null),
|
|
||||||
(KnownDirectories.UserProfile, () => UserProfile),
|
|
||||||
(KnownDirectories.AppDir, () => AppDir_Relative),
|
|
||||||
(KnownDirectories.WinTemp, () => WinTemp),
|
|
||||||
(KnownDirectories.MyDocs, () => MyDocs),
|
|
||||||
// this is important to not let very early calls try to accidentally load LibationFiles too early.
|
|
||||||
// also, keep this at bottom of this list
|
|
||||||
(KnownDirectories.LibationFiles, () => libationFilesPathCache)
|
|
||||||
};
|
|
||||||
public static string GetKnownDirectoryPath(KnownDirectories directory)
|
|
||||||
{
|
|
||||||
var dirFunc = directoryOptionsPaths.SingleOrDefault(dirFunc => dirFunc.directory == directory);
|
|
||||||
return dirFunc == default ? null : dirFunc.getPathFunc();
|
|
||||||
}
|
|
||||||
public static KnownDirectories GetKnownDirectory(string directory)
|
|
||||||
{
|
|
||||||
// especially important so a very early call doesn't match null => LibationFiles
|
|
||||||
if (string.IsNullOrWhiteSpace(directory))
|
|
||||||
return KnownDirectories.None;
|
|
||||||
|
|
||||||
// 'First' instead of 'Single' because LibationFiles could match other directories. eg: default value of LibationFiles == UserProfile.
|
|
||||||
// since it's a list, order matters and non-LibationFiles will be returned first
|
|
||||||
var dirFunc = directoryOptionsPaths.FirstOrDefault(dirFunc => dirFunc.getPathFunc() == directory);
|
|
||||||
return dirFunc == default ? KnownDirectories.None : dirFunc.directory;
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region logging
|
|
||||||
private IConfigurationRoot configuration;
|
|
||||||
|
|
||||||
public void ConfigureLogging()
|
|
||||||
{
|
|
||||||
configuration = new ConfigurationBuilder()
|
|
||||||
.AddJsonFile(SettingsFilePath, optional: false, reloadOnChange: true)
|
|
||||||
.Build();
|
|
||||||
Log.Logger = new LoggerConfiguration()
|
|
||||||
.ReadFrom.Configuration(configuration)
|
|
||||||
.CreateLogger();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("The importance of a log event")]
|
|
||||||
public LogEventLevel LogLevel
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var logLevelStr = persistentDictionary.GetStringFromJsonPath("Serilog", "MinimumLevel");
|
|
||||||
return Enum.TryParse<LogEventLevel>(logLevelStr, out var logLevelEnum) ? logLevelEnum : LogEventLevel.Information;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
var valueWasChanged = persistentDictionary.SetWithJsonPath("Serilog", "MinimumLevel", value.ToString());
|
|
||||||
if (!valueWasChanged)
|
|
||||||
{
|
|
||||||
Log.Logger.Debug("LogLevel.set attempt. No change");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
configuration.Reload();
|
|
||||||
|
|
||||||
Log.Logger.Information("Updated LogLevel MinimumLevel. {@DebugInfo}", new
|
|
||||||
{
|
|
||||||
LogLevel_Verbose_Enabled = Log.Logger.IsVerboseEnabled(),
|
|
||||||
LogLevel_Debug_Enabled = Log.Logger.IsDebugEnabled(),
|
|
||||||
LogLevel_Information_Enabled = Log.Logger.IsInformationEnabled(),
|
|
||||||
LogLevel_Warning_Enabled = Log.Logger.IsWarningEnabled(),
|
|
||||||
LogLevel_Error_Enabled = Log.Logger.IsErrorEnabled(),
|
|
||||||
LogLevel_Fatal_Enabled = Log.Logger.IsFatalEnabled()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region singleton stuff
|
#region singleton stuff
|
||||||
public static Configuration Instance { get; } = new Configuration();
|
public static Configuration Instance { get; } = new Configuration();
|
||||||
private Configuration() { }
|
private Configuration() { }
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region LibationFiles
|
|
||||||
private static string APPSETTINGS_JSON { get; } = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "appsettings.json");
|
|
||||||
private const string LIBATION_FILES_KEY = "LibationFiles";
|
|
||||||
|
|
||||||
[Description("Location for storage of program-created files")]
|
|
||||||
public string LibationFiles
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (libationFilesPathCache is not null)
|
|
||||||
return libationFilesPathCache;
|
|
||||||
|
|
||||||
// FIRST: must write here before SettingsFilePath in next step reads cache
|
|
||||||
libationFilesPathCache = getLibationFilesSettingFromJson();
|
|
||||||
|
|
||||||
// SECOND. before setting to json file with SetWithJsonPath, PersistentDictionary must exist
|
|
||||||
persistentDictionary = new PersistentDictionary(SettingsFilePath);
|
|
||||||
|
|
||||||
// Config init in ensureSerilogConfig() only happens when serilog setting is first created (prob on 1st run).
|
|
||||||
// This Set() enforces current LibationFiles every time we restart Libation or redirect LibationFiles
|
|
||||||
var logPath = Path.Combine(LibationFiles, "Log.log");
|
|
||||||
|
|
||||||
// BAD: Serilog.WriteTo[1].Args
|
|
||||||
// "[1]" assumes ordinal position
|
|
||||||
// GOOD: Serilog.WriteTo[?(@.Name=='File')].Args
|
|
||||||
var jsonpath = "Serilog.WriteTo[?(@.Name=='File')].Args";
|
|
||||||
|
|
||||||
SetWithJsonPath(jsonpath, "path", logPath, true);
|
|
||||||
|
|
||||||
return libationFilesPathCache;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string libationFilesPathCache { get; set; }
|
|
||||||
|
|
||||||
private string getLibationFilesSettingFromJson()
|
|
||||||
{
|
|
||||||
string startingContents = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (File.Exists(APPSETTINGS_JSON))
|
|
||||||
{
|
|
||||||
startingContents = File.ReadAllText(APPSETTINGS_JSON);
|
|
||||||
var startingJObj = JObject.Parse(startingContents);
|
|
||||||
|
|
||||||
if (startingJObj.ContainsKey(LIBATION_FILES_KEY))
|
|
||||||
{
|
|
||||||
var startingValue = startingJObj[LIBATION_FILES_KEY].Value<string>();
|
|
||||||
if (!string.IsNullOrWhiteSpace(startingValue))
|
|
||||||
return startingValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
|
|
||||||
// not found. write to file. read from file
|
|
||||||
var endingContents = new JObject { { LIBATION_FILES_KEY, UserProfile.ToString() } }.ToString(Formatting.Indented);
|
|
||||||
if (startingContents != endingContents)
|
|
||||||
{
|
|
||||||
File.WriteAllText(APPSETTINGS_JSON, endingContents);
|
|
||||||
System.Threading.Thread.Sleep(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
// do not check whether directory exists. special/meta directory (eg: AppDir) is valid
|
|
||||||
// verify from live file. no try/catch. want failures to be visible
|
|
||||||
var jObjFinal = JObject.Parse(File.ReadAllText(APPSETTINGS_JSON));
|
|
||||||
var valueFinal = jObjFinal[LIBATION_FILES_KEY].Value<string>();
|
|
||||||
return valueFinal;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetLibationFiles(string directory)
|
|
||||||
{
|
|
||||||
// ensure exists
|
|
||||||
if (!File.Exists(APPSETTINGS_JSON))
|
|
||||||
{
|
|
||||||
// getter creates new file, loads PersistentDictionary
|
|
||||||
var _ = LibationFiles;
|
|
||||||
System.Threading.Thread.Sleep(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
libationFilesPathCache = null;
|
|
||||||
|
|
||||||
var startingContents = File.ReadAllText(APPSETTINGS_JSON);
|
|
||||||
var jObj = JObject.Parse(startingContents);
|
|
||||||
|
|
||||||
jObj[LIBATION_FILES_KEY] = directory;
|
|
||||||
|
|
||||||
var endingContents = JsonConvert.SerializeObject(jObj, Formatting.Indented);
|
|
||||||
if (startingContents == endingContents)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// now it's set in the file again but no settings have moved yet
|
|
||||||
File.WriteAllText(APPSETTINGS_JSON, endingContents);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Log.Logger.Information("Libation files changed {@DebugInfo}", new { APPSETTINGS_JSON, LIBATION_FILES_KEY, directory });
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
Source/LibationFileManager/IInteropFunctions.cs
Normal file
10
Source/LibationFileManager/IInteropFunctions.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace LibationFileManager
|
||||||
|
{
|
||||||
|
public interface IInteropFunctions
|
||||||
|
{
|
||||||
|
void SetFolderIcon(string image, string directory);
|
||||||
|
void DeleteFolderIcon(string directory);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,28 +7,34 @@ using System.Reflection;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
|
|
||||||
namespace AppScaffolding.OSInterop
|
namespace LibationFileManager
|
||||||
{
|
{
|
||||||
public class OSInteropProxy : IInteropFunctions
|
public static class InteropFactory
|
||||||
{
|
{
|
||||||
public static bool IsWindows { get; } = OperatingSystem.IsWindows();
|
public static Type InteropFunctionsType { get; }
|
||||||
public static bool IsLinux { get; } = OperatingSystem.IsLinux();
|
|
||||||
public static bool IsMacOs { get; } = OperatingSystem.IsMacOS();
|
public static IInteropFunctions Create() => _create();
|
||||||
|
|
||||||
|
//// examples of the pattern which could be useful later
|
||||||
|
//public static IInteropFunctions Create(string str, int i) => _create(str, i);
|
||||||
|
//public static IInteropFunctions Create(params object[] values) => _create(values);
|
||||||
|
|
||||||
|
private static IInteropFunctions _create(params object[] values)
|
||||||
|
=> InteropFunctionsType is null ? new NullInteropFunctions()
|
||||||
|
//: values is null || values.Length == 0 ? Activator.CreateInstance(InteropFunctionsType) as IInteropFunctions
|
||||||
|
: Activator.CreateInstance(InteropFunctionsType, values) as IInteropFunctions;
|
||||||
|
|
||||||
|
#region load types
|
||||||
|
|
||||||
public static Func<string, bool> MatchesOS { get; }
|
public static Func<string, bool> MatchesOS { get; }
|
||||||
= IsWindows ? a => Path.GetFileName(a).StartsWithInsensitive("win")
|
= Configuration.IsWindows ? a => Path.GetFileName(a).StartsWithInsensitive("win")
|
||||||
: IsLinux ? a => Path.GetFileName(a).StartsWithInsensitive("linux")
|
: Configuration.IsLinux ? a => Path.GetFileName(a).StartsWithInsensitive("linux")
|
||||||
: IsMacOs ? a => Path.GetFileName(a).StartsWithInsensitive("mac") || a.StartsWithInsensitive("osx")
|
: Configuration.IsMacOs ? a => Path.GetFileName(a).StartsWithInsensitive("mac") || Path.GetFileName(a).StartsWithInsensitive("osx")
|
||||||
: _ => false;
|
: _ => false;
|
||||||
|
|
||||||
public IInteropFunctions InteropFunctions { get; } = new NullInteropFunctions();
|
|
||||||
|
|
||||||
#region Singleton Stuff
|
|
||||||
|
|
||||||
private const string CONFIG_APP_ENDING = "ConfigApp.exe";
|
private const string CONFIG_APP_ENDING = "ConfigApp.exe";
|
||||||
private static List<ProcessModule> ModuleList { get; } = new();
|
private static List<ProcessModule> ModuleList { get; } = new();
|
||||||
public static Type InteropFunctionsType { get; }
|
static InteropFactory()
|
||||||
static OSInteropProxy()
|
|
||||||
{
|
{
|
||||||
// searches file names for potential matches; doesn't run anything
|
// searches file names for potential matches; doesn't run anything
|
||||||
var configApp = getOSConfigApp();
|
var configApp = getOSConfigApp();
|
||||||
@ -118,30 +124,5 @@ namespace AppScaffolding.OSInterop
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public OSInteropProxy() : this(new object[0]) { }
|
|
||||||
|
|
||||||
//// example of the pattern which could be useful later
|
|
||||||
//public OSInteropProxy(string str, int i) : this(new object[] { str, i }) { }
|
|
||||||
|
|
||||||
private OSInteropProxy(params object[] values)
|
|
||||||
{
|
|
||||||
if (InteropFunctionsType is null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
InteropFunctions =
|
|
||||||
values is null || values.Length == 0
|
|
||||||
? Activator.CreateInstance(InteropFunctionsType) as IInteropFunctions
|
|
||||||
: Activator.CreateInstance(InteropFunctionsType, values) as IInteropFunctions;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface Members
|
|
||||||
/*
|
|
||||||
// examples until the real interface is filled out
|
|
||||||
public void CopyTextToClipboard(string text) => InteropFunctions.CopyTextToClipboard(text);
|
|
||||||
public void ShowForm() => InteropFunctions.ShowForm();
|
|
||||||
public string TransformInit1() => InteropFunctions.TransformInit1();
|
|
||||||
public int TransformInit2() => InteropFunctions.TransformInit2();
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -14,6 +14,16 @@
|
|||||||
<ProjectReference Include="..\FileManager\FileManager.csproj" />
|
<ProjectReference Include="..\FileManager\FileManager.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Update="Configuration.*.cs">
|
||||||
|
<DependentUpon>Configuration.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="OSInterop\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
<DebugType>embedded</DebugType>
|
<DebugType>embedded</DebugType>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|||||||
13
Source/LibationFileManager/NullInteropFunctions.cs
Normal file
13
Source/LibationFileManager/NullInteropFunctions.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace LibationFileManager
|
||||||
|
{
|
||||||
|
public class NullInteropFunctions : IInteropFunctions
|
||||||
|
{
|
||||||
|
public NullInteropFunctions() { }
|
||||||
|
public NullInteropFunctions(params object[] values) { }
|
||||||
|
|
||||||
|
public void SetFolderIcon(string image, string directory) => throw new PlatformNotSupportedException();
|
||||||
|
public void DeleteFolderIcon(string directory) => throw new PlatformNotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
40
Source/LibationFileManager/WindowsDirectory.cs
Normal file
40
Source/LibationFileManager/WindowsDirectory.cs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace LibationFileManager
|
||||||
|
{
|
||||||
|
public static class WindowsDirectory
|
||||||
|
{
|
||||||
|
public static void SetCoverAsFolderIcon(string pictureId, string directory)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!Configuration.Instance.UseCoverAsFolderIcon || !Configuration.IsWindows)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// get path of cover art in Images dir. Download first if not exists
|
||||||
|
var coverArtPath = PictureStorage.GetPicturePathSynchronously(new(pictureId, PictureSize._300x300));
|
||||||
|
|
||||||
|
InteropFactory.Create().SetFolderIcon(image: coverArtPath, directory: directory);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Failure to 'set cover as folder icon' should not be considered a failure to download the book
|
||||||
|
Serilog.Log.Logger.Error(ex, "Error setting cover art as folder icon. {@DebugInfo}", new { directory });
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
InteropFactory.Create().DeleteFolderIcon(directory);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Serilog.Log.Logger.Error(ex, "Error rolling back: setting cover art as folder icon. {@DebugInfo}", new { directory });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -51,6 +51,7 @@
|
|||||||
this.loggingLevelCb = new System.Windows.Forms.ComboBox();
|
this.loggingLevelCb = new System.Windows.Forms.ComboBox();
|
||||||
this.tabControl = new System.Windows.Forms.TabControl();
|
this.tabControl = new System.Windows.Forms.TabControl();
|
||||||
this.tab1ImportantSettings = new System.Windows.Forms.TabPage();
|
this.tab1ImportantSettings = new System.Windows.Forms.TabPage();
|
||||||
|
this.betaOptInCbox = new System.Windows.Forms.CheckBox();
|
||||||
this.booksGb = new System.Windows.Forms.GroupBox();
|
this.booksGb = new System.Windows.Forms.GroupBox();
|
||||||
this.saveEpisodesToSeriesFolderCbox = new System.Windows.Forms.CheckBox();
|
this.saveEpisodesToSeriesFolderCbox = new System.Windows.Forms.CheckBox();
|
||||||
this.tab2ImportLibrary = new System.Windows.Forms.TabPage();
|
this.tab2ImportLibrary = new System.Windows.Forms.TabPage();
|
||||||
@ -58,6 +59,7 @@
|
|||||||
this.autoScanCb = new System.Windows.Forms.CheckBox();
|
this.autoScanCb = new System.Windows.Forms.CheckBox();
|
||||||
this.showImportedStatsCb = new System.Windows.Forms.CheckBox();
|
this.showImportedStatsCb = new System.Windows.Forms.CheckBox();
|
||||||
this.tab3DownloadDecrypt = new System.Windows.Forms.TabPage();
|
this.tab3DownloadDecrypt = new System.Windows.Forms.TabPage();
|
||||||
|
this.useCoverAsFolderIconCb = new System.Windows.Forms.CheckBox();
|
||||||
this.inProgressFilesGb = new System.Windows.Forms.GroupBox();
|
this.inProgressFilesGb = new System.Windows.Forms.GroupBox();
|
||||||
this.customFileNamingGb = new System.Windows.Forms.GroupBox();
|
this.customFileNamingGb = new System.Windows.Forms.GroupBox();
|
||||||
this.editCharreplacementBtn = new System.Windows.Forms.Button();
|
this.editCharreplacementBtn = new System.Windows.Forms.Button();
|
||||||
@ -110,7 +112,6 @@
|
|||||||
this.retainAaxFileCbox = new System.Windows.Forms.CheckBox();
|
this.retainAaxFileCbox = new System.Windows.Forms.CheckBox();
|
||||||
this.downloadCoverArtCbox = new System.Windows.Forms.CheckBox();
|
this.downloadCoverArtCbox = new System.Windows.Forms.CheckBox();
|
||||||
this.createCueSheetCbox = new System.Windows.Forms.CheckBox();
|
this.createCueSheetCbox = new System.Windows.Forms.CheckBox();
|
||||||
this.betaOptInCbox = new System.Windows.Forms.CheckBox();
|
|
||||||
this.badBookGb.SuspendLayout();
|
this.badBookGb.SuspendLayout();
|
||||||
this.tabControl.SuspendLayout();
|
this.tabControl.SuspendLayout();
|
||||||
this.tab1ImportantSettings.SuspendLayout();
|
this.tab1ImportantSettings.SuspendLayout();
|
||||||
@ -153,7 +154,7 @@
|
|||||||
// saveBtn
|
// saveBtn
|
||||||
//
|
//
|
||||||
this.saveBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
this.saveBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.saveBtn.Location = new System.Drawing.Point(667, 461);
|
this.saveBtn.Location = new System.Drawing.Point(667, 491);
|
||||||
this.saveBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
this.saveBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||||
this.saveBtn.Name = "saveBtn";
|
this.saveBtn.Name = "saveBtn";
|
||||||
this.saveBtn.Size = new System.Drawing.Size(88, 27);
|
this.saveBtn.Size = new System.Drawing.Size(88, 27);
|
||||||
@ -166,7 +167,7 @@
|
|||||||
//
|
//
|
||||||
this.cancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
this.cancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.cancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
this.cancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||||
this.cancelBtn.Location = new System.Drawing.Point(785, 461);
|
this.cancelBtn.Location = new System.Drawing.Point(785, 491);
|
||||||
this.cancelBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
this.cancelBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||||
this.cancelBtn.Name = "cancelBtn";
|
this.cancelBtn.Name = "cancelBtn";
|
||||||
this.cancelBtn.Size = new System.Drawing.Size(88, 27);
|
this.cancelBtn.Size = new System.Drawing.Size(88, 27);
|
||||||
@ -197,13 +198,15 @@
|
|||||||
//
|
//
|
||||||
// badBookGb
|
// badBookGb
|
||||||
//
|
//
|
||||||
|
this.badBookGb.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.badBookGb.Controls.Add(this.badBookIgnoreRb);
|
this.badBookGb.Controls.Add(this.badBookIgnoreRb);
|
||||||
this.badBookGb.Controls.Add(this.badBookRetryRb);
|
this.badBookGb.Controls.Add(this.badBookRetryRb);
|
||||||
this.badBookGb.Controls.Add(this.badBookAbortRb);
|
this.badBookGb.Controls.Add(this.badBookAbortRb);
|
||||||
this.badBookGb.Controls.Add(this.badBookAskRb);
|
this.badBookGb.Controls.Add(this.badBookAskRb);
|
||||||
this.badBookGb.Location = new System.Drawing.Point(7, 6);
|
this.badBookGb.Location = new System.Drawing.Point(7, 6);
|
||||||
this.badBookGb.Name = "badBookGb";
|
this.badBookGb.Name = "badBookGb";
|
||||||
this.badBookGb.Size = new System.Drawing.Size(888, 76);
|
this.badBookGb.Size = new System.Drawing.Size(834, 76);
|
||||||
this.badBookGb.TabIndex = 13;
|
this.badBookGb.TabIndex = 13;
|
||||||
this.badBookGb.TabStop = false;
|
this.badBookGb.TabStop = false;
|
||||||
this.badBookGb.Text = "[bad book desc]";
|
this.badBookGb.Text = "[bad book desc]";
|
||||||
@ -370,7 +373,7 @@
|
|||||||
this.tabControl.Location = new System.Drawing.Point(12, 12);
|
this.tabControl.Location = new System.Drawing.Point(12, 12);
|
||||||
this.tabControl.Name = "tabControl";
|
this.tabControl.Name = "tabControl";
|
||||||
this.tabControl.SelectedIndex = 0;
|
this.tabControl.SelectedIndex = 0;
|
||||||
this.tabControl.Size = new System.Drawing.Size(862, 443);
|
this.tabControl.Size = new System.Drawing.Size(862, 473);
|
||||||
this.tabControl.TabIndex = 100;
|
this.tabControl.TabIndex = 100;
|
||||||
//
|
//
|
||||||
// tab1ImportantSettings
|
// tab1ImportantSettings
|
||||||
@ -383,11 +386,23 @@
|
|||||||
this.tab1ImportantSettings.Location = new System.Drawing.Point(4, 24);
|
this.tab1ImportantSettings.Location = new System.Drawing.Point(4, 24);
|
||||||
this.tab1ImportantSettings.Name = "tab1ImportantSettings";
|
this.tab1ImportantSettings.Name = "tab1ImportantSettings";
|
||||||
this.tab1ImportantSettings.Padding = new System.Windows.Forms.Padding(3);
|
this.tab1ImportantSettings.Padding = new System.Windows.Forms.Padding(3);
|
||||||
this.tab1ImportantSettings.Size = new System.Drawing.Size(854, 415);
|
this.tab1ImportantSettings.Size = new System.Drawing.Size(854, 445);
|
||||||
this.tab1ImportantSettings.TabIndex = 0;
|
this.tab1ImportantSettings.TabIndex = 0;
|
||||||
this.tab1ImportantSettings.Text = "Important settings";
|
this.tab1ImportantSettings.Text = "Important settings";
|
||||||
this.tab1ImportantSettings.UseVisualStyleBackColor = true;
|
this.tab1ImportantSettings.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
|
// betaOptInCbox
|
||||||
|
//
|
||||||
|
this.betaOptInCbox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||||
|
this.betaOptInCbox.AutoSize = true;
|
||||||
|
this.betaOptInCbox.Enabled = false;
|
||||||
|
this.betaOptInCbox.Location = new System.Drawing.Point(13, 399);
|
||||||
|
this.betaOptInCbox.Name = "betaOptInCbox";
|
||||||
|
this.betaOptInCbox.Size = new System.Drawing.Size(107, 19);
|
||||||
|
this.betaOptInCbox.TabIndex = 6;
|
||||||
|
this.betaOptInCbox.Text = "[Opt in to Beta]";
|
||||||
|
this.betaOptInCbox.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
// booksGb
|
// booksGb
|
||||||
//
|
//
|
||||||
this.booksGb.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
this.booksGb.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
@ -422,7 +437,7 @@
|
|||||||
this.tab2ImportLibrary.Location = new System.Drawing.Point(4, 24);
|
this.tab2ImportLibrary.Location = new System.Drawing.Point(4, 24);
|
||||||
this.tab2ImportLibrary.Name = "tab2ImportLibrary";
|
this.tab2ImportLibrary.Name = "tab2ImportLibrary";
|
||||||
this.tab2ImportLibrary.Padding = new System.Windows.Forms.Padding(3);
|
this.tab2ImportLibrary.Padding = new System.Windows.Forms.Padding(3);
|
||||||
this.tab2ImportLibrary.Size = new System.Drawing.Size(854, 415);
|
this.tab2ImportLibrary.Size = new System.Drawing.Size(854, 445);
|
||||||
this.tab2ImportLibrary.TabIndex = 1;
|
this.tab2ImportLibrary.TabIndex = 1;
|
||||||
this.tab2ImportLibrary.Text = "Import library";
|
this.tab2ImportLibrary.Text = "Import library";
|
||||||
this.tab2ImportLibrary.UseVisualStyleBackColor = true;
|
this.tab2ImportLibrary.UseVisualStyleBackColor = true;
|
||||||
@ -459,17 +474,28 @@
|
|||||||
//
|
//
|
||||||
// tab3DownloadDecrypt
|
// tab3DownloadDecrypt
|
||||||
//
|
//
|
||||||
|
this.tab3DownloadDecrypt.Controls.Add(this.useCoverAsFolderIconCb);
|
||||||
this.tab3DownloadDecrypt.Controls.Add(this.inProgressFilesGb);
|
this.tab3DownloadDecrypt.Controls.Add(this.inProgressFilesGb);
|
||||||
this.tab3DownloadDecrypt.Controls.Add(this.customFileNamingGb);
|
this.tab3DownloadDecrypt.Controls.Add(this.customFileNamingGb);
|
||||||
this.tab3DownloadDecrypt.Controls.Add(this.badBookGb);
|
this.tab3DownloadDecrypt.Controls.Add(this.badBookGb);
|
||||||
this.tab3DownloadDecrypt.Location = new System.Drawing.Point(4, 24);
|
this.tab3DownloadDecrypt.Location = new System.Drawing.Point(4, 24);
|
||||||
this.tab3DownloadDecrypt.Name = "tab3DownloadDecrypt";
|
this.tab3DownloadDecrypt.Name = "tab3DownloadDecrypt";
|
||||||
this.tab3DownloadDecrypt.Padding = new System.Windows.Forms.Padding(3);
|
this.tab3DownloadDecrypt.Padding = new System.Windows.Forms.Padding(3);
|
||||||
this.tab3DownloadDecrypt.Size = new System.Drawing.Size(854, 415);
|
this.tab3DownloadDecrypt.Size = new System.Drawing.Size(854, 445);
|
||||||
this.tab3DownloadDecrypt.TabIndex = 2;
|
this.tab3DownloadDecrypt.TabIndex = 2;
|
||||||
this.tab3DownloadDecrypt.Text = "Download/Decrypt";
|
this.tab3DownloadDecrypt.Text = "Download/Decrypt";
|
||||||
this.tab3DownloadDecrypt.UseVisualStyleBackColor = true;
|
this.tab3DownloadDecrypt.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
|
// useCoverAsFolderIconCb
|
||||||
|
//
|
||||||
|
this.useCoverAsFolderIconCb.AutoSize = true;
|
||||||
|
this.useCoverAsFolderIconCb.Location = new System.Drawing.Point(7, 415);
|
||||||
|
this.useCoverAsFolderIconCb.Name = "useCoverAsFolderIconCb";
|
||||||
|
this.useCoverAsFolderIconCb.Size = new System.Drawing.Size(180, 19);
|
||||||
|
this.useCoverAsFolderIconCb.TabIndex = 22;
|
||||||
|
this.useCoverAsFolderIconCb.Text = "[UseCoverAsFolderIcon desc]";
|
||||||
|
this.useCoverAsFolderIconCb.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
// inProgressFilesGb
|
// inProgressFilesGb
|
||||||
//
|
//
|
||||||
this.inProgressFilesGb.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
this.inProgressFilesGb.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
@ -618,7 +644,7 @@
|
|||||||
this.tab4AudioFileOptions.Location = new System.Drawing.Point(4, 24);
|
this.tab4AudioFileOptions.Location = new System.Drawing.Point(4, 24);
|
||||||
this.tab4AudioFileOptions.Name = "tab4AudioFileOptions";
|
this.tab4AudioFileOptions.Name = "tab4AudioFileOptions";
|
||||||
this.tab4AudioFileOptions.Padding = new System.Windows.Forms.Padding(3);
|
this.tab4AudioFileOptions.Padding = new System.Windows.Forms.Padding(3);
|
||||||
this.tab4AudioFileOptions.Size = new System.Drawing.Size(854, 415);
|
this.tab4AudioFileOptions.Size = new System.Drawing.Size(854, 445);
|
||||||
this.tab4AudioFileOptions.TabIndex = 3;
|
this.tab4AudioFileOptions.TabIndex = 3;
|
||||||
this.tab4AudioFileOptions.Text = "Audio File Options";
|
this.tab4AudioFileOptions.Text = "Audio File Options";
|
||||||
this.tab4AudioFileOptions.UseVisualStyleBackColor = true;
|
this.tab4AudioFileOptions.UseVisualStyleBackColor = true;
|
||||||
@ -1050,24 +1076,13 @@
|
|||||||
this.createCueSheetCbox.UseVisualStyleBackColor = true;
|
this.createCueSheetCbox.UseVisualStyleBackColor = true;
|
||||||
this.createCueSheetCbox.CheckedChanged += new System.EventHandler(this.allowLibationFixupCbox_CheckedChanged);
|
this.createCueSheetCbox.CheckedChanged += new System.EventHandler(this.allowLibationFixupCbox_CheckedChanged);
|
||||||
//
|
//
|
||||||
// betaOptInCbox
|
|
||||||
//
|
|
||||||
this.betaOptInCbox.AutoSize = true;
|
|
||||||
this.betaOptInCbox.Enabled = false;
|
|
||||||
this.betaOptInCbox.Location = new System.Drawing.Point(13, 358);
|
|
||||||
this.betaOptInCbox.Name = "betaOptInCbox";
|
|
||||||
this.betaOptInCbox.Size = new System.Drawing.Size(107, 19);
|
|
||||||
this.betaOptInCbox.TabIndex = 6;
|
|
||||||
this.betaOptInCbox.Text = "[Opt in to Beta]";
|
|
||||||
this.betaOptInCbox.UseVisualStyleBackColor = true;
|
|
||||||
//
|
|
||||||
// SettingsDialog
|
// SettingsDialog
|
||||||
//
|
//
|
||||||
this.AcceptButton = this.saveBtn;
|
this.AcceptButton = this.saveBtn;
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.CancelButton = this.cancelBtn;
|
this.CancelButton = this.cancelBtn;
|
||||||
this.ClientSize = new System.Drawing.Size(886, 504);
|
this.ClientSize = new System.Drawing.Size(886, 534);
|
||||||
this.Controls.Add(this.tabControl);
|
this.Controls.Add(this.tabControl);
|
||||||
this.Controls.Add(this.cancelBtn);
|
this.Controls.Add(this.cancelBtn);
|
||||||
this.Controls.Add(this.saveBtn);
|
this.Controls.Add(this.saveBtn);
|
||||||
@ -1089,6 +1104,7 @@
|
|||||||
this.tab2ImportLibrary.ResumeLayout(false);
|
this.tab2ImportLibrary.ResumeLayout(false);
|
||||||
this.tab2ImportLibrary.PerformLayout();
|
this.tab2ImportLibrary.PerformLayout();
|
||||||
this.tab3DownloadDecrypt.ResumeLayout(false);
|
this.tab3DownloadDecrypt.ResumeLayout(false);
|
||||||
|
this.tab3DownloadDecrypt.PerformLayout();
|
||||||
this.inProgressFilesGb.ResumeLayout(false);
|
this.inProgressFilesGb.ResumeLayout(false);
|
||||||
this.inProgressFilesGb.PerformLayout();
|
this.inProgressFilesGb.PerformLayout();
|
||||||
this.customFileNamingGb.ResumeLayout(false);
|
this.customFileNamingGb.ResumeLayout(false);
|
||||||
@ -1197,5 +1213,6 @@
|
|||||||
private System.Windows.Forms.CheckBox mergeOpeningEndCreditsCbox;
|
private System.Windows.Forms.CheckBox mergeOpeningEndCreditsCbox;
|
||||||
private System.Windows.Forms.GroupBox audiobookFixupsGb;
|
private System.Windows.Forms.GroupBox audiobookFixupsGb;
|
||||||
private System.Windows.Forms.CheckBox betaOptInCbox;
|
private System.Windows.Forms.CheckBox betaOptInCbox;
|
||||||
|
private System.Windows.Forms.CheckBox useCoverAsFolderIconCb;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -29,6 +29,7 @@ namespace LibationWinForms.Dialogs
|
|||||||
badBookAbortRb.Text = Configuration.BadBookAction.Abort.GetDescription();
|
badBookAbortRb.Text = Configuration.BadBookAction.Abort.GetDescription();
|
||||||
badBookRetryRb.Text = Configuration.BadBookAction.Retry.GetDescription();
|
badBookRetryRb.Text = Configuration.BadBookAction.Retry.GetDescription();
|
||||||
badBookIgnoreRb.Text = Configuration.BadBookAction.Ignore.GetDescription();
|
badBookIgnoreRb.Text = Configuration.BadBookAction.Ignore.GetDescription();
|
||||||
|
useCoverAsFolderIconCb.Text = desc(nameof(config.UseCoverAsFolderIcon));
|
||||||
|
|
||||||
inProgressSelectControl.SetDirectoryItems(new()
|
inProgressSelectControl.SetDirectoryItems(new()
|
||||||
{
|
{
|
||||||
@ -56,6 +57,7 @@ namespace LibationWinForms.Dialogs
|
|||||||
folderTemplateTb.Text = config.FolderTemplate;
|
folderTemplateTb.Text = config.FolderTemplate;
|
||||||
fileTemplateTb.Text = config.FileTemplate;
|
fileTemplateTb.Text = config.FileTemplate;
|
||||||
chapterFileTemplateTb.Text = config.ChapterFileTemplate;
|
chapterFileTemplateTb.Text = config.ChapterFileTemplate;
|
||||||
|
useCoverAsFolderIconCb.Checked = config.UseCoverAsFolderIcon;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Save_DownloadDecrypt(Configuration config)
|
private void Save_DownloadDecrypt(Configuration config)
|
||||||
@ -72,6 +74,7 @@ namespace LibationWinForms.Dialogs
|
|||||||
config.FolderTemplate = folderTemplateTb.Text;
|
config.FolderTemplate = folderTemplateTb.Text;
|
||||||
config.FileTemplate = fileTemplateTb.Text;
|
config.FileTemplate = fileTemplateTb.Text;
|
||||||
config.ChapterFileTemplate = chapterFileTemplateTb.Text;
|
config.ChapterFileTemplate = chapterFileTemplateTb.Text;
|
||||||
|
config.UseCoverAsFolderIcon = useCoverAsFolderIconCb.Checked;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,7 +44,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Autoupdater.NET.Official" Version="1.7.4" />
|
<PackageReference Include="Autoupdater.NET.Official" Version="1.7.4" />
|
||||||
<PackageReference Include="Dinah.Core.WindowsDesktop" Version="5.2.0.1" />
|
<PackageReference Include="Dinah.Core.WindowsDesktop" Version="5.2.1.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
using AppScaffolding.OSInterop;
|
using LibationFileManager;
|
||||||
|
|
||||||
namespace LinuxConfigApp
|
namespace LinuxConfigApp
|
||||||
{
|
{
|
||||||
@ -7,22 +7,7 @@ namespace LinuxConfigApp
|
|||||||
public LinuxInterop() { }
|
public LinuxInterop() { }
|
||||||
public LinuxInterop(params object[] values) { }
|
public LinuxInterop(params object[] values) { }
|
||||||
|
|
||||||
|
public void SetFolderIcon(string image, string directory) => throw new PlatformNotSupportedException();
|
||||||
// examples until the real interface is filled out
|
public void DeleteFolderIcon(string directory) => throw new PlatformNotSupportedException();
|
||||||
private string InitValue1 { get; }
|
|
||||||
private int InitValue2 { get; }
|
|
||||||
|
|
||||||
public LinuxInterop(string initValue1, int initValue2)
|
|
||||||
{
|
|
||||||
InitValue1 = initValue1;
|
|
||||||
InitValue2 = initValue2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string TransformInit1() => InitValue1.ToLower();
|
|
||||||
|
|
||||||
public int TransformInit2() => InitValue2 + InitValue2;
|
|
||||||
|
|
||||||
public void CopyTextToClipboard(string text) => throw new PlatformNotSupportedException();
|
|
||||||
public void ShowForm() => throw new PlatformNotSupportedException();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
using AppScaffolding.OSInterop;
|
using AppScaffolding;
|
||||||
|
|
||||||
namespace LinuxConfigApp
|
namespace LinuxConfigApp
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
using AppScaffolding.OSInterop;
|
using LibationFileManager;
|
||||||
|
|
||||||
namespace MacOSConfigApp
|
namespace MacOSConfigApp
|
||||||
{
|
{
|
||||||
@ -7,22 +7,7 @@ namespace MacOSConfigApp
|
|||||||
public MacOSInterop() { }
|
public MacOSInterop() { }
|
||||||
public MacOSInterop(params object[] values) { }
|
public MacOSInterop(params object[] values) { }
|
||||||
|
|
||||||
|
public void SetFolderIcon(string image, string directory) => throw new PlatformNotSupportedException();
|
||||||
// examples until the real interface is filled out
|
public void DeleteFolderIcon(string directory) => throw new PlatformNotSupportedException();
|
||||||
private string InitValue1 { get; }
|
|
||||||
private int InitValue2 { get; }
|
|
||||||
|
|
||||||
public MacOSInterop(string initValue1, int initValue2)
|
|
||||||
{
|
|
||||||
InitValue1 = initValue1;
|
|
||||||
InitValue2 = initValue2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string TransformInit1() => InitValue1.ToLower();
|
|
||||||
|
|
||||||
public int TransformInit2() => InitValue2 + InitValue2;
|
|
||||||
|
|
||||||
public void CopyTextToClipboard(string text) => throw new PlatformNotSupportedException();
|
|
||||||
public void ShowForm() => throw new PlatformNotSupportedException();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
using AppScaffolding.OSInterop;
|
using AppScaffolding;
|
||||||
|
|
||||||
namespace MacOSConfigApp
|
namespace MacOSConfigApp
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1 +1,7 @@
|
|||||||
Streamlined example is in \Source\_Demos\LoadByOS
|
Streamlined example is in \Source\_Demos\LoadByOS
|
||||||
|
|
||||||
|
MUST follow naming conventions in InteropFactory
|
||||||
|
|
||||||
|
Windows : Path.GetFileName(a).StartsWithInsensitive("win")
|
||||||
|
Linux : Path.GetFileName(a).StartsWithInsensitive("linux")
|
||||||
|
MacOs : Path.GetFileName(a).StartsWithInsensitive("mac") || a.StartsWithInsensitive("osx")
|
||||||
@ -1,4 +1,4 @@
|
|||||||
using AppScaffolding.OSInterop;
|
using AppScaffolding;
|
||||||
|
|
||||||
namespace WindowsConfigApp
|
namespace WindowsConfigApp
|
||||||
{
|
{
|
||||||
@ -9,6 +9,7 @@ namespace WindowsConfigApp
|
|||||||
{
|
{
|
||||||
typeof(Form1),
|
typeof(Form1),
|
||||||
typeof(Bitmap),
|
typeof(Bitmap),
|
||||||
|
typeof(Icon),
|
||||||
typeof(Accessibility.IAccIdentity),
|
typeof(Accessibility.IAccIdentity),
|
||||||
typeof(Microsoft.Win32.SystemEvents)
|
typeof(Microsoft.Win32.SystemEvents)
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,4 +1,11 @@
|
|||||||
using AppScaffolding.OSInterop;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Dinah.Core.WindowsDesktop;
|
||||||
|
using Dinah.Core.WindowsDesktop.Drawing;
|
||||||
|
using LibationFileManager;
|
||||||
|
|
||||||
namespace WindowsConfigApp
|
namespace WindowsConfigApp
|
||||||
{
|
{
|
||||||
@ -7,27 +14,26 @@ namespace WindowsConfigApp
|
|||||||
public WinInterop() { }
|
public WinInterop() { }
|
||||||
public WinInterop(params object[] values) { }
|
public WinInterop(params object[] values) { }
|
||||||
|
|
||||||
|
public void SetFolderIcon(string image, string directory)
|
||||||
// examples until the real interface is filled out
|
|
||||||
private string InitValue1 { get; }
|
|
||||||
private int InitValue2 { get; }
|
|
||||||
|
|
||||||
public WinInterop(string initValue1, int initValue2)
|
|
||||||
{
|
{
|
||||||
InitValue1 = initValue1;
|
string iconPath = null;
|
||||||
InitValue2 = initValue2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CopyTextToClipboard(string text) => Clipboard.SetDataObject(text, true, 5, 150);
|
try
|
||||||
|
|
||||||
public void ShowForm()
|
|
||||||
{
|
{
|
||||||
ApplicationConfiguration.Initialize();
|
var icon = ImageReader.ToIcon(image);
|
||||||
Application.Run(new Form1());
|
iconPath = Path.Combine(directory, $"{Guid.NewGuid()}.ico");
|
||||||
|
icon.Save(iconPath);
|
||||||
|
|
||||||
|
new DirectoryInfo(directory).SetIcon(iconPath, Directories.FolderTypes.Music);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (File.Exists(iconPath))
|
||||||
|
File.Delete(iconPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string TransformInit1() => InitValue1.ToUpper();
|
public void DeleteFolderIcon(string directory)
|
||||||
|
=> new DirectoryInfo(directory)?.DeleteIcon();
|
||||||
public int TransformInit2() => InitValue2 * InitValue2;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,10 @@
|
|||||||
<DebugType>embedded</DebugType>
|
<DebugType>embedded</DebugType>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Dinah.Core.WindowsDesktop" Version="5.2.1.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\AppScaffolding\AppScaffolding.csproj" />
|
<ProjectReference Include="..\..\AppScaffolding\AppScaffolding.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@ -18,7 +18,7 @@ namespace CrossPlatformClientExe
|
|||||||
public static Func<string, bool> MatchesOS { get; }
|
public static Func<string, bool> MatchesOS { get; }
|
||||||
= IsWindows ? a => Path.GetFileName(a).StartsWithInsensitive("win")
|
= IsWindows ? a => Path.GetFileName(a).StartsWithInsensitive("win")
|
||||||
: IsLinux ? a => Path.GetFileName(a).StartsWithInsensitive("linux")
|
: IsLinux ? a => Path.GetFileName(a).StartsWithInsensitive("linux")
|
||||||
: IsMacOs ? a => Path.GetFileName(a).StartsWithInsensitive("mac") || a.StartsWithInsensitive("osx")
|
: IsMacOs ? a => Path.GetFileName(a).StartsWithInsensitive("mac") || Path.GetFileName(a).StartsWithInsensitive("osx")
|
||||||
: _ => false;
|
: _ => false;
|
||||||
|
|
||||||
private IInteropFunctions InteropFunctions { get; } = new NullInteropFunctions();
|
private IInteropFunctions InteropFunctions { get; } = new NullInteropFunctions();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user