mirror of
https://github.com/rmcrackan/Libation.git
synced 2025-12-24 06:28:02 -05:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ad541c199 | ||
|
|
9606acda26 | ||
|
|
9abb9e376d | ||
|
|
f93498bfe3 | ||
|
|
a13e1f27bb | ||
|
|
c7c1b4505b | ||
|
|
d9e0f1aedf | ||
|
|
d8a0124b68 |
@@ -293,14 +293,14 @@ namespace AaxDecrypter
|
||||
|
||||
public bool Step3_Chapterize()
|
||||
{
|
||||
string str1 = "";
|
||||
var str1 = "";
|
||||
if (chapters.FirstChapterStart != 0.0)
|
||||
{
|
||||
str1 = " -ss " + chapters.FirstChapterStart.ToString("0.000", CultureInfo.InvariantCulture) + " -t " + (chapters.LastChapterStart - 1.0).ToString("0.000", CultureInfo.InvariantCulture) + " ";
|
||||
}
|
||||
|
||||
string ffmpegTags = tags.GenerateFfmpegTags();
|
||||
string ffmpegChapters = chapters.GenerateFfmpegChapters();
|
||||
var ffmpegTags = tags.GenerateFfmpegTags();
|
||||
var ffmpegChapters = chapters.GenerateFfmpegChapters();
|
||||
File.WriteAllText(ff_txt_file, ffmpegTags + ffmpegChapters);
|
||||
|
||||
var tagAndChapterInfo = new ProcessStartInfo
|
||||
|
||||
@@ -17,10 +17,10 @@ namespace AaxDecrypter
|
||||
|
||||
public Chapters(string file, double totalTime)
|
||||
{
|
||||
this.markers = getAAXChapters(file);
|
||||
markers = getAAXChapters(file);
|
||||
|
||||
// add end time
|
||||
this.markers.Add(totalTime);
|
||||
markers.Add(totalTime);
|
||||
}
|
||||
|
||||
private static List<double> getAAXChapters(string file)
|
||||
@@ -42,7 +42,7 @@ namespace AaxDecrypter
|
||||
}
|
||||
|
||||
// subtract 1 b/c end time marker is a real entry but isn't a real chapter
|
||||
public int Count() => this.markers.Count - 1;
|
||||
public int Count() => markers.Count - 1;
|
||||
|
||||
public string GetCuefromChapters(string fileName)
|
||||
{
|
||||
@@ -56,7 +56,7 @@ namespace AaxDecrypter
|
||||
{
|
||||
var chapter = i + 1;
|
||||
|
||||
var timeSpan = TimeSpan.FromSeconds(this.markers[i]);
|
||||
var timeSpan = TimeSpan.FromSeconds(markers[i]);
|
||||
var minutes = Math.Floor(timeSpan.TotalMinutes).ToString();
|
||||
var seconds = timeSpan.Seconds.ToString("D2");
|
||||
var milliseconds = (timeSpan.Milliseconds / 10).ToString("D2");
|
||||
@@ -78,8 +78,8 @@ namespace AaxDecrypter
|
||||
{
|
||||
var chapter = i + 1;
|
||||
|
||||
var start = this.markers[i] * 1000.0;
|
||||
var end = this.markers[i + 1] * 1000.0;
|
||||
var start = markers[i] * 1000.0;
|
||||
var end = markers[i + 1] * 1000.0;
|
||||
var chapterName = chapter.ToString("D3");
|
||||
|
||||
stringBuilder.Append("[CHAPTER]\n");
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using TagLib;
|
||||
using TagLib.Mpeg4;
|
||||
using Dinah.Core;
|
||||
@@ -23,52 +18,50 @@ namespace AaxDecrypter
|
||||
public string genre { get; }
|
||||
public TimeSpan duration { get; }
|
||||
|
||||
// input file
|
||||
public Tags(string file)
|
||||
{
|
||||
using TagLib.File tagLibFile = TagLib.File.Create(file, "audio/mp4", ReadStyle.Average);
|
||||
this.title = tagLibFile.Tag.Title.Replace(" (Unabridged)", "");
|
||||
this.album = tagLibFile.Tag.Album.Replace(" (Unabridged)", "");
|
||||
this.author = tagLibFile.Tag.FirstPerformer ?? "[unknown]";
|
||||
this.year = tagLibFile.Tag.Year.ToString();
|
||||
this.comments = tagLibFile.Tag.Comment;
|
||||
this.duration = tagLibFile.Properties.Duration;
|
||||
this.genre = tagLibFile.Tag.FirstGenre;
|
||||
using var tagLibFile = TagLib.File.Create(file, "audio/mp4", ReadStyle.Average);
|
||||
title = tagLibFile.Tag.Title.Replace(" (Unabridged)", "");
|
||||
album = tagLibFile.Tag.Album.Replace(" (Unabridged)", "");
|
||||
author = tagLibFile.Tag.FirstPerformer ?? "[unknown]";
|
||||
year = tagLibFile.Tag.Year.ToString();
|
||||
comments = tagLibFile.Tag.Comment ?? "";
|
||||
duration = tagLibFile.Properties.Duration;
|
||||
genre = tagLibFile.Tag.FirstGenre ?? "";
|
||||
|
||||
var tag = tagLibFile.GetTag(TagTypes.Apple, true);
|
||||
this.publisher = tag.Publisher;
|
||||
this.narrator = string.IsNullOrWhiteSpace(tagLibFile.Tag.FirstComposer) ? tag.Narrator : tagLibFile.Tag.FirstComposer;
|
||||
this.comments = !string.IsNullOrWhiteSpace(tag.LongDescription) ? tag.LongDescription : tag.Description;
|
||||
this.id = tag.AudibleCDEK;
|
||||
var tag = tagLibFile.GetTag(TagTypes.Apple, true);
|
||||
publisher = tag.Publisher ?? "";
|
||||
narrator = string.IsNullOrWhiteSpace(tagLibFile.Tag.FirstComposer) ? tag.Narrator : tagLibFile.Tag.FirstComposer;
|
||||
comments = !string.IsNullOrWhiteSpace(tag.LongDescription) ? tag.LongDescription : tag.Description;
|
||||
id = tag.AudibleCDEK;
|
||||
}
|
||||
|
||||
// my best guess of what this step is doing:
|
||||
// re-publish the data we read from the input file => output file
|
||||
public void AddAppleTags(string file)
|
||||
{
|
||||
using var file1 = TagLib.File.Create(file, "audio/mp4", ReadStyle.Average);
|
||||
var tag = (AppleTag)file1.GetTag(TagTypes.Apple, true);
|
||||
tag.Publisher = this.publisher;
|
||||
tag.LongDescription = this.comments;
|
||||
tag.Description = this.comments;
|
||||
file1.Save();
|
||||
using var tagLibFile = TagLib.File.Create(file, "audio/mp4", ReadStyle.Average);
|
||||
var tag = (AppleTag)tagLibFile.GetTag(TagTypes.Apple, true);
|
||||
tag.Publisher = publisher;
|
||||
tag.LongDescription = comments;
|
||||
tag.Description = comments;
|
||||
tagLibFile.Save();
|
||||
}
|
||||
|
||||
public string GenerateFfmpegTags()
|
||||
{
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
stringBuilder.Append(";FFMETADATA1\n");
|
||||
stringBuilder.Append("major_brand=aax\n");
|
||||
stringBuilder.Append("minor_version=1\n");
|
||||
stringBuilder.Append("compatible_brands=aax M4B mp42isom\n");
|
||||
stringBuilder.Append("date=" + this.year + "\n");
|
||||
stringBuilder.Append("genre=" + this.genre + "\n");
|
||||
stringBuilder.Append("title=" + this.title + "\n");
|
||||
stringBuilder.Append("artist=" + this.author + "\n");
|
||||
stringBuilder.Append("album=" + this.album + "\n");
|
||||
stringBuilder.Append("composer=" + this.narrator + "\n");
|
||||
stringBuilder.Append("comment=" + this.comments.Truncate(254) + "\n");
|
||||
stringBuilder.Append("description=" + this.comments + "\n");
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
=> $";FFMETADATA1"
|
||||
+ $"\nmajor_brand=aax"
|
||||
+ $"\nminor_version=1"
|
||||
+ $"\ncompatible_brands=aax M4B mp42isom"
|
||||
+ $"\ndate={year}"
|
||||
+ $"\ngenre={genre}"
|
||||
+ $"\ntitle={title}"
|
||||
+ $"\nartist={author}"
|
||||
+ $"\nalbum={album}"
|
||||
+ $"\ncomposer={narrator}"
|
||||
+ $"\ncomment={comments.Truncate(254)}"
|
||||
+ $"\ndescription={comments}"
|
||||
+ $"\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using DataLayer;
|
||||
using Dinah.Core;
|
||||
using Dinah.Core.ErrorHandling;
|
||||
using FileManager;
|
||||
|
||||
@@ -44,11 +45,17 @@ namespace FileLiberator
|
||||
tempAaxFilename,
|
||||
(p) => api.DownloadAaxWorkaroundAsync(libraryBook.Book.AudibleProductId, tempAaxFilename, p));
|
||||
|
||||
// if bad file download, a 0-33 byte file will be created
|
||||
System.Threading.Thread.Sleep(100);
|
||||
// if bad file download, a 0-33 byte file will be created
|
||||
// if service unavailable, a 52 byte string will be saved as file
|
||||
if (new FileInfo(actualFilePath).Length < 100)
|
||||
{
|
||||
var contents = File.ReadAllText(actualFilePath);
|
||||
File.Delete(actualFilePath);
|
||||
|
||||
var unavailable = "Content Delivery Companion Service is not available.";
|
||||
if (contents.StartsWithInsensitive(unavailable))
|
||||
throw new Exception(unavailable);
|
||||
throw new Exception("Error downloading file");
|
||||
}
|
||||
|
||||
|
||||
35
FileLiberator/UNTESTED/DownloadFile.cs
Normal file
35
FileLiberator/UNTESTED/DownloadFile.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Dinah.Core.Net.Http;
|
||||
|
||||
namespace FileLiberator
|
||||
{
|
||||
// frustratingly copy pasta from DownloadableBase and DownloadPdf
|
||||
public class DownloadFile : IDownloadable
|
||||
{
|
||||
public event EventHandler<string> DownloadBegin;
|
||||
public event EventHandler<DownloadProgress> DownloadProgressChanged;
|
||||
public event EventHandler<string> DownloadCompleted;
|
||||
|
||||
public async Task<string> PerformDownloadFileAsync(string downloadUrl, string proposedDownloadFilePath)
|
||||
{
|
||||
var client = new HttpClient();
|
||||
|
||||
var progress = new Progress<DownloadProgress>();
|
||||
progress.ProgressChanged += (_, e) => DownloadProgressChanged?.Invoke(this, e);
|
||||
|
||||
DownloadBegin?.Invoke(this, proposedDownloadFilePath);
|
||||
|
||||
try
|
||||
{
|
||||
var actualDownloadedFilePath = await client.DownloadFileAsync(downloadUrl, proposedDownloadFilePath, progress);
|
||||
return actualDownloadedFilePath;
|
||||
}
|
||||
finally
|
||||
{
|
||||
DownloadCompleted?.Invoke(this, proposedDownloadFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,6 @@ namespace FileLiberator
|
||||
=> !string.IsNullOrWhiteSpace(getdownloadUrl(libraryBook))
|
||||
&& !AudibleFileStorage.PDF.Exists(libraryBook.Book.AudibleProductId);
|
||||
|
||||
private static string getdownloadUrl(LibraryBook libraryBook)
|
||||
=> libraryBook?.Book?.Supplements?.FirstOrDefault()?.Url;
|
||||
|
||||
public override async Task<StatusHandler> ProcessItemAsync(LibraryBook libraryBook)
|
||||
{
|
||||
var proposedDownloadFilePath = getProposedDownloadFilePath(libraryBook);
|
||||
@@ -26,6 +23,11 @@ namespace FileLiberator
|
||||
return verifyDownload(libraryBook);
|
||||
}
|
||||
|
||||
private static StatusHandler verifyDownload(LibraryBook libraryBook)
|
||||
=> !AudibleFileStorage.PDF.Exists(libraryBook.Book.AudibleProductId)
|
||||
? new StatusHandler { "Downloaded PDF cannot be found" }
|
||||
: new StatusHandler();
|
||||
|
||||
private static string getProposedDownloadFilePath(LibraryBook libraryBook)
|
||||
{
|
||||
// if audio file exists, get it's dir. else return base Book dir
|
||||
@@ -39,15 +41,15 @@ namespace FileLiberator
|
||||
|
||||
private async Task downloadPdfAsync(LibraryBook libraryBook, string proposedDownloadFilePath)
|
||||
{
|
||||
var downloadUrl = getdownloadUrl(libraryBook);
|
||||
|
||||
var client = new HttpClient();
|
||||
var actualDownloadedFilePath = await PerformDownloadAsync(
|
||||
proposedDownloadFilePath,
|
||||
(p) => client.DownloadFileAsync(getdownloadUrl(libraryBook), proposedDownloadFilePath, p));
|
||||
(p) => client.DownloadFileAsync(downloadUrl, proposedDownloadFilePath, p));
|
||||
}
|
||||
|
||||
private static StatusHandler verifyDownload(LibraryBook libraryBook)
|
||||
=> !AudibleFileStorage.PDF.Exists(libraryBook.Book.AudibleProductId)
|
||||
? new StatusHandler { "Downloaded PDF cannot be found" }
|
||||
: new StatusHandler();
|
||||
private static string getdownloadUrl(LibraryBook libraryBook)
|
||||
=> libraryBook?.Book?.Supplements?.FirstOrDefault()?.Url;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ using Dinah.Core.Net.Http;
|
||||
|
||||
namespace FileLiberator
|
||||
{
|
||||
public abstract class DownloadableBase : IDownloadable
|
||||
public abstract class DownloadableBase : IDownloadableProcessable
|
||||
{
|
||||
public event EventHandler<LibraryBook> Begin;
|
||||
public event EventHandler<LibraryBook> Completed;
|
||||
|
||||
@@ -3,7 +3,7 @@ using Dinah.Core.Net.Http;
|
||||
|
||||
namespace FileLiberator
|
||||
{
|
||||
public interface IDownloadable : IProcessable
|
||||
public interface IDownloadable
|
||||
{
|
||||
event EventHandler<string> DownloadBegin;
|
||||
event EventHandler<DownloadProgress> DownloadProgressChanged;
|
||||
|
||||
4
FileLiberator/UNTESTED/IDownloadableProcessable.cs
Normal file
4
FileLiberator/UNTESTED/IDownloadableProcessable.cs
Normal file
@@ -0,0 +1,4 @@
|
||||
namespace FileLiberator
|
||||
{
|
||||
public interface IDownloadableProcessable : IDownloadable, IProcessable { }
|
||||
}
|
||||
@@ -64,7 +64,34 @@ namespace FileManager
|
||||
// set cache
|
||||
stringCache[propertyName] = newValue;
|
||||
|
||||
// set in file
|
||||
writeFile(propertyName, newValue);
|
||||
}
|
||||
|
||||
public void Set(string propertyName, object newValue)
|
||||
{
|
||||
// set cache
|
||||
objectCache[propertyName] = newValue;
|
||||
|
||||
var parsedNewValue = JToken.Parse(JsonConvert.SerializeObject(newValue));
|
||||
writeFile(propertyName, parsedNewValue);
|
||||
}
|
||||
|
||||
private void writeFile(string propertyName, JToken newValue)
|
||||
{
|
||||
try
|
||||
{
|
||||
var str = newValue?.ToString();
|
||||
var formattedValue
|
||||
= str is null ? "[null]"
|
||||
: string.IsNullOrEmpty(str) ? "[empty]"
|
||||
: string.IsNullOrWhiteSpace(str) ? $"[whitespace. Length={str.Length}]"
|
||||
: str.Length > 100 ? $"[Length={str.Length}] {str[0..50]}...{str[^50..^0]}"
|
||||
: str;
|
||||
Serilog.Log.Logger.Information($"Config changed. {propertyName}={formattedValue}");
|
||||
}
|
||||
catch { }
|
||||
|
||||
// write new setting to file
|
||||
lock (locker)
|
||||
{
|
||||
var jObject = readFile();
|
||||
@@ -73,20 +100,7 @@ namespace FileManager
|
||||
}
|
||||
}
|
||||
|
||||
public void Set(string propertyName, object newValue)
|
||||
{
|
||||
// set cache
|
||||
objectCache[propertyName] = newValue;
|
||||
|
||||
// set in file
|
||||
lock (locker)
|
||||
{
|
||||
var jObject = readFile();
|
||||
jObject[propertyName] = JToken.Parse(JsonConvert.SerializeObject(newValue));
|
||||
File.WriteAllText(Filepath, JsonConvert.SerializeObject(jObject, Formatting.Indented));
|
||||
}
|
||||
}
|
||||
|
||||
// special case: no caching. no logging
|
||||
public void SetWithJsonPath(string jsonPath, string propertyName, string newValue)
|
||||
{
|
||||
lock (locker)
|
||||
@@ -94,6 +108,7 @@ namespace FileManager
|
||||
var jObject = readFile();
|
||||
var token = jObject.SelectToken(jsonPath);
|
||||
var debug_oldValue = (string)token[propertyName];
|
||||
|
||||
token[propertyName] = newValue;
|
||||
File.WriteAllText(Filepath, JsonConvert.SerializeObject(jObject, Formatting.Indented));
|
||||
}
|
||||
|
||||
4
LibationLauncher/.msbump
Normal file
4
LibationLauncher/.msbump
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"//": "https://github.com/BalassaMarton/MSBump",
|
||||
BumpRevision: true
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
@@ -11,8 +12,18 @@
|
||||
<PublishReadyToRun>true</PublishReadyToRun>
|
||||
<!-- <PublishSingleFile>true</PublishSingleFile> -->
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
|
||||
<Version>3.1.3.1</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MSBump" Version="2.3.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Octokit" Version="0.36.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\LibationWinForms\LibationWinForms.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using Dinah.Core.Logging;
|
||||
using FileManager;
|
||||
using LibationWinForms;
|
||||
using LibationWinForms.Dialogs;
|
||||
@@ -21,7 +21,12 @@ namespace LibationLauncher
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
|
||||
createSettings();
|
||||
initLogging();
|
||||
|
||||
ensureLoggingConfig();
|
||||
ensureSerilogConfig();
|
||||
configureLogging();
|
||||
checkForUpdate();
|
||||
logStartupState();
|
||||
|
||||
Application.Run(new Form1());
|
||||
}
|
||||
@@ -71,47 +76,11 @@ namespace LibationLauncher
|
||||
&& !string.IsNullOrWhiteSpace(config.DownloadsInProgressEnum)
|
||||
&& !string.IsNullOrWhiteSpace(config.DecryptInProgressEnum);
|
||||
|
||||
private static void initLogging()
|
||||
private static string defaultLoggingLevel { get; } = "Information";
|
||||
private static void ensureLoggingConfig()
|
||||
{
|
||||
var config = Configuration.Instance;
|
||||
|
||||
ensureLoggingConfig(config);
|
||||
ensureSerilogConfig(config);
|
||||
|
||||
// override path. always use current libation files
|
||||
var logPath = Path.Combine(Configuration.Instance.LibationFiles, "Log.log");
|
||||
config.SetWithJsonPath("Serilog.WriteTo[1].Args", "path", logPath);
|
||||
|
||||
//// hack which achieves the same
|
||||
//configuration["Serilog:WriteTo:1:Args:path"] = logPath;
|
||||
|
||||
// CONFIGURATION-DRIVEN (json)
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddJsonFile(config.SettingsJsonPath)
|
||||
.Build();
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.ReadFrom.Configuration(configuration)
|
||||
.CreateLogger();
|
||||
|
||||
//// MANUAL HARD CODED
|
||||
//Log.Logger = new LoggerConfiguration()
|
||||
// .Enrich.WithCaller()
|
||||
// .MinimumLevel.Information()
|
||||
// .WriteTo.File(logPath,
|
||||
// rollingInterval: RollingInterval.Month,
|
||||
// outputTemplate: code_outputTemplate)
|
||||
// .CreateLogger();
|
||||
|
||||
Log.Logger.Information("Begin Libation");
|
||||
|
||||
// .Here() captures debug info via System.Runtime.CompilerServices attributes. Warning: expensive
|
||||
//var withLineNumbers_outputTemplate = "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message}{NewLine}in method {MemberName} at {FilePath}:{LineNumber}{NewLine}{Exception}{NewLine}";
|
||||
//Log.Logger.Here().Debug("Begin Libation. Debug with line numbers");
|
||||
}
|
||||
|
||||
private static string defaultLoggingLevel { get; } = "Information";
|
||||
private static void ensureLoggingConfig(Configuration config)
|
||||
{
|
||||
if (config.GetObject("Logging") != null)
|
||||
return;
|
||||
|
||||
@@ -129,8 +98,10 @@ namespace LibationLauncher
|
||||
config.SetObject("Logging", loggingObj);
|
||||
}
|
||||
|
||||
private static void ensureSerilogConfig(Configuration config)
|
||||
private static void ensureSerilogConfig()
|
||||
{
|
||||
var config = Configuration.Instance;
|
||||
|
||||
if (config.GetObject("Serilog") != null)
|
||||
return;
|
||||
|
||||
@@ -184,5 +155,101 @@ namespace LibationLauncher
|
||||
};
|
||||
config.SetObject("Serilog", serilogObj);
|
||||
}
|
||||
|
||||
private static void configureLogging()
|
||||
{
|
||||
var config = Configuration.Instance;
|
||||
|
||||
// override path. always use current libation files
|
||||
var logPath = Path.Combine(Configuration.Instance.LibationFiles, "Log.log");
|
||||
config.SetWithJsonPath("Serilog.WriteTo[1].Args", "path", logPath);
|
||||
|
||||
//// hack which achieves the same
|
||||
//configuration["Serilog:WriteTo:1:Args:path"] = logPath;
|
||||
|
||||
// CONFIGURATION-DRIVEN (json)
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddJsonFile(config.SettingsJsonPath)
|
||||
.Build();
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.ReadFrom.Configuration(configuration)
|
||||
.CreateLogger();
|
||||
|
||||
//// MANUAL HARD CODED
|
||||
//Log.Logger = new LoggerConfiguration()
|
||||
// // requires: using Dinah.Core.Logging;
|
||||
// .Enrich.WithCaller()
|
||||
// .MinimumLevel.Information()
|
||||
// .WriteTo.File(logPath,
|
||||
// rollingInterval: RollingInterval.Month,
|
||||
// outputTemplate: code_outputTemplate)
|
||||
// .CreateLogger();
|
||||
|
||||
// .Here() captures debug info via System.Runtime.CompilerServices attributes. Warning: expensive
|
||||
//var withLineNumbers_outputTemplate = "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message}{NewLine}in method {MemberName} at {FilePath}:{LineNumber}{NewLine}{Exception}{NewLine}";
|
||||
//Log.Logger.Here().Debug("Begin Libation. Debug with line numbers");
|
||||
}
|
||||
|
||||
private static void checkForUpdate()
|
||||
{
|
||||
try
|
||||
{
|
||||
var gitHubClient = new Octokit.GitHubClient(new Octokit.ProductHeaderValue("Libation"));
|
||||
|
||||
// https://octokitnet.readthedocs.io/en/latest/releases/
|
||||
var releases = gitHubClient.Repository.Release.GetAll("rmcrackan", "Libation").GetAwaiter().GetResult();
|
||||
var latest = releases.First(r => !r.Draft);
|
||||
|
||||
var latestVersionString = latest.TagName.Trim('v');
|
||||
if (!Version.TryParse(latestVersionString, out var latestRelease))
|
||||
return;
|
||||
|
||||
// we're up to date
|
||||
if (latestRelease <= BuildVersion)
|
||||
return;
|
||||
|
||||
// we have an update
|
||||
var zip = latest.Assets.FirstOrDefault(a => a.BrowserDownloadUrl.EndsWith(".zip"));
|
||||
var zipUrl = zip?.BrowserDownloadUrl;
|
||||
if (zipUrl is null)
|
||||
{
|
||||
MessageBox.Show(latest.HtmlUrl, "New version available");
|
||||
return;
|
||||
}
|
||||
|
||||
var result = MessageBox.Show($"New version available @ {latest.HtmlUrl}\r\nDownload the zip file?", "New version available", MessageBoxButtons.YesNo, MessageBoxIcon.Information);
|
||||
if (result != DialogResult.Yes)
|
||||
return;
|
||||
|
||||
using var fileSelector = new SaveFileDialog { FileName = zip.Name, Filter = "Zip Files (*.zip)|*.zip|All files (*.*)|*.*" };
|
||||
if (fileSelector.ShowDialog() != DialogResult.OK)
|
||||
return;
|
||||
var selectedPath = fileSelector.FileName;
|
||||
|
||||
try
|
||||
{
|
||||
LibationWinForms.BookLiberation.ProcessorAutomationController.DownloadFileAsync(zipUrl, selectedPath).GetAwaiter().GetResult();
|
||||
MessageBox.Show($"File downloaded");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"ERROR: {ex.Message}\r\n{ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Error checking for update. ERROR: {ex.Message}\r\n{ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void logStartupState()
|
||||
{
|
||||
Log.Logger.Information("Begin Libation");
|
||||
Log.Logger.Information($"Version: {BuildVersion}");
|
||||
Log.Logger.Information($"LibationFiles: {Configuration.Instance.LibationFiles}");
|
||||
Log.Logger.Information($"Audible locale: {Configuration.Instance.LocaleCountryCode}");
|
||||
}
|
||||
|
||||
private static Version BuildVersion => System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
|
||||
}
|
||||
}
|
||||
@@ -33,13 +33,13 @@ namespace LibationWinForms.BookLiberation
|
||||
}
|
||||
|
||||
#region timer
|
||||
Timer timer = new Timer { Interval = 1000 };
|
||||
private Timer timer { get; } = new Timer { Interval = 1000 };
|
||||
private void DownloadForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
timer.Tick += new EventHandler(timer_Tick);
|
||||
timer.Start();
|
||||
}
|
||||
DateTime lastDownloadProgress = DateTime.Now;
|
||||
private DateTime lastDownloadProgress = DateTime.Now;
|
||||
private void timer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
// if no update in the last 30 seconds, display frozen label
|
||||
|
||||
@@ -6,15 +6,28 @@ using FileLiberator;
|
||||
|
||||
namespace LibationWinForms.BookLiberation
|
||||
{
|
||||
// matches a file processor with a form
|
||||
public static class ProcessorAutomationController
|
||||
{
|
||||
//
|
||||
// these utility methods ensure proper wiring
|
||||
// 1) we can't forget to do it
|
||||
// 2) we can't accidentally do it mult times becaues we lost track of complexity
|
||||
//
|
||||
public static BackupBook GetWiredUpBackupBook(EventHandler<LibraryBook> completedAction = null)
|
||||
public static async Task BackupSingleBookAsync(string productId, EventHandler<LibraryBook> completedAction = null)
|
||||
{
|
||||
var backupBook = getWiredUpBackupBook(completedAction);
|
||||
|
||||
var automatedBackupsForm = attachToBackupsForm(backupBook);
|
||||
automatedBackupsForm.KeepGoingVisible = false;
|
||||
|
||||
await runSingleBackupAsync(backupBook, automatedBackupsForm, productId);
|
||||
}
|
||||
|
||||
public static async Task BackupAllBooksAsync(EventHandler<LibraryBook> completedAction = null)
|
||||
{
|
||||
var backupBook = getWiredUpBackupBook(completedAction);
|
||||
|
||||
var automatedBackupsForm = attachToBackupsForm(backupBook);
|
||||
|
||||
await runBackupLoopAsync(backupBook, automatedBackupsForm);
|
||||
}
|
||||
|
||||
private static BackupBook getWiredUpBackupBook(EventHandler<LibraryBook> completedAction)
|
||||
{
|
||||
var backupBook = new BackupBook();
|
||||
|
||||
@@ -32,7 +45,65 @@ namespace LibationWinForms.BookLiberation
|
||||
return backupBook;
|
||||
}
|
||||
|
||||
public static DownloadPdf GetWiredUpDownloadPdf(EventHandler<LibraryBook> completedAction = null)
|
||||
private static AutomatedBackupsForm attachToBackupsForm(BackupBook backupBook)
|
||||
{
|
||||
#region create form
|
||||
var automatedBackupsForm = new AutomatedBackupsForm();
|
||||
#endregion
|
||||
|
||||
#region define how model actions will affect form behavior
|
||||
void downloadBookBegin(object _, LibraryBook libraryBook) => automatedBackupsForm.AppendText($"Download Step, Begin: {libraryBook.Book}");
|
||||
void statusUpdate(object _, string str) => automatedBackupsForm.AppendText("- " + str);
|
||||
void downloadBookCompleted(object _, LibraryBook libraryBook) => automatedBackupsForm.AppendText($"Download Step, Completed: {libraryBook.Book}");
|
||||
void decryptBookBegin(object _, LibraryBook libraryBook) => automatedBackupsForm.AppendText($"Decrypt Step, Begin: {libraryBook.Book}");
|
||||
// extra line after book is completely finished
|
||||
void decryptBookCompleted(object _, LibraryBook libraryBook) => automatedBackupsForm.AppendText($"Decrypt Step, Completed: {libraryBook.Book}{Environment.NewLine}");
|
||||
void downloadPdfBegin(object _, LibraryBook libraryBook) => automatedBackupsForm.AppendText($"PDF Step, Begin: {libraryBook.Book}");
|
||||
// extra line after book is completely finished
|
||||
void downloadPdfCompleted(object _, LibraryBook libraryBook) => automatedBackupsForm.AppendText($"PDF Step, Completed: {libraryBook.Book}{Environment.NewLine}");
|
||||
#endregion
|
||||
|
||||
#region subscribe new form to model's events
|
||||
backupBook.DownloadBook.Begin += downloadBookBegin;
|
||||
backupBook.DownloadBook.StatusUpdate += statusUpdate;
|
||||
backupBook.DownloadBook.Completed += downloadBookCompleted;
|
||||
backupBook.DecryptBook.Begin += decryptBookBegin;
|
||||
backupBook.DecryptBook.StatusUpdate += statusUpdate;
|
||||
backupBook.DecryptBook.Completed += decryptBookCompleted;
|
||||
backupBook.DownloadPdf.Begin += downloadPdfBegin;
|
||||
backupBook.DownloadPdf.StatusUpdate += statusUpdate;
|
||||
backupBook.DownloadPdf.Completed += downloadPdfCompleted;
|
||||
#endregion
|
||||
|
||||
#region when form closes, unsubscribe from model's events
|
||||
// unsubscribe so disposed forms aren't still trying to receive notifications
|
||||
automatedBackupsForm.FormClosing += (_, __) =>
|
||||
{
|
||||
backupBook.DownloadBook.Begin -= downloadBookBegin;
|
||||
backupBook.DownloadBook.StatusUpdate -= statusUpdate;
|
||||
backupBook.DownloadBook.Completed -= downloadBookCompleted;
|
||||
backupBook.DecryptBook.Begin -= decryptBookBegin;
|
||||
backupBook.DecryptBook.StatusUpdate -= statusUpdate;
|
||||
backupBook.DecryptBook.Completed -= decryptBookCompleted;
|
||||
backupBook.DownloadPdf.Begin -= downloadPdfBegin;
|
||||
backupBook.DownloadPdf.StatusUpdate -= statusUpdate;
|
||||
backupBook.DownloadPdf.Completed -= downloadPdfCompleted;
|
||||
};
|
||||
#endregion
|
||||
|
||||
return automatedBackupsForm;
|
||||
}
|
||||
|
||||
public static async Task BackupAllPdfsAsync(EventHandler<LibraryBook> completedAction = null)
|
||||
{
|
||||
var downloadPdf = getWiredUpDownloadPdf(completedAction);
|
||||
|
||||
var automatedBackupsForm = attachToBackupsForm(downloadPdf);
|
||||
|
||||
await runBackupLoopAsync(downloadPdf, automatedBackupsForm);
|
||||
}
|
||||
|
||||
private static DownloadPdf getWiredUpDownloadPdf(EventHandler<LibraryBook> completedAction)
|
||||
{
|
||||
var downloadPdf = new DownloadPdf();
|
||||
|
||||
@@ -44,8 +115,25 @@ namespace LibationWinForms.BookLiberation
|
||||
return downloadPdf;
|
||||
}
|
||||
|
||||
public static async Task DownloadFileAsync(string url, string destination)
|
||||
{
|
||||
var downloadFile = new DownloadFile();
|
||||
|
||||
// frustratingly copy pasta from wireUpEvents(IDownloadable downloadable) due to Completed being EventHandler<LibraryBook>
|
||||
var downloadDialog = new DownloadForm();
|
||||
downloadFile.DownloadBegin += (_, str) =>
|
||||
{
|
||||
downloadDialog.UpdateFilename(str);
|
||||
downloadDialog.Show();
|
||||
};
|
||||
downloadFile.DownloadProgressChanged += (_, progress) => downloadDialog.DownloadProgressChanged(progress.BytesReceived, progress.TotalBytesToReceive.Value);
|
||||
downloadFile.DownloadCompleted += (_, __) => downloadDialog.Close();
|
||||
|
||||
await downloadFile.PerformDownloadFileAsync(url, destination);
|
||||
}
|
||||
|
||||
// subscribed to Begin event because a new form should be created+processed+closed on each iteration
|
||||
private static void wireUpEvents(IDownloadable downloadable)
|
||||
private static void wireUpEvents(IDownloadableProcessable downloadable)
|
||||
{
|
||||
#region create form
|
||||
var downloadDialog = new DownloadForm();
|
||||
@@ -156,13 +244,7 @@ namespace LibationWinForms.BookLiberation
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static async Task RunAutomaticDownloadAsync(IDownloadable downloadable)
|
||||
{
|
||||
AutomatedBackupsForm automatedBackupsForm = attachToBackupsForm(downloadable);
|
||||
await runBackupLoopAsync(downloadable, automatedBackupsForm);
|
||||
}
|
||||
|
||||
private static AutomatedBackupsForm attachToBackupsForm(IDownloadable downloadable)
|
||||
private static AutomatedBackupsForm attachToBackupsForm(IDownloadableProcessable downloadable)
|
||||
{
|
||||
#region create form
|
||||
var automatedBackupsForm = new AutomatedBackupsForm();
|
||||
@@ -194,68 +276,6 @@ namespace LibationWinForms.BookLiberation
|
||||
return automatedBackupsForm;
|
||||
}
|
||||
|
||||
public static async Task RunAutomaticBackupAsync(BackupBook backupBook)
|
||||
{
|
||||
var automatedBackupsForm = attachToBackupsForm(backupBook);
|
||||
await runBackupLoopAsync(backupBook, automatedBackupsForm);
|
||||
}
|
||||
|
||||
public static async Task RunSingleBackupAsync(BackupBook backupBook, string productId)
|
||||
{
|
||||
var automatedBackupsForm = attachToBackupsForm(backupBook);
|
||||
automatedBackupsForm.KeepGoingVisible = false;
|
||||
await runSingleBackupAsync(backupBook, automatedBackupsForm, productId);
|
||||
}
|
||||
|
||||
private static AutomatedBackupsForm attachToBackupsForm(BackupBook backupBook)
|
||||
{
|
||||
#region create form
|
||||
var automatedBackupsForm = new AutomatedBackupsForm();
|
||||
#endregion
|
||||
|
||||
#region define how model actions will affect form behavior
|
||||
void downloadBookBegin(object _, LibraryBook libraryBook) => automatedBackupsForm.AppendText($"Download Step, Begin: {libraryBook.Book}");
|
||||
void statusUpdate(object _, string str) => automatedBackupsForm.AppendText("- " + str);
|
||||
void downloadBookCompleted(object _, LibraryBook libraryBook) => automatedBackupsForm.AppendText($"Download Step, Completed: {libraryBook.Book}");
|
||||
void decryptBookBegin(object _, LibraryBook libraryBook) => automatedBackupsForm.AppendText($"Decrypt Step, Begin: {libraryBook.Book}");
|
||||
// extra line after book is completely finished
|
||||
void decryptBookCompleted(object _, LibraryBook libraryBook) => automatedBackupsForm.AppendText($"Decrypt Step, Completed: {libraryBook.Book}{Environment.NewLine}");
|
||||
void downloadPdfBegin(object _, LibraryBook libraryBook) => automatedBackupsForm.AppendText($"PDF Step, Begin: {libraryBook.Book}");
|
||||
// extra line after book is completely finished
|
||||
void downloadPdfCompleted(object _, LibraryBook libraryBook) => automatedBackupsForm.AppendText($"PDF Step, Completed: {libraryBook.Book}{Environment.NewLine}");
|
||||
#endregion
|
||||
|
||||
#region subscribe new form to model's events
|
||||
backupBook.DownloadBook.Begin += downloadBookBegin;
|
||||
backupBook.DownloadBook.StatusUpdate += statusUpdate;
|
||||
backupBook.DownloadBook.Completed += downloadBookCompleted;
|
||||
backupBook.DecryptBook.Begin += decryptBookBegin;
|
||||
backupBook.DecryptBook.StatusUpdate += statusUpdate;
|
||||
backupBook.DecryptBook.Completed += decryptBookCompleted;
|
||||
backupBook.DownloadPdf.Begin += downloadPdfBegin;
|
||||
backupBook.DownloadPdf.StatusUpdate += statusUpdate;
|
||||
backupBook.DownloadPdf.Completed += downloadPdfCompleted;
|
||||
#endregion
|
||||
|
||||
#region when form closes, unsubscribe from model's events
|
||||
// unsubscribe so disposed forms aren't still trying to receive notifications
|
||||
automatedBackupsForm.FormClosing += (_, __) =>
|
||||
{
|
||||
backupBook.DownloadBook.Begin -= downloadBookBegin;
|
||||
backupBook.DownloadBook.StatusUpdate -= statusUpdate;
|
||||
backupBook.DownloadBook.Completed -= downloadBookCompleted;
|
||||
backupBook.DecryptBook.Begin -= decryptBookBegin;
|
||||
backupBook.DecryptBook.StatusUpdate -= statusUpdate;
|
||||
backupBook.DecryptBook.Completed -= decryptBookCompleted;
|
||||
backupBook.DownloadPdf.Begin -= downloadPdfBegin;
|
||||
backupBook.DownloadPdf.StatusUpdate -= statusUpdate;
|
||||
backupBook.DownloadPdf.Completed -= downloadPdfCompleted;
|
||||
};
|
||||
#endregion
|
||||
|
||||
return automatedBackupsForm;
|
||||
}
|
||||
|
||||
// automated backups looper feels like a composible IProcessable: logic, UI, begin + process child + end
|
||||
// however the process step doesn't follow the pattern: Validate(product) + Process(product)
|
||||
private static async Task runBackupLoopAsync(IProcessable processable, AutomatedBackupsForm automatedBackupsForm)
|
||||
|
||||
@@ -40,13 +40,15 @@
|
||||
this.welcomeLbl.AutoSize = true;
|
||||
this.welcomeLbl.Location = new System.Drawing.Point(12, 9);
|
||||
this.welcomeLbl.Name = "welcomeLbl";
|
||||
this.welcomeLbl.Size = new System.Drawing.Size(399, 78);
|
||||
this.welcomeLbl.Size = new System.Drawing.Size(399, 117);
|
||||
this.welcomeLbl.TabIndex = 0;
|
||||
this.welcomeLbl.Text = resources.GetString("welcomeLbl.Text");
|
||||
//
|
||||
// noQuestionsBtn
|
||||
//
|
||||
this.noQuestionsBtn.Location = new System.Drawing.Point(15, 90);
|
||||
this.noQuestionsBtn.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.noQuestionsBtn.Location = new System.Drawing.Point(15, 129);
|
||||
this.noQuestionsBtn.Name = "noQuestionsBtn";
|
||||
this.noQuestionsBtn.Size = new System.Drawing.Size(396, 57);
|
||||
this.noQuestionsBtn.TabIndex = 1;
|
||||
@@ -55,7 +57,9 @@
|
||||
//
|
||||
// basicBtn
|
||||
//
|
||||
this.basicBtn.Location = new System.Drawing.Point(15, 153);
|
||||
this.basicBtn.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.basicBtn.Location = new System.Drawing.Point(15, 192);
|
||||
this.basicBtn.Name = "basicBtn";
|
||||
this.basicBtn.Size = new System.Drawing.Size(396, 57);
|
||||
this.basicBtn.TabIndex = 2;
|
||||
@@ -64,7 +68,9 @@
|
||||
//
|
||||
// advancedBtn
|
||||
//
|
||||
this.advancedBtn.Location = new System.Drawing.Point(15, 216);
|
||||
this.advancedBtn.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.advancedBtn.Location = new System.Drawing.Point(15, 255);
|
||||
this.advancedBtn.Name = "advancedBtn";
|
||||
this.advancedBtn.Size = new System.Drawing.Size(396, 57);
|
||||
this.advancedBtn.TabIndex = 3;
|
||||
@@ -75,7 +81,7 @@
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(423, 285);
|
||||
this.ClientSize = new System.Drawing.Size(423, 324);
|
||||
this.Controls.Add(this.advancedBtn);
|
||||
this.Controls.Add(this.basicBtn);
|
||||
this.Controls.Add(this.noQuestionsBtn);
|
||||
|
||||
@@ -123,6 +123,9 @@
|
||||
Please fill in a few settings. You can also change these settings later.
|
||||
|
||||
After you make your selections, get started by importing your library.
|
||||
Go to Import > Scan Library</value>
|
||||
Go to Import > Scan Library
|
||||
|
||||
Download your entire library from the "Liberate" tab or
|
||||
liberate your books one at a time by clicking the stoplight</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -49,12 +49,10 @@ namespace LibationWinForms
|
||||
// also applies filter. ONLY call AFTER loading grid
|
||||
loadInitialQuickFilterState();
|
||||
|
||||
{ // init bottom counts
|
||||
backupsCountsLbl.Text = "[Calculating backed up book quantities]";
|
||||
pdfsCountsLbl.Text = "[Calculating backed up PDFs]";
|
||||
|
||||
setBackupCounts(null, null);
|
||||
}
|
||||
// init bottom counts
|
||||
backupsCountsLbl.Text = "[Calculating backed up book quantities]";
|
||||
pdfsCountsLbl.Text = "[Calculating backed up PDFs]";
|
||||
setBackupCounts(null, null);
|
||||
}
|
||||
|
||||
#region reload grid
|
||||
@@ -112,7 +110,7 @@ namespace LibationWinForms
|
||||
enum AudioFileState { full, aax, none }
|
||||
private void setBookBackupCounts(IEnumerable<Book> books)
|
||||
{
|
||||
AudioFileState getAudioFileState(string productId)
|
||||
static AudioFileState getAudioFileState(string productId)
|
||||
{
|
||||
if (AudibleFileStorage.Audio.Exists(productId))
|
||||
return AudioFileState.full;
|
||||
@@ -142,6 +140,7 @@ namespace LibationWinForms
|
||||
= pending > 0
|
||||
? $"{pending} remaining"
|
||||
: "All books have been liberated";
|
||||
Serilog.Log.Logger.Information(menuItemText);
|
||||
menuStrip1.UIThread(() => beginBookBackupsToolStripMenuItem.Enabled = pending > 0);
|
||||
menuStrip1.UIThread(() => beginBookBackupsToolStripMenuItem.Text = string.Format(beginBookBackupsToolStripMenuItem_format, menuItemText));
|
||||
}
|
||||
@@ -167,6 +166,7 @@ namespace LibationWinForms
|
||||
= notDownloaded > 0
|
||||
? $"{notDownloaded} remaining"
|
||||
: "All PDFs have been downloaded";
|
||||
Serilog.Log.Logger.Information(menuItemText);
|
||||
menuStrip1.UIThread(() => beginPdfBackupsToolStripMenuItem.Enabled = notDownloaded > 0);
|
||||
menuStrip1.UIThread(() => beginPdfBackupsToolStripMenuItem.Text = string.Format(beginPdfBackupsToolStripMenuItem_format, menuItemText));
|
||||
}
|
||||
@@ -237,16 +237,10 @@ namespace LibationWinForms
|
||||
|
||||
#region liberate menu
|
||||
private async void beginBookBackupsToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
var backupBook = BookLiberation.ProcessorAutomationController.GetWiredUpBackupBook(updateGridRow);
|
||||
await BookLiberation.ProcessorAutomationController.RunAutomaticBackupAsync(backupBook);
|
||||
}
|
||||
=> await BookLiberation.ProcessorAutomationController.BackupAllBooksAsync(updateGridRow);
|
||||
|
||||
private async void beginPdfBackupsToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
var downloadPdf = BookLiberation.ProcessorAutomationController.GetWiredUpDownloadPdf(updateGridRow);
|
||||
await BookLiberation.ProcessorAutomationController.RunAutomaticDownloadAsync(downloadPdf);
|
||||
}
|
||||
=> await BookLiberation.ProcessorAutomationController.BackupAllPdfsAsync(updateGridRow);
|
||||
|
||||
private void updateGridRow(object _, LibraryBook libraryBook) => currProductsGrid.RefreshRow(libraryBook.Book.AudibleProductId);
|
||||
#endregion
|
||||
|
||||
@@ -174,18 +174,23 @@ namespace LibationWinForms
|
||||
|
||||
var productId = getGridEntry(e.RowIndex).GetBook().AudibleProductId;
|
||||
|
||||
// if liberated, open explorer to file
|
||||
// liberated: open explorer to file
|
||||
if (FileManager.AudibleFileStorage.Audio.Exists(productId))
|
||||
{
|
||||
var filePath = FileManager.AudibleFileStorage.Audio.GetPath(productId);
|
||||
System.Diagnostics.Process.Start("explorer.exe", $"/select, \"{filePath}\"");
|
||||
return;
|
||||
}
|
||||
// else liberate
|
||||
|
||||
// not liberated: liberate
|
||||
var msg
|
||||
= "Liberate entire library instead?"
|
||||
+ "\r\n\r\nClick Yes to begin liberating your entire library"
|
||||
+ "\r\n\r\nClick No to liberate this book only";
|
||||
if (MessageBox.Show(msg, "Liberate entire library?", MessageBoxButtons.YesNo) == DialogResult.Yes)
|
||||
await BookLiberation.ProcessorAutomationController.BackupAllBooksAsync((_, libraryBook) => RefreshRow(libraryBook.Book.AudibleProductId));
|
||||
else
|
||||
{
|
||||
var backupBook = BookLiberation.ProcessorAutomationController.GetWiredUpBackupBook((_, __) => RefreshRow(productId));
|
||||
await BookLiberation.ProcessorAutomationController.RunSingleBackupAsync(backupBook, productId);
|
||||
}
|
||||
await BookLiberation.ProcessorAutomationController.BackupSingleBookAsync(productId, (_, __) => RefreshRow(productId));
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
-- begin VERSIONING ---------------------------------------------------------------------------------------------------------------------
|
||||
https://github.com/rmcrackan/Libation/releases
|
||||
|
||||
v3.1.3 : fix weirdness with build number
|
||||
v3.1.2 : minor bug fixes
|
||||
v3.1.1 : Check if upgrade available on github
|
||||
v3.1.0 : FIRST PUBLIC RELEASE
|
||||
v3.1-beta.11 : Improved configuration and settings file management. Configurable logging
|
||||
v3.1-beta.10 : New feature: clicking Liberate button on a liberated item navigates to that audio file
|
||||
|
||||
16
WinFormsDesigner/Dialogs/SetupDialog.Designer.cs
generated
16
WinFormsDesigner/Dialogs/SetupDialog.Designer.cs
generated
@@ -40,13 +40,15 @@
|
||||
this.welcomeLbl.AutoSize = true;
|
||||
this.welcomeLbl.Location = new System.Drawing.Point(12, 9);
|
||||
this.welcomeLbl.Name = "welcomeLbl";
|
||||
this.welcomeLbl.Size = new System.Drawing.Size(399, 78);
|
||||
this.welcomeLbl.Size = new System.Drawing.Size(399, 117);
|
||||
this.welcomeLbl.TabIndex = 0;
|
||||
this.welcomeLbl.Text = resources.GetString("welcomeLbl.Text");
|
||||
//
|
||||
// noQuestionsBtn
|
||||
//
|
||||
this.noQuestionsBtn.Location = new System.Drawing.Point(15, 90);
|
||||
this.noQuestionsBtn.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.noQuestionsBtn.Location = new System.Drawing.Point(15, 129);
|
||||
this.noQuestionsBtn.Name = "noQuestionsBtn";
|
||||
this.noQuestionsBtn.Size = new System.Drawing.Size(396, 57);
|
||||
this.noQuestionsBtn.TabIndex = 1;
|
||||
@@ -55,7 +57,9 @@
|
||||
//
|
||||
// basicBtn
|
||||
//
|
||||
this.basicBtn.Location = new System.Drawing.Point(15, 153);
|
||||
this.basicBtn.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.basicBtn.Location = new System.Drawing.Point(15, 192);
|
||||
this.basicBtn.Name = "basicBtn";
|
||||
this.basicBtn.Size = new System.Drawing.Size(396, 57);
|
||||
this.basicBtn.TabIndex = 2;
|
||||
@@ -64,7 +68,9 @@
|
||||
//
|
||||
// advancedBtn
|
||||
//
|
||||
this.advancedBtn.Location = new System.Drawing.Point(15, 216);
|
||||
this.advancedBtn.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.advancedBtn.Location = new System.Drawing.Point(15, 255);
|
||||
this.advancedBtn.Name = "advancedBtn";
|
||||
this.advancedBtn.Size = new System.Drawing.Size(396, 57);
|
||||
this.advancedBtn.TabIndex = 3;
|
||||
@@ -75,7 +81,7 @@
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(423, 285);
|
||||
this.ClientSize = new System.Drawing.Size(423, 324);
|
||||
this.Controls.Add(this.advancedBtn);
|
||||
this.Controls.Add(this.basicBtn);
|
||||
this.Controls.Add(this.noQuestionsBtn);
|
||||
|
||||
@@ -123,6 +123,9 @@
|
||||
Please fill in a few settings. You can also change these settings later.
|
||||
|
||||
After you make your selections, get started by importing your library.
|
||||
Go to Import > Scan Library</value>
|
||||
Go to Import > Scan Library
|
||||
|
||||
Download your entire library from the "Liberate" tab or
|
||||
liberate your books one at a time by clicking the stoplight</value>
|
||||
</data>
|
||||
</root>
|
||||
Reference in New Issue
Block a user