mirror of
https://github.com/rmcrackan/Libation.git
synced 2026-08-07 04:42:17 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d25c32ff45 | ||
|
|
642a500f87 | ||
|
|
0e2469db64 | ||
|
|
9aa4ef70af | ||
|
|
1812fc2c7c | ||
|
|
f9849abb7b | ||
|
|
9cfe8ee6ca | ||
|
|
44e2cef18c | ||
|
|
4dc29affc3 | ||
|
|
2df38706f7 | ||
|
|
f30e9dae6f | ||
|
|
50843e5102 | ||
|
|
a13b00d520 | ||
|
|
b5ebe3db23 | ||
|
|
40f3e4503b | ||
|
|
0d93243b66 | ||
|
|
59c3845d21 | ||
|
|
a3ee3c2881 | ||
|
|
e971d34948 | ||
|
|
2b3f67fb99 | ||
|
|
4509b8c8eb | ||
|
|
2e40bebd7d | ||
|
|
dfc4121ab0 | ||
|
|
3648607d4d | ||
|
|
b22c35f841 | ||
|
|
2795690199 | ||
|
|
b1f92343cf | ||
|
|
9e1d657f60 | ||
|
|
389761355d | ||
|
|
69054afaa0 | ||
|
|
aacdcea1e1 | ||
|
|
0beb3bf437 | ||
|
|
e925b57f7f | ||
|
|
5deaa06d78 | ||
|
|
eda62975ba | ||
|
|
d91e02db29 | ||
|
|
cd604d03b1 | ||
|
|
d5cd569319 | ||
|
|
a58f51a8ce | ||
|
|
d24c10ddf5 | ||
|
|
a12391f0ab | ||
|
|
60f1d8117d | ||
|
|
20b6f28cb5 | ||
|
|
9a1fa89f6f | ||
|
|
2a294f4f85 | ||
|
|
0938c84929 | ||
|
|
99cc6a6425 | ||
|
|
0025825d5c | ||
|
|
81b6833118 | ||
|
|
a51e76d44d | ||
|
|
755a7338e9 | ||
|
|
56732a5365 | ||
|
|
dd3b032b21 | ||
|
|
dd25792864 | ||
|
|
6979ab4450 | ||
|
|
4b31207f91 | ||
|
|
84a847a838 | ||
|
|
6900a68b9d | ||
|
|
743644c4e9 | ||
|
|
e9e380dbe6 | ||
|
|
515dfceb73 | ||
|
|
3941906d72 | ||
|
|
6407d15fe0 | ||
|
|
be84fb317e | ||
|
|
3af010c1f5 | ||
|
|
714bb2ba50 | ||
|
|
2e5360f0ba | ||
|
|
258775ff3f | ||
|
|
82318ffab7 | ||
|
|
901572e7bb | ||
|
|
cfa938360a | ||
|
|
80017ce9fd | ||
|
|
c67972a327 | ||
|
|
57ee150d3c | ||
|
|
57302e1b5c | ||
|
|
09dbc67914 | ||
|
|
b768362eae | ||
|
|
04a32533cb | ||
|
|
1ad2135a3f | ||
|
|
643ae09b2b | ||
|
|
8391e43b03 | ||
|
|
8a54eda4a0 | ||
|
|
e0406378cb | ||
|
|
e1299331cc | ||
|
|
248b336867 | ||
|
|
b7d96ae447 | ||
|
|
8ab2af1c5d | ||
|
|
2d459bb2cf | ||
|
|
aeb0d2a82b | ||
|
|
f50dab94a4 | ||
|
|
efa5cefa23 | ||
|
|
2e4a97fde7 | ||
|
|
2f241806fa | ||
|
|
e417f60a36 | ||
|
|
b00f2bd908 | ||
|
|
220cda42e7 | ||
|
|
f992a7ec64 | ||
|
|
c54c45df33 | ||
|
|
a8b9e187e6 | ||
|
|
53f252e56f | ||
|
|
2827bc8904 | ||
|
|
98a775fc5a |
No files matched your search
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -276,13 +276,13 @@ namespace AaxDecrypter
|
|||||||
};
|
};
|
||||||
info.EnvironmentVariables["VARIABLE"] = decryptKey;
|
info.EnvironmentVariables["VARIABLE"] = decryptKey;
|
||||||
|
|
||||||
var (output, exitCode) = info.RunHidden();
|
var result = info.RunHidden();
|
||||||
|
|
||||||
// bad checksum -- bad decrypt key
|
// bad checksum -- bad decrypt key
|
||||||
if (output.Contains("checksums mismatch, aborting!"))
|
if (result.Output.Contains("checksums mismatch, aborting!"))
|
||||||
return -99;
|
return -99;
|
||||||
|
|
||||||
return exitCode;
|
return result.ExitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// temp file names for steps 3, 4, 5
|
// temp file names for steps 3, 4, 5
|
||||||
|
|||||||
@@ -21,8 +21,7 @@ namespace AaxDecrypter
|
|||||||
};
|
};
|
||||||
|
|
||||||
// checksum is in the debug info. ffprobe's debug info is written to stderr, not stdout
|
// checksum is in the debug info. ffprobe's debug info is written to stderr, not stdout
|
||||||
var readErrorOutput = true;
|
var ffprobeStderr = info.RunHidden().Error;
|
||||||
var ffprobeStderr = info.RunHidden(readErrorOutput).Output;
|
|
||||||
|
|
||||||
// example checksum line:
|
// example checksum line:
|
||||||
// ... [aax] file checksum == 0c527840c4f18517157eb0b4f9d6f9317ce60cd1
|
// ... [aax] file checksum == 0c527840c4f18517157eb0b4f9d6f9317ce60cd1
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="CsvHelper" Version="16.0.0" />
|
||||||
|
<PackageReference Include="NPOI" Version="2.5.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\audible api\AudibleApi\AudibleApiDTOs\AudibleApiDTOs.csproj" />
|
<ProjectReference Include="..\..\audible api\AudibleApi\AudibleApiDTOs\AudibleApiDTOs.csproj" />
|
||||||
<ProjectReference Include="..\..\audible api\AudibleApi\AudibleApi\AudibleApi.csproj" />
|
<ProjectReference Include="..\..\audible api\AudibleApi\AudibleApi\AudibleApi.csproj" />
|
||||||
|
|||||||
@@ -1,39 +1,103 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AudibleApi;
|
using AudibleApi;
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
|
using Dinah.Core;
|
||||||
using DtoImporterService;
|
using DtoImporterService;
|
||||||
using InternalUtilities;
|
using InternalUtilities;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
namespace ApplicationServices
|
namespace ApplicationServices
|
||||||
{
|
{
|
||||||
public static class LibraryCommands
|
public static class LibraryCommands
|
||||||
{
|
{
|
||||||
public static async Task<(int totalCount, int newCount)> ImportLibraryAsync(ILoginCallback callback)
|
public static async Task<(int totalCount, int newCount)> ImportAccountAsync(Func<Account, ILoginCallback> loginCallbackFactoryFunc, params Account[] accounts)
|
||||||
{
|
{
|
||||||
|
if (accounts is null || accounts.Length == 0)
|
||||||
|
return (0, 0);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var audibleApiActions = new AudibleApiActions();
|
var importItems = await scanAccountsAsync(loginCallbackFactoryFunc, accounts);
|
||||||
var items = await audibleApiActions.GetAllLibraryItemsAsync(callback);
|
|
||||||
var totalCount = items.Count;
|
|
||||||
Serilog.Log.Logger.Information($"GetAllLibraryItems: Total count {totalCount}");
|
|
||||||
|
|
||||||
using var context = DbContexts.GetContext();
|
var totalCount = importItems.Count;
|
||||||
var libImporter = new LibraryImporter(context);
|
Log.Logger.Information($"GetAllLibraryItems: Total count {totalCount}");
|
||||||
var newCount = await Task.Run(() => libImporter.Import(items));
|
|
||||||
context.SaveChanges();
|
var newCount = await importIntoDbAsync(importItems);
|
||||||
Serilog.Log.Logger.Information($"Import: New count {newCount}");
|
Log.Logger.Information($"Import: New count {newCount}");
|
||||||
|
|
||||||
await Task.Run(() => SearchEngineCommands.FullReIndex());
|
await Task.Run(() => SearchEngineCommands.FullReIndex());
|
||||||
Serilog.Log.Logger.Information("FullReIndex: success");
|
Log.Logger.Information("FullReIndex: success");
|
||||||
|
|
||||||
return (totalCount, newCount);
|
return (totalCount, newCount);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (AudibleApi.Authentication.LoginFailedException lfEx)
|
||||||
{
|
{
|
||||||
Serilog.Log.Logger.Error(ex, "Error importing library");
|
lfEx.MoveResponseBodyFile(FileManager.Configuration.Instance.LibationFiles);
|
||||||
|
|
||||||
|
// nuget Serilog.Exceptions would automatically log custom properties
|
||||||
|
// However, it comes with a scary warning when used with EntityFrameworkCore which I'm not yet ready to implement:
|
||||||
|
// https://github.com/RehanSaeed/Serilog.Exceptions
|
||||||
|
// work-around: use 3rd param. don't just put exception object in 3rd param -- info overload: stack trace, etc
|
||||||
|
Log.Logger.Error(lfEx, "Error importing library. Login failed. {@DebugInfo}", new {
|
||||||
|
lfEx.RequestUrl,
|
||||||
|
ResponseStatusCodeNumber = (int)lfEx.ResponseStatusCode,
|
||||||
|
ResponseStatusCodeDesc = lfEx.ResponseStatusCode,
|
||||||
|
lfEx.ResponseInputFields,
|
||||||
|
lfEx.ResponseBodyFilePath
|
||||||
|
});
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Logger.Error(ex, "Error importing library");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<List<ImportItem>> scanAccountsAsync(Func<Account, ILoginCallback> loginCallbackFactoryFunc, Account[] accounts)
|
||||||
|
{
|
||||||
|
var tasks = new List<Task<List<ImportItem>>>();
|
||||||
|
foreach (var account in accounts)
|
||||||
|
{
|
||||||
|
var callback = loginCallbackFactoryFunc(account);
|
||||||
|
|
||||||
|
// get APIs in serial, esp b/c of logins
|
||||||
|
var api = await AudibleApiActions.GetApiAsync(callback, account);
|
||||||
|
|
||||||
|
// add scanAccountAsync as a TASK: do not await
|
||||||
|
tasks.Add(scanAccountAsync(api, account));
|
||||||
|
}
|
||||||
|
|
||||||
|
// import library in parallel
|
||||||
|
var arrayOfLists = await Task.WhenAll(tasks);
|
||||||
|
var importItems = arrayOfLists.SelectMany(a => a).ToList();
|
||||||
|
return importItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<List<ImportItem>> scanAccountAsync(Api api, Account account)
|
||||||
|
{
|
||||||
|
ArgumentValidator.EnsureNotNull(account, nameof(account));
|
||||||
|
|
||||||
|
Log.Logger.Information("ImportLibraryAsync. {@DebugInfo}", new
|
||||||
|
{
|
||||||
|
Account = account?.MaskedLogEntry ?? "[null]"
|
||||||
|
});
|
||||||
|
|
||||||
|
var dtoItems = await AudibleApiActions.GetLibraryValidatedAsync(api);
|
||||||
|
return dtoItems.Select(d => new ImportItem { DtoItem = d, AccountId = account.AccountId, LocaleName = account.Locale?.Name }).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<int> importIntoDbAsync(List<ImportItem> importItems)
|
||||||
|
{
|
||||||
|
using var context = DbContexts.GetContext();
|
||||||
|
var libraryImporter = new LibraryImporter(context);
|
||||||
|
var newCount = await Task.Run(() => libraryImporter.Import(importItems));
|
||||||
|
context.SaveChanges();
|
||||||
|
|
||||||
|
return newCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int UpdateTags(this LibationContext context, Book book, string newTags)
|
public static int UpdateTags(this LibationContext context, Book book, string newTags)
|
||||||
@@ -51,7 +115,7 @@ namespace ApplicationServices
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Serilog.Log.Logger.Error(ex, "Error updating tags");
|
Log.Logger.Error(ex, "Error updating tags");
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,268 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using CsvHelper;
|
||||||
|
using CsvHelper.Configuration.Attributes;
|
||||||
|
using DataLayer;
|
||||||
|
using NPOI.XSSF.UserModel;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
|
namespace ApplicationServices
|
||||||
|
{
|
||||||
|
public class ExportDto
|
||||||
|
{
|
||||||
|
public static string GetName(string fieldName)
|
||||||
|
{
|
||||||
|
var property = typeof(ExportDto).GetProperty(fieldName);
|
||||||
|
var attribute = property.GetCustomAttributes(typeof(NameAttribute), true)[0];
|
||||||
|
var description = (NameAttribute)attribute;
|
||||||
|
var text = description.Names;
|
||||||
|
return text[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
[Name("Account")]
|
||||||
|
public string Account { get; set; }
|
||||||
|
|
||||||
|
[Name("Date Added to library")]
|
||||||
|
public DateTime DateAdded { get; set; }
|
||||||
|
|
||||||
|
[Name("Audible Product Id")]
|
||||||
|
public string AudibleProductId { get; set; }
|
||||||
|
|
||||||
|
[Name("Locale")]
|
||||||
|
public string Locale { get; set; }
|
||||||
|
|
||||||
|
[Name("Title")]
|
||||||
|
public string Title { get; set; }
|
||||||
|
|
||||||
|
[Name("Authors")]
|
||||||
|
public string AuthorNames { get; set; }
|
||||||
|
|
||||||
|
[Name("Narrators")]
|
||||||
|
public string NarratorNames { get; set; }
|
||||||
|
|
||||||
|
[Name("Length In Minutes")]
|
||||||
|
public int LengthInMinutes { get; set; }
|
||||||
|
|
||||||
|
[Name("Publisher")]
|
||||||
|
public string Publisher { get; set; }
|
||||||
|
|
||||||
|
[Name("Pdf url")]
|
||||||
|
public string PdfUrl { get; set; }
|
||||||
|
|
||||||
|
[Name("Series Names")]
|
||||||
|
public string SeriesNames { get; set; }
|
||||||
|
|
||||||
|
[Name("Series Order")]
|
||||||
|
public string SeriesOrder { get; set; }
|
||||||
|
|
||||||
|
[Name("Community Rating: Overall")]
|
||||||
|
public float? CommunityRatingOverall { get; set; }
|
||||||
|
|
||||||
|
[Name("Community Rating: Performance")]
|
||||||
|
public float? CommunityRatingPerformance { get; set; }
|
||||||
|
|
||||||
|
[Name("Community Rating: Story")]
|
||||||
|
public float? CommunityRatingStory { get; set; }
|
||||||
|
|
||||||
|
[Name("Cover Id")]
|
||||||
|
public string PictureId { get; set; }
|
||||||
|
|
||||||
|
[Name("Is Abridged?")]
|
||||||
|
public bool IsAbridged { get; set; }
|
||||||
|
|
||||||
|
[Name("Date Published")]
|
||||||
|
public DateTime? DatePublished { get; set; }
|
||||||
|
|
||||||
|
[Name("Categories")]
|
||||||
|
public string CategoriesNames { get; set; }
|
||||||
|
|
||||||
|
[Name("My Rating: Overall")]
|
||||||
|
public float? MyRatingOverall { get; set; }
|
||||||
|
|
||||||
|
[Name("My Rating: Performance")]
|
||||||
|
public float? MyRatingPerformance { get; set; }
|
||||||
|
|
||||||
|
[Name("My Rating: Story")]
|
||||||
|
public float? MyRatingStory { get; set; }
|
||||||
|
|
||||||
|
[Name("My Libation Tags")]
|
||||||
|
public string MyLibationTags { get; set; }
|
||||||
|
}
|
||||||
|
public static class LibToDtos
|
||||||
|
{
|
||||||
|
public static List<ExportDto> ToDtos(this IEnumerable<LibraryBook> library)
|
||||||
|
=> library.Select(a => new ExportDto
|
||||||
|
{
|
||||||
|
Account = a.Account,
|
||||||
|
DateAdded = a.DateAdded,
|
||||||
|
AudibleProductId = a.Book.AudibleProductId,
|
||||||
|
Locale = a.Book.Locale,
|
||||||
|
Title = a.Book.Title,
|
||||||
|
AuthorNames = a.Book.AuthorNames,
|
||||||
|
NarratorNames = a.Book.NarratorNames,
|
||||||
|
LengthInMinutes = a.Book.LengthInMinutes,
|
||||||
|
Publisher = a.Book.Publisher,
|
||||||
|
PdfUrl = a.Book.Supplements?.FirstOrDefault()?.Url,
|
||||||
|
SeriesNames = a.Book.SeriesNames,
|
||||||
|
SeriesOrder = a.Book.SeriesLink.Any() ? a.Book.SeriesLink?.Select(sl => $"{sl.Index} : {sl.Series.Name}").Aggregate((a, b) => $"{a}, {b}") : "",
|
||||||
|
CommunityRatingOverall = a.Book.Rating?.OverallRating,
|
||||||
|
CommunityRatingPerformance = a.Book.Rating?.PerformanceRating,
|
||||||
|
CommunityRatingStory = a.Book.Rating?.StoryRating,
|
||||||
|
PictureId = a.Book.PictureId,
|
||||||
|
IsAbridged = a.Book.IsAbridged,
|
||||||
|
DatePublished = a.Book.DatePublished,
|
||||||
|
CategoriesNames = a.Book.CategoriesNames.Any() ? a.Book.CategoriesNames.Aggregate((a, b) => $"{a}, {b}") : "",
|
||||||
|
MyRatingOverall = a.Book.UserDefinedItem.Rating.OverallRating,
|
||||||
|
MyRatingPerformance = a.Book.UserDefinedItem.Rating.PerformanceRating,
|
||||||
|
MyRatingStory = a.Book.UserDefinedItem.Rating.StoryRating,
|
||||||
|
MyLibationTags = a.Book.UserDefinedItem.Tags
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
public static class LibraryExporter
|
||||||
|
{
|
||||||
|
public static void ToCsv(string saveFilePath)
|
||||||
|
{
|
||||||
|
using var context = DbContexts.GetContext();
|
||||||
|
var dtos = context.GetLibrary_Flat_NoTracking().ToDtos();
|
||||||
|
|
||||||
|
if (!dtos.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
using var writer = new System.IO.StreamWriter(saveFilePath);
|
||||||
|
using var csv = new CsvWriter(writer, System.Globalization.CultureInfo.CurrentCulture);
|
||||||
|
|
||||||
|
csv.WriteHeader(typeof(ExportDto));
|
||||||
|
csv.NextRecord();
|
||||||
|
csv.WriteRecords(dtos);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ToJson(string saveFilePath)
|
||||||
|
{
|
||||||
|
using var context = DbContexts.GetContext();
|
||||||
|
var dtos = context.GetLibrary_Flat_NoTracking().ToDtos();
|
||||||
|
|
||||||
|
var json = Newtonsoft.Json.JsonConvert.SerializeObject(dtos, Newtonsoft.Json.Formatting.Indented);
|
||||||
|
System.IO.File.WriteAllText(saveFilePath, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ToXlsx(string saveFilePath)
|
||||||
|
{
|
||||||
|
using var context = DbContexts.GetContext();
|
||||||
|
var dtos = context.GetLibrary_Flat_NoTracking().ToDtos();
|
||||||
|
|
||||||
|
var workbook = new XSSFWorkbook();
|
||||||
|
var sheet = workbook.CreateSheet("Library");
|
||||||
|
|
||||||
|
var detailSubtotalFont = workbook.CreateFont();
|
||||||
|
detailSubtotalFont.IsBold = true;
|
||||||
|
|
||||||
|
var detailSubtotalCellStyle = workbook.CreateCellStyle();
|
||||||
|
detailSubtotalCellStyle.SetFont(detailSubtotalFont);
|
||||||
|
|
||||||
|
// headers
|
||||||
|
var rowIndex = 0;
|
||||||
|
var row = sheet.CreateRow(rowIndex);
|
||||||
|
|
||||||
|
var columns = new[] {
|
||||||
|
nameof (ExportDto.Account),
|
||||||
|
nameof (ExportDto.DateAdded),
|
||||||
|
nameof (ExportDto.AudibleProductId),
|
||||||
|
nameof (ExportDto.Locale),
|
||||||
|
nameof (ExportDto.Title),
|
||||||
|
nameof (ExportDto.AuthorNames),
|
||||||
|
nameof (ExportDto.NarratorNames),
|
||||||
|
nameof (ExportDto.LengthInMinutes),
|
||||||
|
nameof (ExportDto.Publisher),
|
||||||
|
nameof (ExportDto.PdfUrl),
|
||||||
|
nameof (ExportDto.SeriesNames),
|
||||||
|
nameof (ExportDto.SeriesOrder),
|
||||||
|
nameof (ExportDto.CommunityRatingOverall),
|
||||||
|
nameof (ExportDto.CommunityRatingPerformance),
|
||||||
|
nameof (ExportDto.CommunityRatingStory),
|
||||||
|
nameof (ExportDto.PictureId),
|
||||||
|
nameof (ExportDto.IsAbridged),
|
||||||
|
nameof (ExportDto.DatePublished),
|
||||||
|
nameof (ExportDto.CategoriesNames),
|
||||||
|
nameof (ExportDto.MyRatingOverall),
|
||||||
|
nameof (ExportDto.MyRatingPerformance),
|
||||||
|
nameof (ExportDto.MyRatingStory),
|
||||||
|
nameof (ExportDto.MyLibationTags)
|
||||||
|
};
|
||||||
|
var col = 0;
|
||||||
|
foreach (var c in columns)
|
||||||
|
{
|
||||||
|
var cell = row.CreateCell(col++);
|
||||||
|
var name = ExportDto.GetName(c);
|
||||||
|
cell.SetCellValue(name);
|
||||||
|
cell.CellStyle = detailSubtotalCellStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dateFormat = workbook.CreateDataFormat();
|
||||||
|
var dateStyle = workbook.CreateCellStyle();
|
||||||
|
dateStyle.DataFormat = dateFormat.GetFormat("MM/dd/yyyy HH:mm:ss");
|
||||||
|
|
||||||
|
rowIndex++;
|
||||||
|
|
||||||
|
// Add data rows
|
||||||
|
foreach (var dto in dtos)
|
||||||
|
{
|
||||||
|
col = 0;
|
||||||
|
|
||||||
|
row = sheet.CreateRow(rowIndex);
|
||||||
|
|
||||||
|
row.CreateCell(col++).SetCellValue(dto.Account);
|
||||||
|
|
||||||
|
var dateAddedCell = row.CreateCell(col++);
|
||||||
|
dateAddedCell.CellStyle = dateStyle;
|
||||||
|
dateAddedCell.SetCellValue(dto.DateAdded);
|
||||||
|
|
||||||
|
row.CreateCell(col++).SetCellValue(dto.AudibleProductId);
|
||||||
|
row.CreateCell(col++).SetCellValue(dto.Locale);
|
||||||
|
row.CreateCell(col++).SetCellValue(dto.Title);
|
||||||
|
row.CreateCell(col++).SetCellValue(dto.AuthorNames);
|
||||||
|
row.CreateCell(col++).SetCellValue(dto.NarratorNames);
|
||||||
|
row.CreateCell(col++).SetCellValue(dto.LengthInMinutes);
|
||||||
|
row.CreateCell(col++).SetCellValue(dto.Publisher);
|
||||||
|
row.CreateCell(col++).SetCellValue(dto.PdfUrl);
|
||||||
|
row.CreateCell(col++).SetCellValue(dto.SeriesNames);
|
||||||
|
row.CreateCell(col++).SetCellValue(dto.SeriesOrder);
|
||||||
|
|
||||||
|
col = createCell(row, col, dto.CommunityRatingOverall);
|
||||||
|
col = createCell(row, col, dto.CommunityRatingPerformance);
|
||||||
|
col = createCell(row, col, dto.CommunityRatingStory);
|
||||||
|
|
||||||
|
row.CreateCell(col++).SetCellValue(dto.PictureId);
|
||||||
|
row.CreateCell(col++).SetCellValue(dto.IsAbridged);
|
||||||
|
|
||||||
|
var datePubCell = row.CreateCell(col++);
|
||||||
|
datePubCell.CellStyle = dateStyle;
|
||||||
|
if (dto.DatePublished.HasValue)
|
||||||
|
datePubCell.SetCellValue(dto.DatePublished.Value);
|
||||||
|
else
|
||||||
|
datePubCell.SetCellValue("");
|
||||||
|
|
||||||
|
row.CreateCell(col++).SetCellValue(dto.CategoriesNames);
|
||||||
|
|
||||||
|
col = createCell(row, col, dto.MyRatingOverall);
|
||||||
|
col = createCell(row, col, dto.MyRatingPerformance);
|
||||||
|
col = createCell(row, col, dto.MyRatingStory);
|
||||||
|
|
||||||
|
row.CreateCell(col++).SetCellValue(dto.MyLibationTags);
|
||||||
|
|
||||||
|
rowIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var fileData = new System.IO.FileStream(saveFilePath, System.IO.FileMode.Create);
|
||||||
|
workbook.Write(fileData);
|
||||||
|
}
|
||||||
|
private static int createCell(NPOI.SS.UserModel.IRow row, int col, float? nullableFloat)
|
||||||
|
{
|
||||||
|
if (nullableFloat.HasValue)
|
||||||
|
row.CreateCell(col++).SetCellValue(nullableFloat.Value);
|
||||||
|
else
|
||||||
|
row.CreateCell(col++).SetCellValue("");
|
||||||
|
return col;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netcoreapp3.1;netstandard2.1</TargetFrameworks>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
@@ -12,13 +12,13 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.0">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.0">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.0" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.0" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.0">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.0">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
|||||||
@@ -0,0 +1,338 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using DataLayer;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
namespace DataLayer.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(LibationContext))]
|
||||||
|
[Migration("20200812152646_AddLocaleAndAccount")]
|
||||||
|
partial class AddLocaleAndAccount
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "3.1.7");
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.Book", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("BookId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("AudibleProductId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("CategoryId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DatePublished")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("IsAbridged")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("LengthInMinutes")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Locale")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PictureId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("BookId");
|
||||||
|
|
||||||
|
b.HasIndex("AudibleProductId");
|
||||||
|
|
||||||
|
b.HasIndex("CategoryId");
|
||||||
|
|
||||||
|
b.ToTable("Books");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.BookContributor", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("BookId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("ContributorId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Role")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<byte>("Order")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("BookId", "ContributorId", "Role");
|
||||||
|
|
||||||
|
b.HasIndex("BookId");
|
||||||
|
|
||||||
|
b.HasIndex("ContributorId");
|
||||||
|
|
||||||
|
b.ToTable("BookContributor");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.Category", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("CategoryId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("AudibleCategoryId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("ParentCategoryCategoryId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("CategoryId");
|
||||||
|
|
||||||
|
b.HasIndex("AudibleCategoryId");
|
||||||
|
|
||||||
|
b.HasIndex("ParentCategoryCategoryId");
|
||||||
|
|
||||||
|
b.ToTable("Categories");
|
||||||
|
|
||||||
|
b.HasData(
|
||||||
|
new
|
||||||
|
{
|
||||||
|
CategoryId = -1,
|
||||||
|
AudibleCategoryId = "",
|
||||||
|
Name = ""
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.Contributor", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("ContributorId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("AudibleContributorId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("ContributorId");
|
||||||
|
|
||||||
|
b.HasIndex("Name");
|
||||||
|
|
||||||
|
b.ToTable("Contributors");
|
||||||
|
|
||||||
|
b.HasData(
|
||||||
|
new
|
||||||
|
{
|
||||||
|
ContributorId = -1,
|
||||||
|
Name = ""
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.LibraryBook", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("BookId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Account")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("DateAdded")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("BookId");
|
||||||
|
|
||||||
|
b.ToTable("Library");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.Series", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("SeriesId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("AudibleSeriesId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("SeriesId");
|
||||||
|
|
||||||
|
b.HasIndex("AudibleSeriesId");
|
||||||
|
|
||||||
|
b.ToTable("Series");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.SeriesBook", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("SeriesId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("BookId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<float?>("Index")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b.HasKey("SeriesId", "BookId");
|
||||||
|
|
||||||
|
b.HasIndex("BookId");
|
||||||
|
|
||||||
|
b.HasIndex("SeriesId");
|
||||||
|
|
||||||
|
b.ToTable("SeriesBook");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.Book", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DataLayer.Category", "Category")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CategoryId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.OwnsOne("DataLayer.Rating", "Rating", b1 =>
|
||||||
|
{
|
||||||
|
b1.Property<int>("BookId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b1.Property<float>("OverallRating")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b1.Property<float>("PerformanceRating")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b1.Property<float>("StoryRating")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b1.HasKey("BookId");
|
||||||
|
|
||||||
|
b1.ToTable("Books");
|
||||||
|
|
||||||
|
b1.WithOwner()
|
||||||
|
.HasForeignKey("BookId");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.OwnsMany("DataLayer.Supplement", "Supplements", b1 =>
|
||||||
|
{
|
||||||
|
b1.Property<int>("SupplementId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b1.Property<int>("BookId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b1.Property<string>("Url")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b1.HasKey("SupplementId");
|
||||||
|
|
||||||
|
b1.HasIndex("BookId");
|
||||||
|
|
||||||
|
b1.ToTable("Supplement");
|
||||||
|
|
||||||
|
b1.WithOwner("Book")
|
||||||
|
.HasForeignKey("BookId");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.OwnsOne("DataLayer.UserDefinedItem", "UserDefinedItem", b1 =>
|
||||||
|
{
|
||||||
|
b1.Property<int>("BookId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b1.Property<string>("Tags")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b1.HasKey("BookId");
|
||||||
|
|
||||||
|
b1.ToTable("UserDefinedItem");
|
||||||
|
|
||||||
|
b1.WithOwner("Book")
|
||||||
|
.HasForeignKey("BookId");
|
||||||
|
|
||||||
|
b1.OwnsOne("DataLayer.Rating", "Rating", b2 =>
|
||||||
|
{
|
||||||
|
b2.Property<int>("UserDefinedItemBookId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b2.Property<float>("OverallRating")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b2.Property<float>("PerformanceRating")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b2.Property<float>("StoryRating")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b2.HasKey("UserDefinedItemBookId");
|
||||||
|
|
||||||
|
b2.ToTable("UserDefinedItem");
|
||||||
|
|
||||||
|
b2.WithOwner()
|
||||||
|
.HasForeignKey("UserDefinedItemBookId");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.BookContributor", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DataLayer.Book", "Book")
|
||||||
|
.WithMany("ContributorsLink")
|
||||||
|
.HasForeignKey("BookId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("DataLayer.Contributor", "Contributor")
|
||||||
|
.WithMany("BooksLink")
|
||||||
|
.HasForeignKey("ContributorId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.Category", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DataLayer.Category", "ParentCategory")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ParentCategoryCategoryId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.LibraryBook", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DataLayer.Book", "Book")
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey("DataLayer.LibraryBook", "BookId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DataLayer.SeriesBook", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DataLayer.Book", "Book")
|
||||||
|
.WithMany("SeriesLink")
|
||||||
|
.HasForeignKey("BookId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("DataLayer.Series", "Series")
|
||||||
|
.WithMany("BooksLink")
|
||||||
|
.HasForeignKey("SeriesId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
namespace DataLayer.Migrations
|
||||||
|
{
|
||||||
|
public partial class AddLocaleAndAccount : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "Account",
|
||||||
|
table: "Library",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "Locale",
|
||||||
|
table: "Books",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Account",
|
||||||
|
table: "Library");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Locale",
|
||||||
|
table: "Books");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@ namespace DataLayer.Migrations
|
|||||||
{
|
{
|
||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder
|
modelBuilder
|
||||||
.HasAnnotation("ProductVersion", "3.0.0");
|
.HasAnnotation("ProductVersion", "3.1.7");
|
||||||
|
|
||||||
modelBuilder.Entity("DataLayer.Book", b =>
|
modelBuilder.Entity("DataLayer.Book", b =>
|
||||||
{
|
{
|
||||||
@@ -40,6 +40,9 @@ namespace DataLayer.Migrations
|
|||||||
b.Property<int>("LengthInMinutes")
|
b.Property<int>("LengthInMinutes")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Locale")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<string>("PictureId")
|
b.Property<string>("PictureId")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
@@ -141,6 +144,9 @@ namespace DataLayer.Migrations
|
|||||||
b.Property<int>("BookId")
|
b.Property<int>("BookId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Account")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<DateTime>("DateAdded")
|
b.Property<DateTime>("DateAdded")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ namespace DataLayer
|
|||||||
public string Description { get; private set; }
|
public string Description { get; private set; }
|
||||||
public int LengthInMinutes { get; private set; }
|
public int LengthInMinutes { get; private set; }
|
||||||
|
|
||||||
|
// immutable-ish. should be immutable. mutability is necessary for v3 => v4 upgrades
|
||||||
|
public string Locale { get; private set; }
|
||||||
|
|
||||||
// mutable
|
// mutable
|
||||||
public string PictureId { get; set; }
|
public string PictureId { get; set; }
|
||||||
|
|
||||||
@@ -63,7 +66,7 @@ namespace DataLayer
|
|||||||
int lengthInMinutes,
|
int lengthInMinutes,
|
||||||
IEnumerable<Contributor> authors,
|
IEnumerable<Contributor> authors,
|
||||||
IEnumerable<Contributor> narrators,
|
IEnumerable<Contributor> narrators,
|
||||||
Category category)
|
Category category, string localeName)
|
||||||
{
|
{
|
||||||
// validate
|
// validate
|
||||||
ArgumentValidator.EnsureNotNull(audibleProductId, nameof(audibleProductId));
|
ArgumentValidator.EnsureNotNull(audibleProductId, nameof(audibleProductId));
|
||||||
@@ -72,6 +75,7 @@ namespace DataLayer
|
|||||||
|
|
||||||
// assign as soon as possible. stuff below relies on this
|
// assign as soon as possible. stuff below relies on this
|
||||||
AudibleProductId = productId;
|
AudibleProductId = productId;
|
||||||
|
Locale = localeName;
|
||||||
|
|
||||||
ArgumentValidator.EnsureNotNullOrWhiteSpace(title, nameof(title));
|
ArgumentValidator.EnsureNotNullOrWhiteSpace(title, nameof(title));
|
||||||
|
|
||||||
@@ -240,6 +244,10 @@ namespace DataLayer
|
|||||||
Category = category;
|
Category = category;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString() => $"[{AudibleProductId}] {Title}";
|
// needed for v3 => v4 upgrade
|
||||||
|
public void UpdateLocale(string localeName)
|
||||||
|
=> Locale ??= localeName;
|
||||||
|
|
||||||
|
public override string ToString() => $"[{AudibleProductId}] {Title}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,14 +10,24 @@ namespace DataLayer
|
|||||||
|
|
||||||
public DateTime DateAdded { get; private set; }
|
public DateTime DateAdded { get; private set; }
|
||||||
|
|
||||||
|
// immutable-ish. should be immutable. mutability is necessary for v3 => v4 upgrades
|
||||||
|
public string Account { get; private set; }
|
||||||
|
|
||||||
private LibraryBook() { }
|
private LibraryBook() { }
|
||||||
public LibraryBook(Book book, DateTime dateAdded)
|
public LibraryBook(Book book, DateTime dateAdded, string account)
|
||||||
{
|
{
|
||||||
ArgumentValidator.EnsureNotNull(book, nameof(book));
|
ArgumentValidator.EnsureNotNull(book, nameof(book));
|
||||||
|
ArgumentValidator.EnsureNotNull(account, nameof(account));
|
||||||
|
|
||||||
Book = book;
|
Book = book;
|
||||||
DateAdded = dateAdded;
|
DateAdded = dateAdded;
|
||||||
|
Account = account;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString() => $"{DateAdded:d} {Book}";
|
// needed for v3 => v4 upgrade
|
||||||
|
public void UpdateAccount(string account)
|
||||||
|
=> Account ??= account;
|
||||||
|
|
||||||
|
public override string ToString() => $"{DateAdded:d} {Book}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
HOW TO CREATE: EF CORE PROJECT
|
FOR QUICK MIGRATION INSTRUCTIONS:
|
||||||
|
_DB_NOTES.txt
|
||||||
|
|
||||||
|
|
||||||
|
HOW TO CREATE: EF CORE PROJECT
|
||||||
==============================
|
==============================
|
||||||
example is for sqlite but the same works with MsSql
|
example is for sqlite but the same works with MsSql
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -11,23 +11,23 @@ namespace DtoImporterService
|
|||||||
{
|
{
|
||||||
public BookImporter(LibationContext context) : base(context) { }
|
public BookImporter(LibationContext context) : base(context) { }
|
||||||
|
|
||||||
public override IEnumerable<Exception> Validate(IEnumerable<Item> items) => new BookValidator().Validate(items);
|
public override IEnumerable<Exception> Validate(IEnumerable<ImportItem> importItems) => new BookValidator().Validate(importItems.Select(i => i.DtoItem));
|
||||||
|
|
||||||
protected override int DoImport(IEnumerable<Item> items)
|
protected override int DoImport(IEnumerable<ImportItem> importItems)
|
||||||
{
|
{
|
||||||
// pre-req.s
|
// pre-req.s
|
||||||
new ContributorImporter(DbContext).Import(items);
|
new ContributorImporter(DbContext).Import(importItems);
|
||||||
new SeriesImporter(DbContext).Import(items);
|
new SeriesImporter(DbContext).Import(importItems);
|
||||||
new CategoryImporter(DbContext).Import(items);
|
new CategoryImporter(DbContext).Import(importItems);
|
||||||
|
|
||||||
// get distinct
|
// get distinct
|
||||||
var productIds = items.Select(i => i.ProductId).ToList();
|
var productIds = importItems.Select(i => i.DtoItem.ProductId).ToList();
|
||||||
|
|
||||||
// load db existing => .Local
|
// load db existing => .Local
|
||||||
loadLocal_books(productIds);
|
loadLocal_books(productIds);
|
||||||
|
|
||||||
// upsert
|
// upsert
|
||||||
var qtyNew = upsertBooks(items);
|
var qtyNew = upsertBooks(importItems);
|
||||||
return qtyNew;
|
return qtyNew;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,13 +44,13 @@ namespace DtoImporterService
|
|||||||
DbContext.Books.GetBooks(b => remainingProductIds.Contains(b.AudibleProductId)).ToList();
|
DbContext.Books.GetBooks(b => remainingProductIds.Contains(b.AudibleProductId)).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int upsertBooks(IEnumerable<Item> items)
|
private int upsertBooks(IEnumerable<ImportItem> importItems)
|
||||||
{
|
{
|
||||||
var qtyNew = 0;
|
var qtyNew = 0;
|
||||||
|
|
||||||
foreach (var item in items)
|
foreach (var item in importItems)
|
||||||
{
|
{
|
||||||
var book = DbContext.Books.Local.SingleOrDefault(p => p.AudibleProductId == item.ProductId);
|
var book = DbContext.Books.Local.SingleOrDefault(p => p.AudibleProductId == item.DtoItem.ProductId);
|
||||||
if (book is null)
|
if (book is null)
|
||||||
{
|
{
|
||||||
book = createNewBook(item);
|
book = createNewBook(item);
|
||||||
@@ -63,8 +63,10 @@ namespace DtoImporterService
|
|||||||
return qtyNew;
|
return qtyNew;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Book createNewBook(Item item)
|
private Book createNewBook(ImportItem importItem)
|
||||||
{
|
{
|
||||||
|
var item = importItem.DtoItem;
|
||||||
|
|
||||||
// absence of authors is very rare, but possible
|
// absence of authors is very rare, but possible
|
||||||
if (!item.Authors?.Any() ?? true)
|
if (!item.Authors?.Any() ?? true)
|
||||||
item.Authors = new[] { new Person { Name = "", Asin = null } };
|
item.Authors = new[] { new Person { Name = "", Asin = null } };
|
||||||
@@ -86,8 +88,16 @@ namespace DtoImporterService
|
|||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
// categories are laid out for a breadcrumb. category is 1st, subcategory is 2nd
|
// categories are laid out for a breadcrumb. category is 1st, subcategory is 2nd
|
||||||
// absence of categories is very rare, but possible
|
// absence of categories is also possible
|
||||||
var lastCategory = item.Categories.LastOrDefault()?.CategoryId ?? "";
|
|
||||||
|
// CATEGORY HACK: only use the 1st 2 categories
|
||||||
|
// (real impl: var lastCategory = item.Categories.LastOrDefault()?.CategoryId ?? "";)
|
||||||
|
var lastCategory
|
||||||
|
= item.Categories.Length == 0 ? ""
|
||||||
|
: item.Categories.Length == 1 ? item.Categories[0].CategoryId
|
||||||
|
// 2+
|
||||||
|
: item.Categories[1].CategoryId;
|
||||||
|
|
||||||
var category = DbContext.Categories.Local.SingleOrDefault(c => c.AudibleCategoryId == lastCategory);
|
var category = DbContext.Categories.Local.SingleOrDefault(c => c.AudibleCategoryId == lastCategory);
|
||||||
|
|
||||||
var book = DbContext.Books.Add(new Book(
|
var book = DbContext.Books.Add(new Book(
|
||||||
@@ -97,7 +107,8 @@ namespace DtoImporterService
|
|||||||
item.LengthInMinutes,
|
item.LengthInMinutes,
|
||||||
authors,
|
authors,
|
||||||
narrators,
|
narrators,
|
||||||
category)
|
category,
|
||||||
|
importItem.LocaleName)
|
||||||
).Entity;
|
).Entity;
|
||||||
|
|
||||||
var publisherName = item.Publisher;
|
var publisherName = item.Publisher;
|
||||||
@@ -115,12 +126,17 @@ namespace DtoImporterService
|
|||||||
return book;
|
return book;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateBook(Item item, Book book)
|
private void updateBook(ImportItem importItem, Book book)
|
||||||
{
|
{
|
||||||
|
var item = importItem.DtoItem;
|
||||||
|
|
||||||
// set/update book-specific info which may have changed
|
// set/update book-specific info which may have changed
|
||||||
book.PictureId = item.PictureId;
|
book.PictureId = item.PictureId;
|
||||||
book.UpdateProductRating(item.Product_OverallStars, item.Product_PerformanceStars, item.Product_StoryStars);
|
book.UpdateProductRating(item.Product_OverallStars, item.Product_PerformanceStars, item.Product_StoryStars);
|
||||||
|
|
||||||
|
// needed during v3 => v4 migration
|
||||||
|
book.UpdateLocale(importItem.LocaleName);
|
||||||
|
|
||||||
// important to update user-specific info. this will have changed if user has rated/reviewed the book since last library import
|
// important to update user-specific info. this will have changed if user has rated/reviewed the book since last library import
|
||||||
book.UserDefinedItem.UpdateRating(item.MyUserRating_Overall, item.MyUserRating_Performance, item.MyUserRating_Story);
|
book.UserDefinedItem.UpdateRating(item.MyUserRating_Overall, item.MyUserRating_Performance, item.MyUserRating_Story);
|
||||||
|
|
||||||
|
|||||||
@@ -11,18 +11,24 @@ namespace DtoImporterService
|
|||||||
{
|
{
|
||||||
public CategoryImporter(LibationContext context) : base(context) { }
|
public CategoryImporter(LibationContext context) : base(context) { }
|
||||||
|
|
||||||
public override IEnumerable<Exception> Validate(IEnumerable<Item> items) => new CategoryValidator().Validate(items);
|
public override IEnumerable<Exception> Validate(IEnumerable<ImportItem> importItems) => new CategoryValidator().Validate(importItems.Select(i => i.DtoItem));
|
||||||
|
|
||||||
protected override int DoImport(IEnumerable<Item> items)
|
protected override int DoImport(IEnumerable<ImportItem> importItems)
|
||||||
{
|
{
|
||||||
// get distinct
|
// get distinct
|
||||||
var categoryIds = items.GetCategoriesDistinct().Select(c => c.CategoryId).ToList();
|
var categoryIds = importItems
|
||||||
|
.Select(i => i.DtoItem)
|
||||||
|
.GetCategoriesDistinct()
|
||||||
|
.Select(c => c.CategoryId).ToList();
|
||||||
|
|
||||||
// load db existing => .Local
|
// load db existing => .Local
|
||||||
loadLocal_categories(categoryIds);
|
loadLocal_categories(categoryIds);
|
||||||
|
|
||||||
// upsert
|
// upsert
|
||||||
var categoryPairs = items.GetCategoryPairsDistinct().ToList();
|
var categoryPairs = importItems
|
||||||
|
.Select(i => i.DtoItem)
|
||||||
|
.GetCategoryPairsDistinct()
|
||||||
|
.ToList();
|
||||||
var qtyNew = upsertCategories(categoryPairs);
|
var qtyNew = upsertCategories(categoryPairs);
|
||||||
return qtyNew;
|
return qtyNew;
|
||||||
}
|
}
|
||||||
@@ -51,6 +57,10 @@ namespace DtoImporterService
|
|||||||
{
|
{
|
||||||
for (var i = 0; i < pair.Length; i++)
|
for (var i = 0; i < pair.Length; i++)
|
||||||
{
|
{
|
||||||
|
// CATEGORY HACK: not yet supported: depth beyond 0 and 1
|
||||||
|
if (i > 1)
|
||||||
|
break;
|
||||||
|
|
||||||
var id = pair[i].CategoryId;
|
var id = pair[i].CategoryId;
|
||||||
var name = pair[i].CategoryName;
|
var name = pair[i].CategoryName;
|
||||||
|
|
||||||
|
|||||||
@@ -11,14 +11,23 @@ namespace DtoImporterService
|
|||||||
{
|
{
|
||||||
public ContributorImporter(LibationContext context) : base(context) { }
|
public ContributorImporter(LibationContext context) : base(context) { }
|
||||||
|
|
||||||
public override IEnumerable<Exception> Validate(IEnumerable<Item> items) => new ContributorValidator().Validate(items);
|
public override IEnumerable<Exception> Validate(IEnumerable<ImportItem> importItems) => new ContributorValidator().Validate(importItems.Select(i => i.DtoItem));
|
||||||
|
|
||||||
protected override int DoImport(IEnumerable<Item> items)
|
protected override int DoImport(IEnumerable<ImportItem> importItems)
|
||||||
{
|
{
|
||||||
// get distinct
|
// get distinct
|
||||||
var authors = items.GetAuthorsDistinct().ToList();
|
var authors = importItems
|
||||||
var narrators = items.GetNarratorsDistinct().ToList();
|
.Select(i => i.DtoItem)
|
||||||
var publishers = items.GetPublishersDistinct().ToList();
|
.GetAuthorsDistinct()
|
||||||
|
.ToList();
|
||||||
|
var narrators = importItems
|
||||||
|
.Select(i => i.DtoItem)
|
||||||
|
.GetNarratorsDistinct()
|
||||||
|
.ToList();
|
||||||
|
var publishers = importItems
|
||||||
|
.Select(i => i.DtoItem)
|
||||||
|
.GetPublishersDistinct()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
// load db existing => .Local
|
// load db existing => .Local
|
||||||
var allNames = publishers
|
var allNames = publishers
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using AudibleApiDTOs;
|
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
|
using InternalUtilities;
|
||||||
|
|
||||||
namespace DtoImporterService
|
namespace DtoImporterService
|
||||||
{
|
{
|
||||||
@@ -11,7 +11,7 @@ namespace DtoImporterService
|
|||||||
{
|
{
|
||||||
protected LibationContext DbContext { get; }
|
protected LibationContext DbContext { get; }
|
||||||
|
|
||||||
public ImporterBase(LibationContext context)
|
protected ImporterBase(LibationContext context)
|
||||||
{
|
{
|
||||||
ArgumentValidator.EnsureNotNull(context, nameof(context));
|
ArgumentValidator.EnsureNotNull(context, nameof(context));
|
||||||
DbContext = context;
|
DbContext = context;
|
||||||
@@ -50,8 +50,8 @@ namespace DtoImporterService
|
|||||||
public abstract IEnumerable<Exception> Validate(T param);
|
public abstract IEnumerable<Exception> Validate(T param);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class ItemsImporterBase : ImporterBase<IEnumerable<Item>>
|
public abstract class ItemsImporterBase : ImporterBase<IEnumerable<ImportItem>>
|
||||||
{
|
{
|
||||||
public ItemsImporterBase(LibationContext context) : base(context) { }
|
protected ItemsImporterBase(LibationContext context) : base(context) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,29 +11,50 @@ namespace DtoImporterService
|
|||||||
{
|
{
|
||||||
public LibraryImporter(LibationContext context) : base(context) { }
|
public LibraryImporter(LibationContext context) : base(context) { }
|
||||||
|
|
||||||
public override IEnumerable<Exception> Validate(IEnumerable<Item> items) => new LibraryValidator().Validate(items);
|
public override IEnumerable<Exception> Validate(IEnumerable<ImportItem> importItems) => new LibraryValidator().Validate(importItems.Select(i => i.DtoItem));
|
||||||
|
|
||||||
protected override int DoImport(IEnumerable<Item> items)
|
protected override int DoImport(IEnumerable<ImportItem> importItems)
|
||||||
{
|
{
|
||||||
new BookImporter(DbContext).Import(items);
|
new BookImporter(DbContext).Import(importItems);
|
||||||
|
|
||||||
var qtyNew = upsertLibraryBooks(items);
|
var qtyNew = upsertLibraryBooks(importItems);
|
||||||
return qtyNew;
|
return qtyNew;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int upsertLibraryBooks(IEnumerable<Item> items)
|
private int upsertLibraryBooks(IEnumerable<ImportItem> importItems)
|
||||||
{
|
{
|
||||||
|
// technically, we should be able to have duplicate books from separate accounts.
|
||||||
|
// this would violate the current pk and would be difficult to deal with elsewhere:
|
||||||
|
// - what to show in the grid
|
||||||
|
// - which to consider liberated
|
||||||
|
//
|
||||||
|
// sqlite cannot alter pk. the work around is an extensive headache. it'll be fixed in pre .net5/efcore5
|
||||||
|
//
|
||||||
|
// currently, inserting LibraryBook will throw error if the same book is in multiple accounts for the same region.
|
||||||
|
//
|
||||||
|
// CURRENT SOLUTION: don't re-insert
|
||||||
|
|
||||||
var currentLibraryProductIds = DbContext.Library.Select(l => l.Book.AudibleProductId).ToList();
|
var currentLibraryProductIds = DbContext.Library.Select(l => l.Book.AudibleProductId).ToList();
|
||||||
var newItems = items.Where(dto => !currentLibraryProductIds.Contains(dto.ProductId)).ToList();
|
var newItems = importItems.Where(dto => !currentLibraryProductIds.Contains(dto.DtoItem.ProductId)).ToList();
|
||||||
|
|
||||||
foreach (var newItem in newItems)
|
foreach (var newItem in newItems)
|
||||||
{
|
{
|
||||||
var libraryBook = new LibraryBook(
|
var libraryBook = new LibraryBook(
|
||||||
DbContext.Books.Local.Single(b => b.AudibleProductId == newItem.ProductId),
|
DbContext.Books.Local.Single(b => b.AudibleProductId == newItem.DtoItem.ProductId),
|
||||||
newItem.DateAdded);
|
newItem.DtoItem.DateAdded,
|
||||||
|
newItem.AccountId);
|
||||||
DbContext.Library.Add(libraryBook);
|
DbContext.Library.Add(libraryBook);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// needed for v3 => v4 upgrade
|
||||||
|
var toUpdate = DbContext.Library.Where(l => l.Account == null);
|
||||||
|
foreach (var u in toUpdate)
|
||||||
|
{
|
||||||
|
var item = importItems.FirstOrDefault(ii => ii.DtoItem.ProductId == u.Book.AudibleProductId);
|
||||||
|
if (item != null)
|
||||||
|
u.UpdateAccount(item.AccountId);
|
||||||
|
}
|
||||||
|
|
||||||
var qtyNew = newItems.Count;
|
var qtyNew = newItems.Count;
|
||||||
return qtyNew;
|
return qtyNew;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,12 +11,15 @@ namespace DtoImporterService
|
|||||||
{
|
{
|
||||||
public SeriesImporter(LibationContext context) : base(context) { }
|
public SeriesImporter(LibationContext context) : base(context) { }
|
||||||
|
|
||||||
public override IEnumerable<Exception> Validate(IEnumerable<Item> items) => new SeriesValidator().Validate(items);
|
public override IEnumerable<Exception> Validate(IEnumerable<ImportItem> importItems) => new SeriesValidator().Validate(importItems.Select(i => i.DtoItem));
|
||||||
|
|
||||||
protected override int DoImport(IEnumerable<Item> items)
|
protected override int DoImport(IEnumerable<ImportItem> importItems)
|
||||||
{
|
{
|
||||||
// get distinct
|
// get distinct
|
||||||
var series = items.GetSeriesDistinct().ToList();
|
var series = importItems
|
||||||
|
.Select(i => i.DtoItem)
|
||||||
|
.GetSeriesDistinct()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
// load db existing => .Local
|
// load db existing => .Local
|
||||||
loadLocal_series(series);
|
loadLocal_series(series);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using DataLayer;
|
|||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
using Dinah.Core.ErrorHandling;
|
using Dinah.Core.ErrorHandling;
|
||||||
using FileManager;
|
using FileManager;
|
||||||
|
using InternalUtilities;
|
||||||
|
|
||||||
namespace FileLiberator
|
namespace FileLiberator
|
||||||
{
|
{
|
||||||
@@ -55,16 +56,27 @@ namespace FileLiberator
|
|||||||
if (AudibleFileStorage.Audio.Exists(libraryBook.Book.AudibleProductId))
|
if (AudibleFileStorage.Audio.Exists(libraryBook.Book.AudibleProductId))
|
||||||
return new StatusHandler { "Cannot find decrypt. Final audio file already exists" };
|
return new StatusHandler { "Cannot find decrypt. Final audio file already exists" };
|
||||||
|
|
||||||
var proposedOutputFile = Path.Combine(AudibleFileStorage.DecryptInProgress, $"[{libraryBook.Book.AudibleProductId}].m4b");
|
var outputAudioFilename = await aaxToM4bConverterDecrypt(aaxFilename, libraryBook);
|
||||||
var outputAudioFilename = await aaxToM4bConverterDecrypt(proposedOutputFile, aaxFilename);
|
|
||||||
|
|
||||||
// decrypt failed
|
// decrypt failed
|
||||||
if (outputAudioFilename == null)
|
if (outputAudioFilename == null)
|
||||||
return new StatusHandler { "Decrypt failed" };
|
return new StatusHandler { "Decrypt failed" };
|
||||||
|
|
||||||
moveFilesToBooksDir(libraryBook.Book, outputAudioFilename);
|
var destinationDir = moveFilesToBooksDir(libraryBook.Book, outputAudioFilename);
|
||||||
|
|
||||||
Dinah.Core.IO.FileExt.SafeDelete(aaxFilename);
|
var config = Configuration.Instance;
|
||||||
|
if (config.RetainAaxFiles)
|
||||||
|
{
|
||||||
|
var newAaxFilename = FileUtility.GetValidFilename(
|
||||||
|
destinationDir,
|
||||||
|
Path.GetFileNameWithoutExtension(aaxFilename),
|
||||||
|
"aax");
|
||||||
|
File.Move(aaxFilename, newAaxFilename);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Dinah.Core.IO.FileExt.SafeDelete(aaxFilename);
|
||||||
|
}
|
||||||
|
|
||||||
var statusHandler = new StatusHandler();
|
var statusHandler = new StatusHandler();
|
||||||
var finalAudioExists = AudibleFileStorage.Audio.Exists(libraryBook.Book.AudibleProductId);
|
var finalAudioExists = AudibleFileStorage.Audio.Exists(libraryBook.Book.AudibleProductId);
|
||||||
@@ -78,13 +90,19 @@ namespace FileLiberator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> aaxToM4bConverterDecrypt(string proposedOutputFile, string aaxFilename)
|
private async Task<string> aaxToM4bConverterDecrypt(string aaxFilename, LibraryBook libraryBook)
|
||||||
{
|
{
|
||||||
DecryptBegin?.Invoke(this, $"Begin decrypting {aaxFilename}");
|
DecryptBegin?.Invoke(this, $"Begin decrypting {aaxFilename}");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var converter = await AaxToM4bConverter.CreateAsync(aaxFilename, Configuration.Instance.DecryptKey);
|
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||||
|
|
||||||
|
var account = persister
|
||||||
|
.AccountsSettings
|
||||||
|
.GetAccount(libraryBook.Account, libraryBook.Book.Locale);
|
||||||
|
|
||||||
|
var converter = await AaxToM4bConverter.CreateAsync(aaxFilename, account.DecryptKey);
|
||||||
converter.AppName = "Libation";
|
converter.AppName = "Libation";
|
||||||
|
|
||||||
TitleDiscovered?.Invoke(this, converter.tags.title);
|
TitleDiscovered?.Invoke(this, converter.tags.title);
|
||||||
@@ -92,7 +110,8 @@ namespace FileLiberator
|
|||||||
NarratorsDiscovered?.Invoke(this, converter.tags.narrator);
|
NarratorsDiscovered?.Invoke(this, converter.tags.narrator);
|
||||||
CoverImageFilepathDiscovered?.Invoke(this, converter.coverBytes);
|
CoverImageFilepathDiscovered?.Invoke(this, converter.coverBytes);
|
||||||
|
|
||||||
// override default which was set in CreateAsync
|
// override default which was set in CreateAsync
|
||||||
|
var proposedOutputFile = Path.Combine(AudibleFileStorage.DecryptInProgress, $"[{libraryBook.Book.AudibleProductId}].m4b");
|
||||||
converter.SetOutputFilename(proposedOutputFile);
|
converter.SetOutputFilename(proposedOutputFile);
|
||||||
converter.DecryptProgressUpdate += (s, progress) => UpdateProgress?.Invoke(this, progress);
|
converter.DecryptProgressUpdate += (s, progress) => UpdateProgress?.Invoke(this, progress);
|
||||||
|
|
||||||
@@ -103,7 +122,7 @@ namespace FileLiberator
|
|||||||
if (!success)
|
if (!success)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
Configuration.Instance.DecryptKey = converter.decryptKey;
|
account.DecryptKey = converter.decryptKey;
|
||||||
|
|
||||||
return converter.outputFileName;
|
return converter.outputFileName;
|
||||||
}
|
}
|
||||||
@@ -113,12 +132,12 @@ namespace FileLiberator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void moveFilesToBooksDir(Book product, string outputAudioFilename)
|
private static string moveFilesToBooksDir(Book product, string outputAudioFilename)
|
||||||
{
|
{
|
||||||
// create final directory. move each file into it. MOVE AUDIO FILE LAST
|
// create final directory. move each file into it. MOVE AUDIO FILE LAST
|
||||||
// new dir: safetitle_limit50char + " [" + productId + "]"
|
// new dir: safetitle_limit50char + " [" + productId + "]"
|
||||||
|
|
||||||
var destinationDir = getDestDir(product);
|
var destinationDir = AudibleFileStorage.Audio.GetDestDir(product.Title, product.AudibleProductId);
|
||||||
Directory.CreateDirectory(destinationDir);
|
Directory.CreateDirectory(destinationDir);
|
||||||
|
|
||||||
var sortedFiles = getProductFilesSorted(product, outputAudioFilename);
|
var sortedFiles = getProductFilesSorted(product, outputAudioFilename);
|
||||||
@@ -135,19 +154,9 @@ namespace FileLiberator
|
|||||||
|
|
||||||
File.Move(f.FullName, dest);
|
File.Move(f.FullName, dest);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private static string getDestDir(Book product)
|
return destinationDir;
|
||||||
{
|
}
|
||||||
// to prevent the paths from getting too long, we don't need after the 1st ":" for the folder
|
|
||||||
var underscoreIndex = product.Title.IndexOf(':');
|
|
||||||
var titleDir
|
|
||||||
= underscoreIndex < 4
|
|
||||||
? product.Title
|
|
||||||
: product.Title.Substring(0, underscoreIndex);
|
|
||||||
var finalDir = FileUtility.GetValidFilename(AudibleFileStorage.BooksDirectory, titleDir, null, product.AudibleProductId);
|
|
||||||
return finalDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<FileInfo> getProductFilesSorted(Book product, string outputAudioFilename)
|
private static List<FileInfo> getProductFilesSorted(Book product, string outputAudioFilename)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using AudibleApi;
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
using Dinah.Core.ErrorHandling;
|
using Dinah.Core.ErrorHandling;
|
||||||
using FileManager;
|
using FileManager;
|
||||||
|
using InternalUtilities;
|
||||||
|
|
||||||
namespace FileLiberator
|
namespace FileLiberator
|
||||||
{
|
{
|
||||||
@@ -41,7 +43,9 @@ namespace FileLiberator
|
|||||||
|
|
||||||
private async Task<string> downloadBookAsync(LibraryBook libraryBook, string tempAaxFilename)
|
private async Task<string> downloadBookAsync(LibraryBook libraryBook, string tempAaxFilename)
|
||||||
{
|
{
|
||||||
var api = await AudibleApi.EzApiCreator.GetApiAsync(AudibleApiStorage.IdentityTokensFile);
|
validate(libraryBook);
|
||||||
|
|
||||||
|
var api = await GetApiAsync(libraryBook);
|
||||||
|
|
||||||
var actualFilePath = await PerformDownloadAsync(
|
var actualFilePath = await PerformDownloadAsync(
|
||||||
tempAaxFilename,
|
tempAaxFilename,
|
||||||
@@ -63,10 +67,12 @@ namespace FileLiberator
|
|||||||
: "Error downloading file";
|
: "Error downloading file";
|
||||||
|
|
||||||
var ex = new Exception(exMsg);
|
var ex = new Exception(exMsg);
|
||||||
Serilog.Log.Error(ex, "Download error {@DebugInfo}", new
|
Serilog.Log.Logger.Error(ex, "Download error {@DebugInfo}", new
|
||||||
{
|
{
|
||||||
libraryBook.Book.Title,
|
libraryBook.Book.Title,
|
||||||
libraryBook.Book.AudibleProductId,
|
libraryBook.Book.AudibleProductId,
|
||||||
|
libraryBook.Book.Locale,
|
||||||
|
Account = libraryBook.Account?.ToMask() ?? "[empty]",
|
||||||
tempAaxFilename,
|
tempAaxFilename,
|
||||||
actualFilePath,
|
actualFilePath,
|
||||||
length,
|
length,
|
||||||
@@ -75,6 +81,28 @@ namespace FileLiberator
|
|||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void validate(LibraryBook libraryBook)
|
||||||
|
{
|
||||||
|
string errorString(string field)
|
||||||
|
=> $"{errorTitle()}\r\nCannot download book. {field} is not known. Try re-importing the account which owns this book.";
|
||||||
|
|
||||||
|
string errorTitle()
|
||||||
|
{
|
||||||
|
var title
|
||||||
|
= (libraryBook.Book.Title.Length > 53)
|
||||||
|
? $"{libraryBook.Book.Title.Truncate(50)}..."
|
||||||
|
: libraryBook.Book.Title;
|
||||||
|
var errorBookTitle = $"{title} [{libraryBook.Book.AudibleProductId}]";
|
||||||
|
return errorBookTitle;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(libraryBook.Account))
|
||||||
|
throw new Exception(errorString("Account"));
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(libraryBook.Book.Locale))
|
||||||
|
throw new Exception(errorString("Locale"));
|
||||||
|
}
|
||||||
|
|
||||||
private void moveBook(LibraryBook libraryBook, string actualFilePath)
|
private void moveBook(LibraryBook libraryBook, string actualFilePath)
|
||||||
{
|
{
|
||||||
var newAaxFilename = FileUtility.GetValidFilename(
|
var newAaxFilename = FileUtility.GetValidFilename(
|
||||||
|
|||||||
@@ -31,17 +31,24 @@ namespace FileLiberator
|
|||||||
private static string getProposedDownloadFilePath(LibraryBook libraryBook)
|
private static string getProposedDownloadFilePath(LibraryBook libraryBook)
|
||||||
{
|
{
|
||||||
// if audio file exists, get it's dir. else return base Book dir
|
// if audio file exists, get it's dir. else return base Book dir
|
||||||
var destinationDir =
|
var existingPath = Path.GetDirectoryName(AudibleFileStorage.Audio.GetPath(libraryBook.Book.AudibleProductId));
|
||||||
// this is safe b/c GetDirectoryName(null) == null
|
var file = getdownloadUrl(libraryBook);
|
||||||
Path.GetDirectoryName(AudibleFileStorage.Audio.GetPath(libraryBook.Book.AudibleProductId))
|
|
||||||
?? AudibleFileStorage.PDF.StorageDirectory;
|
|
||||||
|
|
||||||
return Path.Combine(destinationDir, Path.GetFileName(getdownloadUrl(libraryBook)));
|
if (existingPath != null)
|
||||||
|
return Path.Combine(existingPath, Path.GetFileName(file));
|
||||||
|
|
||||||
|
var full = FileUtility.GetValidFilename(
|
||||||
|
AudibleFileStorage.PDF.StorageDirectory,
|
||||||
|
libraryBook.Book.Title,
|
||||||
|
Path.GetExtension(file),
|
||||||
|
libraryBook.Book.AudibleProductId);
|
||||||
|
return full;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task downloadPdfAsync(LibraryBook libraryBook, string proposedDownloadFilePath)
|
private async Task downloadPdfAsync(LibraryBook libraryBook, string proposedDownloadFilePath)
|
||||||
{
|
{
|
||||||
var downloadUrl = getdownloadUrl(libraryBook);
|
var api = await GetApiAsync(libraryBook);
|
||||||
|
var downloadUrl = await api.GetPdfDownloadLinkAsync(libraryBook.Book.AudibleProductId);
|
||||||
|
|
||||||
var client = new HttpClient();
|
var client = new HttpClient();
|
||||||
var actualDownloadedFilePath = await PerformDownloadAsync(
|
var actualDownloadedFilePath = await PerformDownloadAsync(
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ namespace FileLiberator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static Task<AudibleApi.Api> GetApiAsync(LibraryBook libraryBook)
|
||||||
|
=> InternalUtilities.AudibleApiActions.GetApiAsync(libraryBook.Account, libraryBook.Book.Locale);
|
||||||
|
|
||||||
protected async Task<string> PerformDownloadAsync(string proposedDownloadFilePath, Func<Progress<DownloadProgress>, Task<string>> func)
|
protected async Task<string> PerformDownloadAsync(string proposedDownloadFilePath, Func<Progress<DownloadProgress>, Task<string>> func)
|
||||||
{
|
{
|
||||||
var progress = new Progress<DownloadProgress>();
|
var progress = new Progress<DownloadProgress>();
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using ApplicationServices;
|
using ApplicationServices;
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
|
using Dinah.Core;
|
||||||
using Dinah.Core.ErrorHandling;
|
using Dinah.Core.ErrorHandling;
|
||||||
|
|
||||||
namespace FileLiberator
|
namespace FileLiberator
|
||||||
@@ -15,37 +17,44 @@ namespace FileLiberator
|
|||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
/// <summary>Process the first valid product. Create default context</summary>
|
// when used in foreach: stateful. deferred execution
|
||||||
/// <returns>Returns either the status handler from the process, or null if all books have been processed</returns>
|
public static IEnumerable<LibraryBook> GetValidLibraryBooks(this IProcessable processable)
|
||||||
public static async Task<StatusHandler> ProcessFirstValidAsync(this IProcessable processable)
|
=> DbContexts.GetContext()
|
||||||
{
|
.GetLibrary_Flat_NoTracking()
|
||||||
var libraryBook = processable.getNextValidBook();
|
.Where(libraryBook => processable.Validate(libraryBook));
|
||||||
if (libraryBook == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return await processBookAsync(processable, libraryBook);
|
public static LibraryBook GetSingleLibraryBook(string productId)
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Process the first valid product. Create default context</summary>
|
|
||||||
/// <returns>Returns either the status handler from the process, or null if all books have been processed</returns>
|
|
||||||
public static async Task<StatusHandler> ProcessSingleAsync(this IProcessable processable, string productId)
|
|
||||||
{
|
{
|
||||||
using var context = DbContexts.GetContext();
|
using var context = DbContexts.GetContext();
|
||||||
var libraryBook = context
|
var libraryBook = context
|
||||||
.Library
|
.Library
|
||||||
.GetLibrary()
|
.GetLibrary()
|
||||||
.SingleOrDefault(lb => lb.Book.AudibleProductId == productId);
|
.SingleOrDefault(lb => lb.Book.AudibleProductId == productId);
|
||||||
|
return libraryBook;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Process the first valid product. Create default context</summary>
|
||||||
|
/// <returns>Returns either the status handler from the process, or null if all books have been processed</returns>
|
||||||
|
public static async Task<StatusHandler> ProcessSingleAsync(this IProcessable processable, LibraryBook libraryBook)
|
||||||
|
{
|
||||||
if (libraryBook == null)
|
if (libraryBook == null)
|
||||||
return null;
|
return null;
|
||||||
if (!processable.Validate(libraryBook))
|
if (!processable.Validate(libraryBook))
|
||||||
return new StatusHandler { "Validation failed" };
|
return new StatusHandler { "Validation failed" };
|
||||||
|
|
||||||
return await processBookAsync(processable, libraryBook);
|
return await processable.ProcessBookAsync_NoValidation(libraryBook);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<StatusHandler> processBookAsync(IProcessable processable, LibraryBook libraryBook)
|
public static async Task<StatusHandler> ProcessBookAsync_NoValidation(this IProcessable processable, LibraryBook libraryBook)
|
||||||
{
|
{
|
||||||
|
Serilog.Log.Logger.Information("Begin " + nameof(ProcessBookAsync_NoValidation) + " {@DebugInfo}", new
|
||||||
|
{
|
||||||
|
libraryBook.Book.Title,
|
||||||
|
libraryBook.Book.AudibleProductId,
|
||||||
|
libraryBook.Book.Locale,
|
||||||
|
Account = libraryBook.Account?.ToMask() ?? "[empty]"
|
||||||
|
});
|
||||||
|
|
||||||
// this should never happen. check anyway. ProcessFirstValidAsync returning null is the signal that we're done. we can't let another IProcessable accidentally send this command
|
// this should never happen. check anyway. ProcessFirstValidAsync returning null is the signal that we're done. we can't let another IProcessable accidentally send this command
|
||||||
var status = await processable.ProcessAsync(libraryBook);
|
var status = await processable.ProcessAsync(libraryBook);
|
||||||
if (status == null)
|
if (status == null)
|
||||||
@@ -54,17 +63,6 @@ namespace FileLiberator
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LibraryBook getNextValidBook(this IProcessable processable)
|
|
||||||
{
|
|
||||||
var libraryBooks = DbContexts.GetContext().GetLibrary_Flat_NoTracking();
|
|
||||||
|
|
||||||
foreach (var libraryBook in libraryBooks)
|
|
||||||
if (processable.Validate(libraryBook))
|
|
||||||
return libraryBook;
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<StatusHandler> TryProcessAsync(this IProcessable processable, LibraryBook libraryBook)
|
public static async Task<StatusHandler> TryProcessAsync(this IProcessable processable, LibraryBook libraryBook)
|
||||||
=> processable.Validate(libraryBook)
|
=> processable.Validate(libraryBook)
|
||||||
? await processable.ProcessAsync(libraryBook)
|
? await processable.ProcessAsync(libraryBook)
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Polly" Version="7.2.0" />
|
<PackageReference Include="Polly" Version="7.2.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace FileManager
|
|
||||||
{
|
|
||||||
public static class AudibleApiStorage
|
|
||||||
{
|
|
||||||
// not customizable. don't move to config
|
|
||||||
public static string IdentityTokensFile => Path.Combine(Configuration.Instance.LibationFiles, "IdentityTokens.json");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,76 +11,77 @@ namespace FileManager
|
|||||||
// could add images here, but for now images are stored in a well-known location
|
// could add images here, but for now images are stored in a well-known location
|
||||||
public enum FileType { Unknown, Audio, AAX, PDF }
|
public enum FileType { Unknown, Audio, AAX, PDF }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Files are large. File contents are never read by app.
|
/// Files are large. File contents are never read by app.
|
||||||
/// Paths are varied.
|
/// Paths are varied.
|
||||||
/// Files are written during download/decrypt/backup/liberate.
|
/// Files are written during download/decrypt/backup/liberate.
|
||||||
/// Paths are read at app launch and during download/decrypt/backup/liberate.
|
/// Paths are read at app launch and during download/decrypt/backup/liberate.
|
||||||
/// Many files are often looked up at once
|
/// Many files are often looked up at once
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class AudibleFileStorage : Enumeration<AudibleFileStorage>
|
public abstract class AudibleFileStorage : Enumeration<AudibleFileStorage>
|
||||||
{
|
{
|
||||||
#region static
|
public abstract string[] Extensions { get; }
|
||||||
public static AudibleFileStorage Audio { get; }
|
public abstract string StorageDirectory { get; }
|
||||||
public static AudibleFileStorage AAX { get; }
|
|
||||||
public static AudibleFileStorage PDF { get; }
|
|
||||||
|
|
||||||
public static string DownloadsInProgress { get; }
|
#region static
|
||||||
public static string DecryptInProgress { get; }
|
public static AudioFileStorage Audio { get; } = new AudioFileStorage();
|
||||||
public static string BooksDirectory => Configuration.Instance.Books;
|
public static AudibleFileStorage AAX { get; } = new AaxFileStorage();
|
||||||
// not customizable. don't move to config
|
public static AudibleFileStorage PDF { get; } = new PdfFileStorage();
|
||||||
public static string DownloadsFinal { get; }
|
|
||||||
= new DirectoryInfo(Configuration.Instance.LibationFiles).CreateSubdirectory("DownloadsFinal").FullName;
|
|
||||||
|
|
||||||
static AudibleFileStorage()
|
public static string DownloadsInProgress
|
||||||
{
|
{
|
||||||
#region init DecryptInProgress
|
get
|
||||||
if (!Configuration.Instance.DecryptInProgressEnum.In("WinTemp", "LibationFiles"))
|
{
|
||||||
Configuration.Instance.DecryptInProgressEnum = "WinTemp";
|
if (!Configuration.Instance.DownloadsInProgressEnum.In("WinTemp", "LibationFiles"))
|
||||||
var M4bRootDir
|
Configuration.Instance.DownloadsInProgressEnum = "WinTemp";
|
||||||
= Configuration.Instance.DecryptInProgressEnum == "WinTemp" // else "LibationFiles"
|
var AaxRootDir
|
||||||
? Configuration.WinTemp
|
= Configuration.Instance.DownloadsInProgressEnum == "WinTemp"
|
||||||
: Configuration.Instance.LibationFiles;
|
? Configuration.WinTemp
|
||||||
DecryptInProgress = Path.Combine(M4bRootDir, "DecryptInProgress");
|
: Configuration.Instance.LibationFiles;
|
||||||
Directory.CreateDirectory(DecryptInProgress);
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region init DownloadsInProgress
|
return Directory.CreateDirectory(Path.Combine(AaxRootDir, "DownloadsInProgress")).FullName;
|
||||||
if (!Configuration.Instance.DownloadsInProgressEnum.In("WinTemp", "LibationFiles"))
|
}
|
||||||
Configuration.Instance.DownloadsInProgressEnum = "WinTemp";
|
}
|
||||||
var AaxRootDir
|
|
||||||
= Configuration.Instance.DownloadsInProgressEnum == "WinTemp" // else "LibationFiles"
|
|
||||||
? Configuration.WinTemp
|
|
||||||
: Configuration.Instance.LibationFiles;
|
|
||||||
DownloadsInProgress = Path.Combine(AaxRootDir, "DownloadsInProgress");
|
|
||||||
Directory.CreateDirectory(DownloadsInProgress);
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region init BooksDirectory
|
public static string DecryptInProgress
|
||||||
if (string.IsNullOrWhiteSpace(Configuration.Instance.Books))
|
{
|
||||||
Configuration.Instance.Books = Path.Combine(Configuration.Instance.LibationFiles, "Books");
|
get
|
||||||
Directory.CreateDirectory(Configuration.Instance.Books);
|
{
|
||||||
#endregion
|
if (!Configuration.Instance.DecryptInProgressEnum.In("WinTemp", "LibationFiles"))
|
||||||
|
Configuration.Instance.DecryptInProgressEnum = "WinTemp";
|
||||||
|
|
||||||
// must do this in static ctor, not w/inline properties
|
var M4bRootDir
|
||||||
// static properties init before static ctor so these dir.s would still be null
|
= Configuration.Instance.DecryptInProgressEnum == "WinTemp"
|
||||||
Audio = new AudibleFileStorage(FileType.Audio, BooksDirectory, "m4b", "mp3", "aac", "mp4", "m4a", "ogg", "flac");
|
? Configuration.WinTemp
|
||||||
AAX = new AudibleFileStorage(FileType.AAX, DownloadsFinal, "aax");
|
: Configuration.Instance.LibationFiles;
|
||||||
PDF = new AudibleFileStorage(FileType.PDF, BooksDirectory, "pdf", "zip");
|
|
||||||
|
return Directory.CreateDirectory(Path.Combine(M4bRootDir, "DecryptInProgress")).FullName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// not customizable. don't move to config
|
||||||
|
public static string DownloadsFinal => new DirectoryInfo(Configuration.Instance.LibationFiles).CreateSubdirectory("DownloadsFinal").FullName;
|
||||||
|
|
||||||
|
public static string BooksDirectory
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(Configuration.Instance.Books))
|
||||||
|
Configuration.Instance.Books = Path.Combine(Configuration.Instance.LibationFiles, "Books");
|
||||||
|
return Directory.CreateDirectory(Configuration.Instance.Books).FullName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region instance
|
#region instance
|
||||||
public FileType FileType => (FileType)Value;
|
public FileType FileType => (FileType)Value;
|
||||||
|
|
||||||
public string StorageDirectory => DisplayName;
|
|
||||||
|
|
||||||
private IEnumerable<string> extensions_noDots { get; }
|
private IEnumerable<string> extensions_noDots { get; }
|
||||||
private string extAggr { get; }
|
private string extAggr { get; }
|
||||||
|
|
||||||
private AudibleFileStorage(FileType fileType, string storageDirectory, params string[] extensions) : base((int)fileType, storageDirectory)
|
protected AudibleFileStorage(FileType fileType) : base((int)fileType, fileType.ToString())
|
||||||
{
|
{
|
||||||
extensions_noDots = extensions.Select(ext => ext.Trim('.')).ToList();
|
extensions_noDots = Extensions.Select(ext => ext.Trim('.')).ToList();
|
||||||
extAggr = extensions_noDots.Aggregate((a, b) => $"{a}|{b}");
|
extAggr = extensions_noDots.Aggregate((a, b) => $"{a}|{b}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,30 +91,84 @@ namespace FileManager
|
|||||||
/// - a directory name has the product id and an audio file is immediately inside
|
/// - a directory name has the product id and an audio file is immediately inside
|
||||||
/// - any audio filename contains the product id
|
/// - any audio filename contains the product id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Exists(string productId)
|
public bool Exists(string productId) => GetPath(productId) != null;
|
||||||
=> GetPath(productId) != null;
|
|
||||||
|
|
||||||
public string GetPath(string productId)
|
public string GetPath(string productId)
|
||||||
{
|
{
|
||||||
{
|
var cachedFile = FilePathCache.GetPath(productId, FileType);
|
||||||
var cachedFile = FilePathCache.GetPath(productId, FileType);
|
if (cachedFile != null)
|
||||||
if (cachedFile != null)
|
return cachedFile;
|
||||||
return cachedFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
var firstOrNull =
|
var firstOrNull =
|
||||||
Directory
|
Directory
|
||||||
.EnumerateFiles(StorageDirectory, "*.*", SearchOption.AllDirectories)
|
.EnumerateFiles(StorageDirectory, "*.*", SearchOption.AllDirectories)
|
||||||
.FirstOrDefault(s => Regex.IsMatch(s, $@"{productId}.*?\.({extAggr})$", RegexOptions.IgnoreCase));
|
.FirstOrDefault(s => Regex.IsMatch(s, $@"{productId}.*?\.({extAggr})$", RegexOptions.IgnoreCase));
|
||||||
|
|
||||||
if (firstOrNull is null)
|
if (firstOrNull is null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
FilePathCache.Upsert(productId, FileType, firstOrNull);
|
FilePathCache.Upsert(productId, FileType, firstOrNull);
|
||||||
return firstOrNull;
|
return firstOrNull;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string GetDestDir(string title, string asin)
|
||||||
|
{
|
||||||
|
// to prevent the paths from getting too long, we don't need after the 1st ":" for the folder
|
||||||
|
var underscoreIndex = title.IndexOf(':');
|
||||||
|
var titleDir
|
||||||
|
= underscoreIndex < 4
|
||||||
|
? title
|
||||||
|
: title.Substring(0, underscoreIndex);
|
||||||
|
var finalDir = FileUtility.GetValidFilename(StorageDirectory, titleDir, null, asin);
|
||||||
|
return finalDir;
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsFileTypeMatch(FileInfo fileInfo)
|
public bool IsFileTypeMatch(FileInfo fileInfo)
|
||||||
=> extensions_noDots.ContainsInsensative(fileInfo.Extension.Trim('.'));
|
=> extensions_noDots.ContainsInsensative(fileInfo.Extension.Trim('.'));
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class AudioFileStorage : AudibleFileStorage
|
||||||
|
{
|
||||||
|
public const string SKIP_FILE_EXT = "libhack";
|
||||||
|
|
||||||
|
public override string[] Extensions { get; } = new[] { "m4b", "mp3", "aac", "mp4", "m4a", "ogg", "flac", SKIP_FILE_EXT };
|
||||||
|
|
||||||
|
// we always want to use the latest config value, therefore
|
||||||
|
// - DO use 'get' arrow "=>"
|
||||||
|
// - do NOT use assign "="
|
||||||
|
public override string StorageDirectory => BooksDirectory;
|
||||||
|
|
||||||
|
public AudioFileStorage() : base(FileType.Audio) { }
|
||||||
|
|
||||||
|
public void CreateSkipFile(string title, string asin, string contents)
|
||||||
|
{
|
||||||
|
var path = FileUtility.GetValidFilename(GetDestDir(title, asin), title, SKIP_FILE_EXT, asin);
|
||||||
|
File.WriteAllText(path, contents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AaxFileStorage : AudibleFileStorage
|
||||||
|
{
|
||||||
|
public override string[] Extensions { get; } = new[] { "aax" };
|
||||||
|
|
||||||
|
// we always want to use the latest config value, therefore
|
||||||
|
// - DO use 'get' arrow "=>"
|
||||||
|
// - do NOT use assign "="
|
||||||
|
public override string StorageDirectory => DownloadsFinal;
|
||||||
|
|
||||||
|
public AaxFileStorage() : base(FileType.AAX) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PdfFileStorage : AudibleFileStorage
|
||||||
|
{
|
||||||
|
public override string[] Extensions { get; } = new[] { "pdf", "zip" };
|
||||||
|
|
||||||
|
// we always want to use the latest config value, therefore
|
||||||
|
// - DO use 'get' arrow "=>"
|
||||||
|
// - do NOT use assign "="
|
||||||
|
public override string StorageDirectory => BooksDirectory;
|
||||||
|
|
||||||
|
public PdfFileStorage() : base(FileType.PDF) { }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -37,18 +37,11 @@ namespace FileManager
|
|||||||
|
|
||||||
public bool FilesExist
|
public bool FilesExist
|
||||||
=> File.Exists(APPSETTINGS_JSON)
|
=> File.Exists(APPSETTINGS_JSON)
|
||||||
&& File.Exists(SettingsJsonPath)
|
&& File.Exists(SettingsFilePath)
|
||||||
&& Directory.Exists(LibationFiles)
|
&& Directory.Exists(LibationFiles)
|
||||||
&& Directory.Exists(Books);
|
&& Directory.Exists(Books);
|
||||||
|
|
||||||
public string SettingsJsonPath => Path.Combine(LibationFiles, "Settings.json");
|
public string SettingsFilePath => Path.Combine(LibationFiles, "Settings.json");
|
||||||
|
|
||||||
[Description("Your user-specific key used to decrypt your audible files (*.aax) into audio files you can use anywhere (*.m4b). Leave alone in most cases")]
|
|
||||||
public string DecryptKey
|
|
||||||
{
|
|
||||||
get => persistentDictionary.GetString(nameof(DecryptKey));
|
|
||||||
set => persistentDictionary.Set(nameof(DecryptKey), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("Location for book storage. Includes destination of newly liberated books")]
|
[Description("Location for book storage. Includes destination of newly liberated books")]
|
||||||
public string Books
|
public string Books
|
||||||
@@ -90,10 +83,11 @@ namespace FileManager
|
|||||||
set => persistentDictionary.Set(nameof(DecryptInProgressEnum), value);
|
set => persistentDictionary.Set(nameof(DecryptInProgressEnum), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string LocaleCountryCode
|
[Description("Retain .aax files after decrypting?")]
|
||||||
|
public bool RetainAaxFiles
|
||||||
{
|
{
|
||||||
get => persistentDictionary.GetString(nameof(LocaleCountryCode));
|
get => persistentDictionary.Get<bool>(nameof(RetainAaxFiles));
|
||||||
set => persistentDictionary.Set(nameof(LocaleCountryCode), value);
|
set => persistentDictionary.Set(nameof(RetainAaxFiles), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
// 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
|
||||||
@@ -115,11 +109,11 @@ namespace FileManager
|
|||||||
if (wellKnownPaths.ContainsKey(value))
|
if (wellKnownPaths.ContainsKey(value))
|
||||||
value = wellKnownPaths[value];
|
value = wellKnownPaths[value];
|
||||||
|
|
||||||
// must write here before SettingsJsonPath in next step reads cache
|
// must write here before SettingsFilePath in next step reads cache
|
||||||
libationFilesPathCache = value;
|
libationFilesPathCache = value;
|
||||||
|
|
||||||
// load json values into memory. create if not exists
|
// load json values into memory. create if not exists
|
||||||
persistentDictionary = new PersistentDictionary(SettingsJsonPath);
|
persistentDictionary = new PersistentDictionary(SettingsFilePath);
|
||||||
|
|
||||||
return libationFilesPathCache;
|
return libationFilesPathCache;
|
||||||
}
|
}
|
||||||
@@ -171,7 +165,7 @@ namespace FileManager
|
|||||||
// if moving from default, delete old settings file and dir (if empty)
|
// if moving from default, delete old settings file and dir (if empty)
|
||||||
if (LibationFiles.EqualsInsensitive(AppDir))
|
if (LibationFiles.EqualsInsensitive(AppDir))
|
||||||
{
|
{
|
||||||
File.Delete(SettingsJsonPath);
|
File.Delete(SettingsFilePath);
|
||||||
System.Threading.Thread.Sleep(100);
|
System.Threading.Thread.Sleep(100);
|
||||||
if (!Directory.EnumerateDirectories(AppDir).Any() && !Directory.EnumerateFiles(AppDir).Any())
|
if (!Directory.EnumerateDirectories(AppDir).Any() && !Directory.EnumerateFiles(AppDir).Any())
|
||||||
Directory.Delete(AppDir);
|
Directory.Delete(AppDir);
|
||||||
|
|||||||
@@ -40,8 +40,13 @@ namespace FileManager
|
|||||||
return stringCache[propertyName];
|
return stringCache[propertyName];
|
||||||
}
|
}
|
||||||
|
|
||||||
public T Get<T>(string propertyName) where T : class
|
public T Get<T>(string propertyName)
|
||||||
=> GetObject(propertyName) is T obj ? obj : default;
|
{
|
||||||
|
var o = GetObject(propertyName);
|
||||||
|
if (o is null) return default;
|
||||||
|
if (o is JToken jt) return jt.Value<T>();
|
||||||
|
return (T)o;
|
||||||
|
}
|
||||||
|
|
||||||
public object GetObject(string propertyName)
|
public object GetObject(string propertyName)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using AudibleApi;
|
||||||
|
using AudibleApi.Authorization;
|
||||||
|
using Dinah.Core;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace InternalUtilities
|
||||||
|
{
|
||||||
|
public class Account : IUpdatable
|
||||||
|
{
|
||||||
|
public event EventHandler Updated;
|
||||||
|
private void update(object sender = null, EventArgs e = null)
|
||||||
|
=> Updated?.Invoke(this, new EventArgs());
|
||||||
|
|
||||||
|
// canonical. immutable. email or phone number
|
||||||
|
public string AccountId { get; }
|
||||||
|
|
||||||
|
// user-friendly, non-canonical name. mutable
|
||||||
|
private string _accountName;
|
||||||
|
public string AccountName
|
||||||
|
{
|
||||||
|
get => _accountName;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
|
return;
|
||||||
|
var v = value.Trim();
|
||||||
|
if (v == _accountName)
|
||||||
|
return;
|
||||||
|
_accountName = v;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// whether to include this account when scanning libraries.
|
||||||
|
// technically this is an app setting; not an attribute of account. but since it's managed with accounts, it makes sense to put this exception-to-the-rule here
|
||||||
|
private bool _libraryScan = true;
|
||||||
|
public bool LibraryScan
|
||||||
|
{
|
||||||
|
get => _libraryScan;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == _libraryScan)
|
||||||
|
return;
|
||||||
|
_libraryScan = value;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _decryptKey = "";
|
||||||
|
/// <summary>aka: activation bytes</summary>
|
||||||
|
public string DecryptKey
|
||||||
|
{
|
||||||
|
get => _decryptKey;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
var v = (value ?? "").Trim();
|
||||||
|
if (v == _decryptKey)
|
||||||
|
return;
|
||||||
|
_decryptKey = v;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Identity _identity;
|
||||||
|
public Identity IdentityTokens
|
||||||
|
{
|
||||||
|
get => _identity;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_identity is null && value is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_identity != null)
|
||||||
|
_identity.Updated -= update;
|
||||||
|
|
||||||
|
if (value != null)
|
||||||
|
value.Updated += update;
|
||||||
|
|
||||||
|
_identity = value;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public Locale Locale => IdentityTokens?.Locale;
|
||||||
|
|
||||||
|
public Account(string accountId)
|
||||||
|
{
|
||||||
|
AccountId = ArgumentValidator.EnsureNotNullOrWhiteSpace(accountId, nameof(accountId)).Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() => $"{AccountId} - {Locale?.Name ?? "[empty]"}";
|
||||||
|
|
||||||
|
public string MaskedLogEntry => @$"AccountId={mask(AccountId)}|AccountName={mask(AccountName)}|Locale={Locale?.Name ?? "[empty]"}";
|
||||||
|
private static string mask(string str)
|
||||||
|
=> str is null ? "[null]"
|
||||||
|
: str == string.Empty ? "[empty]"
|
||||||
|
: str.ToMask();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using AudibleApi;
|
||||||
|
using AudibleApi.Authorization;
|
||||||
|
using Dinah.Core;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace InternalUtilities
|
||||||
|
{
|
||||||
|
// 'AccountsSettings' is intentionally NOT IEnumerable<> so that properties can be added/extended
|
||||||
|
// from newtonsoft (https://www.newtonsoft.com/json/help/html/SerializationGuide.htm):
|
||||||
|
// .NET : IList, IEnumerable, IList<T>, Array
|
||||||
|
// JSON : Array (properties on the collection will not be serialized)
|
||||||
|
public class AccountsSettings : IUpdatable
|
||||||
|
{
|
||||||
|
public event EventHandler Updated;
|
||||||
|
private void update(object sender = null, EventArgs e = null)
|
||||||
|
{
|
||||||
|
foreach (var account in Accounts)
|
||||||
|
validate(account);
|
||||||
|
update_no_validate();
|
||||||
|
}
|
||||||
|
private void update_no_validate() => Updated?.Invoke(this, new EventArgs());
|
||||||
|
|
||||||
|
public AccountsSettings() { }
|
||||||
|
|
||||||
|
// for some reason this will make the json instantiator use _accounts_json.set()
|
||||||
|
[JsonConstructor]
|
||||||
|
protected AccountsSettings(List<Account> accountsSettings) { }
|
||||||
|
|
||||||
|
#region Accounts
|
||||||
|
private List<Account> _accounts_backing = new List<Account>();
|
||||||
|
[JsonProperty(PropertyName = nameof(Accounts))]
|
||||||
|
private List<Account> _accounts_json
|
||||||
|
{
|
||||||
|
get => _accounts_backing;
|
||||||
|
// 'set' is only used by json deser
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var account in value)
|
||||||
|
_add(account);
|
||||||
|
|
||||||
|
update_no_validate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[JsonIgnore]
|
||||||
|
public IReadOnlyList<Account> Accounts => _accounts_json.AsReadOnly();
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region de/serialize
|
||||||
|
public static AccountsSettings FromJson(string json)
|
||||||
|
=> JsonConvert.DeserializeObject<AccountsSettings>(json, Identity.GetJsonSerializerSettings());
|
||||||
|
|
||||||
|
public string ToJson(Formatting formatting = Formatting.Indented)
|
||||||
|
=> JsonConvert.SerializeObject(this, formatting, Identity.GetJsonSerializerSettings());
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
// more common naming convention alias for internal collection
|
||||||
|
public IReadOnlyList<Account> GetAll() => Accounts;
|
||||||
|
|
||||||
|
public Account Upsert(string accountId, string locale)
|
||||||
|
{
|
||||||
|
var acct = GetAccount(accountId, locale);
|
||||||
|
|
||||||
|
if (acct != null)
|
||||||
|
return acct;
|
||||||
|
|
||||||
|
var l = Localization.Get(locale);
|
||||||
|
var id = new Identity(l);
|
||||||
|
|
||||||
|
var account = new Account(accountId) { IdentityTokens = id };
|
||||||
|
Add(account);
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(Account account)
|
||||||
|
{
|
||||||
|
_add(account);
|
||||||
|
update_no_validate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void _add(Account account)
|
||||||
|
{
|
||||||
|
validate(account);
|
||||||
|
|
||||||
|
_accounts_backing.Add(account);
|
||||||
|
account.Updated += update;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account GetAccount(string accountId, string locale)
|
||||||
|
{
|
||||||
|
if (locale is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return Accounts.SingleOrDefault(a => a.AccountId == accountId && a.IdentityTokens.Locale.Name == locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Delete(string accountId, string locale)
|
||||||
|
{
|
||||||
|
var acct = GetAccount(accountId, locale);
|
||||||
|
if (acct is null)
|
||||||
|
return false;
|
||||||
|
return Delete(acct);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Delete(Account account)
|
||||||
|
{
|
||||||
|
if (!_accounts_backing.Contains(account))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
account.Updated -= update;
|
||||||
|
var result = _accounts_backing.Remove(account);
|
||||||
|
update_no_validate();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validate(Account account)
|
||||||
|
{
|
||||||
|
ArgumentValidator.EnsureNotNull(account, nameof(account));
|
||||||
|
|
||||||
|
var accountId = account.AccountId;
|
||||||
|
var locale = account?.IdentityTokens?.Locale?.Name;
|
||||||
|
|
||||||
|
var acct = GetAccount(accountId, locale);
|
||||||
|
|
||||||
|
// new: ok
|
||||||
|
if (acct is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// same account instance: ok
|
||||||
|
if (acct == account)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// same account id + locale, different instance: bad
|
||||||
|
throw new InvalidOperationException("Cannot add an account with the same account Id and Locale");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
using AudibleApi.Authorization;
|
||||||
|
using Dinah.Core.IO;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace InternalUtilities
|
||||||
|
{
|
||||||
|
public class AccountsSettingsPersister : JsonFilePersister<AccountsSettings>
|
||||||
|
{
|
||||||
|
/// <summary>Alias for Target </summary>
|
||||||
|
public AccountsSettings AccountsSettings => Target;
|
||||||
|
|
||||||
|
/// <summary>uses path. create file if doesn't yet exist</summary>
|
||||||
|
public AccountsSettingsPersister(AccountsSettings target, string path, string jsonPath = null)
|
||||||
|
: base(target, path, jsonPath) { }
|
||||||
|
|
||||||
|
/// <summary>load from existing file</summary>
|
||||||
|
public AccountsSettingsPersister(string path, string jsonPath = null)
|
||||||
|
: base(path, jsonPath) { }
|
||||||
|
|
||||||
|
protected override JsonSerializerSettings GetSerializerSettings()
|
||||||
|
=> Identity.GetJsonSerializerSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using AudibleApiDTOs;
|
||||||
|
|
||||||
|
namespace InternalUtilities
|
||||||
|
{
|
||||||
|
public class ImportItem
|
||||||
|
{
|
||||||
|
public Item DtoItem { get; set; }
|
||||||
|
public string AccountId { get; set; }
|
||||||
|
public string LocaleName { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -4,35 +4,64 @@ using System.Linq;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AudibleApi;
|
using AudibleApi;
|
||||||
using AudibleApiDTOs;
|
using AudibleApiDTOs;
|
||||||
using FileManager;
|
using Dinah.Core;
|
||||||
using Polly;
|
using Polly;
|
||||||
using Polly.Retry;
|
using Polly.Retry;
|
||||||
|
|
||||||
namespace InternalUtilities
|
namespace InternalUtilities
|
||||||
{
|
{
|
||||||
public class AudibleApiActions
|
public static class AudibleApiActions
|
||||||
{
|
{
|
||||||
private AsyncRetryPolicy policy { get; }
|
/// <summary>USE THIS from within Libation. It wraps the call with correct JSONPath</summary>
|
||||||
|
public static Task<Api> GetApiAsync(string username, string localeName, ILoginCallback loginCallback = null)
|
||||||
|
{
|
||||||
|
Serilog.Log.Logger.Information("GetApiAsync. {@DebugInfo}", new
|
||||||
|
{
|
||||||
|
Username = username.ToMask(),
|
||||||
|
LocaleName = localeName,
|
||||||
|
});
|
||||||
|
return EzApiCreator.GetApiAsync(
|
||||||
|
Localization.Get(localeName),
|
||||||
|
AudibleApiStorage.AccountsSettingsFile,
|
||||||
|
AudibleApiStorage.GetIdentityTokensJsonPath(username, localeName),
|
||||||
|
loginCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>USE THIS from within Libation. It wraps the call with correct JSONPath</summary>
|
||||||
|
public static Task<Api> GetApiAsync(ILoginCallback loginCallback, Account account)
|
||||||
|
{
|
||||||
|
Serilog.Log.Logger.Information("GetApiAsync. {@DebugInfo}", new
|
||||||
|
{
|
||||||
|
Account = account?.MaskedLogEntry ?? "[null]",
|
||||||
|
LocaleName = account?.Locale?.Name
|
||||||
|
});
|
||||||
|
return EzApiCreator.GetApiAsync(
|
||||||
|
account.Locale,
|
||||||
|
AudibleApiStorage.AccountsSettingsFile,
|
||||||
|
account.GetIdentityTokensJsonPath(),
|
||||||
|
loginCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AsyncRetryPolicy policy { get; }
|
||||||
= Policy.Handle<Exception>()
|
= Policy.Handle<Exception>()
|
||||||
// 2 retries == 3 total
|
// 2 retries == 3 total
|
||||||
.RetryAsync(2);
|
.RetryAsync(2);
|
||||||
|
|
||||||
public async Task<List<Item>> GetAllLibraryItemsAsync(ILoginCallback callback)
|
public static Task<List<Item>> GetLibraryValidatedAsync(Api api)
|
||||||
{
|
{
|
||||||
// bug on audible's side. the 1st time after a long absence, a query to get library will return without titles or authors. a subsequent identical query will be successful. this is true whether or tokens are refreshed
|
// bug on audible's side. the 1st time after a long absence, a query to get library will return without titles or authors. a subsequent identical query will be successful. this is true whether or tokens are refreshed
|
||||||
// worse, this 1st dummy call doesn't seem to help:
|
// worse, this 1st dummy call doesn't seem to help:
|
||||||
// var page = await api.GetLibraryAsync(new AudibleApi.LibraryOptions { NumberOfResultPerPage = 1, PageNumber = 1, PurchasedAfter = DateTime.Now.AddYears(-20), ResponseGroups = AudibleApi.LibraryOptions.ResponseGroupOptions.ALL_OPTIONS });
|
// var page = await api.GetLibraryAsync(new AudibleApi.LibraryOptions { NumberOfResultPerPage = 1, PageNumber = 1, PurchasedAfter = DateTime.Now.AddYears(-20), ResponseGroups = AudibleApi.LibraryOptions.ResponseGroupOptions.ALL_OPTIONS });
|
||||||
// i don't want to incur the cost of making a full dummy call every time because it fails sometimes
|
// i don't want to incur the cost of making a full dummy call every time because it fails sometimes
|
||||||
return await policy.ExecuteAsync(() => getItemsAsync(callback));
|
return policy.ExecuteAsync(() => getItemsAsync(api));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<List<Item>> getItemsAsync(ILoginCallback callback)
|
private static async Task<List<Item>> getItemsAsync(Api api)
|
||||||
{
|
{
|
||||||
var api = await EzApiCreator.GetApiAsync(AudibleApiStorage.IdentityTokensFile, callback, Configuration.Instance.LocaleCountryCode);
|
var items = await api.GetAllLibraryItemsAsync();
|
||||||
var items = await AudibleApiExtensions.GetAllLibraryItemsAsync(api);
|
|
||||||
|
|
||||||
// remove episode parents
|
// remove episode parents and 'audible plus' check-outs
|
||||||
items.RemoveAll(i => i.IsEpisodes);
|
items.RemoveAll(i => i.IsEpisodes || i.IsNonLibraryAudiblePlus);
|
||||||
|
|
||||||
#region // episode handling. doesn't quite work
|
#region // episode handling. doesn't quite work
|
||||||
// // add individual/children episodes
|
// // add individual/children episodes
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using AudibleApi;
|
|
||||||
using AudibleApiDTOs;
|
|
||||||
|
|
||||||
//
|
|
||||||
// probably not the best place for this
|
|
||||||
// but good enough for now
|
|
||||||
//
|
|
||||||
namespace InternalUtilities
|
|
||||||
{
|
|
||||||
public static class AudibleApiExtensions
|
|
||||||
{
|
|
||||||
public static async Task<List<Item>> GetAllLibraryItemsAsync(this Api api)
|
|
||||||
{
|
|
||||||
var allItems = new List<Item>();
|
|
||||||
|
|
||||||
for (var i = 1; ; i++)
|
|
||||||
{
|
|
||||||
var page = await api.GetLibraryAsync(new LibraryOptions
|
|
||||||
{
|
|
||||||
NumberOfResultPerPage = 1000,
|
|
||||||
PageNumber = i,
|
|
||||||
PurchasedAfter = new DateTime(2000, 1, 1),
|
|
||||||
ResponseGroups = LibraryOptions.ResponseGroupOptions.ALL_OPTIONS
|
|
||||||
});
|
|
||||||
|
|
||||||
var pageStr = page.ToString();
|
|
||||||
|
|
||||||
LibraryDtoV10 libResult;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// important! use this convert method
|
|
||||||
libResult = LibraryDtoV10.FromJson(pageStr);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Serilog.Log.Logger.Error(ex, "Error converting library for importing use. Full library:\r\n" + pageStr);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!libResult.Items.Any())
|
|
||||||
break;
|
|
||||||
else
|
|
||||||
Serilog.Log.Logger.Information($"Page {i}: {libResult.Items.Length} results");
|
|
||||||
|
|
||||||
allItems.AddRange(libResult.Items);
|
|
||||||
}
|
|
||||||
|
|
||||||
return allItems;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using FileManager;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace InternalUtilities
|
||||||
|
{
|
||||||
|
public static class AudibleApiStorage
|
||||||
|
{
|
||||||
|
public static string AccountsSettingsFile => Path.Combine(Configuration.Instance.LibationFiles, "AccountsSettings.json");
|
||||||
|
|
||||||
|
public static void EnsureAccountsSettingsFileExists()
|
||||||
|
{
|
||||||
|
// saves. BEWARE: this will overwrite an existing file
|
||||||
|
if (!File.Exists(AccountsSettingsFile))
|
||||||
|
_ = new AccountsSettingsPersister(new AccountsSettings(), AccountsSettingsFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>If you use this, be a good citizen and DISPOSE of it</summary>
|
||||||
|
public static AccountsSettingsPersister GetAccountsSettingsPersister() => new AccountsSettingsPersister(AccountsSettingsFile);
|
||||||
|
|
||||||
|
public static string GetIdentityTokensJsonPath(this Account account)
|
||||||
|
=> GetIdentityTokensJsonPath(account.AccountId, account.Locale?.Name);
|
||||||
|
public static string GetIdentityTokensJsonPath(string username, string localeName)
|
||||||
|
{
|
||||||
|
var usernameSanitized = trimSurroundingQuotes(JsonConvert.ToString(username));
|
||||||
|
var localeNameSanitized = trimSurroundingQuotes(JsonConvert.ToString(localeName));
|
||||||
|
|
||||||
|
return $"$.Accounts[?(@.AccountId == '{usernameSanitized}' && @.IdentityTokens.LocaleName == '{localeNameSanitized}')].IdentityTokens";
|
||||||
|
}
|
||||||
|
private static string trimSurroundingQuotes(string str)
|
||||||
|
{
|
||||||
|
// SubString algo is better than .Trim("\"")
|
||||||
|
// orig string "
|
||||||
|
// json string "\""
|
||||||
|
// Eg:
|
||||||
|
// => str.Trim("\"")
|
||||||
|
// output \
|
||||||
|
// vs
|
||||||
|
// => str.Substring(1, str.Length - 2)
|
||||||
|
// output \"
|
||||||
|
// also works with surrounding single quotes
|
||||||
|
|
||||||
|
return str.Substring(1, str.Length - 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -51,9 +51,6 @@ namespace InternalUtilities
|
|||||||
if (distinct.Any(s => s.CategoryName is null))
|
if (distinct.Any(s => s.CategoryName is null))
|
||||||
exceptions.Add(new ArgumentException($"Collection contains {nameof(Item.Categories)} with null {nameof(Ladder.CategoryName)}", nameof(items)));
|
exceptions.Add(new ArgumentException($"Collection contains {nameof(Item.Categories)} with null {nameof(Ladder.CategoryName)}", nameof(items)));
|
||||||
|
|
||||||
if (items.GetCategoryPairsDistinct().Any(p => p.Length > 2))
|
|
||||||
exceptions.Add(new ArgumentException($"Collection contains {nameof(Item.Categories)} with wrong number of categories. Expecting 0, 1, or 2 categories per title", nameof(items)));
|
|
||||||
|
|
||||||
return exceptions;
|
return exceptions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-5
@@ -10,13 +10,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Solution Items", "_Solutio
|
|||||||
REFERENCE.txt = REFERENCE.txt
|
REFERENCE.txt = REFERENCE.txt
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "3.2 Domain Utilities (database aware)", "3.2 Domain Utilities (database aware)", "{41CDCC73-9B81-49DD-9570-C54406E852AF}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "5 Domain Utilities (db aware)", "5 Domain Utilities (db aware)", "{41CDCC73-9B81-49DD-9570-C54406E852AF}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "4 Application", "4 Application", "{8679CAC8-9164-4007-BDD2-F004810EDA14}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "6 Application", "6 Application", "{8679CAC8-9164-4007-BDD2-F004810EDA14}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "1 Core Libraries", "1 Core Libraries", "{43E3ACB3-E0BC-4370-8DBB-E3720C8C8FD1}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "1 Core Libraries", "1 Core Libraries", "{43E3ACB3-E0BC-4370-8DBB-E3720C8C8FD1}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "3 Domain", "3 Domain", "{751093DD-5DBA-463E-ADBE-E05FAFB6983E}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "4 Domain (db)", "4 Domain (db)", "{751093DD-5DBA-463E-ADBE-E05FAFB6983E}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2 Utilities (domain ignorant)", "2 Utilities (domain ignorant)", "{7FBBB086-0807-4998-85BF-6D1A49C8AD05}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2 Utilities (domain ignorant)", "2 Utilities (domain ignorant)", "{7FBBB086-0807-4998-85BF-6D1A49C8AD05}"
|
||||||
EndProject
|
EndProject
|
||||||
@@ -30,7 +30,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileLiberator", "FileLibera
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InternalUtilities", "InternalUtilities\InternalUtilities.csproj", "{06882742-27A6-4347-97D9-56162CEC9C11}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InternalUtilities", "InternalUtilities\InternalUtilities.csproj", "{06882742-27A6-4347-97D9-56162CEC9C11}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "3.1 Domain Internal Utilities (db ignorant)", "3.1 Domain Internal Utilities (db ignorant)", "{F0CBB7A7-D3FB-41FF-8F47-CF3F6A592249}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "3 Domain Internal Utilities (db ignorant)", "3 Domain Internal Utilities (db ignorant)", "{F0CBB7A7-D3FB-41FF-8F47-CF3F6A592249}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibationSearchEngine", "LibationSearchEngine\LibationSearchEngine.csproj", "{2E1F5DB4-40CC-4804-A893-5DCE0193E598}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibationSearchEngine", "LibationSearchEngine\LibationSearchEngine.csproj", "{2E1F5DB4-40CC-4804-A893-5DCE0193E598}"
|
||||||
EndProject
|
EndProject
|
||||||
@@ -80,7 +80,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dinah.Core.WindowsDesktop",
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WindowsDesktopUtilities", "WindowsDesktopUtilities\WindowsDesktopUtilities.csproj", "{E7EFD64D-6630-4426-B09C-B6862A92E3FD}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WindowsDesktopUtilities", "WindowsDesktopUtilities\WindowsDesktopUtilities.csproj", "{E7EFD64D-6630-4426-B09C-B6862A92E3FD}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibationLauncher", "LibationLauncher\LibationLauncher.csproj", "{F3B04A3A-20C8-4582-A54A-715AF6A5D859}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibationLauncher", "LibationLauncher\LibationLauncher.csproj", "{F3B04A3A-20C8-4582-A54A-715AF6A5D859}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "0 Libation Tests", "0 Libation Tests", "{67E66E82-5532-4440-AFB3-9FB1DF9DEF53}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InternalUtilities.Tests", "_Tests\InternalUtilities.Tests\InternalUtilities.Tests.csproj", "{8447C956-B03E-4F59-9DD4-877793B849D9}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@@ -200,6 +204,10 @@ Global
|
|||||||
{F3B04A3A-20C8-4582-A54A-715AF6A5D859}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{F3B04A3A-20C8-4582-A54A-715AF6A5D859}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{F3B04A3A-20C8-4582-A54A-715AF6A5D859}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{F3B04A3A-20C8-4582-A54A-715AF6A5D859}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{F3B04A3A-20C8-4582-A54A-715AF6A5D859}.Release|Any CPU.Build.0 = Release|Any CPU
|
{F3B04A3A-20C8-4582-A54A-715AF6A5D859}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{8447C956-B03E-4F59-9DD4-877793B849D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{8447C956-B03E-4F59-9DD4-877793B849D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{8447C956-B03E-4F59-9DD4-877793B849D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{8447C956-B03E-4F59-9DD4-877793B849D9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -233,6 +241,7 @@ Global
|
|||||||
{059CE32C-9AD6-45E9-A166-790DFFB0B730} = {43E3ACB3-E0BC-4370-8DBB-E3720C8C8FD1}
|
{059CE32C-9AD6-45E9-A166-790DFFB0B730} = {43E3ACB3-E0BC-4370-8DBB-E3720C8C8FD1}
|
||||||
{E7EFD64D-6630-4426-B09C-B6862A92E3FD} = {F0CBB7A7-D3FB-41FF-8F47-CF3F6A592249}
|
{E7EFD64D-6630-4426-B09C-B6862A92E3FD} = {F0CBB7A7-D3FB-41FF-8F47-CF3F6A592249}
|
||||||
{F3B04A3A-20C8-4582-A54A-715AF6A5D859} = {8679CAC8-9164-4007-BDD2-F004810EDA14}
|
{F3B04A3A-20C8-4582-A54A-715AF6A5D859} = {8679CAC8-9164-4007-BDD2-F004810EDA14}
|
||||||
|
{8447C956-B03E-4F59-9DD4-877793B849D9} = {67E66E82-5532-4440-AFB3-9FB1DF9DEF53}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {615E00ED-BAEF-4E8E-A92A-9B82D87942A9}
|
SolutionGuid = {615E00ED-BAEF-4E8E-A92A-9B82D87942A9}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0-windows</TargetFramework>
|
||||||
<UseWindowsForms>true</UseWindowsForms>
|
<UseWindowsForms>true</UseWindowsForms>
|
||||||
<ApplicationIcon>libation.ico</ApplicationIcon>
|
<ApplicationIcon>libation.ico</ApplicationIcon>
|
||||||
<AssemblyName>Libation</AssemblyName>
|
<AssemblyName>Libation</AssemblyName>
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
<!-- <PublishSingleFile>true</PublishSingleFile> -->
|
<!-- <PublishSingleFile>true</PublishSingleFile> -->
|
||||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
|
|
||||||
<Version>3.1.9.1</Version>
|
<Version>4.1.5.1</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Octokit" Version="0.36.0" />
|
<PackageReference Include="Octokit" Version="0.48.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -2,10 +2,13 @@ using System;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using AudibleApi;
|
||||||
using FileManager;
|
using FileManager;
|
||||||
|
using InternalUtilities;
|
||||||
using LibationWinForms;
|
using LibationWinForms;
|
||||||
using LibationWinForms.Dialogs;
|
using LibationWinForms.Dialogs;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
@@ -22,6 +25,11 @@ namespace LibationLauncher
|
|||||||
|
|
||||||
createSettings();
|
createSettings();
|
||||||
|
|
||||||
|
AudibleApiStorage.EnsureAccountsSettingsFileExists();
|
||||||
|
|
||||||
|
migrate_to_v4_0_0();
|
||||||
|
migrate_to_v4_0_3(); // add setting for whether to delete/retain aax
|
||||||
|
|
||||||
ensureLoggingConfig();
|
ensureLoggingConfig();
|
||||||
ensureSerilogConfig();
|
ensureSerilogConfig();
|
||||||
configureLogging();
|
configureLogging();
|
||||||
@@ -33,6 +41,11 @@ namespace LibationLauncher
|
|||||||
|
|
||||||
private static void createSettings()
|
private static void createSettings()
|
||||||
{
|
{
|
||||||
|
static bool configSetupIsComplete(Configuration config)
|
||||||
|
=> config.FilesExist
|
||||||
|
&& !string.IsNullOrWhiteSpace(config.DownloadsInProgressEnum)
|
||||||
|
&& !string.IsNullOrWhiteSpace(config.DecryptInProgressEnum);
|
||||||
|
|
||||||
var config = Configuration.Instance;
|
var config = Configuration.Instance;
|
||||||
if (configSetupIsComplete(config))
|
if (configSetupIsComplete(config))
|
||||||
return;
|
return;
|
||||||
@@ -42,8 +55,6 @@ namespace LibationLauncher
|
|||||||
var setupDialog = new SetupDialog();
|
var setupDialog = new SetupDialog();
|
||||||
setupDialog.NoQuestionsBtn_Click += (_, __) =>
|
setupDialog.NoQuestionsBtn_Click += (_, __) =>
|
||||||
{
|
{
|
||||||
config.DecryptKey ??= "";
|
|
||||||
config.LocaleCountryCode ??= "us";
|
|
||||||
config.DownloadsInProgressEnum ??= "WinTemp";
|
config.DownloadsInProgressEnum ??= "WinTemp";
|
||||||
config.DecryptInProgressEnum ??= "WinTemp";
|
config.DecryptInProgressEnum ??= "WinTemp";
|
||||||
config.Books ??= Configuration.AppDir;
|
config.Books ??= Configuration.AppDir;
|
||||||
@@ -70,11 +81,151 @@ namespace LibationLauncher
|
|||||||
Environment.Exit(0);
|
Environment.Exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool configSetupIsComplete(Configuration config)
|
#region v3 => v4 migration
|
||||||
=> config.FilesExist
|
static string AccountsSettingsFileLegacy30 => Path.Combine(Configuration.Instance.LibationFiles, "IdentityTokens.json");
|
||||||
&& !string.IsNullOrWhiteSpace(config.LocaleCountryCode)
|
|
||||||
&& !string.IsNullOrWhiteSpace(config.DownloadsInProgressEnum)
|
private static void migrate_to_v4_0_0()
|
||||||
&& !string.IsNullOrWhiteSpace(config.DecryptInProgressEnum);
|
{
|
||||||
|
migrateLegacyIdentityFile();
|
||||||
|
|
||||||
|
updateSettingsFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void migrateLegacyIdentityFile()
|
||||||
|
{
|
||||||
|
if (File.Exists(AccountsSettingsFileLegacy30))
|
||||||
|
{
|
||||||
|
// don't always rely on applicable POCOs. some is legacy and must be: json file => JObject
|
||||||
|
try
|
||||||
|
{
|
||||||
|
updateLegacyFileWithLocale();
|
||||||
|
|
||||||
|
var account = createAccountFromLegacySettings();
|
||||||
|
account.DecryptKey = getDecryptKey(account);
|
||||||
|
|
||||||
|
// the next few methods need persistence. to be a good citizen, dispose of persister at the end of current scope
|
||||||
|
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||||
|
persister.AccountsSettings.Add(account);
|
||||||
|
}
|
||||||
|
// migration is a convenience. if something goes wrong: just move on
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
// delete legacy token file
|
||||||
|
File.Delete(AccountsSettingsFileLegacy30);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void updateLegacyFileWithLocale()
|
||||||
|
{
|
||||||
|
var legacyContents = File.ReadAllText(AccountsSettingsFileLegacy30);
|
||||||
|
var legacyJObj = JObject.Parse(legacyContents);
|
||||||
|
|
||||||
|
// attempt to update legacy token file with locale from settings
|
||||||
|
if (!legacyJObj.ContainsKey("LocaleName"))
|
||||||
|
{
|
||||||
|
var settings = File.ReadAllText(Configuration.Instance.SettingsFilePath);
|
||||||
|
var settingsJObj = JObject.Parse(settings);
|
||||||
|
if (settingsJObj.TryGetValue("LocaleCountryCode", out var localeName))
|
||||||
|
{
|
||||||
|
// update legacy token file with locale from settings
|
||||||
|
legacyJObj.AddFirst(new JProperty("LocaleName", localeName.Value<string>()));
|
||||||
|
|
||||||
|
// save
|
||||||
|
var newContents = legacyJObj.ToString(Formatting.Indented);
|
||||||
|
File.WriteAllText(AccountsSettingsFileLegacy30, newContents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Account createAccountFromLegacySettings()
|
||||||
|
{
|
||||||
|
// get required locale from settings file
|
||||||
|
var settingsContents = File.ReadAllText(Configuration.Instance.SettingsFilePath);
|
||||||
|
if (!JObject.Parse(settingsContents).TryGetValue("LocaleCountryCode", out var jLocale))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var localeName = jLocale.Value<string>();
|
||||||
|
var locale = Localization.Get(localeName);
|
||||||
|
|
||||||
|
var api = EzApiCreator.GetApiAsync(locale, AccountsSettingsFileLegacy30).GetAwaiter().GetResult();
|
||||||
|
var email = api.GetEmailAsync().GetAwaiter().GetResult();
|
||||||
|
|
||||||
|
// identity has likely been updated above. re-get contents
|
||||||
|
var legacyContents = File.ReadAllText(AccountsSettingsFileLegacy30);
|
||||||
|
|
||||||
|
var identity = AudibleApi.Authorization.Identity.FromJson(legacyContents);
|
||||||
|
|
||||||
|
if (!identity.IsValid)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var account = new Account(email)
|
||||||
|
{
|
||||||
|
AccountName = $"{email} - {locale.Name}",
|
||||||
|
LibraryScan = true,
|
||||||
|
IdentityTokens = identity
|
||||||
|
};
|
||||||
|
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string getDecryptKey(Account account)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(account?.DecryptKey))
|
||||||
|
return account.DecryptKey;
|
||||||
|
|
||||||
|
if (!File.Exists(Configuration.Instance.SettingsFilePath) || account is null)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
var settingsContents = File.ReadAllText(Configuration.Instance.SettingsFilePath);
|
||||||
|
if (JObject.Parse(settingsContents).TryGetValue("DecryptKey", out var jToken))
|
||||||
|
return jToken.Value<string>() ?? "";
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void updateSettingsFile()
|
||||||
|
{
|
||||||
|
if (!File.Exists(Configuration.Instance.SettingsFilePath))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// use JObject to remove decrypt key and locale from Settings.json
|
||||||
|
var settingsContents = File.ReadAllText(Configuration.Instance.SettingsFilePath);
|
||||||
|
var jObj = JObject.Parse(settingsContents);
|
||||||
|
|
||||||
|
var jLocale = jObj.Property("LocaleCountryCode");
|
||||||
|
var jDecryptKey = jObj.Property("DecryptKey");
|
||||||
|
|
||||||
|
jDecryptKey?.Remove();
|
||||||
|
jLocale?.Remove();
|
||||||
|
|
||||||
|
if (jDecryptKey != null || jLocale != null)
|
||||||
|
{
|
||||||
|
var newContents = jObj.ToString(Formatting.Indented);
|
||||||
|
File.WriteAllText(Configuration.Instance.SettingsFilePath, newContents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region migrate_to_v4_0_3 add setting for whether to delete/retain aax
|
||||||
|
private static void migrate_to_v4_0_3()
|
||||||
|
{
|
||||||
|
if (!File.Exists(Configuration.Instance.SettingsFilePath))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// use JObject to remove decrypt key and locale from Settings.json
|
||||||
|
var settingsContents = File.ReadAllText(Configuration.Instance.SettingsFilePath);
|
||||||
|
var jObj = JObject.Parse(settingsContents);
|
||||||
|
|
||||||
|
var jRetainAaxFiles = jObj.Property("RetainAaxFiles");
|
||||||
|
if (jRetainAaxFiles is null)
|
||||||
|
{
|
||||||
|
jObj.Add("RetainAaxFiles", false);
|
||||||
|
|
||||||
|
var newContents = jObj.ToString(Formatting.Indented);
|
||||||
|
File.WriteAllText(Configuration.Instance.SettingsFilePath, newContents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
private static string defaultLoggingLevel { get; } = "Information";
|
private static string defaultLoggingLevel { get; } = "Information";
|
||||||
private static void ensureLoggingConfig()
|
private static void ensureLoggingConfig()
|
||||||
@@ -169,11 +320,11 @@ namespace LibationLauncher
|
|||||||
|
|
||||||
// CONFIGURATION-DRIVEN (json)
|
// CONFIGURATION-DRIVEN (json)
|
||||||
var configuration = new ConfigurationBuilder()
|
var configuration = new ConfigurationBuilder()
|
||||||
.AddJsonFile(config.SettingsJsonPath)
|
.AddJsonFile(config.SettingsFilePath)
|
||||||
.Build();
|
.Build();
|
||||||
Log.Logger = new LoggerConfiguration()
|
Log.Logger = new LoggerConfiguration()
|
||||||
.ReadFrom.Configuration(configuration)
|
.ReadFrom.Configuration(configuration)
|
||||||
.CreateLogger();
|
.CreateLogger();
|
||||||
|
|
||||||
//// MANUAL HARD CODED
|
//// MANUAL HARD CODED
|
||||||
//Log.Logger = new LoggerConfiguration()
|
//Log.Logger = new LoggerConfiguration()
|
||||||
@@ -250,7 +401,6 @@ namespace LibationLauncher
|
|||||||
{
|
{
|
||||||
Version = BuildVersion.ToString(),
|
Version = BuildVersion.ToString(),
|
||||||
|
|
||||||
AudibleLocale = config.LocaleCountryCode,
|
|
||||||
config.LibationFiles,
|
config.LibationFiles,
|
||||||
AudibleFileStorage.BooksDirectory,
|
AudibleFileStorage.BooksDirectory,
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -72,7 +72,12 @@ namespace LibationSearchEngine
|
|||||||
["CategoriesId"] = lb => lb.Book.CategoriesIds == null ? null : string.Join(", ", lb.Book.CategoriesIds),
|
["CategoriesId"] = lb => lb.Book.CategoriesIds == null ? null : string.Join(", ", lb.Book.CategoriesIds),
|
||||||
["CategoryId"] = lb => lb.Book.CategoriesIds == null ? null : string.Join(", ", lb.Book.CategoriesIds),
|
["CategoryId"] = lb => lb.Book.CategoriesIds == null ? null : string.Join(", ", lb.Book.CategoriesIds),
|
||||||
|
|
||||||
[TAGS.FirstCharToUpper()] = lb => lb.Book.UserDefinedItem.Tags
|
[TAGS.FirstCharToUpper()] = lb => lb.Book.UserDefinedItem.Tags,
|
||||||
|
|
||||||
|
["Locale"] = lb => lb.Book.Locale,
|
||||||
|
["Region"] = lb => lb.Book.Locale,
|
||||||
|
["Account"] = lb => lb.Account,
|
||||||
|
["Email"] = lb => lb.Account
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0-windows</TargetFramework>
|
||||||
<UseWindowsForms>true</UseWindowsForms>
|
<UseWindowsForms>true</UseWindowsForms>
|
||||||
<ApplicationIcon>libation.ico</ApplicationIcon>
|
<ApplicationIcon>libation.ico</ApplicationIcon>
|
||||||
|
|
||||||
@@ -23,27 +23,27 @@
|
|||||||
<AutoGen>True</AutoGen>
|
<AutoGen>True</AutoGen>
|
||||||
<DependentUpon>Resources.resx</DependentUpon>
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Update="UNTESTED\Dialogs\LibationFilesDialog.cs">
|
<Compile Update="UNTESTED\Dialogs\LibationFilesDialog.cs" />
|
||||||
<SubType>Form</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Update="UNTESTED\Dialogs\LibationFilesDialog.Designer.cs">
|
<Compile Update="UNTESTED\Dialogs\LibationFilesDialog.Designer.cs">
|
||||||
<DependentUpon>LibationFilesDialog.cs</DependentUpon>
|
<DependentUpon>LibationFilesDialog.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Update="UNTESTED\Dialogs\SettingsDialog.cs">
|
<Compile Update="UNTESTED\Dialogs\Login\ApprovalNeededDialog.cs" />
|
||||||
<SubType>Form</SubType>
|
<Compile Update="UNTESTED\Dialogs\Login\ApprovalNeededDialog.Designer.cs">
|
||||||
|
<DependentUpon>ApprovalNeededDialog.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Update="UNTESTED\Dialogs\ScanAccountsDialog.cs" />
|
||||||
|
<Compile Update="UNTESTED\Dialogs\ScanAccountsDialog.Designer.cs">
|
||||||
|
<DependentUpon>ScanAccountsDialog.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Update="UNTESTED\Dialogs\SettingsDialog.cs" />
|
||||||
<Compile Update="UNTESTED\Dialogs\SettingsDialog.Designer.cs">
|
<Compile Update="UNTESTED\Dialogs\SettingsDialog.Designer.cs">
|
||||||
<DependentUpon>SettingsDialog.cs</DependentUpon>
|
<DependentUpon>SettingsDialog.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Update="UNTESTED\Dialogs\IndexLibraryDialog.cs">
|
<Compile Update="UNTESTED\Dialogs\IndexLibraryDialog.cs" />
|
||||||
<SubType>Form</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Update="UNTESTED\Dialogs\IndexLibraryDialog.Designer.cs">
|
<Compile Update="UNTESTED\Dialogs\IndexLibraryDialog.Designer.cs">
|
||||||
<DependentUpon>IndexLibraryDialog.cs</DependentUpon>
|
<DependentUpon>IndexLibraryDialog.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Update="UNTESTED\Dialogs\SetupDialog.cs">
|
<Compile Update="UNTESTED\Dialogs\SetupDialog.cs" />
|
||||||
<SubType>Form</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Update="UNTESTED\Dialogs\SetupDialog.Designer.cs">
|
<Compile Update="UNTESTED\Dialogs\SetupDialog.Designer.cs">
|
||||||
<DependentUpon>SetupDialog.cs</DependentUpon>
|
<DependentUpon>SetupDialog.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -57,6 +57,9 @@
|
|||||||
<EmbeddedResource Update="UNTESTED\Dialogs\LibationFilesDialog.resx">
|
<EmbeddedResource Update="UNTESTED\Dialogs\LibationFilesDialog.resx">
|
||||||
<DependentUpon>LibationFilesDialog.cs</DependentUpon>
|
<DependentUpon>LibationFilesDialog.cs</DependentUpon>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Update="UNTESTED\Dialogs\ScanAccountsDialog.resx">
|
||||||
|
<DependentUpon>ScanAccountsDialog.cs</DependentUpon>
|
||||||
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Update="UNTESTED\Dialogs\SettingsDialog.resx">
|
<EmbeddedResource Update="UNTESTED\Dialogs\SettingsDialog.resx">
|
||||||
<DependentUpon>SettingsDialog.cs</DependentUpon>
|
<DependentUpon>SettingsDialog.cs</DependentUpon>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
|
|||||||
@@ -17,15 +17,15 @@ namespace LibationWinForms.BookLiberation
|
|||||||
// thread-safe UI updates
|
// thread-safe UI updates
|
||||||
public void UpdateFilename(string title) => filenameLbl.UIThread(() => filenameLbl.Text = title);
|
public void UpdateFilename(string title) => filenameLbl.UIThread(() => filenameLbl.Text = title);
|
||||||
|
|
||||||
public void DownloadProgressChanged(long BytesReceived, long TotalBytesToReceive)
|
public void DownloadProgressChanged(long BytesReceived, long? TotalBytesToReceive)
|
||||||
{
|
{
|
||||||
// this won't happen with download file. it will happen with download string
|
// this won't happen with download file. it will happen with download string
|
||||||
if (TotalBytesToReceive < 0)
|
if (!TotalBytesToReceive.HasValue || TotalBytesToReceive.Value <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
progressLbl.UIThread(() => progressLbl.Text = $"{BytesReceived:#,##0} of {TotalBytesToReceive:#,##0}");
|
progressLbl.UIThread(() => progressLbl.Text = $"{BytesReceived:#,##0} of {TotalBytesToReceive.Value:#,##0}");
|
||||||
|
|
||||||
var d = double.Parse(BytesReceived.ToString()) / double.Parse(TotalBytesToReceive.ToString()) * 100.0;
|
var d = double.Parse(BytesReceived.ToString()) / double.Parse(TotalBytesToReceive.Value.ToString()) * 100.0;
|
||||||
var i = int.Parse(Math.Truncate(d).ToString());
|
var i = int.Parse(Math.Truncate(d).ToString());
|
||||||
progressBar1.UIThread(() => progressBar1.Value = i);
|
progressBar1.UIThread(() => progressBar1.Value = i);
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ namespace LibationWinForms.BookLiberation
|
|||||||
{
|
{
|
||||||
public static async Task BackupSingleBookAsync(string productId, EventHandler<LibraryBook> completedAction = null)
|
public static async Task BackupSingleBookAsync(string productId, EventHandler<LibraryBook> completedAction = null)
|
||||||
{
|
{
|
||||||
|
Serilog.Log.Logger.Information("Begin " + nameof(BackupSingleBookAsync) + " {@DebugInfo}", new { productId });
|
||||||
|
|
||||||
var backupBook = getWiredUpBackupBook(completedAction);
|
var backupBook = getWiredUpBackupBook(completedAction);
|
||||||
|
|
||||||
var automatedBackupsForm = attachToBackupsForm(backupBook);
|
var automatedBackupsForm = attachToBackupsForm(backupBook);
|
||||||
@@ -20,6 +22,8 @@ namespace LibationWinForms.BookLiberation
|
|||||||
|
|
||||||
public static async Task BackupAllBooksAsync(EventHandler<LibraryBook> completedAction = null)
|
public static async Task BackupAllBooksAsync(EventHandler<LibraryBook> completedAction = null)
|
||||||
{
|
{
|
||||||
|
Serilog.Log.Logger.Information("Begin " + nameof(BackupAllBooksAsync));
|
||||||
|
|
||||||
var backupBook = getWiredUpBackupBook(completedAction);
|
var backupBook = getWiredUpBackupBook(completedAction);
|
||||||
|
|
||||||
var automatedBackupsForm = attachToBackupsForm(backupBook);
|
var automatedBackupsForm = attachToBackupsForm(backupBook);
|
||||||
@@ -96,6 +100,8 @@ namespace LibationWinForms.BookLiberation
|
|||||||
|
|
||||||
public static async Task BackupAllPdfsAsync(EventHandler<LibraryBook> completedAction = null)
|
public static async Task BackupAllPdfsAsync(EventHandler<LibraryBook> completedAction = null)
|
||||||
{
|
{
|
||||||
|
Serilog.Log.Logger.Information("Begin " + nameof(BackupAllPdfsAsync));
|
||||||
|
|
||||||
var downloadPdf = getWiredUpDownloadPdf(completedAction);
|
var downloadPdf = getWiredUpDownloadPdf(completedAction);
|
||||||
|
|
||||||
var automatedBackupsForm = attachToBackupsForm(downloadPdf);
|
var automatedBackupsForm = attachToBackupsForm(downloadPdf);
|
||||||
@@ -126,7 +132,7 @@ namespace LibationWinForms.BookLiberation
|
|||||||
downloadDialog.UpdateFilename(str);
|
downloadDialog.UpdateFilename(str);
|
||||||
downloadDialog.Show();
|
downloadDialog.Show();
|
||||||
};
|
};
|
||||||
downloadFile.DownloadProgressChanged += (_, progress) => downloadDialog.DownloadProgressChanged(progress.BytesReceived, progress.TotalBytesToReceive.Value);
|
downloadFile.DownloadProgressChanged += (_, progress) => downloadDialog.DownloadProgressChanged(progress.BytesReceived, progress.TotalBytesToReceive);
|
||||||
downloadFile.DownloadCompleted += (_, __) => downloadDialog.Close();
|
downloadFile.DownloadCompleted += (_, __) => downloadDialog.Close();
|
||||||
|
|
||||||
await downloadFile.PerformDownloadFileAsync(url, destination);
|
await downloadFile.PerformDownloadFileAsync(url, destination);
|
||||||
@@ -161,7 +167,7 @@ namespace LibationWinForms.BookLiberation
|
|||||||
void fileDownloadCompleted(object _, string __) => downloadDialog.Close();
|
void fileDownloadCompleted(object _, string __) => downloadDialog.Close();
|
||||||
|
|
||||||
void downloadProgressChanged(object _, Dinah.Core.Net.Http.DownloadProgress progress)
|
void downloadProgressChanged(object _, Dinah.Core.Net.Http.DownloadProgress progress)
|
||||||
=> downloadDialog.DownloadProgressChanged(progress.BytesReceived, progress.TotalBytesToReceive.Value);
|
=> downloadDialog.DownloadProgressChanged(progress.BytesReceived, progress.TotalBytesToReceive);
|
||||||
|
|
||||||
void unsubscribe(object _ = null, EventArgs __ = null)
|
void unsubscribe(object _ = null, EventArgs __ = null)
|
||||||
{
|
{
|
||||||
@@ -282,13 +288,22 @@ namespace LibationWinForms.BookLiberation
|
|||||||
{
|
{
|
||||||
automatedBackupsForm.Show();
|
automatedBackupsForm.Show();
|
||||||
|
|
||||||
|
// processable.ProcessFirstValidAsync used to be encapsulated elsewhere. however, support for 'skip this time only' requires state. iterators provide this state for free. therefore: use foreach/iterator here
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var shouldContinue = true;
|
foreach (var libraryBook in processable.GetValidLibraryBooks())
|
||||||
while (shouldContinue)
|
|
||||||
{
|
{
|
||||||
var statusHandler = await processable.ProcessFirstValidAsync();
|
try
|
||||||
shouldContinue = validateStatus(statusHandler, automatedBackupsForm);
|
{
|
||||||
|
var statusHandler = await processable.ProcessBookAsync_NoValidation(libraryBook);
|
||||||
|
if (!validateStatus(statusHandler, automatedBackupsForm))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
automatedBackupsForm.AppendError(e);
|
||||||
|
DisplaySkipDialog(automatedBackupsForm, libraryBook, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -305,17 +320,63 @@ namespace LibationWinForms.BookLiberation
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var statusHandler = await processable.ProcessSingleAsync(productId);
|
var libraryBook = IProcessableExt.GetSingleLibraryBook(productId);
|
||||||
validateStatus(statusHandler, automatedBackupsForm);
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var statusHandler = await processable.ProcessSingleAsync(libraryBook);
|
||||||
|
validateStatus(statusHandler, automatedBackupsForm);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
automatedBackupsForm.AppendError(ex);
|
||||||
|
DisplaySkipDialog(automatedBackupsForm, libraryBook, ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
automatedBackupsForm.AppendError(ex);
|
automatedBackupsForm.AppendError(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
automatedBackupsForm.FinalizeUI();
|
automatedBackupsForm.FinalizeUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void DisplaySkipDialog(AutomatedBackupsForm automatedBackupsForm, LibraryBook libraryBook, Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var text = @$"
|
||||||
|
The below error occurred while trying to process this book. Skip this book permanently?
|
||||||
|
|
||||||
|
- Click YES to skip this book permanently.
|
||||||
|
|
||||||
|
- Click NO to skip the book this time only. We'll try again later.
|
||||||
|
|
||||||
|
Error:
|
||||||
|
{ex.Message}
|
||||||
|
".Trim();
|
||||||
|
var dialogResult = System.Windows.Forms.MessageBox.Show(
|
||||||
|
text,
|
||||||
|
"Skip importing this book?",
|
||||||
|
System.Windows.Forms.MessageBoxButtons.YesNo,
|
||||||
|
System.Windows.Forms.MessageBoxIcon.Question);
|
||||||
|
|
||||||
|
if (dialogResult != System.Windows.Forms.DialogResult.Yes)
|
||||||
|
{
|
||||||
|
FileManager.AudibleFileStorage.Audio.CreateSkipFile(
|
||||||
|
libraryBook.Book.Title,
|
||||||
|
libraryBook.Book.AudibleProductId,
|
||||||
|
ex.Message + "\r\n|\r\n" + ex.StackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exc)
|
||||||
|
{
|
||||||
|
automatedBackupsForm.AppendText($"Error attempting to display {nameof(DisplaySkipDialog)}");
|
||||||
|
automatedBackupsForm.AppendError(exc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static bool validateStatus(StatusHandler statusHandler, AutomatedBackupsForm automatedBackupsForm)
|
private static bool validateStatus(StatusHandler statusHandler, AutomatedBackupsForm automatedBackupsForm)
|
||||||
{
|
{
|
||||||
if (statusHandler == null)
|
if (statusHandler == null)
|
||||||
|
|||||||
@@ -0,0 +1,146 @@
|
|||||||
|
namespace LibationWinForms.Dialogs
|
||||||
|
{
|
||||||
|
partial class AccountsDialog
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing && (components != null))
|
||||||
|
{
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Windows Form Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
this.cancelBtn = new System.Windows.Forms.Button();
|
||||||
|
this.saveBtn = new System.Windows.Forms.Button();
|
||||||
|
this.dataGridView1 = new System.Windows.Forms.DataGridView();
|
||||||
|
this.DeleteAccount = new System.Windows.Forms.DataGridViewButtonColumn();
|
||||||
|
this.LibraryScan = new System.Windows.Forms.DataGridViewCheckBoxColumn();
|
||||||
|
this.AccountId = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||||
|
this.Locale = new System.Windows.Forms.DataGridViewComboBoxColumn();
|
||||||
|
this.AccountName = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// cancelBtn
|
||||||
|
//
|
||||||
|
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.Location = new System.Drawing.Point(713, 415);
|
||||||
|
this.cancelBtn.Name = "cancelBtn";
|
||||||
|
this.cancelBtn.Size = new System.Drawing.Size(75, 23);
|
||||||
|
this.cancelBtn.TabIndex = 2;
|
||||||
|
this.cancelBtn.Text = "Cancel";
|
||||||
|
this.cancelBtn.UseVisualStyleBackColor = true;
|
||||||
|
this.cancelBtn.Click += new System.EventHandler(this.cancelBtn_Click);
|
||||||
|
//
|
||||||
|
// saveBtn
|
||||||
|
//
|
||||||
|
this.saveBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.saveBtn.Location = new System.Drawing.Point(612, 415);
|
||||||
|
this.saveBtn.Name = "saveBtn";
|
||||||
|
this.saveBtn.Size = new System.Drawing.Size(75, 23);
|
||||||
|
this.saveBtn.TabIndex = 1;
|
||||||
|
this.saveBtn.Text = "Save";
|
||||||
|
this.saveBtn.UseVisualStyleBackColor = true;
|
||||||
|
this.saveBtn.Click += new System.EventHandler(this.saveBtn_Click);
|
||||||
|
//
|
||||||
|
// dataGridView1
|
||||||
|
//
|
||||||
|
this.dataGridView1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.dataGridView1.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.AllCells;
|
||||||
|
this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
|
||||||
|
this.dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
|
||||||
|
this.DeleteAccount,
|
||||||
|
this.LibraryScan,
|
||||||
|
this.AccountId,
|
||||||
|
this.Locale,
|
||||||
|
this.AccountName});
|
||||||
|
this.dataGridView1.Location = new System.Drawing.Point(12, 12);
|
||||||
|
this.dataGridView1.MultiSelect = false;
|
||||||
|
this.dataGridView1.Name = "dataGridView1";
|
||||||
|
this.dataGridView1.Size = new System.Drawing.Size(776, 397);
|
||||||
|
this.dataGridView1.TabIndex = 0;
|
||||||
|
this.dataGridView1.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.DataGridView1_CellContentClick);
|
||||||
|
this.dataGridView1.DefaultValuesNeeded += new System.Windows.Forms.DataGridViewRowEventHandler(this.dataGridView1_DefaultValuesNeeded);
|
||||||
|
//
|
||||||
|
// DeleteAccount
|
||||||
|
//
|
||||||
|
this.DeleteAccount.HeaderText = "Delete";
|
||||||
|
this.DeleteAccount.Name = "DeleteAccount";
|
||||||
|
this.DeleteAccount.ReadOnly = true;
|
||||||
|
this.DeleteAccount.Text = "x";
|
||||||
|
this.DeleteAccount.Width = 44;
|
||||||
|
//
|
||||||
|
// LibraryScan
|
||||||
|
//
|
||||||
|
this.LibraryScan.HeaderText = "Include in library scan?";
|
||||||
|
this.LibraryScan.Name = "LibraryScan";
|
||||||
|
this.LibraryScan.Width = 83;
|
||||||
|
//
|
||||||
|
// AccountId
|
||||||
|
//
|
||||||
|
this.AccountId.HeaderText = "Audible email/login";
|
||||||
|
this.AccountId.Name = "AccountId";
|
||||||
|
this.AccountId.Width = 111;
|
||||||
|
//
|
||||||
|
// Locale
|
||||||
|
//
|
||||||
|
this.Locale.HeaderText = "Locale";
|
||||||
|
this.Locale.Name = "Locale";
|
||||||
|
this.Locale.Width = 45;
|
||||||
|
//
|
||||||
|
// AccountName
|
||||||
|
//
|
||||||
|
this.AccountName.HeaderText = "Account nickname (optional)";
|
||||||
|
this.AccountName.Name = "AccountName";
|
||||||
|
this.AccountName.Width = 152;
|
||||||
|
//
|
||||||
|
// AccountsDialog
|
||||||
|
//
|
||||||
|
this.AcceptButton = this.saveBtn;
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.CancelButton = this.cancelBtn;
|
||||||
|
this.ClientSize = new System.Drawing.Size(800, 450);
|
||||||
|
this.Controls.Add(this.dataGridView1);
|
||||||
|
this.Controls.Add(this.saveBtn);
|
||||||
|
this.Controls.Add(this.cancelBtn);
|
||||||
|
this.Name = "AccountsDialog";
|
||||||
|
this.Text = "Audible Accounts";
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.Button cancelBtn;
|
||||||
|
private System.Windows.Forms.Button saveBtn;
|
||||||
|
private System.Windows.Forms.DataGridView dataGridView1;
|
||||||
|
private System.Windows.Forms.DataGridViewButtonColumn DeleteAccount;
|
||||||
|
private System.Windows.Forms.DataGridViewCheckBoxColumn LibraryScan;
|
||||||
|
private System.Windows.Forms.DataGridViewTextBoxColumn AccountId;
|
||||||
|
private System.Windows.Forms.DataGridViewComboBoxColumn Locale;
|
||||||
|
private System.Windows.Forms.DataGridViewTextBoxColumn AccountName;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using AudibleApi;
|
||||||
|
using InternalUtilities;
|
||||||
|
|
||||||
|
namespace LibationWinForms.Dialogs
|
||||||
|
{
|
||||||
|
public partial class AccountsDialog : Form
|
||||||
|
{
|
||||||
|
const string COL_Delete = nameof(DeleteAccount);
|
||||||
|
const string COL_LibraryScan = nameof(LibraryScan);
|
||||||
|
const string COL_AccountId = nameof(AccountId);
|
||||||
|
const string COL_AccountName = nameof(AccountName);
|
||||||
|
const string COL_Locale = nameof(Locale);
|
||||||
|
|
||||||
|
Form1 _parent { get; }
|
||||||
|
|
||||||
|
public AccountsDialog(Form1 parent)
|
||||||
|
{
|
||||||
|
_parent = parent;
|
||||||
|
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
dataGridView1.Columns[COL_AccountName].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
|
||||||
|
|
||||||
|
populateDropDown();
|
||||||
|
|
||||||
|
populateGridValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void populateDropDown()
|
||||||
|
=> (dataGridView1.Columns[COL_Locale] as DataGridViewComboBoxColumn).DataSource
|
||||||
|
= Localization.Locales
|
||||||
|
.Select(l => l.Name)
|
||||||
|
.OrderBy(a => a).ToList();
|
||||||
|
|
||||||
|
private void populateGridValues()
|
||||||
|
{
|
||||||
|
// WARNING: accounts persister will write ANY EDIT to object immediately to file
|
||||||
|
// here: copy strings and dispose of persister
|
||||||
|
// only persist in 'save' step
|
||||||
|
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||||
|
var accounts = persister.AccountsSettings.Accounts;
|
||||||
|
if (!accounts.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var account in accounts)
|
||||||
|
dataGridView1.Rows.Add(
|
||||||
|
"X",
|
||||||
|
account.LibraryScan,
|
||||||
|
account.AccountId,
|
||||||
|
account.Locale.Name,
|
||||||
|
account.AccountName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dataGridView1_DefaultValuesNeeded(object sender, DataGridViewRowEventArgs e)
|
||||||
|
{
|
||||||
|
e.Row.Cells[COL_Delete].Value = "X";
|
||||||
|
e.Row.Cells[COL_LibraryScan].Value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
|
||||||
|
{
|
||||||
|
var dgv = (DataGridView)sender;
|
||||||
|
|
||||||
|
var col = dgv.Columns[e.ColumnIndex];
|
||||||
|
if (col is DataGridViewButtonColumn && e.RowIndex >= 0)
|
||||||
|
{
|
||||||
|
var row = dgv.Rows[e.RowIndex];
|
||||||
|
switch (col.Name)
|
||||||
|
{
|
||||||
|
case COL_Delete:
|
||||||
|
// if final/edit row: do nothing
|
||||||
|
if (e.RowIndex < dgv.RowCount - 1)
|
||||||
|
dgv.Rows.Remove(row);
|
||||||
|
break;
|
||||||
|
//case COL_MoveUp:
|
||||||
|
// // if top: do nothing
|
||||||
|
// if (e.RowIndex < 1)
|
||||||
|
// break;
|
||||||
|
// dgv.Rows.Remove(row);
|
||||||
|
// dgv.Rows.Insert(e.RowIndex - 1, row);
|
||||||
|
// break;
|
||||||
|
//case COL_MoveDown:
|
||||||
|
// // if final/edit row or bottom filter row: do nothing
|
||||||
|
// if (e.RowIndex >= dgv.RowCount - 2)
|
||||||
|
// break;
|
||||||
|
// dgv.Rows.Remove(row);
|
||||||
|
// dgv.Rows.Insert(e.RowIndex + 1, row);
|
||||||
|
// break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cancelBtn_Click(object sender, EventArgs e) => this.Close();
|
||||||
|
|
||||||
|
class AccountDto
|
||||||
|
{
|
||||||
|
public string AccountId { get; set; }
|
||||||
|
public string AccountName { get; set; }
|
||||||
|
public string LocaleName { get; set; }
|
||||||
|
public bool LibraryScan { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveBtn_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// without transaction, accounts persister will write ANY EDIT immediately to file
|
||||||
|
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||||
|
|
||||||
|
persister.BeginTransation();
|
||||||
|
persist(persister.AccountsSettings);
|
||||||
|
persister.CommitTransation();
|
||||||
|
|
||||||
|
_parent.RefreshImportMenu();
|
||||||
|
this.DialogResult = DialogResult.OK;
|
||||||
|
this.Close();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"Error: {ex.Message}", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void persist(AccountsSettings accountsSettings)
|
||||||
|
{
|
||||||
|
var existingAccounts = accountsSettings.Accounts;
|
||||||
|
var dtos = getRowDtos();
|
||||||
|
|
||||||
|
// editing account id is a special case. an account is defined by its account id, therefore this is really a different account. the user won't care about this distinction though.
|
||||||
|
// these will be caught below by normal means and re-created minus the convenience of persisting identity tokens
|
||||||
|
|
||||||
|
// delete
|
||||||
|
for (var i = existingAccounts.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var existing = existingAccounts[i];
|
||||||
|
if (!dtos.Any(dto =>
|
||||||
|
dto.AccountId?.ToLower().Trim() == existing.AccountId.ToLower()
|
||||||
|
&& dto.LocaleName == existing.Locale?.Name))
|
||||||
|
{
|
||||||
|
accountsSettings.Delete(existing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// upsert each. validation occurs through Account and AccountsSettings
|
||||||
|
foreach (var dto in dtos)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(dto.AccountId))
|
||||||
|
throw new Exception("Please enter an account id for all accounts");
|
||||||
|
if (string.IsNullOrWhiteSpace(dto.LocaleName))
|
||||||
|
throw new Exception("Please select a locale (i.e.: country or region) for all accounts");
|
||||||
|
|
||||||
|
var acct = accountsSettings.Upsert(dto.AccountId, dto.LocaleName);
|
||||||
|
acct.LibraryScan = dto.LibraryScan;
|
||||||
|
acct.AccountName
|
||||||
|
= string.IsNullOrWhiteSpace(dto.AccountName)
|
||||||
|
? $"{dto.AccountId} - {dto.LocaleName}"
|
||||||
|
: dto.AccountName.Trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<AccountDto> getRowDtos()
|
||||||
|
=> dataGridView1.Rows
|
||||||
|
.Cast<DataGridViewRow>()
|
||||||
|
.Where(r => !r.IsNewRow)
|
||||||
|
.Select(r => new AccountDto
|
||||||
|
{
|
||||||
|
AccountId = (string)r.Cells[COL_AccountId].Value,
|
||||||
|
AccountName = (string)r.Cells[COL_AccountName].Value,
|
||||||
|
LocaleName = (string)r.Cells[COL_Locale].Value,
|
||||||
|
LibraryScan = (bool)r.Cells[COL_LibraryScan].Value
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
@@ -117,4 +117,22 @@
|
|||||||
<resheader name="writer">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
|
<metadata name="Original.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>True</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="DeleteAccount.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>True</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="LibraryScan.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>True</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="AccountId.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>True</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="AccountName.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>True</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="Locale.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>True</value>
|
||||||
|
</metadata>
|
||||||
</root>
|
</root>
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
this.cancelBtn.TabIndex = 2;
|
this.cancelBtn.TabIndex = 2;
|
||||||
this.cancelBtn.Text = "Cancel";
|
this.cancelBtn.Text = "Cancel";
|
||||||
this.cancelBtn.UseVisualStyleBackColor = true;
|
this.cancelBtn.UseVisualStyleBackColor = true;
|
||||||
this.cancelBtn.Click += new System.EventHandler(this.CancelBtn_Click);
|
this.cancelBtn.Click += new System.EventHandler(this.cancelBtn_Click);
|
||||||
//
|
//
|
||||||
// saveBtn
|
// saveBtn
|
||||||
//
|
//
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
this.saveBtn.TabIndex = 1;
|
this.saveBtn.TabIndex = 1;
|
||||||
this.saveBtn.Text = "Save";
|
this.saveBtn.Text = "Save";
|
||||||
this.saveBtn.UseVisualStyleBackColor = true;
|
this.saveBtn.UseVisualStyleBackColor = true;
|
||||||
this.saveBtn.Click += new System.EventHandler(this.SaveBtn_Click);
|
this.saveBtn.Click += new System.EventHandler(this.saveBtn_Click);
|
||||||
//
|
//
|
||||||
// dataGridView1
|
// dataGridView1
|
||||||
//
|
//
|
||||||
@@ -81,6 +81,7 @@
|
|||||||
this.dataGridView1.Size = new System.Drawing.Size(776, 397);
|
this.dataGridView1.Size = new System.Drawing.Size(776, 397);
|
||||||
this.dataGridView1.TabIndex = 0;
|
this.dataGridView1.TabIndex = 0;
|
||||||
this.dataGridView1.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.DataGridView1_CellContentClick);
|
this.dataGridView1.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.DataGridView1_CellContentClick);
|
||||||
|
this.dataGridView1.DefaultValuesNeeded += new System.Windows.Forms.DataGridViewRowEventHandler(this.dataGridView1_DefaultValuesNeeded);
|
||||||
//
|
//
|
||||||
// Original
|
// Original
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1,23 +1,20 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Data;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using FileManager;
|
using FileManager;
|
||||||
|
|
||||||
namespace LibationWinForms.Dialogs
|
namespace LibationWinForms.Dialogs
|
||||||
{
|
{
|
||||||
public partial class EditQuickFilters : Form
|
public partial class EditQuickFilters : Form
|
||||||
{
|
{
|
||||||
const string COL_Original = "Original";
|
const string BLACK_UP_POINTING_TRIANGLE = "\u25B2";
|
||||||
const string COL_Delete = "Delete";
|
const string BLACK_DOWN_POINTING_TRIANGLE = "\u25BC";
|
||||||
const string COL_Filter = "Filter";
|
|
||||||
const string COL_MoveUp = "MoveUp";
|
const string COL_Original = nameof(Original);
|
||||||
const string COL_MoveDown = "MoveDown";
|
const string COL_Delete = nameof(Delete);
|
||||||
|
const string COL_Filter = nameof(Filter);
|
||||||
|
const string COL_MoveUp = nameof(MoveUp);
|
||||||
|
const string COL_MoveDown = nameof(MoveDown);
|
||||||
|
|
||||||
Form1 _parent { get; }
|
Form1 _parent { get; }
|
||||||
|
|
||||||
@@ -29,20 +26,27 @@ namespace LibationWinForms.Dialogs
|
|||||||
|
|
||||||
dataGridView1.Columns[COL_Filter].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
|
dataGridView1.Columns[COL_Filter].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
|
||||||
|
|
||||||
populateFilters();
|
populateGridValues();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void populateFilters()
|
private void populateGridValues()
|
||||||
{
|
{
|
||||||
var filters = QuickFilters.Filters;
|
var filters = QuickFilters.Filters;
|
||||||
if (!filters.Any())
|
if (!filters.Any())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var filter in filters)
|
foreach (var filter in filters)
|
||||||
dataGridView1.Rows.Add(filter, "X", filter, "\u25B2", "\u25BC");
|
dataGridView1.Rows.Add(filter, "X", filter, BLACK_UP_POINTING_TRIANGLE, BLACK_DOWN_POINTING_TRIANGLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SaveBtn_Click(object sender, EventArgs e)
|
private void dataGridView1_DefaultValuesNeeded(object sender, DataGridViewRowEventArgs e)
|
||||||
|
{
|
||||||
|
e.Row.Cells[COL_Delete].Value = "X";
|
||||||
|
e.Row.Cells[COL_MoveUp].Value = BLACK_UP_POINTING_TRIANGLE;
|
||||||
|
e.Row.Cells[COL_MoveDown].Value = BLACK_DOWN_POINTING_TRIANGLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveBtn_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
var list = dataGridView1.Rows
|
var list = dataGridView1.Rows
|
||||||
.OfType<DataGridViewRow>()
|
.OfType<DataGridViewRow>()
|
||||||
@@ -51,10 +55,11 @@ namespace LibationWinForms.Dialogs
|
|||||||
QuickFilters.ReplaceAll(list);
|
QuickFilters.ReplaceAll(list);
|
||||||
|
|
||||||
_parent.UpdateFilterDropDown();
|
_parent.UpdateFilterDropDown();
|
||||||
|
this.DialogResult = DialogResult.OK;
|
||||||
this.Close();
|
this.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CancelBtn_Click(object sender, EventArgs e) => this.Close();
|
private void cancelBtn_Click(object sender, EventArgs e) => this.Close();
|
||||||
|
|
||||||
private void DataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
|
private void DataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
this.label1.AutoSize = true;
|
this.label1.AutoSize = true;
|
||||||
this.label1.Location = new System.Drawing.Point(28, 24);
|
this.label1.Location = new System.Drawing.Point(28, 24);
|
||||||
this.label1.Name = "label1";
|
this.label1.Name = "label1";
|
||||||
this.label1.Size = new System.Drawing.Size(260, 13);
|
this.label1.Size = new System.Drawing.Size(263, 13);
|
||||||
this.label1.TabIndex = 0;
|
this.label1.TabIndex = 0;
|
||||||
this.label1.Text = "Scanning Audible library. This may take a few minutes";
|
this.label1.Text = "Scanning Audible library. This may take a few minutes";
|
||||||
//
|
//
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.ClientSize = new System.Drawing.Size(319, 63);
|
this.ClientSize = new System.Drawing.Size(440, 63);
|
||||||
this.Controls.Add(this.label1);
|
this.Controls.Add(this.label1);
|
||||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||||
this.MaximizeBox = false;
|
this.MaximizeBox = false;
|
||||||
|
|||||||
@@ -1,31 +1,44 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using ApplicationServices;
|
using ApplicationServices;
|
||||||
|
using InternalUtilities;
|
||||||
using LibationWinForms.Login;
|
using LibationWinForms.Login;
|
||||||
|
|
||||||
namespace LibationWinForms.Dialogs
|
namespace LibationWinForms.Dialogs
|
||||||
{
|
{
|
||||||
public partial class IndexLibraryDialog : Form
|
public partial class IndexLibraryDialog : Form
|
||||||
{
|
{
|
||||||
|
private Account[] _accounts { get; }
|
||||||
|
|
||||||
public int NewBooksAdded { get; private set; }
|
public int NewBooksAdded { get; private set; }
|
||||||
public int TotalBooksProcessed { get; private set; }
|
public int TotalBooksProcessed { get; private set; }
|
||||||
|
|
||||||
public IndexLibraryDialog()
|
public IndexLibraryDialog(params Account[] accounts)
|
||||||
{
|
{
|
||||||
|
_accounts = accounts;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
this.Shown += IndexLibraryDialog_Shown;
|
this.Shown += IndexLibraryDialog_Shown;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void IndexLibraryDialog_Shown(object sender, System.EventArgs e)
|
private async void IndexLibraryDialog_Shown(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
try
|
if (_accounts != null && _accounts.Length > 0)
|
||||||
{
|
{
|
||||||
(TotalBooksProcessed, NewBooksAdded) = await LibraryCommands.ImportLibraryAsync(new WinformResponder());
|
this.label1.Text
|
||||||
}
|
= (_accounts.Length == 1)
|
||||||
catch (Exception ex)
|
? "Scanning Audible library. This may take a few minutes."
|
||||||
{
|
: $"Scanning Audible library: {_accounts.Length} accounts. This may take a few minutes per account.";
|
||||||
var msg = "Error importing library. Please try again. If this still happens after 2 or 3 tries, stop and contact administrator";
|
|
||||||
MessageBox.Show(msg, "Error importing library", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
try
|
||||||
|
{
|
||||||
|
(TotalBooksProcessed, NewBooksAdded) = await LibraryCommands.ImportAccountAsync((account) => new WinformResponder(account), _accounts);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
var msg = "Error importing library. Please try again. If this still happens after 2 or 3 tries, stop and contact administrator";
|
||||||
|
Serilog.Log.Logger.Error(ex, msg);
|
||||||
|
MessageBox.Show(msg, "Error importing library", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.Close();
|
this.Close();
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
namespace LibationWinForms.Dialogs.Login
|
||||||
|
{
|
||||||
|
partial class ApprovalNeededDialog
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing && (components != null))
|
||||||
|
{
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Windows Form Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
this.approvedBtn = new System.Windows.Forms.Button();
|
||||||
|
this.label1 = new System.Windows.Forms.Label();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// approvedBtn
|
||||||
|
//
|
||||||
|
this.approvedBtn.Location = new System.Drawing.Point(15, 25);
|
||||||
|
this.approvedBtn.Name = "approvedBtn";
|
||||||
|
this.approvedBtn.Size = new System.Drawing.Size(79, 23);
|
||||||
|
this.approvedBtn.TabIndex = 1;
|
||||||
|
this.approvedBtn.Text = "Approved";
|
||||||
|
this.approvedBtn.UseVisualStyleBackColor = true;
|
||||||
|
this.approvedBtn.Click += new System.EventHandler(this.approvedBtn_Click);
|
||||||
|
//
|
||||||
|
// label1
|
||||||
|
//
|
||||||
|
this.label1.AutoSize = true;
|
||||||
|
this.label1.Location = new System.Drawing.Point(12, 9);
|
||||||
|
this.label1.Name = "label1";
|
||||||
|
this.label1.Size = new System.Drawing.Size(104, 13);
|
||||||
|
this.label1.TabIndex = 0;
|
||||||
|
this.label1.Text = "Click after approving";
|
||||||
|
//
|
||||||
|
// ApprovalNeededDialog
|
||||||
|
//
|
||||||
|
this.AcceptButton = this.approvedBtn;
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.ClientSize = new System.Drawing.Size(149, 60);
|
||||||
|
this.Controls.Add(this.label1);
|
||||||
|
this.Controls.Add(this.approvedBtn);
|
||||||
|
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||||
|
this.MaximizeBox = false;
|
||||||
|
this.MinimizeBox = false;
|
||||||
|
this.Name = "ApprovalNeededDialog";
|
||||||
|
this.ShowIcon = false;
|
||||||
|
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||||
|
this.Text = "Approval Needed";
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
this.PerformLayout();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
private System.Windows.Forms.Button approvedBtn;
|
||||||
|
private System.Windows.Forms.Label label1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace LibationWinForms.Dialogs.Login
|
||||||
|
{
|
||||||
|
public partial class ApprovalNeededDialog : Form
|
||||||
|
{
|
||||||
|
public ApprovalNeededDialog()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void approvedBtn_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
DialogResult = DialogResult.OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,10 +29,10 @@
|
|||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
{
|
{
|
||||||
this.passwordLbl = new System.Windows.Forms.Label();
|
this.passwordLbl = new System.Windows.Forms.Label();
|
||||||
this.emailLbl = new System.Windows.Forms.Label();
|
|
||||||
this.passwordTb = new System.Windows.Forms.TextBox();
|
this.passwordTb = new System.Windows.Forms.TextBox();
|
||||||
this.emailTb = new System.Windows.Forms.TextBox();
|
|
||||||
this.submitBtn = new System.Windows.Forms.Button();
|
this.submitBtn = new System.Windows.Forms.Button();
|
||||||
|
this.localeLbl = new System.Windows.Forms.Label();
|
||||||
|
this.usernameLbl = new System.Windows.Forms.Label();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
// passwordLbl
|
// passwordLbl
|
||||||
@@ -44,15 +44,6 @@
|
|||||||
this.passwordLbl.TabIndex = 2;
|
this.passwordLbl.TabIndex = 2;
|
||||||
this.passwordLbl.Text = "Password";
|
this.passwordLbl.Text = "Password";
|
||||||
//
|
//
|
||||||
// emailLbl
|
|
||||||
//
|
|
||||||
this.emailLbl.AutoSize = true;
|
|
||||||
this.emailLbl.Location = new System.Drawing.Point(12, 15);
|
|
||||||
this.emailLbl.Name = "emailLbl";
|
|
||||||
this.emailLbl.Size = new System.Drawing.Size(32, 13);
|
|
||||||
this.emailLbl.TabIndex = 0;
|
|
||||||
this.emailLbl.Text = "Email";
|
|
||||||
//
|
|
||||||
// passwordTb
|
// passwordTb
|
||||||
//
|
//
|
||||||
this.passwordTb.Location = new System.Drawing.Point(71, 38);
|
this.passwordTb.Location = new System.Drawing.Point(71, 38);
|
||||||
@@ -61,13 +52,6 @@
|
|||||||
this.passwordTb.Size = new System.Drawing.Size(200, 20);
|
this.passwordTb.Size = new System.Drawing.Size(200, 20);
|
||||||
this.passwordTb.TabIndex = 3;
|
this.passwordTb.TabIndex = 3;
|
||||||
//
|
//
|
||||||
// emailTb
|
|
||||||
//
|
|
||||||
this.emailTb.Location = new System.Drawing.Point(71, 12);
|
|
||||||
this.emailTb.Name = "emailTb";
|
|
||||||
this.emailTb.Size = new System.Drawing.Size(200, 20);
|
|
||||||
this.emailTb.TabIndex = 1;
|
|
||||||
//
|
|
||||||
// submitBtn
|
// submitBtn
|
||||||
//
|
//
|
||||||
this.submitBtn.Location = new System.Drawing.Point(196, 64);
|
this.submitBtn.Location = new System.Drawing.Point(196, 64);
|
||||||
@@ -78,17 +62,35 @@
|
|||||||
this.submitBtn.UseVisualStyleBackColor = true;
|
this.submitBtn.UseVisualStyleBackColor = true;
|
||||||
this.submitBtn.Click += new System.EventHandler(this.submitBtn_Click);
|
this.submitBtn.Click += new System.EventHandler(this.submitBtn_Click);
|
||||||
//
|
//
|
||||||
|
// localeLbl
|
||||||
|
//
|
||||||
|
this.localeLbl.AutoSize = true;
|
||||||
|
this.localeLbl.Location = new System.Drawing.Point(12, 9);
|
||||||
|
this.localeLbl.Name = "localeLbl";
|
||||||
|
this.localeLbl.Size = new System.Drawing.Size(59, 13);
|
||||||
|
this.localeLbl.TabIndex = 0;
|
||||||
|
this.localeLbl.Text = "Locale: {0}";
|
||||||
|
//
|
||||||
|
// usernameLbl
|
||||||
|
//
|
||||||
|
this.usernameLbl.AutoSize = true;
|
||||||
|
this.usernameLbl.Location = new System.Drawing.Point(12, 22);
|
||||||
|
this.usernameLbl.Name = "usernameLbl";
|
||||||
|
this.usernameLbl.Size = new System.Drawing.Size(75, 13);
|
||||||
|
this.usernameLbl.TabIndex = 1;
|
||||||
|
this.usernameLbl.Text = "Username: {0}";
|
||||||
|
//
|
||||||
// AudibleLoginDialog
|
// AudibleLoginDialog
|
||||||
//
|
//
|
||||||
this.AcceptButton = this.submitBtn;
|
this.AcceptButton = this.submitBtn;
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.ClientSize = new System.Drawing.Size(283, 99);
|
this.ClientSize = new System.Drawing.Size(283, 99);
|
||||||
|
this.Controls.Add(this.usernameLbl);
|
||||||
|
this.Controls.Add(this.localeLbl);
|
||||||
this.Controls.Add(this.submitBtn);
|
this.Controls.Add(this.submitBtn);
|
||||||
this.Controls.Add(this.passwordLbl);
|
this.Controls.Add(this.passwordLbl);
|
||||||
this.Controls.Add(this.emailLbl);
|
|
||||||
this.Controls.Add(this.passwordTb);
|
this.Controls.Add(this.passwordTb);
|
||||||
this.Controls.Add(this.emailTb);
|
|
||||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||||
this.MaximizeBox = false;
|
this.MaximizeBox = false;
|
||||||
this.MinimizeBox = false;
|
this.MinimizeBox = false;
|
||||||
@@ -104,9 +106,9 @@
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private System.Windows.Forms.Label passwordLbl;
|
private System.Windows.Forms.Label passwordLbl;
|
||||||
private System.Windows.Forms.Label emailLbl;
|
|
||||||
private System.Windows.Forms.TextBox passwordTb;
|
private System.Windows.Forms.TextBox passwordTb;
|
||||||
private System.Windows.Forms.TextBox emailTb;
|
|
||||||
private System.Windows.Forms.Button submitBtn;
|
private System.Windows.Forms.Button submitBtn;
|
||||||
|
private System.Windows.Forms.Label localeLbl;
|
||||||
|
private System.Windows.Forms.Label usernameLbl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,23 +1,36 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using InternalUtilities;
|
||||||
|
|
||||||
namespace LibationWinForms.Dialogs.Login
|
namespace LibationWinForms.Dialogs.Login
|
||||||
{
|
{
|
||||||
public partial class AudibleLoginDialog : Form
|
public partial class AudibleLoginDialog : Form
|
||||||
{
|
{
|
||||||
|
private string locale { get; }
|
||||||
|
private string accountId { get; }
|
||||||
|
|
||||||
public string Email { get; private set; }
|
public string Email { get; private set; }
|
||||||
public string Password { get; private set; }
|
public string Password { get; private set; }
|
||||||
|
|
||||||
public AudibleLoginDialog()
|
public AudibleLoginDialog(Account account)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
locale = account.Locale.Name;
|
||||||
|
accountId = account.AccountId;
|
||||||
|
|
||||||
|
// do not allow user to change login id here. if they do then jsonpath will fail
|
||||||
|
this.localeLbl.Text = string.Format(this.localeLbl.Text, locale);
|
||||||
|
this.usernameLbl.Text = string.Format(this.usernameLbl.Text, accountId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void submitBtn_Click(object sender, EventArgs e)
|
private void submitBtn_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
Email = this.emailTb.Text;
|
Email = accountId;
|
||||||
Password = this.passwordTb.Text;
|
Password = this.passwordTb.Text;
|
||||||
|
|
||||||
DialogResult = DialogResult.OK;
|
DialogResult = DialogResult.OK;
|
||||||
|
// Close() not needed for AcceptButton
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,7 +25,9 @@ namespace LibationWinForms.Dialogs.Login
|
|||||||
private void submitBtn_Click(object sender, EventArgs e)
|
private void submitBtn_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
Answer = this.answerTb.Text;
|
Answer = this.answerTb.Text;
|
||||||
|
|
||||||
DialogResult = DialogResult.OK;
|
DialogResult = DialogResult.OK;
|
||||||
|
// Close() not needed for AcceptButton
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,19 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using AudibleApi;
|
||||||
|
using InternalUtilities;
|
||||||
using LibationWinForms.Dialogs.Login;
|
using LibationWinForms.Dialogs.Login;
|
||||||
|
|
||||||
namespace LibationWinForms.Login
|
namespace LibationWinForms.Login
|
||||||
{
|
{
|
||||||
public class WinformResponder : AudibleApi.ILoginCallback
|
public class WinformResponder : ILoginCallback
|
||||||
{
|
{
|
||||||
|
private Account _account { get; }
|
||||||
|
|
||||||
|
public WinformResponder(Account account)
|
||||||
|
{
|
||||||
|
_account = Dinah.Core.ArgumentValidator.EnsureNotNull(account, nameof(account));
|
||||||
|
}
|
||||||
|
|
||||||
public string Get2faCode()
|
public string Get2faCode()
|
||||||
{
|
{
|
||||||
using var dialog = new _2faCodeDialog();
|
using var dialog = new _2faCodeDialog();
|
||||||
@@ -23,10 +32,16 @@ namespace LibationWinForms.Login
|
|||||||
|
|
||||||
public (string email, string password) GetLogin()
|
public (string email, string password) GetLogin()
|
||||||
{
|
{
|
||||||
using var dialog = new AudibleLoginDialog();
|
using var dialog = new AudibleLoginDialog(_account);
|
||||||
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
|
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
|
||||||
return (dialog.Email, dialog.Password);
|
return (dialog.Email, dialog.Password);
|
||||||
return (null, null);
|
return (null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ShowApprovalNeeded()
|
||||||
|
{
|
||||||
|
using var dialog = new ApprovalNeededDialog();
|
||||||
|
dialog.ShowDialog();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
namespace LibationWinForms.Dialogs
|
||||||
|
{
|
||||||
|
partial class ScanAccountsDialog
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing && (components != null))
|
||||||
|
{
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Windows Form Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
this.accountsLbl = new System.Windows.Forms.Label();
|
||||||
|
this.accountsClb = new System.Windows.Forms.CheckedListBox();
|
||||||
|
this.importBtn = new System.Windows.Forms.Button();
|
||||||
|
this.cancelBtn = new System.Windows.Forms.Button();
|
||||||
|
this.editBtn = new System.Windows.Forms.Button();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// accountsLbl
|
||||||
|
//
|
||||||
|
this.accountsLbl.AutoSize = true;
|
||||||
|
this.accountsLbl.Location = new System.Drawing.Point(12, 9);
|
||||||
|
this.accountsLbl.Name = "accountsLbl";
|
||||||
|
this.accountsLbl.Size = new System.Drawing.Size(467, 13);
|
||||||
|
this.accountsLbl.TabIndex = 0;
|
||||||
|
this.accountsLbl.Text = "Check the accounts to scan and import. To change default selections, go to: Setti" +
|
||||||
|
"ngs > Accounts";
|
||||||
|
//
|
||||||
|
// accountsClb
|
||||||
|
//
|
||||||
|
this.accountsClb.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.accountsClb.FormattingEnabled = true;
|
||||||
|
this.accountsClb.Location = new System.Drawing.Point(12, 25);
|
||||||
|
this.accountsClb.Name = "accountsClb";
|
||||||
|
this.accountsClb.Size = new System.Drawing.Size(560, 94);
|
||||||
|
this.accountsClb.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// importBtn
|
||||||
|
//
|
||||||
|
this.importBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.importBtn.Location = new System.Drawing.Point(396, 125);
|
||||||
|
this.importBtn.Name = "importBtn";
|
||||||
|
this.importBtn.Size = new System.Drawing.Size(75, 23);
|
||||||
|
this.importBtn.TabIndex = 3;
|
||||||
|
this.importBtn.Text = "Import";
|
||||||
|
this.importBtn.UseVisualStyleBackColor = true;
|
||||||
|
this.importBtn.Click += new System.EventHandler(this.importBtn_Click);
|
||||||
|
//
|
||||||
|
// cancelBtn
|
||||||
|
//
|
||||||
|
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.Location = new System.Drawing.Point(497, 125);
|
||||||
|
this.cancelBtn.Name = "cancelBtn";
|
||||||
|
this.cancelBtn.Size = new System.Drawing.Size(75, 23);
|
||||||
|
this.cancelBtn.TabIndex = 4;
|
||||||
|
this.cancelBtn.Text = "Cancel";
|
||||||
|
this.cancelBtn.UseVisualStyleBackColor = true;
|
||||||
|
this.cancelBtn.Click += new System.EventHandler(this.cancelBtn_Click);
|
||||||
|
//
|
||||||
|
// editBtn
|
||||||
|
//
|
||||||
|
this.editBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||||
|
this.editBtn.Location = new System.Drawing.Point(12, 125);
|
||||||
|
this.editBtn.Name = "editBtn";
|
||||||
|
this.editBtn.Size = new System.Drawing.Size(90, 23);
|
||||||
|
this.editBtn.TabIndex = 2;
|
||||||
|
this.editBtn.Text = "Edit accounts";
|
||||||
|
this.editBtn.UseVisualStyleBackColor = true;
|
||||||
|
this.editBtn.Click += new System.EventHandler(this.editBtn_Click);
|
||||||
|
//
|
||||||
|
// ScanAccountsDialog
|
||||||
|
//
|
||||||
|
this.AcceptButton = this.importBtn;
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.CancelButton = this.cancelBtn;
|
||||||
|
this.ClientSize = new System.Drawing.Size(584, 160);
|
||||||
|
this.Controls.Add(this.editBtn);
|
||||||
|
this.Controls.Add(this.cancelBtn);
|
||||||
|
this.Controls.Add(this.importBtn);
|
||||||
|
this.Controls.Add(this.accountsClb);
|
||||||
|
this.Controls.Add(this.accountsLbl);
|
||||||
|
this.Name = "ScanAccountsDialog";
|
||||||
|
this.Text = "Which accounts?";
|
||||||
|
this.Load += new System.EventHandler(this.ScanAccountsDialog_Load);
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
this.PerformLayout();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.Label accountsLbl;
|
||||||
|
private System.Windows.Forms.CheckedListBox accountsClb;
|
||||||
|
private System.Windows.Forms.Button importBtn;
|
||||||
|
private System.Windows.Forms.Button cancelBtn;
|
||||||
|
private System.Windows.Forms.Button editBtn;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using InternalUtilities;
|
||||||
|
|
||||||
|
namespace LibationWinForms.Dialogs
|
||||||
|
{
|
||||||
|
public partial class ScanAccountsDialog : Form
|
||||||
|
{
|
||||||
|
public List<Account> CheckedAccounts { get; } = new List<Account>();
|
||||||
|
|
||||||
|
Form1 _parent { get; }
|
||||||
|
|
||||||
|
public ScanAccountsDialog(Form1 parent)
|
||||||
|
{
|
||||||
|
_parent = parent;
|
||||||
|
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
class listItem
|
||||||
|
{
|
||||||
|
public Account Account { get; set; }
|
||||||
|
public string Text { get; set; }
|
||||||
|
public override string ToString() => Text;
|
||||||
|
}
|
||||||
|
private void ScanAccountsDialog_Load(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||||
|
var accounts = persister.AccountsSettings.Accounts;
|
||||||
|
|
||||||
|
foreach (var account in accounts)
|
||||||
|
{
|
||||||
|
var item = new listItem
|
||||||
|
{
|
||||||
|
Account = account,
|
||||||
|
Text = $"{account.AccountName} ({account.AccountId} - {account.Locale.Name})"
|
||||||
|
};
|
||||||
|
this.accountsClb.Items.Add(item, account.LibraryScan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void editBtn_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (new AccountsDialog(_parent).ShowDialog() == DialogResult.OK)
|
||||||
|
{
|
||||||
|
// clear grid
|
||||||
|
this.accountsClb.Items.Clear();
|
||||||
|
|
||||||
|
// reload grid and default checkboxes
|
||||||
|
ScanAccountsDialog_Load(sender, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void importBtn_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
foreach (listItem item in accountsClb.CheckedItems)
|
||||||
|
CheckedAccounts.Add(item.Account);
|
||||||
|
|
||||||
|
this.DialogResult = DialogResult.OK;
|
||||||
|
this.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cancelBtn_Click(object sender, EventArgs e) => this.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
File renamed without changes.
@@ -89,7 +89,7 @@
|
|||||||
//
|
//
|
||||||
this.closeBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
this.closeBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.closeBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
this.closeBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||||
this.closeBtn.Location = new System.Drawing.Point(890, 415);
|
this.closeBtn.Location = new System.Drawing.Point(890, 465);
|
||||||
this.closeBtn.Name = "closeBtn";
|
this.closeBtn.Name = "closeBtn";
|
||||||
this.closeBtn.Size = new System.Drawing.Size(75, 23);
|
this.closeBtn.Size = new System.Drawing.Size(75, 23);
|
||||||
this.closeBtn.TabIndex = 5;
|
this.closeBtn.TabIndex = 5;
|
||||||
@@ -103,7 +103,7 @@
|
|||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.CancelButton = this.closeBtn;
|
this.CancelButton = this.closeBtn;
|
||||||
this.ClientSize = new System.Drawing.Size(977, 450);
|
this.ClientSize = new System.Drawing.Size(977, 500);
|
||||||
this.Controls.Add(this.closeBtn);
|
this.Controls.Add(this.closeBtn);
|
||||||
this.Controls.Add(this.label5);
|
this.Controls.Add(this.label5);
|
||||||
this.Controls.Add(this.label4);
|
this.Controls.Add(this.label4);
|
||||||
|
|||||||
+7
-72
@@ -28,12 +28,9 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
{
|
{
|
||||||
this.decryptKeyLbl = new System.Windows.Forms.Label();
|
|
||||||
this.decryptKeyTb = new System.Windows.Forms.TextBox();
|
|
||||||
this.booksLocationLbl = new System.Windows.Forms.Label();
|
this.booksLocationLbl = new System.Windows.Forms.Label();
|
||||||
this.booksLocationTb = new System.Windows.Forms.TextBox();
|
this.booksLocationTb = new System.Windows.Forms.TextBox();
|
||||||
this.booksLocationSearchBtn = new System.Windows.Forms.Button();
|
this.booksLocationSearchBtn = new System.Windows.Forms.Button();
|
||||||
this.decryptKeyDescLbl = new System.Windows.Forms.Label();
|
|
||||||
this.booksLocationDescLbl = new System.Windows.Forms.Label();
|
this.booksLocationDescLbl = new System.Windows.Forms.Label();
|
||||||
this.downloadsInProgressGb = new System.Windows.Forms.GroupBox();
|
this.downloadsInProgressGb = new System.Windows.Forms.GroupBox();
|
||||||
this.downloadsInProgressLibationFilesRb = new System.Windows.Forms.RadioButton();
|
this.downloadsInProgressLibationFilesRb = new System.Windows.Forms.RadioButton();
|
||||||
@@ -45,30 +42,12 @@
|
|||||||
this.decryptInProgressDescLbl = new System.Windows.Forms.Label();
|
this.decryptInProgressDescLbl = new System.Windows.Forms.Label();
|
||||||
this.saveBtn = new System.Windows.Forms.Button();
|
this.saveBtn = new System.Windows.Forms.Button();
|
||||||
this.cancelBtn = new System.Windows.Forms.Button();
|
this.cancelBtn = new System.Windows.Forms.Button();
|
||||||
this.audibleLocaleLbl = new System.Windows.Forms.Label();
|
|
||||||
this.audibleLocaleCb = new System.Windows.Forms.ComboBox();
|
|
||||||
this.groupBox1 = new System.Windows.Forms.GroupBox();
|
this.groupBox1 = new System.Windows.Forms.GroupBox();
|
||||||
this.downloadsInProgressGb.SuspendLayout();
|
this.downloadsInProgressGb.SuspendLayout();
|
||||||
this.decryptInProgressGb.SuspendLayout();
|
this.decryptInProgressGb.SuspendLayout();
|
||||||
this.groupBox1.SuspendLayout();
|
this.groupBox1.SuspendLayout();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
// decryptKeyLbl
|
|
||||||
//
|
|
||||||
this.decryptKeyLbl.AutoSize = true;
|
|
||||||
this.decryptKeyLbl.Location = new System.Drawing.Point(6, 22);
|
|
||||||
this.decryptKeyLbl.Name = "decryptKeyLbl";
|
|
||||||
this.decryptKeyLbl.Size = new System.Drawing.Size(64, 13);
|
|
||||||
this.decryptKeyLbl.TabIndex = 0;
|
|
||||||
this.decryptKeyLbl.Text = "Decrypt key";
|
|
||||||
//
|
|
||||||
// decryptKeyTb
|
|
||||||
//
|
|
||||||
this.decryptKeyTb.Location = new System.Drawing.Point(76, 19);
|
|
||||||
this.decryptKeyTb.Name = "decryptKeyTb";
|
|
||||||
this.decryptKeyTb.Size = new System.Drawing.Size(100, 20);
|
|
||||||
this.decryptKeyTb.TabIndex = 1;
|
|
||||||
//
|
|
||||||
// booksLocationLbl
|
// booksLocationLbl
|
||||||
//
|
//
|
||||||
this.booksLocationLbl.AutoSize = true;
|
this.booksLocationLbl.AutoSize = true;
|
||||||
@@ -95,15 +74,6 @@
|
|||||||
this.booksLocationSearchBtn.UseVisualStyleBackColor = true;
|
this.booksLocationSearchBtn.UseVisualStyleBackColor = true;
|
||||||
this.booksLocationSearchBtn.Click += new System.EventHandler(this.booksLocationSearchBtn_Click);
|
this.booksLocationSearchBtn.Click += new System.EventHandler(this.booksLocationSearchBtn_Click);
|
||||||
//
|
//
|
||||||
// decryptKeyDescLbl
|
|
||||||
//
|
|
||||||
this.decryptKeyDescLbl.AutoSize = true;
|
|
||||||
this.decryptKeyDescLbl.Location = new System.Drawing.Point(73, 42);
|
|
||||||
this.decryptKeyDescLbl.Name = "decryptKeyDescLbl";
|
|
||||||
this.decryptKeyDescLbl.Size = new System.Drawing.Size(36, 13);
|
|
||||||
this.decryptKeyDescLbl.TabIndex = 2;
|
|
||||||
this.decryptKeyDescLbl.Text = "[desc]";
|
|
||||||
//
|
|
||||||
// booksLocationDescLbl
|
// booksLocationDescLbl
|
||||||
//
|
//
|
||||||
this.booksLocationDescLbl.AutoSize = true;
|
this.booksLocationDescLbl.AutoSize = true;
|
||||||
@@ -118,7 +88,7 @@
|
|||||||
this.downloadsInProgressGb.Controls.Add(this.downloadsInProgressLibationFilesRb);
|
this.downloadsInProgressGb.Controls.Add(this.downloadsInProgressLibationFilesRb);
|
||||||
this.downloadsInProgressGb.Controls.Add(this.downloadsInProgressWinTempRb);
|
this.downloadsInProgressGb.Controls.Add(this.downloadsInProgressWinTempRb);
|
||||||
this.downloadsInProgressGb.Controls.Add(this.downloadsInProgressDescLbl);
|
this.downloadsInProgressGb.Controls.Add(this.downloadsInProgressDescLbl);
|
||||||
this.downloadsInProgressGb.Location = new System.Drawing.Point(15, 58);
|
this.downloadsInProgressGb.Location = new System.Drawing.Point(15, 19);
|
||||||
this.downloadsInProgressGb.Name = "downloadsInProgressGb";
|
this.downloadsInProgressGb.Name = "downloadsInProgressGb";
|
||||||
this.downloadsInProgressGb.Size = new System.Drawing.Size(758, 117);
|
this.downloadsInProgressGb.Size = new System.Drawing.Size(758, 117);
|
||||||
this.downloadsInProgressGb.TabIndex = 4;
|
this.downloadsInProgressGb.TabIndex = 4;
|
||||||
@@ -163,7 +133,7 @@
|
|||||||
this.decryptInProgressGb.Controls.Add(this.decryptInProgressLibationFilesRb);
|
this.decryptInProgressGb.Controls.Add(this.decryptInProgressLibationFilesRb);
|
||||||
this.decryptInProgressGb.Controls.Add(this.decryptInProgressWinTempRb);
|
this.decryptInProgressGb.Controls.Add(this.decryptInProgressWinTempRb);
|
||||||
this.decryptInProgressGb.Controls.Add(this.decryptInProgressDescLbl);
|
this.decryptInProgressGb.Controls.Add(this.decryptInProgressDescLbl);
|
||||||
this.decryptInProgressGb.Location = new System.Drawing.Point(9, 183);
|
this.decryptInProgressGb.Location = new System.Drawing.Point(9, 144);
|
||||||
this.decryptInProgressGb.Name = "decryptInProgressGb";
|
this.decryptInProgressGb.Name = "decryptInProgressGb";
|
||||||
this.decryptInProgressGb.Size = new System.Drawing.Size(758, 117);
|
this.decryptInProgressGb.Size = new System.Drawing.Size(758, 117);
|
||||||
this.decryptInProgressGb.TabIndex = 5;
|
this.decryptInProgressGb.TabIndex = 5;
|
||||||
@@ -206,7 +176,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(612, 404);
|
this.saveBtn.Location = new System.Drawing.Point(612, 328);
|
||||||
this.saveBtn.Name = "saveBtn";
|
this.saveBtn.Name = "saveBtn";
|
||||||
this.saveBtn.Size = new System.Drawing.Size(75, 23);
|
this.saveBtn.Size = new System.Drawing.Size(75, 23);
|
||||||
this.saveBtn.TabIndex = 7;
|
this.saveBtn.TabIndex = 7;
|
||||||
@@ -218,7 +188,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(713, 404);
|
this.cancelBtn.Location = new System.Drawing.Point(713, 328);
|
||||||
this.cancelBtn.Name = "cancelBtn";
|
this.cancelBtn.Name = "cancelBtn";
|
||||||
this.cancelBtn.Size = new System.Drawing.Size(75, 23);
|
this.cancelBtn.Size = new System.Drawing.Size(75, 23);
|
||||||
this.cancelBtn.TabIndex = 8;
|
this.cancelBtn.TabIndex = 8;
|
||||||
@@ -226,40 +196,13 @@
|
|||||||
this.cancelBtn.UseVisualStyleBackColor = true;
|
this.cancelBtn.UseVisualStyleBackColor = true;
|
||||||
this.cancelBtn.Click += new System.EventHandler(this.cancelBtn_Click);
|
this.cancelBtn.Click += new System.EventHandler(this.cancelBtn_Click);
|
||||||
//
|
//
|
||||||
// audibleLocaleLbl
|
|
||||||
//
|
|
||||||
this.audibleLocaleLbl.AutoSize = true;
|
|
||||||
this.audibleLocaleLbl.Location = new System.Drawing.Point(12, 56);
|
|
||||||
this.audibleLocaleLbl.Name = "audibleLocaleLbl";
|
|
||||||
this.audibleLocaleLbl.Size = new System.Drawing.Size(77, 13);
|
|
||||||
this.audibleLocaleLbl.TabIndex = 4;
|
|
||||||
this.audibleLocaleLbl.Text = "Audible Locale";
|
|
||||||
//
|
|
||||||
// audibleLocaleCb
|
|
||||||
//
|
|
||||||
this.audibleLocaleCb.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
|
||||||
this.audibleLocaleCb.FormattingEnabled = true;
|
|
||||||
this.audibleLocaleCb.Items.AddRange(new object[] {
|
|
||||||
"us",
|
|
||||||
"uk",
|
|
||||||
"germany",
|
|
||||||
"france",
|
|
||||||
"canada"});
|
|
||||||
this.audibleLocaleCb.Location = new System.Drawing.Point(95, 53);
|
|
||||||
this.audibleLocaleCb.Name = "audibleLocaleCb";
|
|
||||||
this.audibleLocaleCb.Size = new System.Drawing.Size(53, 21);
|
|
||||||
this.audibleLocaleCb.TabIndex = 5;
|
|
||||||
//
|
|
||||||
// groupBox1
|
// groupBox1
|
||||||
//
|
//
|
||||||
this.groupBox1.Controls.Add(this.decryptKeyTb);
|
|
||||||
this.groupBox1.Controls.Add(this.decryptKeyLbl);
|
|
||||||
this.groupBox1.Controls.Add(this.decryptKeyDescLbl);
|
|
||||||
this.groupBox1.Controls.Add(this.downloadsInProgressGb);
|
this.groupBox1.Controls.Add(this.downloadsInProgressGb);
|
||||||
this.groupBox1.Controls.Add(this.decryptInProgressGb);
|
this.groupBox1.Controls.Add(this.decryptInProgressGb);
|
||||||
this.groupBox1.Location = new System.Drawing.Point(15, 90);
|
this.groupBox1.Location = new System.Drawing.Point(15, 53);
|
||||||
this.groupBox1.Name = "groupBox1";
|
this.groupBox1.Name = "groupBox1";
|
||||||
this.groupBox1.Size = new System.Drawing.Size(773, 308);
|
this.groupBox1.Size = new System.Drawing.Size(773, 269);
|
||||||
this.groupBox1.TabIndex = 6;
|
this.groupBox1.TabIndex = 6;
|
||||||
this.groupBox1.TabStop = false;
|
this.groupBox1.TabStop = false;
|
||||||
this.groupBox1.Text = "Advanced settings for control freaks";
|
this.groupBox1.Text = "Advanced settings for control freaks";
|
||||||
@@ -270,10 +213,8 @@
|
|||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
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(800, 439);
|
this.ClientSize = new System.Drawing.Size(800, 363);
|
||||||
this.Controls.Add(this.groupBox1);
|
this.Controls.Add(this.groupBox1);
|
||||||
this.Controls.Add(this.audibleLocaleCb);
|
|
||||||
this.Controls.Add(this.audibleLocaleLbl);
|
|
||||||
this.Controls.Add(this.cancelBtn);
|
this.Controls.Add(this.cancelBtn);
|
||||||
this.Controls.Add(this.saveBtn);
|
this.Controls.Add(this.saveBtn);
|
||||||
this.Controls.Add(this.booksLocationDescLbl);
|
this.Controls.Add(this.booksLocationDescLbl);
|
||||||
@@ -290,19 +231,15 @@
|
|||||||
this.decryptInProgressGb.ResumeLayout(false);
|
this.decryptInProgressGb.ResumeLayout(false);
|
||||||
this.decryptInProgressGb.PerformLayout();
|
this.decryptInProgressGb.PerformLayout();
|
||||||
this.groupBox1.ResumeLayout(false);
|
this.groupBox1.ResumeLayout(false);
|
||||||
this.groupBox1.PerformLayout();
|
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
this.PerformLayout();
|
this.PerformLayout();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
private System.Windows.Forms.Label decryptKeyLbl;
|
|
||||||
private System.Windows.Forms.TextBox decryptKeyTb;
|
|
||||||
private System.Windows.Forms.Label booksLocationLbl;
|
private System.Windows.Forms.Label booksLocationLbl;
|
||||||
private System.Windows.Forms.TextBox booksLocationTb;
|
private System.Windows.Forms.TextBox booksLocationTb;
|
||||||
private System.Windows.Forms.Button booksLocationSearchBtn;
|
private System.Windows.Forms.Button booksLocationSearchBtn;
|
||||||
private System.Windows.Forms.Label decryptKeyDescLbl;
|
|
||||||
private System.Windows.Forms.Label booksLocationDescLbl;
|
private System.Windows.Forms.Label booksLocationDescLbl;
|
||||||
private System.Windows.Forms.GroupBox downloadsInProgressGb;
|
private System.Windows.Forms.GroupBox downloadsInProgressGb;
|
||||||
private System.Windows.Forms.Label downloadsInProgressDescLbl;
|
private System.Windows.Forms.Label downloadsInProgressDescLbl;
|
||||||
@@ -314,8 +251,6 @@
|
|||||||
private System.Windows.Forms.RadioButton decryptInProgressWinTempRb;
|
private System.Windows.Forms.RadioButton decryptInProgressWinTempRb;
|
||||||
private System.Windows.Forms.Button saveBtn;
|
private System.Windows.Forms.Button saveBtn;
|
||||||
private System.Windows.Forms.Button cancelBtn;
|
private System.Windows.Forms.Button cancelBtn;
|
||||||
private System.Windows.Forms.Label audibleLocaleLbl;
|
|
||||||
private System.Windows.Forms.ComboBox audibleLocaleCb;
|
|
||||||
private System.Windows.Forms.GroupBox groupBox1;
|
private System.Windows.Forms.GroupBox groupBox1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,7 @@ using System.IO;
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
using FileManager;
|
using FileManager;
|
||||||
|
using InternalUtilities;
|
||||||
|
|
||||||
namespace LibationWinForms.Dialogs
|
namespace LibationWinForms.Dialogs
|
||||||
{
|
{
|
||||||
@@ -18,8 +19,6 @@ namespace LibationWinForms.Dialogs
|
|||||||
|
|
||||||
private void SettingsDialog_Load(object sender, EventArgs e)
|
private void SettingsDialog_Load(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
this.decryptKeyTb.Text = config.DecryptKey;
|
|
||||||
this.decryptKeyDescLbl.Text = desc(nameof(config.DecryptKey));
|
|
||||||
this.booksLocationDescLbl.Text = desc(nameof(config.Books));
|
this.booksLocationDescLbl.Text = desc(nameof(config.Books));
|
||||||
this.downloadsInProgressDescLbl.Text = desc(nameof(config.DownloadsInProgressEnum));
|
this.downloadsInProgressDescLbl.Text = desc(nameof(config.DownloadsInProgressEnum));
|
||||||
this.decryptInProgressDescLbl.Text = desc(nameof(config.DecryptInProgressEnum));
|
this.decryptInProgressDescLbl.Text = desc(nameof(config.DecryptInProgressEnum));
|
||||||
@@ -37,11 +36,6 @@ namespace LibationWinForms.Dialogs
|
|||||||
? config.Books
|
? config.Books
|
||||||
: Path.GetDirectoryName(Exe.FileLocationOnDisk);
|
: Path.GetDirectoryName(Exe.FileLocationOnDisk);
|
||||||
|
|
||||||
this.audibleLocaleCb.Text
|
|
||||||
= !string.IsNullOrWhiteSpace(config.LocaleCountryCode)
|
|
||||||
? config.LocaleCountryCode
|
|
||||||
: "us";
|
|
||||||
|
|
||||||
switch (config.DownloadsInProgressEnum)
|
switch (config.DownloadsInProgressEnum)
|
||||||
{
|
{
|
||||||
case "LibationFiles":
|
case "LibationFiles":
|
||||||
@@ -77,8 +71,6 @@ namespace LibationWinForms.Dialogs
|
|||||||
|
|
||||||
private void saveBtn_Click(object sender, EventArgs e)
|
private void saveBtn_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
config.DecryptKey = this.decryptKeyTb.Text;
|
|
||||||
config.LocaleCountryCode = this.audibleLocaleCb.Text;
|
|
||||||
config.DownloadsInProgressEnum = downloadsInProgressLibationFilesRb.Checked ? "LibationFiles" : "WinTemp";
|
config.DownloadsInProgressEnum = downloadsInProgressLibationFilesRb.Checked ? "LibationFiles" : "WinTemp";
|
||||||
config.DecryptInProgressEnum = decryptInProgressLibationFilesRb.Checked ? "LibationFiles" : "WinTemp";
|
config.DecryptInProgressEnum = decryptInProgressLibationFilesRb.Checked ? "LibationFiles" : "WinTemp";
|
||||||
|
|
||||||
|
|||||||
+77
-10
@@ -35,15 +35,20 @@
|
|||||||
this.filterSearchTb = new System.Windows.Forms.TextBox();
|
this.filterSearchTb = new System.Windows.Forms.TextBox();
|
||||||
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
|
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
|
||||||
this.importToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.importToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
this.noAccountsYetAddAccountToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.scanLibraryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.scanLibraryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
this.scanLibraryOfAllAccountsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
this.scanLibraryOfSomeAccountsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.liberateToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.liberateToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.beginBookBackupsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.beginBookBackupsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.beginPdfBackupsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.beginPdfBackupsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
this.exportToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.quickFiltersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.quickFiltersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.firstFilterIsDefaultToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.firstFilterIsDefaultToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.editQuickFiltersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.editQuickFiltersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
|
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
|
||||||
this.settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
this.accountsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.basicSettingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.basicSettingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.advancedSettingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.advancedSettingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.statusStrip1 = new System.Windows.Forms.StatusStrip();
|
this.statusStrip1 = new System.Windows.Forms.StatusStrip();
|
||||||
@@ -52,6 +57,8 @@
|
|||||||
this.backupsCountsLbl = new System.Windows.Forms.ToolStripStatusLabel();
|
this.backupsCountsLbl = new System.Windows.Forms.ToolStripStatusLabel();
|
||||||
this.pdfsCountsLbl = new System.Windows.Forms.ToolStripStatusLabel();
|
this.pdfsCountsLbl = new System.Windows.Forms.ToolStripStatusLabel();
|
||||||
this.addFilterBtn = new System.Windows.Forms.Button();
|
this.addFilterBtn = new System.Windows.Forms.Button();
|
||||||
|
this.noAccountsYetAddAccountToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
this.exportLibraryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.menuStrip1.SuspendLayout();
|
this.menuStrip1.SuspendLayout();
|
||||||
this.statusStrip1.SuspendLayout();
|
this.statusStrip1.SuspendLayout();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
@@ -102,6 +109,7 @@
|
|||||||
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
this.importToolStripMenuItem,
|
this.importToolStripMenuItem,
|
||||||
this.liberateToolStripMenuItem,
|
this.liberateToolStripMenuItem,
|
||||||
|
this.exportToolStripMenuItem,
|
||||||
this.quickFiltersToolStripMenuItem,
|
this.quickFiltersToolStripMenuItem,
|
||||||
this.settingsToolStripMenuItem});
|
this.settingsToolStripMenuItem});
|
||||||
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
|
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
|
||||||
@@ -113,18 +121,41 @@
|
|||||||
// importToolStripMenuItem
|
// importToolStripMenuItem
|
||||||
//
|
//
|
||||||
this.importToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
this.importToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
this.scanLibraryToolStripMenuItem});
|
this.noAccountsYetAddAccountToolStripMenuItem,
|
||||||
|
this.scanLibraryToolStripMenuItem,
|
||||||
|
this.scanLibraryOfAllAccountsToolStripMenuItem,
|
||||||
|
this.scanLibraryOfSomeAccountsToolStripMenuItem});
|
||||||
this.importToolStripMenuItem.Name = "importToolStripMenuItem";
|
this.importToolStripMenuItem.Name = "importToolStripMenuItem";
|
||||||
this.importToolStripMenuItem.Size = new System.Drawing.Size(55, 20);
|
this.importToolStripMenuItem.Size = new System.Drawing.Size(55, 20);
|
||||||
this.importToolStripMenuItem.Text = "&Import";
|
this.importToolStripMenuItem.Text = "&Import";
|
||||||
//
|
//
|
||||||
|
// noAccountsYetAddAccountToolStripMenuItem
|
||||||
|
//
|
||||||
|
this.noAccountsYetAddAccountToolStripMenuItem.Name = "noAccountsYetAddAccountToolStripMenuItem";
|
||||||
|
this.noAccountsYetAddAccountToolStripMenuItem.Size = new System.Drawing.Size(247, 22);
|
||||||
|
this.noAccountsYetAddAccountToolStripMenuItem.Text = "No accounts yet. A&dd Account...";
|
||||||
|
//
|
||||||
// scanLibraryToolStripMenuItem
|
// scanLibraryToolStripMenuItem
|
||||||
//
|
//
|
||||||
this.scanLibraryToolStripMenuItem.Name = "scanLibraryToolStripMenuItem";
|
this.scanLibraryToolStripMenuItem.Name = "scanLibraryToolStripMenuItem";
|
||||||
this.scanLibraryToolStripMenuItem.Size = new System.Drawing.Size(138, 22);
|
this.scanLibraryToolStripMenuItem.Size = new System.Drawing.Size(247, 22);
|
||||||
this.scanLibraryToolStripMenuItem.Text = "Scan &Library";
|
this.scanLibraryToolStripMenuItem.Text = "Scan &Library";
|
||||||
this.scanLibraryToolStripMenuItem.Click += new System.EventHandler(this.scanLibraryToolStripMenuItem_Click);
|
this.scanLibraryToolStripMenuItem.Click += new System.EventHandler(this.scanLibraryToolStripMenuItem_Click);
|
||||||
//
|
//
|
||||||
|
// scanLibraryOfAllAccountsToolStripMenuItem
|
||||||
|
//
|
||||||
|
this.scanLibraryOfAllAccountsToolStripMenuItem.Name = "scanLibraryOfAllAccountsToolStripMenuItem";
|
||||||
|
this.scanLibraryOfAllAccountsToolStripMenuItem.Size = new System.Drawing.Size(247, 22);
|
||||||
|
this.scanLibraryOfAllAccountsToolStripMenuItem.Text = "Scan Library of &All Accounts";
|
||||||
|
this.scanLibraryOfAllAccountsToolStripMenuItem.Click += new System.EventHandler(this.scanLibraryOfAllAccountsToolStripMenuItem_Click);
|
||||||
|
//
|
||||||
|
// scanLibraryOfSomeAccountsToolStripMenuItem
|
||||||
|
//
|
||||||
|
this.scanLibraryOfSomeAccountsToolStripMenuItem.Name = "scanLibraryOfSomeAccountsToolStripMenuItem";
|
||||||
|
this.scanLibraryOfSomeAccountsToolStripMenuItem.Size = new System.Drawing.Size(247, 22);
|
||||||
|
this.scanLibraryOfSomeAccountsToolStripMenuItem.Text = "Scan Library of &Some Accounts...";
|
||||||
|
this.scanLibraryOfSomeAccountsToolStripMenuItem.Click += new System.EventHandler(this.scanLibraryOfSomeAccountsToolStripMenuItem_Click);
|
||||||
|
//
|
||||||
// liberateToolStripMenuItem
|
// liberateToolStripMenuItem
|
||||||
//
|
//
|
||||||
this.liberateToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
this.liberateToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
@@ -148,6 +179,14 @@
|
|||||||
this.beginPdfBackupsToolStripMenuItem.Text = "Begin &PDF Only Backups: {0}";
|
this.beginPdfBackupsToolStripMenuItem.Text = "Begin &PDF Only Backups: {0}";
|
||||||
this.beginPdfBackupsToolStripMenuItem.Click += new System.EventHandler(this.beginPdfBackupsToolStripMenuItem_Click);
|
this.beginPdfBackupsToolStripMenuItem.Click += new System.EventHandler(this.beginPdfBackupsToolStripMenuItem_Click);
|
||||||
//
|
//
|
||||||
|
// exportToolStripMenuItem
|
||||||
|
//
|
||||||
|
this.exportToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
|
this.exportLibraryToolStripMenuItem});
|
||||||
|
this.exportToolStripMenuItem.Name = "exportToolStripMenuItem";
|
||||||
|
this.exportToolStripMenuItem.Size = new System.Drawing.Size(53, 20);
|
||||||
|
this.exportToolStripMenuItem.Text = "E&xport";
|
||||||
|
//
|
||||||
// quickFiltersToolStripMenuItem
|
// quickFiltersToolStripMenuItem
|
||||||
//
|
//
|
||||||
this.quickFiltersToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
this.quickFiltersToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
@@ -169,7 +208,7 @@
|
|||||||
//
|
//
|
||||||
this.editQuickFiltersToolStripMenuItem.Name = "editQuickFiltersToolStripMenuItem";
|
this.editQuickFiltersToolStripMenuItem.Name = "editQuickFiltersToolStripMenuItem";
|
||||||
this.editQuickFiltersToolStripMenuItem.Size = new System.Drawing.Size(256, 22);
|
this.editQuickFiltersToolStripMenuItem.Size = new System.Drawing.Size(256, 22);
|
||||||
this.editQuickFiltersToolStripMenuItem.Text = "&Edit quick filters";
|
this.editQuickFiltersToolStripMenuItem.Text = "&Edit quick filters...";
|
||||||
this.editQuickFiltersToolStripMenuItem.Click += new System.EventHandler(this.EditQuickFiltersToolStripMenuItem_Click);
|
this.editQuickFiltersToolStripMenuItem.Click += new System.EventHandler(this.EditQuickFiltersToolStripMenuItem_Click);
|
||||||
//
|
//
|
||||||
// toolStripSeparator1
|
// toolStripSeparator1
|
||||||
@@ -180,24 +219,32 @@
|
|||||||
// settingsToolStripMenuItem
|
// settingsToolStripMenuItem
|
||||||
//
|
//
|
||||||
this.settingsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
this.settingsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
|
this.accountsToolStripMenuItem,
|
||||||
this.basicSettingsToolStripMenuItem,
|
this.basicSettingsToolStripMenuItem,
|
||||||
this.advancedSettingsToolStripMenuItem});
|
this.advancedSettingsToolStripMenuItem});
|
||||||
this.settingsToolStripMenuItem.Name = "settingsToolStripMenuItem";
|
this.settingsToolStripMenuItem.Name = "settingsToolStripMenuItem";
|
||||||
this.settingsToolStripMenuItem.Size = new System.Drawing.Size(61, 20);
|
this.settingsToolStripMenuItem.Size = new System.Drawing.Size(61, 20);
|
||||||
this.settingsToolStripMenuItem.Text = "&Settings";
|
this.settingsToolStripMenuItem.Text = "&Settings";
|
||||||
//
|
//
|
||||||
|
// accountsToolStripMenuItem
|
||||||
|
//
|
||||||
|
this.accountsToolStripMenuItem.Name = "accountsToolStripMenuItem";
|
||||||
|
this.accountsToolStripMenuItem.Size = new System.Drawing.Size(181, 22);
|
||||||
|
this.accountsToolStripMenuItem.Text = "&Accounts...";
|
||||||
|
this.accountsToolStripMenuItem.Click += new System.EventHandler(this.accountsToolStripMenuItem_Click);
|
||||||
|
//
|
||||||
// basicSettingsToolStripMenuItem
|
// basicSettingsToolStripMenuItem
|
||||||
//
|
//
|
||||||
this.basicSettingsToolStripMenuItem.Name = "basicSettingsToolStripMenuItem";
|
this.basicSettingsToolStripMenuItem.Name = "basicSettingsToolStripMenuItem";
|
||||||
this.basicSettingsToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
|
this.basicSettingsToolStripMenuItem.Size = new System.Drawing.Size(181, 22);
|
||||||
this.basicSettingsToolStripMenuItem.Text = "&Basic Settings";
|
this.basicSettingsToolStripMenuItem.Text = "&Basic Settings...";
|
||||||
this.basicSettingsToolStripMenuItem.Click += new System.EventHandler(this.basicSettingsToolStripMenuItem_Click);
|
this.basicSettingsToolStripMenuItem.Click += new System.EventHandler(this.basicSettingsToolStripMenuItem_Click);
|
||||||
//
|
//
|
||||||
// advancedSettingsToolStripMenuItem
|
// advancedSettingsToolStripMenuItem
|
||||||
//
|
//
|
||||||
this.advancedSettingsToolStripMenuItem.Name = "advancedSettingsToolStripMenuItem";
|
this.advancedSettingsToolStripMenuItem.Name = "advancedSettingsToolStripMenuItem";
|
||||||
this.advancedSettingsToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
|
this.advancedSettingsToolStripMenuItem.Size = new System.Drawing.Size(181, 22);
|
||||||
this.advancedSettingsToolStripMenuItem.Text = "&Advanced Settings";
|
this.advancedSettingsToolStripMenuItem.Text = "Ad&vanced Settings...";
|
||||||
this.advancedSettingsToolStripMenuItem.Click += new System.EventHandler(this.advancedSettingsToolStripMenuItem_Click);
|
this.advancedSettingsToolStripMenuItem.Click += new System.EventHandler(this.advancedSettingsToolStripMenuItem_Click);
|
||||||
//
|
//
|
||||||
// statusStrip1
|
// statusStrip1
|
||||||
@@ -222,7 +269,7 @@
|
|||||||
// springLbl
|
// springLbl
|
||||||
//
|
//
|
||||||
this.springLbl.Name = "springLbl";
|
this.springLbl.Name = "springLbl";
|
||||||
this.springLbl.Size = new System.Drawing.Size(232, 17);
|
this.springLbl.Size = new System.Drawing.Size(233, 17);
|
||||||
this.springLbl.Spring = true;
|
this.springLbl.Spring = true;
|
||||||
//
|
//
|
||||||
// backupsCountsLbl
|
// backupsCountsLbl
|
||||||
@@ -234,7 +281,7 @@
|
|||||||
// pdfsCountsLbl
|
// pdfsCountsLbl
|
||||||
//
|
//
|
||||||
this.pdfsCountsLbl.Name = "pdfsCountsLbl";
|
this.pdfsCountsLbl.Name = "pdfsCountsLbl";
|
||||||
this.pdfsCountsLbl.Size = new System.Drawing.Size(219, 17);
|
this.pdfsCountsLbl.Size = new System.Drawing.Size(218, 17);
|
||||||
this.pdfsCountsLbl.Text = "| PDFs: NOT d/l\'ed: {0} Downloaded: {1}";
|
this.pdfsCountsLbl.Text = "| PDFs: NOT d/l\'ed: {0} Downloaded: {1}";
|
||||||
//
|
//
|
||||||
// addFilterBtn
|
// addFilterBtn
|
||||||
@@ -247,6 +294,20 @@
|
|||||||
this.addFilterBtn.UseVisualStyleBackColor = true;
|
this.addFilterBtn.UseVisualStyleBackColor = true;
|
||||||
this.addFilterBtn.Click += new System.EventHandler(this.AddFilterBtn_Click);
|
this.addFilterBtn.Click += new System.EventHandler(this.AddFilterBtn_Click);
|
||||||
//
|
//
|
||||||
|
// noAccountsYetAddAccountToolStripMenuItem
|
||||||
|
//
|
||||||
|
this.noAccountsYetAddAccountToolStripMenuItem.Name = "noAccountsYetAddAccountToolStripMenuItem";
|
||||||
|
this.noAccountsYetAddAccountToolStripMenuItem.Size = new System.Drawing.Size(247, 22);
|
||||||
|
this.noAccountsYetAddAccountToolStripMenuItem.Text = "No accounts yet. A&dd Account...";
|
||||||
|
this.noAccountsYetAddAccountToolStripMenuItem.Click += new System.EventHandler(this.noAccountsYetAddAccountToolStripMenuItem_Click);
|
||||||
|
//
|
||||||
|
// exportLibraryToolStripMenuItem
|
||||||
|
//
|
||||||
|
this.exportLibraryToolStripMenuItem.Name = "exportLibraryToolStripMenuItem";
|
||||||
|
this.exportLibraryToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
|
||||||
|
this.exportLibraryToolStripMenuItem.Text = "E&xport Library...";
|
||||||
|
this.exportLibraryToolStripMenuItem.Click += new System.EventHandler(this.exportLibraryToolStripMenuItem_Click);
|
||||||
|
//
|
||||||
// Form1
|
// Form1
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
@@ -274,6 +335,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private System.Windows.Forms.Panel gridPanel;
|
private System.Windows.Forms.Panel gridPanel;
|
||||||
private System.Windows.Forms.MenuStrip menuStrip1;
|
private System.Windows.Forms.MenuStrip menuStrip1;
|
||||||
private System.Windows.Forms.ToolStripMenuItem importToolStripMenuItem;
|
private System.Windows.Forms.ToolStripMenuItem importToolStripMenuItem;
|
||||||
@@ -297,6 +359,11 @@
|
|||||||
private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
|
private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
|
||||||
private System.Windows.Forms.ToolStripMenuItem basicSettingsToolStripMenuItem;
|
private System.Windows.Forms.ToolStripMenuItem basicSettingsToolStripMenuItem;
|
||||||
private System.Windows.Forms.ToolStripMenuItem advancedSettingsToolStripMenuItem;
|
private System.Windows.Forms.ToolStripMenuItem advancedSettingsToolStripMenuItem;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem accountsToolStripMenuItem;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem scanLibraryOfAllAccountsToolStripMenuItem;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem scanLibraryOfSomeAccountsToolStripMenuItem;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem noAccountsYetAddAccountToolStripMenuItem;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem exportToolStripMenuItem;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem exportLibraryToolStripMenuItem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8,6 +8,7 @@ using Dinah.Core;
|
|||||||
using Dinah.Core.Drawing;
|
using Dinah.Core.Drawing;
|
||||||
using Dinah.Core.Windows.Forms;
|
using Dinah.Core.Windows.Forms;
|
||||||
using FileManager;
|
using FileManager;
|
||||||
|
using InternalUtilities;
|
||||||
using LibationWinForms.Dialogs;
|
using LibationWinForms.Dialogs;
|
||||||
|
|
||||||
namespace LibationWinForms
|
namespace LibationWinForms
|
||||||
@@ -42,6 +43,8 @@ namespace LibationWinForms
|
|||||||
PictureStorage.SetDefaultImage(PictureSize._300x300, Properties.Resources.default_cover_300x300.ToBytes(format));
|
PictureStorage.SetDefaultImage(PictureSize._300x300, Properties.Resources.default_cover_300x300.ToBytes(format));
|
||||||
PictureStorage.SetDefaultImage(PictureSize._500x500, Properties.Resources.default_cover_500x500.ToBytes(format));
|
PictureStorage.SetDefaultImage(PictureSize._500x500, Properties.Resources.default_cover_500x500.ToBytes(format));
|
||||||
|
|
||||||
|
RefreshImportMenu();
|
||||||
|
|
||||||
setVisibleCount(null, 0);
|
setVisibleCount(null, 0);
|
||||||
|
|
||||||
reloadGrid();
|
reloadGrid();
|
||||||
@@ -55,8 +58,8 @@ namespace LibationWinForms
|
|||||||
setBackupCounts(null, null);
|
setBackupCounts(null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region reload grid
|
#region reload grid
|
||||||
bool isProcessingGridSelect = false;
|
bool isProcessingGridSelect = false;
|
||||||
private void reloadGrid()
|
private void reloadGrid()
|
||||||
{
|
{
|
||||||
// suppressed filter while init'ing UI
|
// suppressed filter while init'ing UI
|
||||||
@@ -127,6 +130,9 @@ namespace LibationWinForms
|
|||||||
var downloadedOnly = results.Count(r => r == AudioFileState.aax);
|
var downloadedOnly = results.Count(r => r == AudioFileState.aax);
|
||||||
var noProgress = results.Count(r => r == AudioFileState.none);
|
var noProgress = results.Count(r => r == AudioFileState.none);
|
||||||
|
|
||||||
|
// enable/disable export
|
||||||
|
exportLibraryToolStripMenuItem.Enabled = results.Any();
|
||||||
|
|
||||||
// update bottom numbers
|
// update bottom numbers
|
||||||
var pending = noProgress + downloadedOnly;
|
var pending = noProgress + downloadedOnly;
|
||||||
var statusStripText
|
var statusStripText
|
||||||
@@ -179,7 +185,7 @@ namespace LibationWinForms
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region filter
|
#region filter
|
||||||
private void filterHelpBtn_Click(object sender, EventArgs e) => new Dialogs.SearchSyntaxDialog().ShowDialog();
|
private void filterHelpBtn_Click(object sender, EventArgs e) => new SearchSyntaxDialog().ShowDialog();
|
||||||
|
|
||||||
private void AddFilterBtn_Click(object sender, EventArgs e)
|
private void AddFilterBtn_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
@@ -223,12 +229,57 @@ namespace LibationWinForms
|
|||||||
doFilter(lastGoodFilter);
|
doFilter(lastGoodFilter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region index menu
|
#region Import menu
|
||||||
private void scanLibraryToolStripMenuItem_Click(object sender, EventArgs e)
|
public void RefreshImportMenu()
|
||||||
|
{
|
||||||
|
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||||
|
var count = persister.AccountsSettings.Accounts.Count;
|
||||||
|
|
||||||
|
noAccountsYetAddAccountToolStripMenuItem.Visible = count == 0;
|
||||||
|
scanLibraryToolStripMenuItem.Visible = count == 1;
|
||||||
|
scanLibraryOfAllAccountsToolStripMenuItem.Visible = count > 1;
|
||||||
|
scanLibraryOfSomeAccountsToolStripMenuItem.Visible = count > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void noAccountsYetAddAccountToolStripMenuItem_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
MessageBox.Show("To load your Audible library, come back here to the Import menu after adding your account");
|
||||||
|
new AccountsDialog(this).ShowDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scanLibraryToolStripMenuItem_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||||
|
var firstAccount = persister.AccountsSettings.GetAll().FirstOrDefault();
|
||||||
|
scanLibraries(firstAccount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scanLibraryOfAllAccountsToolStripMenuItem_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||||
|
var allAccounts = persister.AccountsSettings.GetAll();
|
||||||
|
scanLibraries(allAccounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scanLibraryOfSomeAccountsToolStripMenuItem_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
using var scanAccountsDialog = new ScanAccountsDialog(this);
|
||||||
|
|
||||||
|
if (scanAccountsDialog.ShowDialog() != DialogResult.OK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!scanAccountsDialog.CheckedAccounts.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
scanLibraries(scanAccountsDialog.CheckedAccounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scanLibraries(IEnumerable<Account> accounts) => scanLibraries(accounts.ToArray());
|
||||||
|
private void scanLibraries(params Account[] accounts)
|
||||||
{
|
{
|
||||||
using var dialog = new IndexLibraryDialog();
|
using var dialog = new IndexLibraryDialog(accounts);
|
||||||
dialog.ShowDialog();
|
dialog.ShowDialog();
|
||||||
|
|
||||||
var totalProcessed = dialog.TotalBooksProcessed;
|
var totalProcessed = dialog.TotalBooksProcessed;
|
||||||
@@ -238,7 +289,7 @@ namespace LibationWinForms
|
|||||||
|
|
||||||
if (totalProcessed > 0)
|
if (totalProcessed > 0)
|
||||||
reloadGrid();
|
reloadGrid();
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region liberate menu
|
#region liberate menu
|
||||||
@@ -251,6 +302,45 @@ namespace LibationWinForms
|
|||||||
private void updateGridRow(object _, LibraryBook libraryBook) => currProductsGrid.RefreshRow(libraryBook.Book.AudibleProductId);
|
private void updateGridRow(object _, LibraryBook libraryBook) => currProductsGrid.RefreshRow(libraryBook.Book.AudibleProductId);
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Export menu
|
||||||
|
private void exportLibraryToolStripMenuItem_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var saveFileDialog = new SaveFileDialog
|
||||||
|
{
|
||||||
|
Title = "Where to export Library",
|
||||||
|
Filter = "Excel Workbook (*.xlsx)|*.xlsx|CSV files (*.csv)|*.csv|JSON files (*.json)|*.json" // + "|All files (*.*)|*.*"
|
||||||
|
};
|
||||||
|
|
||||||
|
if (saveFileDialog.ShowDialog() != DialogResult.OK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// FilterIndex is 1-based, NOT 0-based
|
||||||
|
switch (saveFileDialog.FilterIndex)
|
||||||
|
{
|
||||||
|
case 1: // xlsx
|
||||||
|
default:
|
||||||
|
LibraryExporter.ToXlsx(saveFileDialog.FileName);
|
||||||
|
break;
|
||||||
|
case 2: // csv
|
||||||
|
LibraryExporter.ToCsv(saveFileDialog.FileName);
|
||||||
|
break;
|
||||||
|
case 3: // json
|
||||||
|
LibraryExporter.ToJson(saveFileDialog.FileName);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageBox.Show("Library exported to:\r\n" + saveFileDialog.FileName);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Serilog.Log.Logger.Error(ex, "Error attempting to export library");
|
||||||
|
MessageBox.Show("Error attempting to export your library. Error message:\r\n\r\n" + ex.Message, "Error exporting", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region quick filters menu
|
#region quick filters menu
|
||||||
private void loadInitialQuickFilterState()
|
private void loadInitialQuickFilterState()
|
||||||
{
|
{
|
||||||
@@ -296,10 +386,12 @@ namespace LibationWinForms
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EditQuickFiltersToolStripMenuItem_Click(object sender, EventArgs e) => new Dialogs.EditQuickFilters(this).ShowDialog();
|
private void EditQuickFiltersToolStripMenuItem_Click(object sender, EventArgs e) => new EditQuickFilters(this).ShowDialog();
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region settings menu
|
#region settings menu
|
||||||
|
private void accountsToolStripMenuItem_Click(object sender, EventArgs e) => new AccountsDialog(this).ShowDialog();
|
||||||
|
|
||||||
private void basicSettingsToolStripMenuItem_Click(object sender, EventArgs e) => new SettingsDialog().ShowDialog();
|
private void basicSettingsToolStripMenuItem_Click(object sender, EventArgs e) => new SettingsDialog().ShowDialog();
|
||||||
|
|
||||||
private void advancedSettingsToolStripMenuItem_Click(object sender, EventArgs e)
|
private void advancedSettingsToolStripMenuItem_Click(object sender, EventArgs e)
|
||||||
|
|||||||
@@ -174,17 +174,28 @@ namespace LibationWinForms
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
var details = new List<string>();
|
var details = new List<string>();
|
||||||
|
|
||||||
|
var locale
|
||||||
|
= string.IsNullOrWhiteSpace(book.Locale)
|
||||||
|
? "[unknown]"
|
||||||
|
: book.Locale;
|
||||||
|
var acct
|
||||||
|
= string.IsNullOrWhiteSpace(libraryBook.Account)
|
||||||
|
? "[unknown]"
|
||||||
|
: libraryBook.Account;
|
||||||
|
details.Add($"Account: {locale} - {acct}");
|
||||||
|
|
||||||
if (book.HasPdf)
|
if (book.HasPdf)
|
||||||
details.Add("Has PDF");
|
details.Add("Has PDF");
|
||||||
if (book.IsAbridged)
|
if (book.IsAbridged)
|
||||||
details.Add("Abridged");
|
details.Add("Abridged");
|
||||||
if (book.DatePublished.HasValue)
|
if (book.DatePublished.HasValue)
|
||||||
details.Add($"Date pub'd: {book.DatePublished.Value.ToString("MM/dd/yyyy")}");
|
details.Add($"Date pub'd: {book.DatePublished.Value:MM/dd/yyyy}");
|
||||||
// this goes last since it's most likely to have a line-break
|
// this goes last since it's most likely to have a line-break
|
||||||
if (!string.IsNullOrWhiteSpace(book.Publisher))
|
if (!string.IsNullOrWhiteSpace(book.Publisher))
|
||||||
details.Add($"Pub: {book.Publisher}");
|
details.Add($"Pub: {book.Publisher.Trim()}");
|
||||||
|
|
||||||
if (!details.Any())
|
if (!details.Any())
|
||||||
return "[details not imported]";
|
return "[details not imported]";
|
||||||
|
|
||||||
return string.Join("\r\n", details);
|
return string.Join("\r\n", details);
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
# Libation: Liberate your Library
|
# Libation: Liberate your Library
|
||||||
|
|
||||||
|
## [Download Libation](https://github.com/rmcrackan/Libation/releases/latest)
|
||||||
|
|
||||||
# Table of Contents
|
# Table of Contents
|
||||||
|
|
||||||
1. [Audible audiobook manager](#audible-audiobook-manager)
|
1. [Audible audiobook manager](#audible-audiobook-manager)
|
||||||
@@ -7,10 +9,12 @@
|
|||||||
- [The bad](#the-bad)
|
- [The bad](#the-bad)
|
||||||
- [The ugly](#the-ugly)
|
- [The ugly](#the-ugly)
|
||||||
2. [Getting started](#getting-started)
|
2. [Getting started](#getting-started)
|
||||||
|
- [Create Accounts](#create-accounts)
|
||||||
- [Import your library](#import-your-library)
|
- [Import your library](#import-your-library)
|
||||||
- [Download your books -- DRM-free!](#download-your-books----drm-free)
|
- [Download your books -- DRM-free!](#download-your-books----drm-free)
|
||||||
- [Download PDF attachments](#download-pdf-attachments)
|
- [Download PDF attachments](#download-pdf-attachments)
|
||||||
- [Details of downloaded files](#details-of-downloaded-files)
|
- [Details of downloaded files](#details-of-downloaded-files)
|
||||||
|
- [Export your library](#export-your-library)
|
||||||
3. [Searching and filtering](#searching-and-filtering)
|
3. [Searching and filtering](#searching-and-filtering)
|
||||||
- [Tags](#tags)
|
- [Tags](#tags)
|
||||||
- [Searches](#searches)
|
- [Searches](#searches)
|
||||||
@@ -53,12 +57,28 @@ I made this for myself and I want to share it with the great programming and aud
|
|||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
|
#### [Download Libation](https://github.com/rmcrackan/Libation/releases)
|
||||||
|
|
||||||
|
### Create Accounts
|
||||||
|
|
||||||
|
Create your account(s):
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
New locale options include many more regions including old audible accounts which pre-date the amazon acquisition
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
### Import your library
|
### Import your library
|
||||||
|
|
||||||
Select Import > Scan Library:
|
Select Import > Scan Library:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
Or if you have multiple accounts, you'll get to choose whether to scan all accounts or just the ones you select:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
You'll see this window while it's scanning:
|
You'll see this window while it's scanning:
|
||||||
|
|
||||||

|

|
||||||
@@ -125,6 +145,12 @@ When you set up Libation, you'll specify a Books directory. Libation looks insid
|
|||||||
* .cue: this is a file which logs where chapter breaks occur. Many tools are able to use this if you want to split your book into files along chapter lines.
|
* .cue: this is a file which logs where chapter breaks occur. Many tools are able to use this if you want to split your book into files along chapter lines.
|
||||||
* .nfo: This is just some general info about the book and includes some technical stats about the audiofile.
|
* .nfo: This is just some general info about the book and includes some technical stats about the audiofile.
|
||||||
|
|
||||||
|
### Export your library
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Export your library to Excel, CSV, or JSON
|
||||||
|
|
||||||
## Searching and filtering
|
## Searching and filtering
|
||||||
|
|
||||||
### Tags
|
### Tags
|
||||||
|
|||||||
+142
@@ -0,0 +1,142 @@
|
|||||||
|
namespace WinFormsDesigner.Dialogs
|
||||||
|
{
|
||||||
|
partial class AccountsDialog
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing && (components != null))
|
||||||
|
{
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Windows Form Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
this.cancelBtn = new System.Windows.Forms.Button();
|
||||||
|
this.saveBtn = new System.Windows.Forms.Button();
|
||||||
|
this.dataGridView1 = new System.Windows.Forms.DataGridView();
|
||||||
|
this.DeleteAccount = new System.Windows.Forms.DataGridViewButtonColumn();
|
||||||
|
this.LibraryScan = new System.Windows.Forms.DataGridViewCheckBoxColumn();
|
||||||
|
this.AccountId = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||||
|
this.Locale = new System.Windows.Forms.DataGridViewComboBoxColumn();
|
||||||
|
this.AccountName = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// cancelBtn
|
||||||
|
//
|
||||||
|
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.Location = new System.Drawing.Point(713, 415);
|
||||||
|
this.cancelBtn.Name = "cancelBtn";
|
||||||
|
this.cancelBtn.Size = new System.Drawing.Size(75, 23);
|
||||||
|
this.cancelBtn.TabIndex = 2;
|
||||||
|
this.cancelBtn.Text = "Cancel";
|
||||||
|
this.cancelBtn.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// saveBtn
|
||||||
|
//
|
||||||
|
this.saveBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.saveBtn.Location = new System.Drawing.Point(612, 415);
|
||||||
|
this.saveBtn.Name = "saveBtn";
|
||||||
|
this.saveBtn.Size = new System.Drawing.Size(75, 23);
|
||||||
|
this.saveBtn.TabIndex = 1;
|
||||||
|
this.saveBtn.Text = "Save";
|
||||||
|
this.saveBtn.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// dataGridView1
|
||||||
|
//
|
||||||
|
this.dataGridView1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.dataGridView1.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.AllCells;
|
||||||
|
this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
|
||||||
|
this.dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
|
||||||
|
this.DeleteAccount,
|
||||||
|
this.LibraryScan,
|
||||||
|
this.AccountId,
|
||||||
|
this.Locale,
|
||||||
|
this.AccountName});
|
||||||
|
this.dataGridView1.Location = new System.Drawing.Point(12, 12);
|
||||||
|
this.dataGridView1.MultiSelect = false;
|
||||||
|
this.dataGridView1.Name = "dataGridView1";
|
||||||
|
this.dataGridView1.Size = new System.Drawing.Size(776, 397);
|
||||||
|
this.dataGridView1.TabIndex = 0;
|
||||||
|
//
|
||||||
|
// DeleteAccount
|
||||||
|
//
|
||||||
|
this.DeleteAccount.HeaderText = "Delete";
|
||||||
|
this.DeleteAccount.Name = "DeleteAccount";
|
||||||
|
this.DeleteAccount.ReadOnly = true;
|
||||||
|
this.DeleteAccount.Text = "x";
|
||||||
|
this.DeleteAccount.Width = 44;
|
||||||
|
//
|
||||||
|
// LibraryScan
|
||||||
|
//
|
||||||
|
this.LibraryScan.HeaderText = "Include in library scan?";
|
||||||
|
this.LibraryScan.Name = "LibraryScan";
|
||||||
|
this.LibraryScan.Width = 83;
|
||||||
|
//
|
||||||
|
// AccountId
|
||||||
|
//
|
||||||
|
this.AccountId.HeaderText = "Audible email/login";
|
||||||
|
this.AccountId.Name = "AccountId";
|
||||||
|
this.AccountId.Width = 111;
|
||||||
|
//
|
||||||
|
// Locale
|
||||||
|
//
|
||||||
|
this.Locale.HeaderText = "Locale";
|
||||||
|
this.Locale.Name = "Locale";
|
||||||
|
this.Locale.Width = 45;
|
||||||
|
//
|
||||||
|
// AccountName
|
||||||
|
//
|
||||||
|
this.AccountName.HeaderText = "Account nickname (optional)";
|
||||||
|
this.AccountName.Name = "AccountName";
|
||||||
|
this.AccountName.Width = 152;
|
||||||
|
//
|
||||||
|
// AccountsDialog
|
||||||
|
//
|
||||||
|
this.AcceptButton = this.saveBtn;
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.CancelButton = this.cancelBtn;
|
||||||
|
this.ClientSize = new System.Drawing.Size(800, 450);
|
||||||
|
this.Controls.Add(this.dataGridView1);
|
||||||
|
this.Controls.Add(this.saveBtn);
|
||||||
|
this.Controls.Add(this.cancelBtn);
|
||||||
|
this.Name = "AccountsDialog";
|
||||||
|
this.Text = "Audible Accounts";
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.Button cancelBtn;
|
||||||
|
private System.Windows.Forms.Button saveBtn;
|
||||||
|
private System.Windows.Forms.DataGridView dataGridView1;
|
||||||
|
private System.Windows.Forms.DataGridViewButtonColumn DeleteAccount;
|
||||||
|
private System.Windows.Forms.DataGridViewCheckBoxColumn LibraryScan;
|
||||||
|
private System.Windows.Forms.DataGridViewTextBoxColumn AccountId;
|
||||||
|
private System.Windows.Forms.DataGridViewComboBoxColumn Locale;
|
||||||
|
private System.Windows.Forms.DataGridViewTextBoxColumn AccountName;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace WinFormsDesigner.Dialogs
|
||||||
|
{
|
||||||
|
public partial class AccountsDialog : Form
|
||||||
|
{
|
||||||
|
public AccountsDialog(Form1 parent)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<metadata name="Original.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>True</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="DeleteAccount.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>True</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="LibraryScan.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>True</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="AccountId.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>True</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="AccountName.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>True</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="Locale.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>True</value>
|
||||||
|
</metadata>
|
||||||
|
</root>
|
||||||
+3
-3
@@ -36,15 +36,15 @@
|
|||||||
this.label1.AutoSize = true;
|
this.label1.AutoSize = true;
|
||||||
this.label1.Location = new System.Drawing.Point(28, 24);
|
this.label1.Location = new System.Drawing.Point(28, 24);
|
||||||
this.label1.Name = "label1";
|
this.label1.Name = "label1";
|
||||||
this.label1.Size = new System.Drawing.Size(260, 13);
|
this.label1.Size = new System.Drawing.Size(263, 13);
|
||||||
this.label1.TabIndex = 0;
|
this.label1.TabIndex = 0;
|
||||||
this.label1.Text = "Scanning Audible library. This may take a few minutes";
|
this.label1.Text = "Scanning Audible library. This may take a few minutes.";
|
||||||
//
|
//
|
||||||
// IndexLibraryDialog
|
// IndexLibraryDialog
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.ClientSize = new System.Drawing.Size(319, 63);
|
this.ClientSize = new System.Drawing.Size(440, 63);
|
||||||
this.Controls.Add(this.label1);
|
this.Controls.Add(this.label1);
|
||||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||||
this.MaximizeBox = false;
|
this.MaximizeBox = false;
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
namespace WinFormsDesigner.Dialogs.Login
|
||||||
|
{
|
||||||
|
partial class ApprovalNeededDialog
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing && (components != null))
|
||||||
|
{
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Windows Form Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
this.approvedBtn = new System.Windows.Forms.Button();
|
||||||
|
this.label1 = new System.Windows.Forms.Label();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// approvedBtn
|
||||||
|
//
|
||||||
|
this.approvedBtn.Location = new System.Drawing.Point(15, 25);
|
||||||
|
this.approvedBtn.Name = "approvedBtn";
|
||||||
|
this.approvedBtn.Size = new System.Drawing.Size(79, 23);
|
||||||
|
this.approvedBtn.TabIndex = 1;
|
||||||
|
this.approvedBtn.Text = "Approved";
|
||||||
|
this.approvedBtn.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// label1
|
||||||
|
//
|
||||||
|
this.label1.AutoSize = true;
|
||||||
|
this.label1.Location = new System.Drawing.Point(12, 9);
|
||||||
|
this.label1.Name = "label1";
|
||||||
|
this.label1.Size = new System.Drawing.Size(104, 13);
|
||||||
|
this.label1.TabIndex = 0;
|
||||||
|
this.label1.Text = "Click after approving";
|
||||||
|
//
|
||||||
|
// ApprovalNeededDialog
|
||||||
|
//
|
||||||
|
this.AcceptButton = this.approvedBtn;
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.ClientSize = new System.Drawing.Size(149, 60);
|
||||||
|
this.Controls.Add(this.label1);
|
||||||
|
this.Controls.Add(this.approvedBtn);
|
||||||
|
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||||
|
this.MaximizeBox = false;
|
||||||
|
this.MinimizeBox = false;
|
||||||
|
this.Name = "ApprovalNeededDialog";
|
||||||
|
this.ShowIcon = false;
|
||||||
|
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||||
|
this.Text = "Approval Needed";
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
this.PerformLayout();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
private System.Windows.Forms.Button approvedBtn;
|
||||||
|
private System.Windows.Forms.Label label1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace WinFormsDesigner.Dialogs.Login
|
||||||
|
{
|
||||||
|
public partial class ApprovalNeededDialog : Form
|
||||||
|
{
|
||||||
|
public ApprovalNeededDialog()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
-22
@@ -29,10 +29,10 @@
|
|||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
{
|
{
|
||||||
this.passwordLbl = new System.Windows.Forms.Label();
|
this.passwordLbl = new System.Windows.Forms.Label();
|
||||||
this.emailLbl = new System.Windows.Forms.Label();
|
|
||||||
this.passwordTb = new System.Windows.Forms.TextBox();
|
this.passwordTb = new System.Windows.Forms.TextBox();
|
||||||
this.emailTb = new System.Windows.Forms.TextBox();
|
|
||||||
this.submitBtn = new System.Windows.Forms.Button();
|
this.submitBtn = new System.Windows.Forms.Button();
|
||||||
|
this.localeLbl = new System.Windows.Forms.Label();
|
||||||
|
this.usernameLbl = new System.Windows.Forms.Label();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
// passwordLbl
|
// passwordLbl
|
||||||
@@ -44,15 +44,6 @@
|
|||||||
this.passwordLbl.TabIndex = 2;
|
this.passwordLbl.TabIndex = 2;
|
||||||
this.passwordLbl.Text = "Password";
|
this.passwordLbl.Text = "Password";
|
||||||
//
|
//
|
||||||
// emailLbl
|
|
||||||
//
|
|
||||||
this.emailLbl.AutoSize = true;
|
|
||||||
this.emailLbl.Location = new System.Drawing.Point(12, 15);
|
|
||||||
this.emailLbl.Name = "emailLbl";
|
|
||||||
this.emailLbl.Size = new System.Drawing.Size(32, 13);
|
|
||||||
this.emailLbl.TabIndex = 0;
|
|
||||||
this.emailLbl.Text = "Email";
|
|
||||||
//
|
|
||||||
// passwordTb
|
// passwordTb
|
||||||
//
|
//
|
||||||
this.passwordTb.Location = new System.Drawing.Point(71, 38);
|
this.passwordTb.Location = new System.Drawing.Point(71, 38);
|
||||||
@@ -61,13 +52,6 @@
|
|||||||
this.passwordTb.Size = new System.Drawing.Size(200, 20);
|
this.passwordTb.Size = new System.Drawing.Size(200, 20);
|
||||||
this.passwordTb.TabIndex = 3;
|
this.passwordTb.TabIndex = 3;
|
||||||
//
|
//
|
||||||
// emailTb
|
|
||||||
//
|
|
||||||
this.emailTb.Location = new System.Drawing.Point(71, 12);
|
|
||||||
this.emailTb.Name = "emailTb";
|
|
||||||
this.emailTb.Size = new System.Drawing.Size(200, 20);
|
|
||||||
this.emailTb.TabIndex = 1;
|
|
||||||
//
|
|
||||||
// submitBtn
|
// submitBtn
|
||||||
//
|
//
|
||||||
this.submitBtn.Location = new System.Drawing.Point(196, 64);
|
this.submitBtn.Location = new System.Drawing.Point(196, 64);
|
||||||
@@ -77,17 +61,35 @@
|
|||||||
this.submitBtn.Text = "Submit";
|
this.submitBtn.Text = "Submit";
|
||||||
this.submitBtn.UseVisualStyleBackColor = true;
|
this.submitBtn.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
|
// localeLbl
|
||||||
|
//
|
||||||
|
this.localeLbl.AutoSize = true;
|
||||||
|
this.localeLbl.Location = new System.Drawing.Point(12, 9);
|
||||||
|
this.localeLbl.Name = "localeLbl";
|
||||||
|
this.localeLbl.Size = new System.Drawing.Size(59, 13);
|
||||||
|
this.localeLbl.TabIndex = 0;
|
||||||
|
this.localeLbl.Text = "Locale: {0}";
|
||||||
|
//
|
||||||
|
// usernameLbl
|
||||||
|
//
|
||||||
|
this.usernameLbl.AutoSize = true;
|
||||||
|
this.usernameLbl.Location = new System.Drawing.Point(12, 22);
|
||||||
|
this.usernameLbl.Name = "usernameLbl";
|
||||||
|
this.usernameLbl.Size = new System.Drawing.Size(75, 13);
|
||||||
|
this.usernameLbl.TabIndex = 1;
|
||||||
|
this.usernameLbl.Text = "Username: {0}";
|
||||||
|
//
|
||||||
// AudibleLoginDialog
|
// AudibleLoginDialog
|
||||||
//
|
//
|
||||||
this.AcceptButton = this.submitBtn;
|
this.AcceptButton = this.submitBtn;
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.ClientSize = new System.Drawing.Size(283, 99);
|
this.ClientSize = new System.Drawing.Size(283, 99);
|
||||||
|
this.Controls.Add(this.usernameLbl);
|
||||||
|
this.Controls.Add(this.localeLbl);
|
||||||
this.Controls.Add(this.submitBtn);
|
this.Controls.Add(this.submitBtn);
|
||||||
this.Controls.Add(this.passwordLbl);
|
this.Controls.Add(this.passwordLbl);
|
||||||
this.Controls.Add(this.emailLbl);
|
|
||||||
this.Controls.Add(this.passwordTb);
|
this.Controls.Add(this.passwordTb);
|
||||||
this.Controls.Add(this.emailTb);
|
|
||||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||||
this.MaximizeBox = false;
|
this.MaximizeBox = false;
|
||||||
this.MinimizeBox = false;
|
this.MinimizeBox = false;
|
||||||
@@ -103,9 +105,9 @@
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private System.Windows.Forms.Label passwordLbl;
|
private System.Windows.Forms.Label passwordLbl;
|
||||||
private System.Windows.Forms.Label emailLbl;
|
|
||||||
private System.Windows.Forms.TextBox passwordTb;
|
private System.Windows.Forms.TextBox passwordTb;
|
||||||
private System.Windows.Forms.TextBox emailTb;
|
|
||||||
private System.Windows.Forms.Button submitBtn;
|
private System.Windows.Forms.Button submitBtn;
|
||||||
|
private System.Windows.Forms.Label localeLbl;
|
||||||
|
private System.Windows.Forms.Label usernameLbl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,8 +5,6 @@ namespace WinFormsDesigner.Dialogs.Login
|
|||||||
{
|
{
|
||||||
public partial class _2faCodeDialog : Form
|
public partial class _2faCodeDialog : Form
|
||||||
{
|
{
|
||||||
public string NewTags { get; private set; }
|
|
||||||
|
|
||||||
public _2faCodeDialog()
|
public _2faCodeDialog()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|||||||
+117
@@ -0,0 +1,117 @@
|
|||||||
|
namespace WinFormsDesigner.Dialogs
|
||||||
|
{
|
||||||
|
partial class ScanAccountsDialog
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing && (components != null))
|
||||||
|
{
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Windows Form Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
this.accountsLbl = new System.Windows.Forms.Label();
|
||||||
|
this.accountsClb = new System.Windows.Forms.CheckedListBox();
|
||||||
|
this.importBtn = new System.Windows.Forms.Button();
|
||||||
|
this.cancelBtn = new System.Windows.Forms.Button();
|
||||||
|
this.editBtn = new System.Windows.Forms.Button();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// accountsLbl
|
||||||
|
//
|
||||||
|
this.accountsLbl.AutoSize = true;
|
||||||
|
this.accountsLbl.Location = new System.Drawing.Point(12, 9);
|
||||||
|
this.accountsLbl.Name = "accountsLbl";
|
||||||
|
this.accountsLbl.Size = new System.Drawing.Size(467, 13);
|
||||||
|
this.accountsLbl.TabIndex = 0;
|
||||||
|
this.accountsLbl.Text = "Check the accounts to scan and import. To change default selections, go to: Setti" +
|
||||||
|
"ngs > Accounts";
|
||||||
|
//
|
||||||
|
// accountsClb
|
||||||
|
//
|
||||||
|
this.accountsClb.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.accountsClb.FormattingEnabled = true;
|
||||||
|
this.accountsClb.Location = new System.Drawing.Point(12, 25);
|
||||||
|
this.accountsClb.Name = "accountsClb";
|
||||||
|
this.accountsClb.Size = new System.Drawing.Size(560, 94);
|
||||||
|
this.accountsClb.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// importBtn
|
||||||
|
//
|
||||||
|
this.importBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.importBtn.Location = new System.Drawing.Point(396, 125);
|
||||||
|
this.importBtn.Name = "importBtn";
|
||||||
|
this.importBtn.Size = new System.Drawing.Size(75, 23);
|
||||||
|
this.importBtn.TabIndex = 3;
|
||||||
|
this.importBtn.Text = "Import";
|
||||||
|
this.importBtn.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// cancelBtn
|
||||||
|
//
|
||||||
|
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.Location = new System.Drawing.Point(497, 125);
|
||||||
|
this.cancelBtn.Name = "cancelBtn";
|
||||||
|
this.cancelBtn.Size = new System.Drawing.Size(75, 23);
|
||||||
|
this.cancelBtn.TabIndex = 4;
|
||||||
|
this.cancelBtn.Text = "Cancel";
|
||||||
|
this.cancelBtn.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// editBtn
|
||||||
|
//
|
||||||
|
this.editBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||||
|
this.editBtn.Location = new System.Drawing.Point(12, 125);
|
||||||
|
this.editBtn.Name = "editBtn";
|
||||||
|
this.editBtn.Size = new System.Drawing.Size(90, 23);
|
||||||
|
this.editBtn.TabIndex = 2;
|
||||||
|
this.editBtn.Text = "Edit accounts";
|
||||||
|
this.editBtn.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// ScanAccountsDialog
|
||||||
|
//
|
||||||
|
this.AcceptButton = this.importBtn;
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.CancelButton = this.cancelBtn;
|
||||||
|
this.ClientSize = new System.Drawing.Size(584, 160);
|
||||||
|
this.Controls.Add(this.editBtn);
|
||||||
|
this.Controls.Add(this.cancelBtn);
|
||||||
|
this.Controls.Add(this.importBtn);
|
||||||
|
this.Controls.Add(this.accountsClb);
|
||||||
|
this.Controls.Add(this.accountsLbl);
|
||||||
|
this.Name = "ScanAccountsDialog";
|
||||||
|
this.Text = "Which accounts?";
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
this.PerformLayout();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.Label accountsLbl;
|
||||||
|
private System.Windows.Forms.CheckedListBox accountsClb;
|
||||||
|
private System.Windows.Forms.Button importBtn;
|
||||||
|
private System.Windows.Forms.Button cancelBtn;
|
||||||
|
private System.Windows.Forms.Button editBtn;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Data;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace WinFormsDesigner.Dialogs
|
||||||
|
{
|
||||||
|
public partial class ScanAccountsDialog : Form
|
||||||
|
{
|
||||||
|
public ScanAccountsDialog(Form1 parent)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File renamed without changes.
+85
-85
@@ -28,95 +28,95 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
{
|
{
|
||||||
this.label1 = new System.Windows.Forms.Label();
|
this.label1 = new System.Windows.Forms.Label();
|
||||||
this.label2 = new System.Windows.Forms.Label();
|
this.label2 = new System.Windows.Forms.Label();
|
||||||
this.label3 = new System.Windows.Forms.Label();
|
this.label3 = new System.Windows.Forms.Label();
|
||||||
this.label4 = new System.Windows.Forms.Label();
|
this.label4 = new System.Windows.Forms.Label();
|
||||||
this.label5 = new System.Windows.Forms.Label();
|
this.label5 = new System.Windows.Forms.Label();
|
||||||
this.closeBtn = new System.Windows.Forms.Button();
|
this.closeBtn = new System.Windows.Forms.Button();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
// label1
|
// label1
|
||||||
//
|
//
|
||||||
this.label1.AutoSize = true;
|
this.label1.AutoSize = true;
|
||||||
this.label1.Location = new System.Drawing.Point(12, 9);
|
this.label1.Location = new System.Drawing.Point(12, 9);
|
||||||
this.label1.Name = "label1";
|
this.label1.Name = "label1";
|
||||||
this.label1.Size = new System.Drawing.Size(358, 52);
|
this.label1.Size = new System.Drawing.Size(358, 52);
|
||||||
this.label1.TabIndex = 0;
|
this.label1.TabIndex = 0;
|
||||||
this.label1.Text = "Full Lucene query syntax is supported\r\nFields with similar names are synomyns (eg" +
|
this.label1.Text = "Full Lucene query syntax is supported\r\nFields with similar names are synomyns (eg" +
|
||||||
": Author, Authors, AuthorNames)\r\n\r\nTAG FORMAT: [tagName]";
|
": Author, Authors, AuthorNames)\r\n\r\nTAG FORMAT: [tagName]";
|
||||||
//
|
//
|
||||||
// label2
|
// label2
|
||||||
//
|
//
|
||||||
this.label2.AutoSize = true;
|
this.label2.AutoSize = true;
|
||||||
this.label2.Location = new System.Drawing.Point(12, 71);
|
this.label2.Location = new System.Drawing.Point(12, 71);
|
||||||
this.label2.Name = "label2";
|
this.label2.Name = "label2";
|
||||||
this.label2.Size = new System.Drawing.Size(118, 65);
|
this.label2.Size = new System.Drawing.Size(118, 65);
|
||||||
this.label2.TabIndex = 1;
|
this.label2.TabIndex = 1;
|
||||||
this.label2.Text = "STRING FIELDS\r\n\r\nSearch for wizard of oz:\r\n title:oz\r\n title:\"wizard of o" +
|
this.label2.Text = "STRING FIELDS\r\n\r\nSearch for wizard of oz:\r\n title:oz\r\n title:\"wizard of o" +
|
||||||
"z\"";
|
"z\"";
|
||||||
//
|
//
|
||||||
// label3
|
// label3
|
||||||
//
|
//
|
||||||
this.label3.AutoSize = true;
|
this.label3.AutoSize = true;
|
||||||
this.label3.Location = new System.Drawing.Point(233, 71);
|
this.label3.Location = new System.Drawing.Point(233, 71);
|
||||||
this.label3.Name = "label3";
|
this.label3.Name = "label3";
|
||||||
this.label3.Size = new System.Drawing.Size(195, 78);
|
this.label3.Size = new System.Drawing.Size(195, 78);
|
||||||
this.label3.TabIndex = 2;
|
this.label3.TabIndex = 2;
|
||||||
this.label3.Text = "NUMBER FIELDS\r\n\r\nFind books between 1-100 minutes long\r\n length:[1 TO 100]\r\nF" +
|
this.label3.Text = "NUMBER FIELDS\r\n\r\nFind books between 1-100 minutes long\r\n length:[1 TO 100]\r\nF" +
|
||||||
"ind books exactly 1 hr long\r\n length:60";
|
"ind books exactly 1 hr long\r\n length:60";
|
||||||
//
|
//
|
||||||
// label4
|
// label4
|
||||||
//
|
//
|
||||||
this.label4.AutoSize = true;
|
this.label4.AutoSize = true;
|
||||||
this.label4.Location = new System.Drawing.Point(454, 71);
|
this.label4.Location = new System.Drawing.Point(454, 71);
|
||||||
this.label4.Name = "label4";
|
this.label4.Name = "label4";
|
||||||
this.label4.Size = new System.Drawing.Size(168, 52);
|
this.label4.Size = new System.Drawing.Size(168, 52);
|
||||||
this.label4.TabIndex = 3;
|
this.label4.TabIndex = 3;
|
||||||
this.label4.Text = "BOOL FIELDS\r\n\r\nFind books that you haven\'t rated:\r\n -IsRated";
|
this.label4.Text = "BOOL FIELDS\r\n\r\nFind books that you haven\'t rated:\r\n -IsRated";
|
||||||
//
|
//
|
||||||
// label5
|
// label5
|
||||||
//
|
//
|
||||||
this.label5.AutoSize = true;
|
this.label5.AutoSize = true;
|
||||||
this.label5.Location = new System.Drawing.Point(673, 71);
|
this.label5.Location = new System.Drawing.Point(673, 71);
|
||||||
this.label5.Name = "label5";
|
this.label5.Name = "label5";
|
||||||
this.label5.Size = new System.Drawing.Size(257, 78);
|
this.label5.Size = new System.Drawing.Size(257, 78);
|
||||||
this.label5.TabIndex = 4;
|
this.label5.TabIndex = 4;
|
||||||
this.label5.Text = "ID FIELDS\r\n\r\nAlice\'s Adventures in Wonderland (ID: B015D78L0U)\r\n id:B015D78L0" +
|
this.label5.Text = "ID FIELDS\r\n\r\nAlice\'s Adventures in Wonderland (ID: B015D78L0U)\r\n id:B015D78L0" +
|
||||||
"U\r\n\r\nAll of these are synonyms for the ID field";
|
"U\r\n\r\nAll of these are synonyms for the ID field";
|
||||||
//
|
//
|
||||||
// closeBtn
|
// closeBtn
|
||||||
//
|
//
|
||||||
this.closeBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
this.closeBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.closeBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
this.closeBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||||
this.closeBtn.Location = new System.Drawing.Point(890, 415);
|
this.closeBtn.Location = new System.Drawing.Point(890, 465);
|
||||||
this.closeBtn.Name = "closeBtn";
|
this.closeBtn.Name = "closeBtn";
|
||||||
this.closeBtn.Size = new System.Drawing.Size(75, 23);
|
this.closeBtn.Size = new System.Drawing.Size(75, 23);
|
||||||
this.closeBtn.TabIndex = 5;
|
this.closeBtn.TabIndex = 5;
|
||||||
this.closeBtn.Text = "Close";
|
this.closeBtn.Text = "Close";
|
||||||
this.closeBtn.UseVisualStyleBackColor = true;
|
this.closeBtn.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// SearchSyntaxDialog
|
// SearchSyntaxDialog
|
||||||
//
|
//
|
||||||
this.AcceptButton = this.closeBtn;
|
this.AcceptButton = this.closeBtn;
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.CancelButton = this.closeBtn;
|
this.CancelButton = this.closeBtn;
|
||||||
this.ClientSize = new System.Drawing.Size(977, 450);
|
this.ClientSize = new System.Drawing.Size(977, 500);
|
||||||
this.Controls.Add(this.closeBtn);
|
this.Controls.Add(this.closeBtn);
|
||||||
this.Controls.Add(this.label5);
|
this.Controls.Add(this.label5);
|
||||||
this.Controls.Add(this.label4);
|
this.Controls.Add(this.label4);
|
||||||
this.Controls.Add(this.label3);
|
this.Controls.Add(this.label3);
|
||||||
this.Controls.Add(this.label2);
|
this.Controls.Add(this.label2);
|
||||||
this.Controls.Add(this.label1);
|
this.Controls.Add(this.label1);
|
||||||
this.MaximizeBox = false;
|
this.MaximizeBox = false;
|
||||||
this.MinimizeBox = false;
|
this.MinimizeBox = false;
|
||||||
this.Name = "SearchSyntaxDialog";
|
this.Name = "SearchSyntaxDialog";
|
||||||
this.ShowIcon = false;
|
this.ShowIcon = false;
|
||||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||||
this.Text = "Filter options";
|
this.Text = "Filter options";
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
this.PerformLayout();
|
this.PerformLayout();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-72
@@ -28,12 +28,9 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
{
|
{
|
||||||
this.decryptKeyLbl = new System.Windows.Forms.Label();
|
|
||||||
this.decryptKeyTb = new System.Windows.Forms.TextBox();
|
|
||||||
this.booksLocationLbl = new System.Windows.Forms.Label();
|
this.booksLocationLbl = new System.Windows.Forms.Label();
|
||||||
this.booksLocationTb = new System.Windows.Forms.TextBox();
|
this.booksLocationTb = new System.Windows.Forms.TextBox();
|
||||||
this.booksLocationSearchBtn = new System.Windows.Forms.Button();
|
this.booksLocationSearchBtn = new System.Windows.Forms.Button();
|
||||||
this.decryptKeyDescLbl = new System.Windows.Forms.Label();
|
|
||||||
this.booksLocationDescLbl = new System.Windows.Forms.Label();
|
this.booksLocationDescLbl = new System.Windows.Forms.Label();
|
||||||
this.downloadsInProgressGb = new System.Windows.Forms.GroupBox();
|
this.downloadsInProgressGb = new System.Windows.Forms.GroupBox();
|
||||||
this.downloadsInProgressLibationFilesRb = new System.Windows.Forms.RadioButton();
|
this.downloadsInProgressLibationFilesRb = new System.Windows.Forms.RadioButton();
|
||||||
@@ -45,30 +42,12 @@
|
|||||||
this.decryptInProgressDescLbl = new System.Windows.Forms.Label();
|
this.decryptInProgressDescLbl = new System.Windows.Forms.Label();
|
||||||
this.saveBtn = new System.Windows.Forms.Button();
|
this.saveBtn = new System.Windows.Forms.Button();
|
||||||
this.cancelBtn = new System.Windows.Forms.Button();
|
this.cancelBtn = new System.Windows.Forms.Button();
|
||||||
this.audibleLocaleLbl = new System.Windows.Forms.Label();
|
|
||||||
this.audibleLocaleCb = new System.Windows.Forms.ComboBox();
|
|
||||||
this.groupBox1 = new System.Windows.Forms.GroupBox();
|
this.groupBox1 = new System.Windows.Forms.GroupBox();
|
||||||
this.downloadsInProgressGb.SuspendLayout();
|
this.downloadsInProgressGb.SuspendLayout();
|
||||||
this.decryptInProgressGb.SuspendLayout();
|
this.decryptInProgressGb.SuspendLayout();
|
||||||
this.groupBox1.SuspendLayout();
|
this.groupBox1.SuspendLayout();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
// decryptKeyLbl
|
|
||||||
//
|
|
||||||
this.decryptKeyLbl.AutoSize = true;
|
|
||||||
this.decryptKeyLbl.Location = new System.Drawing.Point(6, 22);
|
|
||||||
this.decryptKeyLbl.Name = "decryptKeyLbl";
|
|
||||||
this.decryptKeyLbl.Size = new System.Drawing.Size(64, 13);
|
|
||||||
this.decryptKeyLbl.TabIndex = 0;
|
|
||||||
this.decryptKeyLbl.Text = "Decrypt key";
|
|
||||||
//
|
|
||||||
// decryptKeyTb
|
|
||||||
//
|
|
||||||
this.decryptKeyTb.Location = new System.Drawing.Point(76, 19);
|
|
||||||
this.decryptKeyTb.Name = "decryptKeyTb";
|
|
||||||
this.decryptKeyTb.Size = new System.Drawing.Size(100, 20);
|
|
||||||
this.decryptKeyTb.TabIndex = 1;
|
|
||||||
//
|
|
||||||
// booksLocationLbl
|
// booksLocationLbl
|
||||||
//
|
//
|
||||||
this.booksLocationLbl.AutoSize = true;
|
this.booksLocationLbl.AutoSize = true;
|
||||||
@@ -94,15 +73,6 @@
|
|||||||
this.booksLocationSearchBtn.Text = "...";
|
this.booksLocationSearchBtn.Text = "...";
|
||||||
this.booksLocationSearchBtn.UseVisualStyleBackColor = true;
|
this.booksLocationSearchBtn.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// decryptKeyDescLbl
|
|
||||||
//
|
|
||||||
this.decryptKeyDescLbl.AutoSize = true;
|
|
||||||
this.decryptKeyDescLbl.Location = new System.Drawing.Point(73, 42);
|
|
||||||
this.decryptKeyDescLbl.Name = "decryptKeyDescLbl";
|
|
||||||
this.decryptKeyDescLbl.Size = new System.Drawing.Size(36, 13);
|
|
||||||
this.decryptKeyDescLbl.TabIndex = 2;
|
|
||||||
this.decryptKeyDescLbl.Text = "[desc]";
|
|
||||||
//
|
|
||||||
// booksLocationDescLbl
|
// booksLocationDescLbl
|
||||||
//
|
//
|
||||||
this.booksLocationDescLbl.AutoSize = true;
|
this.booksLocationDescLbl.AutoSize = true;
|
||||||
@@ -117,7 +87,7 @@
|
|||||||
this.downloadsInProgressGb.Controls.Add(this.downloadsInProgressLibationFilesRb);
|
this.downloadsInProgressGb.Controls.Add(this.downloadsInProgressLibationFilesRb);
|
||||||
this.downloadsInProgressGb.Controls.Add(this.downloadsInProgressWinTempRb);
|
this.downloadsInProgressGb.Controls.Add(this.downloadsInProgressWinTempRb);
|
||||||
this.downloadsInProgressGb.Controls.Add(this.downloadsInProgressDescLbl);
|
this.downloadsInProgressGb.Controls.Add(this.downloadsInProgressDescLbl);
|
||||||
this.downloadsInProgressGb.Location = new System.Drawing.Point(15, 58);
|
this.downloadsInProgressGb.Location = new System.Drawing.Point(15, 19);
|
||||||
this.downloadsInProgressGb.Name = "downloadsInProgressGb";
|
this.downloadsInProgressGb.Name = "downloadsInProgressGb";
|
||||||
this.downloadsInProgressGb.Size = new System.Drawing.Size(758, 117);
|
this.downloadsInProgressGb.Size = new System.Drawing.Size(758, 117);
|
||||||
this.downloadsInProgressGb.TabIndex = 4;
|
this.downloadsInProgressGb.TabIndex = 4;
|
||||||
@@ -162,7 +132,7 @@
|
|||||||
this.decryptInProgressGb.Controls.Add(this.decryptInProgressLibationFilesRb);
|
this.decryptInProgressGb.Controls.Add(this.decryptInProgressLibationFilesRb);
|
||||||
this.decryptInProgressGb.Controls.Add(this.decryptInProgressWinTempRb);
|
this.decryptInProgressGb.Controls.Add(this.decryptInProgressWinTempRb);
|
||||||
this.decryptInProgressGb.Controls.Add(this.decryptInProgressDescLbl);
|
this.decryptInProgressGb.Controls.Add(this.decryptInProgressDescLbl);
|
||||||
this.decryptInProgressGb.Location = new System.Drawing.Point(9, 183);
|
this.decryptInProgressGb.Location = new System.Drawing.Point(9, 144);
|
||||||
this.decryptInProgressGb.Name = "decryptInProgressGb";
|
this.decryptInProgressGb.Name = "decryptInProgressGb";
|
||||||
this.decryptInProgressGb.Size = new System.Drawing.Size(758, 117);
|
this.decryptInProgressGb.Size = new System.Drawing.Size(758, 117);
|
||||||
this.decryptInProgressGb.TabIndex = 5;
|
this.decryptInProgressGb.TabIndex = 5;
|
||||||
@@ -205,7 +175,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(612, 404);
|
this.saveBtn.Location = new System.Drawing.Point(612, 328);
|
||||||
this.saveBtn.Name = "saveBtn";
|
this.saveBtn.Name = "saveBtn";
|
||||||
this.saveBtn.Size = new System.Drawing.Size(75, 23);
|
this.saveBtn.Size = new System.Drawing.Size(75, 23);
|
||||||
this.saveBtn.TabIndex = 7;
|
this.saveBtn.TabIndex = 7;
|
||||||
@@ -216,47 +186,20 @@
|
|||||||
//
|
//
|
||||||
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(713, 404);
|
this.cancelBtn.Location = new System.Drawing.Point(713, 328);
|
||||||
this.cancelBtn.Name = "cancelBtn";
|
this.cancelBtn.Name = "cancelBtn";
|
||||||
this.cancelBtn.Size = new System.Drawing.Size(75, 23);
|
this.cancelBtn.Size = new System.Drawing.Size(75, 23);
|
||||||
this.cancelBtn.TabIndex = 8;
|
this.cancelBtn.TabIndex = 8;
|
||||||
this.cancelBtn.Text = "Cancel";
|
this.cancelBtn.Text = "Cancel";
|
||||||
this.cancelBtn.UseVisualStyleBackColor = true;
|
this.cancelBtn.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// audibleLocaleLbl
|
|
||||||
//
|
|
||||||
this.audibleLocaleLbl.AutoSize = true;
|
|
||||||
this.audibleLocaleLbl.Location = new System.Drawing.Point(12, 56);
|
|
||||||
this.audibleLocaleLbl.Name = "audibleLocaleLbl";
|
|
||||||
this.audibleLocaleLbl.Size = new System.Drawing.Size(77, 13);
|
|
||||||
this.audibleLocaleLbl.TabIndex = 4;
|
|
||||||
this.audibleLocaleLbl.Text = "Audible Locale";
|
|
||||||
//
|
|
||||||
// audibleLocaleCb
|
|
||||||
//
|
|
||||||
this.audibleLocaleCb.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
|
||||||
this.audibleLocaleCb.FormattingEnabled = true;
|
|
||||||
this.audibleLocaleCb.Items.AddRange(new object[] {
|
|
||||||
"us",
|
|
||||||
"uk",
|
|
||||||
"germany",
|
|
||||||
"france",
|
|
||||||
"canada"});
|
|
||||||
this.audibleLocaleCb.Location = new System.Drawing.Point(95, 53);
|
|
||||||
this.audibleLocaleCb.Name = "audibleLocaleCb";
|
|
||||||
this.audibleLocaleCb.Size = new System.Drawing.Size(53, 21);
|
|
||||||
this.audibleLocaleCb.TabIndex = 5;
|
|
||||||
//
|
|
||||||
// groupBox1
|
// groupBox1
|
||||||
//
|
//
|
||||||
this.groupBox1.Controls.Add(this.decryptKeyTb);
|
|
||||||
this.groupBox1.Controls.Add(this.decryptKeyLbl);
|
|
||||||
this.groupBox1.Controls.Add(this.decryptKeyDescLbl);
|
|
||||||
this.groupBox1.Controls.Add(this.downloadsInProgressGb);
|
this.groupBox1.Controls.Add(this.downloadsInProgressGb);
|
||||||
this.groupBox1.Controls.Add(this.decryptInProgressGb);
|
this.groupBox1.Controls.Add(this.decryptInProgressGb);
|
||||||
this.groupBox1.Location = new System.Drawing.Point(15, 90);
|
this.groupBox1.Location = new System.Drawing.Point(15, 53);
|
||||||
this.groupBox1.Name = "groupBox1";
|
this.groupBox1.Name = "groupBox1";
|
||||||
this.groupBox1.Size = new System.Drawing.Size(773, 308);
|
this.groupBox1.Size = new System.Drawing.Size(773, 269);
|
||||||
this.groupBox1.TabIndex = 6;
|
this.groupBox1.TabIndex = 6;
|
||||||
this.groupBox1.TabStop = false;
|
this.groupBox1.TabStop = false;
|
||||||
this.groupBox1.Text = "Advanced settings for control freaks";
|
this.groupBox1.Text = "Advanced settings for control freaks";
|
||||||
@@ -267,10 +210,8 @@
|
|||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
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(800, 439);
|
this.ClientSize = new System.Drawing.Size(800, 363);
|
||||||
this.Controls.Add(this.groupBox1);
|
this.Controls.Add(this.groupBox1);
|
||||||
this.Controls.Add(this.audibleLocaleCb);
|
|
||||||
this.Controls.Add(this.audibleLocaleLbl);
|
|
||||||
this.Controls.Add(this.cancelBtn);
|
this.Controls.Add(this.cancelBtn);
|
||||||
this.Controls.Add(this.saveBtn);
|
this.Controls.Add(this.saveBtn);
|
||||||
this.Controls.Add(this.booksLocationDescLbl);
|
this.Controls.Add(this.booksLocationDescLbl);
|
||||||
@@ -286,19 +227,15 @@
|
|||||||
this.decryptInProgressGb.ResumeLayout(false);
|
this.decryptInProgressGb.ResumeLayout(false);
|
||||||
this.decryptInProgressGb.PerformLayout();
|
this.decryptInProgressGb.PerformLayout();
|
||||||
this.groupBox1.ResumeLayout(false);
|
this.groupBox1.ResumeLayout(false);
|
||||||
this.groupBox1.PerformLayout();
|
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
this.PerformLayout();
|
this.PerformLayout();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
private System.Windows.Forms.Label decryptKeyLbl;
|
|
||||||
private System.Windows.Forms.TextBox decryptKeyTb;
|
|
||||||
private System.Windows.Forms.Label booksLocationLbl;
|
private System.Windows.Forms.Label booksLocationLbl;
|
||||||
private System.Windows.Forms.TextBox booksLocationTb;
|
private System.Windows.Forms.TextBox booksLocationTb;
|
||||||
private System.Windows.Forms.Button booksLocationSearchBtn;
|
private System.Windows.Forms.Button booksLocationSearchBtn;
|
||||||
private System.Windows.Forms.Label decryptKeyDescLbl;
|
|
||||||
private System.Windows.Forms.Label booksLocationDescLbl;
|
private System.Windows.Forms.Label booksLocationDescLbl;
|
||||||
private System.Windows.Forms.GroupBox downloadsInProgressGb;
|
private System.Windows.Forms.GroupBox downloadsInProgressGb;
|
||||||
private System.Windows.Forms.Label downloadsInProgressDescLbl;
|
private System.Windows.Forms.Label downloadsInProgressDescLbl;
|
||||||
@@ -310,8 +247,6 @@
|
|||||||
private System.Windows.Forms.RadioButton decryptInProgressWinTempRb;
|
private System.Windows.Forms.RadioButton decryptInProgressWinTempRb;
|
||||||
private System.Windows.Forms.Button saveBtn;
|
private System.Windows.Forms.Button saveBtn;
|
||||||
private System.Windows.Forms.Button cancelBtn;
|
private System.Windows.Forms.Button cancelBtn;
|
||||||
private System.Windows.Forms.Label audibleLocaleLbl;
|
|
||||||
private System.Windows.Forms.ComboBox audibleLocaleCb;
|
|
||||||
private System.Windows.Forms.GroupBox groupBox1;
|
private System.Windows.Forms.GroupBox groupBox1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Generated
+113
-58
@@ -1,33 +1,33 @@
|
|||||||
namespace WinFormsDesigner
|
namespace WinFormsDesigner
|
||||||
{
|
{
|
||||||
partial class Form1
|
partial class Form1
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Required designer variable.
|
/// Required designer variable.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private System.ComponentModel.IContainer components = null;
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Clean up any resources being used.
|
/// Clean up any resources being used.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (disposing && (components != null))
|
if (disposing && (components != null))
|
||||||
{
|
{
|
||||||
components.Dispose();
|
components.Dispose();
|
||||||
}
|
}
|
||||||
base.Dispose(disposing);
|
base.Dispose(disposing);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Windows Form Designer generated code
|
#region Windows Form Designer generated code
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Required method for Designer support - do not modify
|
/// Required method for Designer support - do not modify
|
||||||
/// the contents of this method with the code editor.
|
/// the contents of this method with the code editor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
{
|
{
|
||||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
|
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
|
||||||
this.gridPanel = new System.Windows.Forms.Panel();
|
this.gridPanel = new System.Windows.Forms.Panel();
|
||||||
this.filterHelpBtn = new System.Windows.Forms.Button();
|
this.filterHelpBtn = new System.Windows.Forms.Button();
|
||||||
@@ -35,15 +35,20 @@
|
|||||||
this.filterSearchTb = new System.Windows.Forms.TextBox();
|
this.filterSearchTb = new System.Windows.Forms.TextBox();
|
||||||
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
|
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
|
||||||
this.importToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.importToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
this.noAccountsYetAddAccountToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.scanLibraryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.scanLibraryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
this.scanLibraryOfAllAccountsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
this.scanLibraryOfSomeAccountsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.liberateToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.liberateToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.beginBookBackupsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.beginBookBackupsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.beginPdfBackupsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.beginPdfBackupsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
this.exportToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.quickFiltersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.quickFiltersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.firstFilterIsDefaultToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.firstFilterIsDefaultToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.editQuickFiltersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.editQuickFiltersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
|
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
|
||||||
this.settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
this.accountsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.basicSettingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.basicSettingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.advancedSettingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.advancedSettingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.statusStrip1 = new System.Windows.Forms.StatusStrip();
|
this.statusStrip1 = new System.Windows.Forms.StatusStrip();
|
||||||
@@ -52,6 +57,7 @@
|
|||||||
this.backupsCountsLbl = new System.Windows.Forms.ToolStripStatusLabel();
|
this.backupsCountsLbl = new System.Windows.Forms.ToolStripStatusLabel();
|
||||||
this.pdfsCountsLbl = new System.Windows.Forms.ToolStripStatusLabel();
|
this.pdfsCountsLbl = new System.Windows.Forms.ToolStripStatusLabel();
|
||||||
this.addFilterBtn = new System.Windows.Forms.Button();
|
this.addFilterBtn = new System.Windows.Forms.Button();
|
||||||
|
this.exportLibraryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.menuStrip1.SuspendLayout();
|
this.menuStrip1.SuspendLayout();
|
||||||
this.statusStrip1.SuspendLayout();
|
this.statusStrip1.SuspendLayout();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
@@ -99,6 +105,7 @@
|
|||||||
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
this.importToolStripMenuItem,
|
this.importToolStripMenuItem,
|
||||||
this.liberateToolStripMenuItem,
|
this.liberateToolStripMenuItem,
|
||||||
|
this.exportToolStripMenuItem,
|
||||||
this.quickFiltersToolStripMenuItem,
|
this.quickFiltersToolStripMenuItem,
|
||||||
this.settingsToolStripMenuItem});
|
this.settingsToolStripMenuItem});
|
||||||
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
|
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
|
||||||
@@ -110,17 +117,38 @@
|
|||||||
// importToolStripMenuItem
|
// importToolStripMenuItem
|
||||||
//
|
//
|
||||||
this.importToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
this.importToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
this.scanLibraryToolStripMenuItem});
|
this.noAccountsYetAddAccountToolStripMenuItem,
|
||||||
|
this.scanLibraryToolStripMenuItem,
|
||||||
|
this.scanLibraryOfAllAccountsToolStripMenuItem,
|
||||||
|
this.scanLibraryOfSomeAccountsToolStripMenuItem});
|
||||||
this.importToolStripMenuItem.Name = "importToolStripMenuItem";
|
this.importToolStripMenuItem.Name = "importToolStripMenuItem";
|
||||||
this.importToolStripMenuItem.Size = new System.Drawing.Size(55, 20);
|
this.importToolStripMenuItem.Size = new System.Drawing.Size(55, 20);
|
||||||
this.importToolStripMenuItem.Text = "&Import";
|
this.importToolStripMenuItem.Text = "&Import";
|
||||||
//
|
//
|
||||||
|
// noAccountsYetAddAccountToolStripMenuItem
|
||||||
|
//
|
||||||
|
this.noAccountsYetAddAccountToolStripMenuItem.Name = "noAccountsYetAddAccountToolStripMenuItem";
|
||||||
|
this.noAccountsYetAddAccountToolStripMenuItem.Size = new System.Drawing.Size(247, 22);
|
||||||
|
this.noAccountsYetAddAccountToolStripMenuItem.Text = "No accounts yet. A&dd Account...";
|
||||||
|
//
|
||||||
// scanLibraryToolStripMenuItem
|
// scanLibraryToolStripMenuItem
|
||||||
//
|
//
|
||||||
this.scanLibraryToolStripMenuItem.Name = "scanLibraryToolStripMenuItem";
|
this.scanLibraryToolStripMenuItem.Name = "scanLibraryToolStripMenuItem";
|
||||||
this.scanLibraryToolStripMenuItem.Size = new System.Drawing.Size(138, 22);
|
this.scanLibraryToolStripMenuItem.Size = new System.Drawing.Size(247, 22);
|
||||||
this.scanLibraryToolStripMenuItem.Text = "Scan &Library";
|
this.scanLibraryToolStripMenuItem.Text = "Scan &Library";
|
||||||
//
|
//
|
||||||
|
// scanLibraryOfAllAccountsToolStripMenuItem
|
||||||
|
//
|
||||||
|
this.scanLibraryOfAllAccountsToolStripMenuItem.Name = "scanLibraryOfAllAccountsToolStripMenuItem";
|
||||||
|
this.scanLibraryOfAllAccountsToolStripMenuItem.Size = new System.Drawing.Size(247, 22);
|
||||||
|
this.scanLibraryOfAllAccountsToolStripMenuItem.Text = "Scan Library of &All Accounts";
|
||||||
|
//
|
||||||
|
// scanLibraryOfSomeAccountsToolStripMenuItem
|
||||||
|
//
|
||||||
|
this.scanLibraryOfSomeAccountsToolStripMenuItem.Name = "scanLibraryOfSomeAccountsToolStripMenuItem";
|
||||||
|
this.scanLibraryOfSomeAccountsToolStripMenuItem.Size = new System.Drawing.Size(247, 22);
|
||||||
|
this.scanLibraryOfSomeAccountsToolStripMenuItem.Text = "Scan Library of &Some Accounts...";
|
||||||
|
//
|
||||||
// liberateToolStripMenuItem
|
// liberateToolStripMenuItem
|
||||||
//
|
//
|
||||||
this.liberateToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
this.liberateToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
@@ -142,6 +170,14 @@
|
|||||||
this.beginPdfBackupsToolStripMenuItem.Size = new System.Drawing.Size(248, 22);
|
this.beginPdfBackupsToolStripMenuItem.Size = new System.Drawing.Size(248, 22);
|
||||||
this.beginPdfBackupsToolStripMenuItem.Text = "Begin &PDF Only Backups: {0}";
|
this.beginPdfBackupsToolStripMenuItem.Text = "Begin &PDF Only Backups: {0}";
|
||||||
//
|
//
|
||||||
|
// exportToolStripMenuItem
|
||||||
|
//
|
||||||
|
this.exportToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
|
this.exportLibraryToolStripMenuItem});
|
||||||
|
this.exportToolStripMenuItem.Name = "exportToolStripMenuItem";
|
||||||
|
this.exportToolStripMenuItem.Size = new System.Drawing.Size(53, 20);
|
||||||
|
this.exportToolStripMenuItem.Text = "E&xport";
|
||||||
|
//
|
||||||
// quickFiltersToolStripMenuItem
|
// quickFiltersToolStripMenuItem
|
||||||
//
|
//
|
||||||
this.quickFiltersToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
this.quickFiltersToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
@@ -162,7 +198,7 @@
|
|||||||
//
|
//
|
||||||
this.editQuickFiltersToolStripMenuItem.Name = "editQuickFiltersToolStripMenuItem";
|
this.editQuickFiltersToolStripMenuItem.Name = "editQuickFiltersToolStripMenuItem";
|
||||||
this.editQuickFiltersToolStripMenuItem.Size = new System.Drawing.Size(256, 22);
|
this.editQuickFiltersToolStripMenuItem.Size = new System.Drawing.Size(256, 22);
|
||||||
this.editQuickFiltersToolStripMenuItem.Text = "&Edit quick filters";
|
this.editQuickFiltersToolStripMenuItem.Text = "&Edit quick filters...";
|
||||||
//
|
//
|
||||||
// toolStripSeparator1
|
// toolStripSeparator1
|
||||||
//
|
//
|
||||||
@@ -172,23 +208,30 @@
|
|||||||
// settingsToolStripMenuItem
|
// settingsToolStripMenuItem
|
||||||
//
|
//
|
||||||
this.settingsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
this.settingsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
|
this.accountsToolStripMenuItem,
|
||||||
this.basicSettingsToolStripMenuItem,
|
this.basicSettingsToolStripMenuItem,
|
||||||
this.advancedSettingsToolStripMenuItem});
|
this.advancedSettingsToolStripMenuItem});
|
||||||
this.settingsToolStripMenuItem.Name = "settingsToolStripMenuItem";
|
this.settingsToolStripMenuItem.Name = "settingsToolStripMenuItem";
|
||||||
this.settingsToolStripMenuItem.Size = new System.Drawing.Size(61, 20);
|
this.settingsToolStripMenuItem.Size = new System.Drawing.Size(61, 20);
|
||||||
this.settingsToolStripMenuItem.Text = "&Settings";
|
this.settingsToolStripMenuItem.Text = "&Settings";
|
||||||
//
|
//
|
||||||
|
// accountsToolStripMenuItem
|
||||||
|
//
|
||||||
|
this.accountsToolStripMenuItem.Name = "accountsToolStripMenuItem";
|
||||||
|
this.accountsToolStripMenuItem.Size = new System.Drawing.Size(181, 22);
|
||||||
|
this.accountsToolStripMenuItem.Text = "&Accounts...";
|
||||||
|
//
|
||||||
// basicSettingsToolStripMenuItem
|
// basicSettingsToolStripMenuItem
|
||||||
//
|
//
|
||||||
this.basicSettingsToolStripMenuItem.Name = "basicSettingsToolStripMenuItem";
|
this.basicSettingsToolStripMenuItem.Name = "basicSettingsToolStripMenuItem";
|
||||||
this.basicSettingsToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
|
this.basicSettingsToolStripMenuItem.Size = new System.Drawing.Size(181, 22);
|
||||||
this.basicSettingsToolStripMenuItem.Text = "&Basic Settings";
|
this.basicSettingsToolStripMenuItem.Text = "&Basic Settings...";
|
||||||
//
|
//
|
||||||
// advancedSettingsToolStripMenuItem
|
// advancedSettingsToolStripMenuItem
|
||||||
//
|
//
|
||||||
this.advancedSettingsToolStripMenuItem.Name = "advancedSettingsToolStripMenuItem";
|
this.advancedSettingsToolStripMenuItem.Name = "advancedSettingsToolStripMenuItem";
|
||||||
this.advancedSettingsToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
|
this.advancedSettingsToolStripMenuItem.Size = new System.Drawing.Size(181, 22);
|
||||||
this.advancedSettingsToolStripMenuItem.Text = "&Advanced Settings";
|
this.advancedSettingsToolStripMenuItem.Text = "Ad&vanced Settings...";
|
||||||
//
|
//
|
||||||
// statusStrip1
|
// statusStrip1
|
||||||
//
|
//
|
||||||
@@ -212,7 +255,7 @@
|
|||||||
// springLbl
|
// springLbl
|
||||||
//
|
//
|
||||||
this.springLbl.Name = "springLbl";
|
this.springLbl.Name = "springLbl";
|
||||||
this.springLbl.Size = new System.Drawing.Size(232, 17);
|
this.springLbl.Size = new System.Drawing.Size(233, 17);
|
||||||
this.springLbl.Spring = true;
|
this.springLbl.Spring = true;
|
||||||
//
|
//
|
||||||
// backupsCountsLbl
|
// backupsCountsLbl
|
||||||
@@ -224,7 +267,7 @@
|
|||||||
// pdfsCountsLbl
|
// pdfsCountsLbl
|
||||||
//
|
//
|
||||||
this.pdfsCountsLbl.Name = "pdfsCountsLbl";
|
this.pdfsCountsLbl.Name = "pdfsCountsLbl";
|
||||||
this.pdfsCountsLbl.Size = new System.Drawing.Size(219, 17);
|
this.pdfsCountsLbl.Size = new System.Drawing.Size(218, 17);
|
||||||
this.pdfsCountsLbl.Text = "| PDFs: NOT d/l\'ed: {0} Downloaded: {1}";
|
this.pdfsCountsLbl.Text = "| PDFs: NOT d/l\'ed: {0} Downloaded: {1}";
|
||||||
//
|
//
|
||||||
// addFilterBtn
|
// addFilterBtn
|
||||||
@@ -236,6 +279,12 @@
|
|||||||
this.addFilterBtn.Text = "Add To Quick Filters";
|
this.addFilterBtn.Text = "Add To Quick Filters";
|
||||||
this.addFilterBtn.UseVisualStyleBackColor = true;
|
this.addFilterBtn.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
|
// exportLibraryToolStripMenuItem
|
||||||
|
//
|
||||||
|
this.exportLibraryToolStripMenuItem.Name = "exportLibraryToolStripMenuItem";
|
||||||
|
this.exportLibraryToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
|
||||||
|
this.exportLibraryToolStripMenuItem.Text = "E&xport Library...";
|
||||||
|
//
|
||||||
// Form1
|
// Form1
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
@@ -259,32 +308,38 @@
|
|||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
this.PerformLayout();
|
this.PerformLayout();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
private System.Windows.Forms.Panel gridPanel;
|
|
||||||
private System.Windows.Forms.MenuStrip menuStrip1;
|
private System.Windows.Forms.Panel gridPanel;
|
||||||
private System.Windows.Forms.ToolStripMenuItem importToolStripMenuItem;
|
private System.Windows.Forms.MenuStrip menuStrip1;
|
||||||
private System.Windows.Forms.StatusStrip statusStrip1;
|
private System.Windows.Forms.ToolStripMenuItem importToolStripMenuItem;
|
||||||
private System.Windows.Forms.ToolStripStatusLabel springLbl;
|
private System.Windows.Forms.StatusStrip statusStrip1;
|
||||||
private System.Windows.Forms.ToolStripStatusLabel visibleCountLbl;
|
private System.Windows.Forms.ToolStripStatusLabel springLbl;
|
||||||
private System.Windows.Forms.ToolStripMenuItem liberateToolStripMenuItem;
|
private System.Windows.Forms.ToolStripStatusLabel visibleCountLbl;
|
||||||
private System.Windows.Forms.ToolStripStatusLabel backupsCountsLbl;
|
private System.Windows.Forms.ToolStripMenuItem liberateToolStripMenuItem;
|
||||||
private System.Windows.Forms.ToolStripMenuItem beginBookBackupsToolStripMenuItem;
|
private System.Windows.Forms.ToolStripStatusLabel backupsCountsLbl;
|
||||||
private System.Windows.Forms.ToolStripStatusLabel pdfsCountsLbl;
|
private System.Windows.Forms.ToolStripMenuItem beginBookBackupsToolStripMenuItem;
|
||||||
private System.Windows.Forms.ToolStripMenuItem beginPdfBackupsToolStripMenuItem;
|
private System.Windows.Forms.ToolStripStatusLabel pdfsCountsLbl;
|
||||||
private System.Windows.Forms.TextBox filterSearchTb;
|
private System.Windows.Forms.ToolStripMenuItem beginPdfBackupsToolStripMenuItem;
|
||||||
private System.Windows.Forms.Button filterBtn;
|
private System.Windows.Forms.TextBox filterSearchTb;
|
||||||
private System.Windows.Forms.Button filterHelpBtn;
|
private System.Windows.Forms.Button filterBtn;
|
||||||
private System.Windows.Forms.ToolStripMenuItem settingsToolStripMenuItem;
|
private System.Windows.Forms.Button filterHelpBtn;
|
||||||
private System.Windows.Forms.ToolStripMenuItem scanLibraryToolStripMenuItem;
|
private System.Windows.Forms.ToolStripMenuItem settingsToolStripMenuItem;
|
||||||
private System.Windows.Forms.ToolStripMenuItem quickFiltersToolStripMenuItem;
|
private System.Windows.Forms.ToolStripMenuItem scanLibraryToolStripMenuItem;
|
||||||
private System.Windows.Forms.ToolStripMenuItem firstFilterIsDefaultToolStripMenuItem;
|
private System.Windows.Forms.ToolStripMenuItem quickFiltersToolStripMenuItem;
|
||||||
private System.Windows.Forms.Button addFilterBtn;
|
private System.Windows.Forms.ToolStripMenuItem firstFilterIsDefaultToolStripMenuItem;
|
||||||
private System.Windows.Forms.ToolStripMenuItem editQuickFiltersToolStripMenuItem;
|
private System.Windows.Forms.Button addFilterBtn;
|
||||||
private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
|
private System.Windows.Forms.ToolStripMenuItem editQuickFiltersToolStripMenuItem;
|
||||||
|
private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
|
||||||
private System.Windows.Forms.ToolStripMenuItem basicSettingsToolStripMenuItem;
|
private System.Windows.Forms.ToolStripMenuItem basicSettingsToolStripMenuItem;
|
||||||
private System.Windows.Forms.ToolStripMenuItem advancedSettingsToolStripMenuItem;
|
private System.Windows.Forms.ToolStripMenuItem advancedSettingsToolStripMenuItem;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem accountsToolStripMenuItem;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem scanLibraryOfAllAccountsToolStripMenuItem;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem scanLibraryOfSomeAccountsToolStripMenuItem;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem noAccountsYetAddAccountToolStripMenuItem;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem exportToolStripMenuItem;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem exportLibraryToolStripMenuItem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12,5 +12,5 @@ namespace WinFormsDesigner
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,6 +65,12 @@
|
|||||||
<Compile Include="BookLiberation\DecryptForm.Designer.cs">
|
<Compile Include="BookLiberation\DecryptForm.Designer.cs">
|
||||||
<DependentUpon>DecryptForm.cs</DependentUpon>
|
<DependentUpon>DecryptForm.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Dialogs\AccountsDialog.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Dialogs\AccountsDialog.Designer.cs">
|
||||||
|
<DependentUpon>AccountsDialog.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Dialogs\EditQuickFilters.cs">
|
<Compile Include="Dialogs\EditQuickFilters.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -83,6 +89,12 @@
|
|||||||
<Compile Include="Dialogs\LibationFilesDialog.Designer.cs">
|
<Compile Include="Dialogs\LibationFilesDialog.Designer.cs">
|
||||||
<DependentUpon>LibationFilesDialog.cs</DependentUpon>
|
<DependentUpon>LibationFilesDialog.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Dialogs\Login\ApprovalNeededDialog.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Dialogs\Login\ApprovalNeededDialog.Designer.cs">
|
||||||
|
<DependentUpon>ApprovalNeededDialog.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Dialogs\Login\AudibleLoginDialog.cs">
|
<Compile Include="Dialogs\Login\AudibleLoginDialog.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -107,6 +119,12 @@
|
|||||||
<Compile Include="Dialogs\EditTagsDialog.Designer.cs">
|
<Compile Include="Dialogs\EditTagsDialog.Designer.cs">
|
||||||
<DependentUpon>EditTagsDialog.cs</DependentUpon>
|
<DependentUpon>EditTagsDialog.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Dialogs\ScanAccountsDialog.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Dialogs\ScanAccountsDialog.Designer.cs">
|
||||||
|
<DependentUpon>ScanAccountsDialog.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Dialogs\SettingsDialog.cs">
|
<Compile Include="Dialogs\SettingsDialog.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -148,8 +166,12 @@
|
|||||||
<EmbeddedResource Include="BookLiberation\DecryptForm.resx">
|
<EmbeddedResource Include="BookLiberation\DecryptForm.resx">
|
||||||
<DependentUpon>DecryptForm.cs</DependentUpon>
|
<DependentUpon>DecryptForm.cs</DependentUpon>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="Dialogs\AccountsDialog.resx">
|
||||||
|
<DependentUpon>AccountsDialog.cs</DependentUpon>
|
||||||
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="Dialogs\EditQuickFilters.resx">
|
<EmbeddedResource Include="Dialogs\EditQuickFilters.resx">
|
||||||
<DependentUpon>EditQuickFilters.cs</DependentUpon>
|
<DependentUpon>EditQuickFilters.cs</DependentUpon>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="Dialogs\EditTagsDialog.resx">
|
<EmbeddedResource Include="Dialogs\EditTagsDialog.resx">
|
||||||
<DependentUpon>EditTagsDialog.cs</DependentUpon>
|
<DependentUpon>EditTagsDialog.cs</DependentUpon>
|
||||||
@@ -160,14 +182,8 @@
|
|||||||
<EmbeddedResource Include="Dialogs\LibationFilesDialog.resx">
|
<EmbeddedResource Include="Dialogs\LibationFilesDialog.resx">
|
||||||
<DependentUpon>LibationFilesDialog.cs</DependentUpon>
|
<DependentUpon>LibationFilesDialog.cs</DependentUpon>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="Dialogs\Login\AudibleLoginDialog.resx">
|
<EmbeddedResource Include="Dialogs\ScanAccountsDialog.resx">
|
||||||
<DependentUpon>AudibleLoginDialog.cs</DependentUpon>
|
<DependentUpon>ScanAccountsDialog.cs</DependentUpon>
|
||||||
</EmbeddedResource>
|
|
||||||
<EmbeddedResource Include="Dialogs\Login\CaptchaDialog.resx">
|
|
||||||
<DependentUpon>CaptchaDialog.cs</DependentUpon>
|
|
||||||
</EmbeddedResource>
|
|
||||||
<EmbeddedResource Include="Dialogs\Login\_2faCodeDialog.resx">
|
|
||||||
<DependentUpon>_2faCodeDialog.cs</DependentUpon>
|
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="Dialogs\SettingsDialog.resx">
|
<EmbeddedResource Include="Dialogs\SettingsDialog.resx">
|
||||||
<DependentUpon>SettingsDialog.cs</DependentUpon>
|
<DependentUpon>SettingsDialog.cs</DependentUpon>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0-windows</TargetFramework>
|
||||||
<UseWindowsForms>true</UseWindowsForms>
|
<UseWindowsForms>true</UseWindowsForms>
|
||||||
<ApplicationIcon />
|
<ApplicationIcon />
|
||||||
<StartupObject />
|
<StartupObject />
|
||||||
|
|||||||
+1
-1
@@ -25,9 +25,9 @@ View > Other Windows > Package Manager Console
|
|||||||
Default project: DataLayer
|
Default project: DataLayer
|
||||||
Startup project: DataLayer
|
Startup project: DataLayer
|
||||||
since we have mult contexts, must use -context:
|
since we have mult contexts, must use -context:
|
||||||
|
|
||||||
Add-Migration MyComment -context LibationContext
|
Add-Migration MyComment -context LibationContext
|
||||||
Update-Database -context LibationContext
|
Update-Database -context LibationContext
|
||||||
|
Startup project: reset to prev. eg: LibationLauncher
|
||||||
|
|
||||||
|
|
||||||
ERROR
|
ERROR
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0-windows</TargetFramework>
|
||||||
<RootNamespace>ffmpeg_decrypt</RootNamespace>
|
<RootNamespace>ffmpeg_decrypt</RootNamespace>
|
||||||
<UseWindowsForms>true</UseWindowsForms>
|
<UseWindowsForms>true</UseWindowsForms>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0-windows</TargetFramework>
|
||||||
<UseWindowsForms>true</UseWindowsForms>
|
<UseWindowsForms>true</UseWindowsForms>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,696 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AudibleApi;
|
||||||
|
using AudibleApi.Authorization;
|
||||||
|
using Dinah.Core;
|
||||||
|
using FluentAssertions;
|
||||||
|
using FluentAssertions.Common;
|
||||||
|
using InternalUtilities;
|
||||||
|
using Microsoft.VisualStudio.TestPlatform.Common.Filtering;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using Moq;
|
||||||
|
using Moq.Protected;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using TestAudibleApiCommon;
|
||||||
|
using TestCommon;
|
||||||
|
using static AuthorizationShared.Shared;
|
||||||
|
using static AuthorizationShared.Shared.AccessTokenTemporality;
|
||||||
|
using static TestAudibleApiCommon.ComputedTestValues;
|
||||||
|
|
||||||
|
namespace AccountsTests
|
||||||
|
{
|
||||||
|
public class AccountsTestBase
|
||||||
|
{
|
||||||
|
protected const string EMPTY_FILE = "{\r\n \"Accounts\": []\r\n}";
|
||||||
|
|
||||||
|
protected string TestFile;
|
||||||
|
protected Locale usLocale => Localization.Get("us");
|
||||||
|
protected Locale ukLocale => Localization.Get("uk");
|
||||||
|
|
||||||
|
protected void WriteToTestFile(string contents)
|
||||||
|
=> File.WriteAllText(TestFile, contents);
|
||||||
|
|
||||||
|
[TestInitialize]
|
||||||
|
public void TestInit()
|
||||||
|
=> TestFile = Guid.NewGuid() + ".txt";
|
||||||
|
|
||||||
|
[TestCleanup]
|
||||||
|
public void TestCleanup()
|
||||||
|
{
|
||||||
|
if (File.Exists(TestFile))
|
||||||
|
File.Delete(TestFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
public class FromJson : AccountsTestBase
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public void _0_accounts()
|
||||||
|
{
|
||||||
|
var accountsSettings = AccountsSettings.FromJson(EMPTY_FILE);
|
||||||
|
accountsSettings.Accounts.Count.Should().Be(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void _1_account_new()
|
||||||
|
{
|
||||||
|
var json = @"
|
||||||
|
{
|
||||||
|
""Accounts"": [
|
||||||
|
{
|
||||||
|
""AccountId"": ""cng"",
|
||||||
|
""AccountName"": ""my main login"",
|
||||||
|
""DecryptKey"": ""asdfasdf"",
|
||||||
|
""IdentityTokens"": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
".Trim();
|
||||||
|
var accountsSettings = AccountsSettings.FromJson(json);
|
||||||
|
accountsSettings.Accounts.Count.Should().Be(1);
|
||||||
|
accountsSettings.Accounts[0].AccountId.Should().Be("cng");
|
||||||
|
accountsSettings.Accounts[0].IdentityTokens.Should().BeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void _1_account_populated()
|
||||||
|
{
|
||||||
|
var id = GetIdentityJson(Future);
|
||||||
|
|
||||||
|
var json = $@"
|
||||||
|
{{
|
||||||
|
""Accounts"": [
|
||||||
|
{{
|
||||||
|
""AccountId"": ""cng"",
|
||||||
|
""AccountName"": ""my main login"",
|
||||||
|
""DecryptKey"": ""asdfasdf"",
|
||||||
|
""IdentityTokens"": {id}
|
||||||
|
}}
|
||||||
|
]
|
||||||
|
}}
|
||||||
|
".Trim();
|
||||||
|
var accountsSettings = AccountsSettings.FromJson(json);
|
||||||
|
accountsSettings.Accounts.Count.Should().Be(1);
|
||||||
|
accountsSettings.Accounts[0].AccountId.Should().Be("cng");
|
||||||
|
accountsSettings.Accounts[0].IdentityTokens.Should().NotBeNull();
|
||||||
|
accountsSettings.Accounts[0].IdentityTokens.ExistingAccessToken.TokenValue.Should().Be(AccessTokenValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
public class ToJson
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public void serialize()
|
||||||
|
{
|
||||||
|
var id = JsonConvert.SerializeObject(Identity.Empty, Identity.GetJsonSerializerSettings());
|
||||||
|
var jsonIn = $@"
|
||||||
|
{{
|
||||||
|
""Accounts"": [
|
||||||
|
{{
|
||||||
|
""AccountId"": ""cng"",
|
||||||
|
""AccountName"": ""my main login"",
|
||||||
|
""DecryptKey"": ""asdfasdf"",
|
||||||
|
""IdentityTokens"": {id}
|
||||||
|
}}
|
||||||
|
]
|
||||||
|
}}
|
||||||
|
".Trim();
|
||||||
|
var accountsSettings = AccountsSettings.FromJson(jsonIn);
|
||||||
|
|
||||||
|
var jsonOut = accountsSettings.ToJson();
|
||||||
|
jsonOut.Should().Be(@"
|
||||||
|
{
|
||||||
|
""Accounts"": [
|
||||||
|
{
|
||||||
|
""AccountId"": ""cng"",
|
||||||
|
""AccountName"": ""my main login"",
|
||||||
|
""LibraryScan"": true,
|
||||||
|
""DecryptKey"": ""asdfasdf"",
|
||||||
|
""IdentityTokens"": {
|
||||||
|
""LocaleName"": ""[empty]"",
|
||||||
|
""ExistingAccessToken"": {
|
||||||
|
""TokenValue"": ""Atna|"",
|
||||||
|
""Expires"": ""0001-01-01T00:00:00""
|
||||||
|
},
|
||||||
|
""PrivateKey"": null,
|
||||||
|
""AdpToken"": null,
|
||||||
|
""RefreshToken"": null,
|
||||||
|
""Cookies"": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
".Trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
public class ctor : AccountsTestBase
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public void create_file()
|
||||||
|
{
|
||||||
|
File.Exists(TestFile).Should().BeFalse();
|
||||||
|
var accountsSettings = new AccountsSettings();
|
||||||
|
_ = new AccountsSettingsPersister(accountsSettings, TestFile);
|
||||||
|
File.Exists(TestFile).Should().BeTrue();
|
||||||
|
File.ReadAllText(TestFile).Should().Be(EMPTY_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void overwrite_existing_file()
|
||||||
|
{
|
||||||
|
File.Exists(TestFile).Should().BeFalse();
|
||||||
|
WriteToTestFile("foo");
|
||||||
|
File.Exists(TestFile).Should().BeTrue();
|
||||||
|
|
||||||
|
var accountsSettings = new AccountsSettings();
|
||||||
|
_ = new AccountsSettingsPersister(accountsSettings, TestFile);
|
||||||
|
File.Exists(TestFile).Should().BeTrue();
|
||||||
|
File.ReadAllText(TestFile).Should().Be(EMPTY_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void save_multiple_children()
|
||||||
|
{
|
||||||
|
var accountsSettings = new AccountsSettings();
|
||||||
|
accountsSettings.Add(new Account("a0") { AccountName = "n0" });
|
||||||
|
accountsSettings.Add(new Account("a1") { AccountName = "n1" });
|
||||||
|
|
||||||
|
// dispose to cease auto-updates
|
||||||
|
using (var p = new AccountsSettingsPersister(accountsSettings, TestFile)) { }
|
||||||
|
|
||||||
|
var persister = new AccountsSettingsPersister(TestFile);
|
||||||
|
persister.AccountsSettings.Accounts.Count.Should().Be(2);
|
||||||
|
persister.AccountsSettings.Accounts[1].AccountName.Should().Be("n1");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void save_with_identity()
|
||||||
|
{
|
||||||
|
var id = new Identity(usLocale);
|
||||||
|
var idJson = JsonConvert.SerializeObject(id, Identity.GetJsonSerializerSettings());
|
||||||
|
|
||||||
|
var accountsSettings = new AccountsSettings();
|
||||||
|
accountsSettings.Add(new Account("a0") { AccountName = "n0", IdentityTokens = id });
|
||||||
|
|
||||||
|
// dispose to cease auto-updates
|
||||||
|
using (var p = new AccountsSettingsPersister(accountsSettings, TestFile)) { }
|
||||||
|
|
||||||
|
var persister = new AccountsSettingsPersister(TestFile);
|
||||||
|
var acct = persister.AccountsSettings.Accounts[0];
|
||||||
|
acct.AccountName.Should().Be("n0");
|
||||||
|
acct.Locale.CountryCode.Should().Be("us");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
public class save : AccountsTestBase
|
||||||
|
{
|
||||||
|
// add/save account after file creation
|
||||||
|
[TestMethod]
|
||||||
|
public void save_1_account()
|
||||||
|
{
|
||||||
|
// create initial file
|
||||||
|
using (var p = new AccountsSettingsPersister(new AccountsSettings(), TestFile)) { }
|
||||||
|
|
||||||
|
// load file. create account
|
||||||
|
using (var p = new AccountsSettingsPersister(TestFile))
|
||||||
|
{
|
||||||
|
var idIn = new Identity(usLocale);
|
||||||
|
var acctIn = new Account("a0") { AccountName = "n0", IdentityTokens = idIn };
|
||||||
|
|
||||||
|
p.AccountsSettings.Add(acctIn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// re-load file. ensure account still exists
|
||||||
|
using (var p = new AccountsSettingsPersister(TestFile))
|
||||||
|
{
|
||||||
|
p.AccountsSettings.Accounts.Count.Should().Be(1);
|
||||||
|
var acct0 = p.AccountsSettings.Accounts[0];
|
||||||
|
acct0.AccountName.Should().Be("n0");
|
||||||
|
acct0.Locale.CountryCode.Should().Be("us");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add/save mult accounts after file creation
|
||||||
|
// separately create 2 accounts. ensure both still exist in the end
|
||||||
|
[TestMethod]
|
||||||
|
public void save_2_accounts()
|
||||||
|
{
|
||||||
|
// create initial file
|
||||||
|
using (var p = new AccountsSettingsPersister(new AccountsSettings(), TestFile)) { }
|
||||||
|
|
||||||
|
// load file. create account 0
|
||||||
|
using (var p = new AccountsSettingsPersister(TestFile))
|
||||||
|
{
|
||||||
|
var idIn = new Identity(usLocale);
|
||||||
|
var acctIn = new Account("a0") { AccountName = "n0", IdentityTokens = idIn };
|
||||||
|
|
||||||
|
p.AccountsSettings.Add(acctIn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// re-load file. ensure account still exists
|
||||||
|
using (var p = new AccountsSettingsPersister(TestFile))
|
||||||
|
{
|
||||||
|
p.AccountsSettings.Accounts.Count.Should().Be(1);
|
||||||
|
|
||||||
|
var acct0 = p.AccountsSettings.Accounts[0];
|
||||||
|
acct0.AccountName.Should().Be("n0");
|
||||||
|
acct0.Locale.CountryCode.Should().Be("us");
|
||||||
|
}
|
||||||
|
|
||||||
|
// load file. create account 1
|
||||||
|
using (var p = new AccountsSettingsPersister(TestFile))
|
||||||
|
{
|
||||||
|
var idIn = new Identity(ukLocale);
|
||||||
|
var acctIn = new Account("a1") { AccountName = "n1", IdentityTokens = idIn };
|
||||||
|
|
||||||
|
p.AccountsSettings.Add(acctIn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// re-load file. ensure both accounts still exist
|
||||||
|
using (var p = new AccountsSettingsPersister(TestFile))
|
||||||
|
{
|
||||||
|
p.AccountsSettings.Accounts.Count.Should().Be(2);
|
||||||
|
|
||||||
|
var acct0 = p.AccountsSettings.Accounts[0];
|
||||||
|
acct0.AccountName.Should().Be("n0");
|
||||||
|
acct0.Locale.CountryCode.Should().Be("us");
|
||||||
|
|
||||||
|
var acct1 = p.AccountsSettings.Accounts[1];
|
||||||
|
acct1.AccountName.Should().Be("n1");
|
||||||
|
acct1.Locale.CountryCode.Should().Be("uk");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void update_Account_field_just_added()
|
||||||
|
{
|
||||||
|
// create initial file
|
||||||
|
using (var p = new AccountsSettingsPersister(new AccountsSettings(), TestFile)) { }
|
||||||
|
|
||||||
|
// load file. create 2 accounts
|
||||||
|
using (var p = new AccountsSettingsPersister(TestFile))
|
||||||
|
{
|
||||||
|
var id1 = new Identity(usLocale);
|
||||||
|
var acct1 = new Account("a0") { AccountName = "n0", IdentityTokens = id1 };
|
||||||
|
p.AccountsSettings.Add(acct1);
|
||||||
|
|
||||||
|
// update just-added item. note: this is different than the subscription which happens on initial collection load. ensure this works also
|
||||||
|
acct1.AccountName = "new";
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify save property
|
||||||
|
using (var p = new AccountsSettingsPersister(TestFile))
|
||||||
|
{
|
||||||
|
var acct0 = p.AccountsSettings.Accounts[0];
|
||||||
|
acct0.AccountName.Should().Be("new");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update Account property. must be non-destructive to all other data
|
||||||
|
[TestMethod]
|
||||||
|
public void update_Account_field()
|
||||||
|
{
|
||||||
|
// create initial file
|
||||||
|
using (var p = new AccountsSettingsPersister(new AccountsSettings(), TestFile)) { }
|
||||||
|
|
||||||
|
// load file. create 2 accounts
|
||||||
|
using (var p = new AccountsSettingsPersister(TestFile))
|
||||||
|
{
|
||||||
|
var id1 = new Identity(usLocale);
|
||||||
|
var acct1 = new Account("a0") { AccountName = "n0", IdentityTokens = id1 };
|
||||||
|
p.AccountsSettings.Add(acct1);
|
||||||
|
|
||||||
|
var id2 = new Identity(ukLocale);
|
||||||
|
var acct2 = new Account("a1") { AccountName = "n1", IdentityTokens = id2 };
|
||||||
|
|
||||||
|
p.AccountsSettings.Add(acct2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update AccountName on existing file
|
||||||
|
using (var p = new AccountsSettingsPersister(TestFile))
|
||||||
|
{
|
||||||
|
var acct0 = p.AccountsSettings.Accounts[0];
|
||||||
|
acct0.AccountName = "new";
|
||||||
|
}
|
||||||
|
|
||||||
|
// re-load file. ensure both accounts still exist
|
||||||
|
using (var p = new AccountsSettingsPersister(TestFile))
|
||||||
|
{
|
||||||
|
p.AccountsSettings.Accounts.Count.Should().Be(2);
|
||||||
|
|
||||||
|
var acct0 = p.AccountsSettings.Accounts[0];
|
||||||
|
// new
|
||||||
|
acct0.AccountName.Should().Be("new");
|
||||||
|
|
||||||
|
// still here
|
||||||
|
acct0.Locale.CountryCode.Should().Be("us");
|
||||||
|
var acct1 = p.AccountsSettings.Accounts[1];
|
||||||
|
acct1.AccountName.Should().Be("n1");
|
||||||
|
acct1.Locale.CountryCode.Should().Be("uk");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update identity. must be non-destructive to all other data
|
||||||
|
[TestMethod]
|
||||||
|
public void replace_identity()
|
||||||
|
{
|
||||||
|
// create initial file
|
||||||
|
using (var p = new AccountsSettingsPersister(new AccountsSettings(), TestFile)) { }
|
||||||
|
|
||||||
|
// load file. create 2 accounts
|
||||||
|
using (var p = new AccountsSettingsPersister(TestFile))
|
||||||
|
{
|
||||||
|
var id1 = new Identity(usLocale);
|
||||||
|
var acct1 = new Account("a0") { AccountName = "n0", IdentityTokens = id1 };
|
||||||
|
p.AccountsSettings.Add(acct1);
|
||||||
|
|
||||||
|
var id2 = new Identity(ukLocale);
|
||||||
|
var acct2 = new Account("a1") { AccountName = "n1", IdentityTokens = id2 };
|
||||||
|
|
||||||
|
p.AccountsSettings.Add(acct2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update identity on existing file
|
||||||
|
using (var p = new AccountsSettingsPersister(TestFile))
|
||||||
|
{
|
||||||
|
var id = new Identity(ukLocale);
|
||||||
|
|
||||||
|
var acct0 = p.AccountsSettings.Accounts[0];
|
||||||
|
acct0.IdentityTokens = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// re-load file. ensure both accounts still exist
|
||||||
|
using (var p = new AccountsSettingsPersister(TestFile))
|
||||||
|
{
|
||||||
|
p.AccountsSettings.Accounts.Count.Should().Be(2);
|
||||||
|
|
||||||
|
var acct0 = p.AccountsSettings.Accounts[0];
|
||||||
|
// new
|
||||||
|
acct0.Locale.CountryCode.Should().Be("uk");
|
||||||
|
|
||||||
|
// still here
|
||||||
|
acct0.AccountName.Should().Be("n0");
|
||||||
|
var acct1 = p.AccountsSettings.Accounts[1];
|
||||||
|
acct1.AccountName.Should().Be("n1");
|
||||||
|
acct1.Locale.CountryCode.Should().Be("uk");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// multi-level subscribe => update
|
||||||
|
// edit field of existing identity. must be non-destructive to all other data
|
||||||
|
[TestMethod]
|
||||||
|
public void update_identity_field()
|
||||||
|
{
|
||||||
|
// create initial file
|
||||||
|
using (var p = new AccountsSettingsPersister(new AccountsSettings(), TestFile)) { }
|
||||||
|
|
||||||
|
// load file. create 2 accounts
|
||||||
|
using (var p = new AccountsSettingsPersister(TestFile))
|
||||||
|
{
|
||||||
|
var id1 = new Identity(usLocale);
|
||||||
|
var acct1 = new Account("a0") { AccountName = "n0", IdentityTokens = id1 };
|
||||||
|
p.AccountsSettings.Add(acct1);
|
||||||
|
|
||||||
|
var id2 = new Identity(ukLocale);
|
||||||
|
var acct2 = new Account("a1") { AccountName = "n1", IdentityTokens = id2 };
|
||||||
|
|
||||||
|
p.AccountsSettings.Add(acct2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update identity on existing file
|
||||||
|
using (var p = new AccountsSettingsPersister(TestFile))
|
||||||
|
{
|
||||||
|
p.AccountsSettings.Accounts[0]
|
||||||
|
.IdentityTokens
|
||||||
|
.Update(new AccessToken("Atna|_NEW_", DateTime.Now.AddDays(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// re-load file. ensure both accounts still exist
|
||||||
|
using (var p = new AccountsSettingsPersister(TestFile))
|
||||||
|
{
|
||||||
|
p.AccountsSettings.Accounts.Count.Should().Be(2);
|
||||||
|
|
||||||
|
var acct0 = p.AccountsSettings.Accounts[0];
|
||||||
|
// new
|
||||||
|
acct0.IdentityTokens.ExistingAccessToken.TokenValue.Should().Be("Atna|_NEW_");
|
||||||
|
|
||||||
|
// still here
|
||||||
|
acct0.AccountName.Should().Be("n0");
|
||||||
|
acct0.Locale.CountryCode.Should().Be("us");
|
||||||
|
var acct1 = p.AccountsSettings.Accounts[1];
|
||||||
|
acct1.AccountName.Should().Be("n1");
|
||||||
|
acct1.Locale.CountryCode.Should().Be("uk");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
public class retrieve : AccountsTestBase
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public void get_where()
|
||||||
|
{
|
||||||
|
var idUs = new Identity(usLocale);
|
||||||
|
var acct1 = new Account("cng") { IdentityTokens = idUs, AccountName = "foo" };
|
||||||
|
|
||||||
|
var idUk = new Identity(ukLocale);
|
||||||
|
var acct2 = new Account("cng") { IdentityTokens = idUk, AccountName = "bar" };
|
||||||
|
|
||||||
|
var accountsSettings = new AccountsSettings();
|
||||||
|
accountsSettings.Add(acct1);
|
||||||
|
accountsSettings.Add(acct2);
|
||||||
|
|
||||||
|
accountsSettings.GetAccount("cng", "uk").AccountName.Should().Be("bar");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
public class upsert : AccountsTestBase
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public void upsert_new()
|
||||||
|
{
|
||||||
|
var accountsSettings = new AccountsSettings();
|
||||||
|
accountsSettings.Accounts.Count.Should().Be(0);
|
||||||
|
|
||||||
|
accountsSettings.Upsert("cng", "us");
|
||||||
|
|
||||||
|
accountsSettings.Accounts.Count.Should().Be(1);
|
||||||
|
accountsSettings.GetAccount("cng", "us").AccountId.Should().Be("cng");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void upsert_exists()
|
||||||
|
{
|
||||||
|
var accountsSettings = new AccountsSettings();
|
||||||
|
var orig = accountsSettings.Upsert("cng", "us");
|
||||||
|
orig.AccountName = "foo";
|
||||||
|
|
||||||
|
var exists = accountsSettings.Upsert("cng", "us");
|
||||||
|
exists.AccountName.Should().Be("foo");
|
||||||
|
|
||||||
|
orig.Should().IsSameOrEqualTo(exists);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
public class delete : AccountsTestBase
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public void delete_account()
|
||||||
|
{
|
||||||
|
var accountsSettings = new AccountsSettings();
|
||||||
|
var acct = accountsSettings.Upsert("cng", "us");
|
||||||
|
accountsSettings.Accounts.Count.Should().Be(1);
|
||||||
|
|
||||||
|
var removed = accountsSettings.Delete(acct);
|
||||||
|
removed.Should().BeTrue();
|
||||||
|
|
||||||
|
accountsSettings.Accounts.Count.Should().Be(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void delete_where()
|
||||||
|
{
|
||||||
|
var accountsSettings = new AccountsSettings();
|
||||||
|
_ = accountsSettings.Upsert("cng", "us");
|
||||||
|
accountsSettings.Accounts.Count.Should().Be(1);
|
||||||
|
|
||||||
|
accountsSettings.Delete("baz", "baz").Should().BeFalse();
|
||||||
|
accountsSettings.Accounts.Count.Should().Be(1);
|
||||||
|
|
||||||
|
accountsSettings.Delete("cng", "us").Should().BeTrue();
|
||||||
|
accountsSettings.Accounts.Count.Should().Be(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void delete_updates()
|
||||||
|
{
|
||||||
|
var i = 0;
|
||||||
|
void update(object sender, EventArgs e) => i++;
|
||||||
|
|
||||||
|
var accountsSettings = new AccountsSettings();
|
||||||
|
accountsSettings.Updated += update;
|
||||||
|
|
||||||
|
accountsSettings.Accounts.Count.Should().Be(0);
|
||||||
|
i.Should().Be(0);
|
||||||
|
|
||||||
|
_ = accountsSettings.Upsert("cng", "us");
|
||||||
|
accountsSettings.Accounts.Count.Should().Be(1);
|
||||||
|
i.Should().Be(1);
|
||||||
|
|
||||||
|
accountsSettings.Delete("baz", "baz").Should().BeFalse();
|
||||||
|
accountsSettings.Accounts.Count.Should().Be(1);
|
||||||
|
i.Should().Be(1);
|
||||||
|
|
||||||
|
accountsSettings.Delete("cng", "us").Should().BeTrue();
|
||||||
|
accountsSettings.Accounts.Count.Should().Be(0);
|
||||||
|
i.Should().Be(2); // <== this is the one being tested
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void deleted_account_should_not_persist_file()
|
||||||
|
{
|
||||||
|
Account acct;
|
||||||
|
|
||||||
|
using (var p = new AccountsSettingsPersister(new AccountsSettings(), TestFile))
|
||||||
|
{
|
||||||
|
acct = p.AccountsSettings.Upsert("foo", "us");
|
||||||
|
p.AccountsSettings.Accounts.Count.Should().Be(1);
|
||||||
|
acct.AccountName = "old";
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var p = new AccountsSettingsPersister(new AccountsSettings(), TestFile))
|
||||||
|
{
|
||||||
|
p.AccountsSettings.Delete(acct);
|
||||||
|
p.AccountsSettings.Accounts.Count.Should().Be(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var p = new AccountsSettingsPersister(new AccountsSettings(), TestFile))
|
||||||
|
{
|
||||||
|
File.ReadAllText(TestFile).Should().Be(EMPTY_FILE);
|
||||||
|
|
||||||
|
acct.AccountName = "new";
|
||||||
|
|
||||||
|
File.ReadAllText(TestFile).Should().Be(EMPTY_FILE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// account.Id + Locale.Name -- must be unique
|
||||||
|
[TestClass]
|
||||||
|
public class validate : AccountsTestBase
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public void violate_validation()
|
||||||
|
{
|
||||||
|
var accountsSettings = new AccountsSettings();
|
||||||
|
|
||||||
|
var idIn = new Identity(usLocale);
|
||||||
|
|
||||||
|
var a1 = new Account("a") { AccountName = "one", IdentityTokens = idIn };
|
||||||
|
accountsSettings.Add(a1);
|
||||||
|
|
||||||
|
var a2 = new Account("a") { AccountName = "two", IdentityTokens = idIn };
|
||||||
|
|
||||||
|
// violation: validate()
|
||||||
|
Assert.ThrowsException<InvalidOperationException>(() => accountsSettings.Add(a2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void identity_violate_validation()
|
||||||
|
{
|
||||||
|
var accountsSettings = new AccountsSettings();
|
||||||
|
|
||||||
|
var idIn = new Identity(usLocale);
|
||||||
|
|
||||||
|
var a1 = new Account("a") { AccountName = "one", IdentityTokens = idIn };
|
||||||
|
accountsSettings.Add(a1);
|
||||||
|
|
||||||
|
var a2 = new Account("a") { AccountName = "two" };
|
||||||
|
accountsSettings.Add(a2);
|
||||||
|
|
||||||
|
// violation: GetAccount.SingleOrDefault
|
||||||
|
Assert.ThrowsException<InvalidOperationException>(() => a2.IdentityTokens = idIn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
public class transactions : AccountsTestBase
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public void atomic_update_at_end()
|
||||||
|
{
|
||||||
|
var p = new AccountsSettingsPersister(new AccountsSettings(), TestFile);
|
||||||
|
p.BeginTransation();
|
||||||
|
|
||||||
|
// upserted account will not persist until CommitTransation
|
||||||
|
var acct = p.AccountsSettings.Upsert("cng", "us");
|
||||||
|
acct.AccountName = "foo";
|
||||||
|
|
||||||
|
File.ReadAllText(TestFile).Should().Be(EMPTY_FILE);
|
||||||
|
p.IsInTransaction.Should().BeTrue();
|
||||||
|
|
||||||
|
p.CommitTransation();
|
||||||
|
p.IsInTransaction.Should().BeFalse();
|
||||||
|
|
||||||
|
|
||||||
|
var jsonOut = File.ReadAllText(TestFile);//.Should().Be(EMPTY_FILE);
|
||||||
|
jsonOut.Should().Be(@"
|
||||||
|
{
|
||||||
|
""Accounts"": [
|
||||||
|
{
|
||||||
|
""AccountId"": ""cng"",
|
||||||
|
""AccountName"": ""foo"",
|
||||||
|
""LibraryScan"": true,
|
||||||
|
""DecryptKey"": """",
|
||||||
|
""IdentityTokens"": {
|
||||||
|
""LocaleName"": ""us"",
|
||||||
|
""ExistingAccessToken"": {
|
||||||
|
""TokenValue"": ""Atna|"",
|
||||||
|
""Expires"": ""0001-01-01T00:00:00""
|
||||||
|
},
|
||||||
|
""PrivateKey"": null,
|
||||||
|
""AdpToken"": null,
|
||||||
|
""RefreshToken"": null,
|
||||||
|
""Cookies"": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
".Trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void abandoned_transaction()
|
||||||
|
{
|
||||||
|
var p = new AccountsSettingsPersister(new AccountsSettings(), TestFile);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
p.BeginTransation();
|
||||||
|
|
||||||
|
var acct = p.AccountsSettings.Upsert("cng", "us");
|
||||||
|
acct.AccountName = "foo";
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
File.ReadAllText(TestFile).Should().Be(EMPTY_FILE);
|
||||||
|
p.IsInTransaction.Should().BeTrue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
|
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" />
|
||||||
|
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
|
||||||
|
<PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
|
||||||
|
<PackageReference Include="coverlet.collector" Version="1.3.0">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\..\audible api\AudibleApi\_Tests\AudibleApi.Tests\AudibleApi.Tests.csproj" />
|
||||||
|
<ProjectReference Include="..\..\..\Dinah.Core\_Tests\TestCommon\TestCommon.csproj" />
|
||||||
|
<ProjectReference Include="..\..\InternalUtilities\InternalUtilities.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 7.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Reference in new issue
Block a user