Compare commits

...

18 Commits

Author SHA1 Message Date
Robert McRackan
bce3bdba7e Feature requests #229 , #148 : Bulk actions on filtered books 2022-05-12 11:57:56 -04:00
Robert McRackan
360f077da3 * fixed where the filter was being called multiple times on launch
* simplified productsGrid init means a lot of defensive code is no longer needed
2022-05-12 10:28:30 -04:00
Robert McRackan
75c5f662dc * Batch actions for visible books: 'remove from library' complete
* refactor entity properties into extension methods
* refactor shared simple message boxes => MessageBoxLib
2022-05-12 09:53:21 -04:00
Robert McRackan
b784bd6b8d Batch actions for visible books: LIberate complete 2022-05-11 22:16:15 -04:00
Robert McRackan
00df6da366 'visible books' only enabled when applicable 2022-05-11 21:47:48 -04:00
rmcrackan
e0248c2d8e Merge pull request #242 from Mbucari/master
Add option to download cover art & full-size cover art viewer
2022-05-11 10:53:11 -04:00
Robert McRackan
b12731e3d5 resolve merge conflict 2022-05-11 10:52:46 -04:00
Robert McRackan
9636aca47c update grid-visible in main form 2022-05-11 10:50:19 -04:00
Robert McRackan
4138183352 improve grid 'visible' 2022-05-11 10:49:41 -04:00
Robert McRackan
c3871d3bca * Bug fix: grid doesn't update correctly if all books are removed
* Beginning (incomplete) new menu for batch actions on visible books
2022-05-11 10:13:07 -04:00
Michael Bucari-Tovo
dd8b0783a9 Address comments 2022-05-10 20:43:07 -06:00
Michael Bucari-Tovo
9a50aa4c7c Start task earlier. 2022-05-10 16:11:44 -06:00
Michael Bucari-Tovo
c40185030f Merge branch 'master' of https://github.com/Mbucari/Libation 2022-05-10 16:11:09 -06:00
Michael Bucari-Tovo
7cba28019c Remove lambda body 2022-05-10 16:10:17 -06:00
Mbucari
926f8a957e Merge branch 'rmcrackan:master' into master 2022-05-10 16:03:52 -06:00
Michael Bucari-Tovo
59aeaf24e4 Full-size cover picture viewer 2022-05-10 16:03:02 -06:00
Michael Bucari-Tovo
64eaa157e5 Add option for downloading cover 2022-05-10 15:36:31 -06:00
Michael Bucari-Tovo
9a5d9f3867 Move cover art downloader to DownloadDecryptBook 2022-05-10 15:25:47 -06:00
39 changed files with 2346 additions and 1410 deletions

View File

@@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<Version>7.4.0.1</Version>
<Version>7.5.0.1</Version>
</PropertyGroup>
<ItemGroup>

View File

@@ -134,6 +134,9 @@ namespace AppScaffolding
if (!config.Exists(nameof(config.GridColumnsWidths)))
config.GridColumnsWidths = new Dictionary<string, int>();
if (!config.Exists(nameof(config.DownloadCoverArt)))
config.DownloadCoverArt = true;
}
/// <summary>Initialize logging. Run after migration</summary>

View File

@@ -259,12 +259,11 @@ namespace ApplicationServices
LibrarySizeChanged?.Invoke(null, null);
}
/// <summary>Occurs when books are added or removed from library</summary>
/// <summary>Occurs when the size of the library changes. ie: books are added or removed</summary>
public static event EventHandler LibrarySizeChanged;
/// <summary>
/// Occurs when <see cref="UserDefinedItem.Tags"/>, <see cref="UserDefinedItem.BookStatus"/>, or <see cref="UserDefinedItem.PdfStatus"/>
/// changed values are successfully persisted.
/// Occurs when the size of the library does not change but book(s) details do. Especially when <see cref="UserDefinedItem.Tags"/>, <see cref="UserDefinedItem.BookStatus"/>, or <see cref="UserDefinedItem.PdfStatus"/> changed values are successfully persisted.
/// </summary>
public static event EventHandler BookUserDefinedItemCommitted;
@@ -314,7 +313,7 @@ namespace ApplicationServices
// must be here instead of in db layer due to AaxcExists
public static LiberatedStatus Liberated_Status(Book book)
=> book.Audio_Exists ? book.UserDefinedItem.BookStatus
=> book.Audio_Exists() ? book.UserDefinedItem.BookStatus
: AudibleFileStorage.AaxcExists(book.AudibleProductId) ? LiberatedStatus.PartialDownload
: LiberatedStatus.NotLiberated;
@@ -341,7 +340,7 @@ namespace ApplicationServices
var boolResults = libraryBooks
.AsParallel()
.Where(lb => lb.Book.HasPdf)
.Where(lb => lb.Book.HasPdf())
.Select(lb => Pdf_Status(lb.Book))
.ToList();
var pdfsDownloaded = boolResults.Count(r => r == LiberatedStatus.Liberated);

View File

@@ -111,13 +111,13 @@ namespace ApplicationServices
AudibleProductId = a.Book.AudibleProductId,
Locale = a.Book.Locale,
Title = a.Book.Title,
AuthorNames = a.Book.AuthorNames,
NarratorNames = a.Book.NarratorNames,
AuthorNames = a.Book.AuthorNames(),
NarratorNames = a.Book.NarratorNames(),
LengthInMinutes = a.Book.LengthInMinutes,
Description = a.Book.Description,
Publisher = a.Book.Publisher,
HasPdf = a.Book.HasPdf,
SeriesNames = a.Book.SeriesNames,
HasPdf = a.Book.HasPdf(),
SeriesNames = a.Book.SeriesNames(),
SeriesOrder = a.Book.SeriesLink.Any() ? a.Book.SeriesLink?.Select(sl => $"{sl.Order} : {sl.Series.Name}").Aggregate((a, b) => $"{a}, {b}") : "",
CommunityRatingOverall = a.Book.Rating?.OverallRating,
CommunityRatingPerformance = a.Book.Rating?.PerformanceRating,
@@ -125,7 +125,7 @@ namespace ApplicationServices
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}") : "",
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,

View File

@@ -43,27 +43,10 @@ namespace DataLayer
// non-null. use "empty pattern"
internal int CategoryId { get; private set; }
public Category Category { get; private set; }
public string[] CategoriesNames
=> Category is null ? new string[0]
: Category.ParentCategory is null ? new[] { Category.Name }
: new[] { Category.ParentCategory.Name, Category.Name };
public string[] CategoriesIds
=> Category is null ? null
: Category.ParentCategory is null ? new[] { Category.AudibleCategoryId }
: new[] { Category.ParentCategory.AudibleCategoryId, Category.AudibleCategoryId };
public string TitleSortable => Formatters.GetSortName(Title);
public string SeriesSortable => Formatters.GetSortName(SeriesNames);
// is owned, not optional 1:1
public UserDefinedItem UserDefinedItem { get; private set; }
// UserDefinedItem convenience properties
/// <summary>True if IsLiberated or Error. False if NotLiberated</summary>
public bool Audio_Exists => UserDefinedItem.BookStatus != LiberatedStatus.NotLiberated;
/// <summary>True if exists and IsLiberated. Else false</summary>
public bool PDF_Exists => UserDefinedItem.PdfStatus == LiberatedStatus.Liberated;
// is owned, not optional 1:1
/// <summary>The product's aggregate community rating</summary>
public Rating Rating { get; private set; } = new Rating(0, 0, 0);
@@ -125,11 +108,7 @@ namespace DataLayer
.ToList();
public IEnumerable<Contributor> Authors => getContributions(Role.Author).Select(bc => bc.Contributor).ToList();
public string AuthorNames => string.Join(", ", Authors.Select(a => a.Name));
public IEnumerable<Contributor> Narrators => getContributions(Role.Narrator).Select(bc => bc.Contributor).ToList();
public string NarratorNames => string.Join(", ", Narrators.Select(n => n.Name));
public string Publisher => getContributions(Role.Publisher).SingleOrDefault()?.Contributor.Name;
public void ReplaceAuthors(IEnumerable<Contributor> authors, DbContext context = null)
@@ -185,30 +164,6 @@ namespace DataLayer
#region series
private HashSet<SeriesBook> _seriesLink;
public IEnumerable<SeriesBook> SeriesLink => _seriesLink?.ToList();
public string SeriesNames
{
get
{
if (_seriesLink is null)
return "";
// first: alphabetical by name
var withNames = _seriesLink
.Where(s => !string.IsNullOrWhiteSpace(s.Series.Name))
.Select(s => s.Series.Name)
.OrderBy(a => a)
.ToList();
// then un-named are alpha by series id
var nullNames = _seriesLink
.Where(s => string.IsNullOrWhiteSpace(s.Series.Name))
.Select(s => s.Series.AudibleSeriesId)
.OrderBy(a => a)
.ToList();
var all = withNames.Union(nullNames).ToList();
return string.Join(", ", all);
}
}
public void UpsertSeries(Series series, string order, DbContext context = null)
{
@@ -230,7 +185,6 @@ namespace DataLayer
#region supplements
private HashSet<Supplement> _supplements;
public IEnumerable<Supplement> Supplements => _supplements?.ToList();
public bool HasPdf => Supplements.Any();
public void AddSupplementDownloadUrl(string url)
{

View File

@@ -38,41 +38,6 @@ namespace DataLayer
yield return StoryRating;
}
public float FirstScore
=> OverallRating > 0 ? OverallRating
: PerformanceRating > 0 ? PerformanceRating
: StoryRating;
/// <summary>character: ★</summary>
const char STAR = '\u2605';
/// <summary>character: ½</summary>
const char HALF = '\u00BD';
string getStars(float score)
{
var fullStars = (int)Math.Floor(score);
var starString = "".PadLeft(fullStars, STAR);
if (score - fullStars == 0.5f)
starString += HALF;
return starString;
}
public string ToStarString()
{
var items = new List<string>();
if (OverallRating > 0)
items.Add($"Overall: {getStars(OverallRating)}");
if (PerformanceRating > 0)
items.Add($"Perform: {getStars(PerformanceRating)}");
if (StoryRating > 0)
items.Add($"Story: {getStars(StoryRating)}");
return string.Join("\r\n", items);
}
public override string ToString() => $"Overall={OverallRating} Perf={PerformanceRating} Story={StoryRating}";
}
}

View File

@@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DataLayer
{
public static class EntityExtensions
{
public static string TitleSortable(this Book book) => Formatters.GetSortName(book.Title);
public static string AuthorNames(this Book book) => string.Join(", ", book.Authors.Select(a => a.Name));
public static string NarratorNames(this Book book) => string.Join(", ", book.Narrators.Select(n => n.Name));
/// <summary>True if IsLiberated or Error. False if NotLiberated</summary>
public static bool Audio_Exists(this Book book) => book.UserDefinedItem.BookStatus != LiberatedStatus.NotLiberated;
/// <summary>True if exists and IsLiberated. Else false</summary>
public static bool PDF_Exists(this Book book) => book.UserDefinedItem.PdfStatus == LiberatedStatus.Liberated;
public static string SeriesSortable(this Book book) => Formatters.GetSortName(book.SeriesNames());
public static bool HasPdf(this Book book) => book.Supplements.Any();
public static string SeriesNames(this Book book)
{
if (book.SeriesLink is null)
return "";
// first: alphabetical by name
var withNames = book.SeriesLink
.Where(s => !string.IsNullOrWhiteSpace(s.Series.Name))
.Select(s => s.Series.Name)
.OrderBy(a => a)
.ToList();
// then un-named are alpha by series id
var nullNames = book.SeriesLink
.Where(s => string.IsNullOrWhiteSpace(s.Series.Name))
.Select(s => s.Series.AudibleSeriesId)
.OrderBy(a => a)
.ToList();
var all = withNames.Union(nullNames).ToList();
return string.Join(", ", all);
}
public static string[] CategoriesNames(this Book book)
=> book.Category is null ? new string[0]
: book.Category.ParentCategory is null ? new[] { book.Category.Name }
: new[] { book.Category.ParentCategory.Name, book.Category.Name };
public static string[] CategoriesIds(this Book book)
=> book.Category is null ? null
: book.Category.ParentCategory is null ? new[] { book.Category.AudibleCategoryId }
: new[] { book.Category.ParentCategory.AudibleCategoryId, book.Category.AudibleCategoryId };
public static string AggregateTitles(this IEnumerable<LibraryBook> libraryBooks, int max = 5)
{
if (libraryBooks is null || !libraryBooks.Any())
return "";
max = Math.Max(max, 1);
var titles = libraryBooks.Select(lb => "- " + lb.Book.Title).ToList();
var titlesAgg = titles.Take(max).Aggregate((a, b) => $"{a}\r\n{b}");
if (titles.Count == max + 1)
titlesAgg += $"\r\n\r\nand 1 other";
else if (titles.Count > max + 1)
titlesAgg += $"\r\n\r\nand {titles.Count - max } others";
return titlesAgg;
}
public static float FirstScore(this Rating rating)
=> rating.OverallRating > 0 ? rating.OverallRating
: rating.PerformanceRating > 0 ? rating.PerformanceRating
: rating.StoryRating;
public static string ToStarString(this Rating rating)
{
var items = new List<string>();
if (rating.OverallRating > 0)
items.Add($"Overall: {getStars(rating.OverallRating)}");
if (rating.PerformanceRating > 0)
items.Add($"Perform: {getStars(rating.PerformanceRating)}");
if (rating.StoryRating > 0)
items.Add($"Story: {getStars(rating.StoryRating)}");
return string.Join("\r\n", items);
}
/// <summary>character: ★</summary>
const char STAR = '\u2605';
/// <summary>character: ½</summary>
const char HALF = '\u00BD';
private static string getStars(float score)
{
var fullStars = (int)Math.Floor(score);
var starString = new string(STAR, fullStars);
if (score - fullStars >= 0.25f)
starString += HALF;
return starString;
}
}
}

View File

@@ -10,7 +10,7 @@ namespace DataLayer
// ========================
// these run against the db. linq queries against these MUST be translatable to sql. primatives only. no POCOs or this error occurs:
// Unable to create a constant value of type 'DataLayer.Contributor'. Only primitive types or enumeration types are supported in this context.
// to use full object-linq, load and use local
// to use full object-linq, load and use Local. HOWEVER, Local is only hashed/indexed on PK. All other searches are very slow
// load full table:
// List<Contributor> contributors = ...;
// Contributors.Load();

View File

@@ -17,7 +17,7 @@ namespace FileLiberator
{
private AudiobookDownloadBase abDownloader;
public override bool Validate(LibraryBook libraryBook) => !libraryBook.Book.Audio_Exists;
public override bool Validate(LibraryBook libraryBook) => !libraryBook.Book.Audio_Exists();
public override void Cancel() => abDownloader?.Cancel();
@@ -42,7 +42,7 @@ namespace FileLiberator
try
{
if (libraryBook.Book.Audio_Exists)
if (libraryBook.Book.Audio_Exists())
return new StatusHandler { "Cannot find decrypt. Final audio file already exists" };
bool success = false;
@@ -75,6 +75,9 @@ namespace FileLiberator
if (!movedAudioFile)
return new StatusHandler { "Cannot find final audio file after decryption" };
if (Configuration.Instance.DownloadCoverArt)
DownloadCoverArt(libraryBook);
libraryBook.Book.UserDefinedItem.BookStatus = LiberatedStatus.Liberated;
ApplicationServices.LibraryCommands.UpdateUserDefinedItem(libraryBook.Book);
@@ -114,7 +117,7 @@ namespace FileLiberator
: new AaxcDownloadSingleConverter(outFileName, cacheDir, audiobookDlLic);
if (config.AllowLibationFixup)
converter.RetrievedMetadata += (_, tags) => tags.Generes = string.Join(", ", libraryBook.Book.CategoriesNames);
converter.RetrievedMetadata += (_, tags) => tags.Generes = string.Join(", ", libraryBook.Book.CategoriesNames());
abDownloader = converter;
}
@@ -129,6 +132,7 @@ namespace FileLiberator
// REAL WORK DONE HERE
var success = await Task.Run(abDownloader.Run);
return success;
}
finally
@@ -188,31 +192,27 @@ namespace FileLiberator
}
}
NAudio.Lame.LameConfig lameConfig = new();
lameConfig.Mode = NAudio.Lame.MPEGMode.Mono;
audiobookDlLic.LameConfig = new();
audiobookDlLic.LameConfig.Mode = NAudio.Lame.MPEGMode.Mono;
if (config.LameTargetBitrate)
{
if (config.LameConstantBitrate)
lameConfig.BitRate = config.LameBitrate;
audiobookDlLic.LameConfig.BitRate = config.LameBitrate;
else
{
lameConfig.ABRRateKbps = config.LameBitrate;
lameConfig.VBR = NAudio.Lame.VBRMode.ABR;
lameConfig.WriteVBRTag = true;
audiobookDlLic.LameConfig.ABRRateKbps = config.LameBitrate;
audiobookDlLic.LameConfig.VBR = NAudio.Lame.VBRMode.ABR;
audiobookDlLic.LameConfig.WriteVBRTag = true;
}
}
else
{
lameConfig.VBR = NAudio.Lame.VBRMode.Default;
lameConfig.VBRQuality = config.LameVBRQuality;
lameConfig.WriteVBRTag = true;
audiobookDlLic.LameConfig.VBR = NAudio.Lame.VBRMode.Default;
audiobookDlLic.LameConfig.VBRQuality = config.LameVBRQuality;
audiobookDlLic.LameConfig.WriteVBRTag = true;
}
audiobookDlLic.LameConfig = lameConfig;
return audiobookDlLic;
}
@@ -278,5 +278,34 @@ namespace FileLiberator
return true;
}
private void DownloadCoverArt(LibraryBook libraryBook)
{
var destinationDir = AudibleFileStorage.Audio.GetDestinationDirectory(libraryBook);
var coverPath = AudibleFileStorage.Audio.GetBooksDirectoryFilename(libraryBook, ".jpg");
coverPath = Path.Combine(destinationDir, Path.GetFileName(coverPath));
try
{
if (File.Exists(coverPath))
FileUtility.SaferDelete(coverPath);
(string picId, PictureSize size) = libraryBook.Book.PictureLarge is null ?
(libraryBook.Book.PictureId, PictureSize.Native) :
(libraryBook.Book.PictureLarge, PictureSize.Native);
var picBytes = PictureStorage.GetPictureSynchronously(new PictureDefinition(picId, size));
if (picBytes.Length > 0)
File.WriteAllBytes(coverPath, picBytes);
}
catch (Exception ex)
{
//Failure to download cover art should not be
//considered a failure to download the book
Serilog.Log.Logger.Error(ex, $"Error downloading cover art of {libraryBook.Book.AudibleProductId} to {coverPath} catalog product.");
}
}
}
}

View File

@@ -16,7 +16,7 @@ namespace FileLiberator
{
public override bool Validate(LibraryBook libraryBook)
=> !string.IsNullOrWhiteSpace(getdownloadUrl(libraryBook))
&& !libraryBook.Book.PDF_Exists;
&& !libraryBook.Book.PDF_Exists();
public override async Task<StatusHandler> ProcessAsync(LibraryBook libraryBook)
{

View File

@@ -48,38 +48,9 @@ namespace FileLiberator
= (await ProcessAsync(libraryBook))
?? new StatusHandler { "Processable should never return a null status" };
if (status.IsSuccess)
DownloadCoverArt(libraryBook);
return status;
}
private void DownloadCoverArt(LibraryBook libraryBook)
{
var destinationDir = AudibleFileStorage.Audio.GetDestinationDirectory(libraryBook);
var coverPath = FileManager.FileUtility.GetValidFilename(System.IO.Path.Combine(destinationDir, "Cover.jpg"), "", true);
if (System.IO.File.Exists(coverPath)) return;
try
{
(string picId, PictureSize size) = libraryBook.Book.PictureLarge is null ?
(libraryBook.Book.PictureId, PictureSize.Native) :
(libraryBook.Book.PictureLarge, PictureSize.Native);
var picBytes = PictureStorage.GetPictureSynchronously(new PictureDefinition(picId, size));
if (picBytes.Length > 0)
System.IO.File.WriteAllBytes(coverPath, picBytes);
}
catch (Exception ex)
{
//Failure to download cover art should not be
//considered a failure to download the book
Serilog.Log.Logger.Error(ex.Message);
}
}
public async Task<StatusHandler> TryProcessAsync(LibraryBook libraryBook)
=> Validate(libraryBook)
? await ProcessAsync(libraryBook)

View File

@@ -134,6 +134,7 @@ namespace FileManager
private void AddPath(string path)
{
if (!File.Exists(path)) return;
if (File.GetAttributes(path).HasFlag(FileAttributes.Directory))
AddUniqueFiles(FileUtility.SaferEnumerateFiles(path, SearchPattern, SearchOption));
else

View File

@@ -200,6 +200,13 @@ namespace LibationFileManager
get => persistentDictionary.GetNonString<Dictionary<string,int>>(nameof(GridColumnsWidths));
set => persistentDictionary.SetNonString(nameof(GridColumnsWidths), value);
}
[Description("Save cover image alongside audiobook?")]
public bool DownloadCoverArt
{
get => persistentDictionary.GetNonString<bool>(nameof(DownloadCoverArt));
set => persistentDictionary.SetNonString(nameof(DownloadCoverArt), value);
}
public enum BadBookAction
{

View File

@@ -30,8 +30,14 @@ namespace LibationSearchEngine
// the workaround which allows displaying all books when query is empty
public const string ALL_QUERY = "*:*";
#region index rules
private static ReadOnlyDictionary<string, Func<LibraryBook, string>> idIndexRules { get; }
#region index rules
// common fields used in the "all" default search field
public const string ALL_AUDIBLE_PRODUCT_ID = nameof(Book.AudibleProductId);
public const string ALL_TITLE = nameof(Book.Title);
public const string ALL_AUTHOR_NAMES = "AuthorNames";
public const string ALL_NARRATOR_NAMES = "NarratorNames";
private static ReadOnlyDictionary<string, Func<LibraryBook, string>> idIndexRules { get; }
= new ReadOnlyDictionary<string, Func<LibraryBook, string>>(
new Dictionary<string, Func<LibraryBook, string>>
{
@@ -50,15 +56,15 @@ namespace LibationSearchEngine
[nameof(Book.DatePublished)] = lb => lb.Book.DatePublished?.ToLuceneString(),
[nameof(Book.Title)] = lb => lb.Book.Title,
[nameof(Book.AuthorNames)] = lb => lb.Book.AuthorNames,
["Author"] = lb => lb.Book.AuthorNames,
["Authors"] = lb => lb.Book.AuthorNames,
[nameof(Book.NarratorNames)] = lb => lb.Book.NarratorNames,
["Narrator"] = lb => lb.Book.NarratorNames,
["Narrators"] = lb => lb.Book.NarratorNames,
[ALL_AUTHOR_NAMES] = lb => lb.Book.AuthorNames(),
["Author"] = lb => lb.Book.AuthorNames(),
["Authors"] = lb => lb.Book.AuthorNames(),
[ALL_NARRATOR_NAMES] = lb => lb.Book.NarratorNames(),
["Narrator"] = lb => lb.Book.NarratorNames(),
["Narrators"] = lb => lb.Book.NarratorNames(),
[nameof(Book.Publisher)] = lb => lb.Book.Publisher,
[nameof(Book.SeriesNames)] = lb => string.Join(
["SeriesNames"] = lb => string.Join(
", ",
lb.Book.SeriesLink
.Where(s => !string.IsNullOrWhiteSpace(s.Series.Name))
@@ -70,11 +76,11 @@ namespace LibationSearchEngine
.Select(s => s.Series.AudibleSeriesId)),
["SeriesId"] = lb => string.Join(", ", lb.Book.SeriesLink.Select(s => s.Series.AudibleSeriesId)),
[nameof(Book.CategoriesNames)] = lb => lb.Book.CategoriesIds is null ? null : string.Join(", ", lb.Book.CategoriesIds),
[nameof(Book.Category)] = lb => lb.Book.CategoriesIds is null ? null : string.Join(", ", lb.Book.CategoriesIds),
["Categories"] = lb => lb.Book.CategoriesIds is null ? null : string.Join(", ", lb.Book.CategoriesIds),
["CategoriesId"] = lb => lb.Book.CategoriesIds is null ? null : string.Join(", ", lb.Book.CategoriesIds),
["CategoryId"] = lb => lb.Book.CategoriesIds is null ? null : string.Join(", ", lb.Book.CategoriesIds),
["CategoriesNames"] = lb => lb.Book.CategoriesIds() is null ? null : string.Join(", ", lb.Book.CategoriesIds()),
[nameof(Book.Category)] = lb => lb.Book.CategoriesIds() is null ? null : string.Join(", ", lb.Book.CategoriesIds()),
["Categories"] = lb => lb.Book.CategoriesIds() is null ? null : string.Join(", ", lb.Book.CategoriesIds()),
["CategoriesId"] = lb => lb.Book.CategoriesIds() is null ? null : string.Join(", ", lb.Book.CategoriesIds()),
["CategoryId"] = lb => lb.Book.CategoriesIds() is null ? null : string.Join(", ", lb.Book.CategoriesIds()),
[TAGS.FirstCharToUpper()] = lb => lb.Book.UserDefinedItem.Tags,
@@ -107,14 +113,14 @@ namespace LibationSearchEngine
= new ReadOnlyDictionary<string, Func<LibraryBook, bool>>(
new Dictionary<string, Func<LibraryBook, bool>>
{
["HasDownloads"] = lb => lb.Book.HasPdf,
["HasDownload"] = lb => lb.Book.HasPdf,
["Downloads"] = lb => lb.Book.HasPdf,
["Download"] = lb => lb.Book.HasPdf,
["HasPDFs"] = lb => lb.Book.HasPdf,
["HasPDF"] = lb => lb.Book.HasPdf,
["PDFs"] = lb => lb.Book.HasPdf,
["PDF"] = lb => lb.Book.HasPdf,
["HasDownloads"] = lb => lb.Book.HasPdf(),
["HasDownload"] = lb => lb.Book.HasPdf(),
["Downloads"] = lb => lb.Book.HasPdf(),
["Download"] = lb => lb.Book.HasPdf(),
["HasPDFs"] = lb => lb.Book.HasPdf(),
["HasPDF"] = lb => lb.Book.HasPdf(),
["PDFs"] = lb => lb.Book.HasPdf(),
["PDF"] = lb => lb.Book.HasPdf(),
["IsRated"] = lb => lb.Book.UserDefinedItem.Rating.OverallRating > 0f,
["Rated"] = lb => lb.Book.UserDefinedItem.Rating.OverallRating > 0f,
@@ -151,10 +157,10 @@ namespace LibationSearchEngine
private static IEnumerable<Func<LibraryBook, string>> allFieldIndexRules { get; }
= new List<Func<LibraryBook, string>>
{
idIndexRules[nameof(Book.AudibleProductId)],
stringIndexRules[nameof(Book.Title)],
stringIndexRules[nameof(Book.AuthorNames)],
stringIndexRules[nameof(Book.NarratorNames)]
idIndexRules[ALL_AUDIBLE_PRODUCT_ID],
stringIndexRules[ALL_TITLE],
stringIndexRules[ALL_AUTHOR_NAMES],
stringIndexRules[ALL_NARRATOR_NAMES]
};
#endregion

View File

@@ -26,8 +26,8 @@ namespace LibationWinForms.BookLiberation
//Set default values from library
AudioDecodable_TitleDiscovered(sender, libraryBook.Book.Title);
AudioDecodable_AuthorsDiscovered(sender, libraryBook.Book.AuthorNames);
AudioDecodable_NarratorsDiscovered(sender, libraryBook.Book.NarratorNames);
AudioDecodable_AuthorsDiscovered(sender, libraryBook.Book.AuthorNames());
AudioDecodable_NarratorsDiscovered(sender, libraryBook.Book.NarratorNames());
AudioDecodable_CoverImageDiscovered(sender,
PictureStorage.GetPicture(
new PictureDefinition(

View File

@@ -240,8 +240,8 @@ namespace LibationWinForms.BookLiberation
details =
$@" Title: {libraryBook.Book.Title}
ID: {libraryBook.Book.AudibleProductId}
Author: {trunc(libraryBook.Book.AuthorNames)}
Narr: {trunc(libraryBook.Book.NarratorNames)}";
Author: {trunc(libraryBook.Book.AuthorNames())}
Narr: {trunc(libraryBook.Book.NarratorNames())}";
}
catch
{

View File

@@ -128,7 +128,7 @@ namespace LibationWinForms.Dialogs
}
catch (Exception ex)
{
MessageBoxAlertAdmin.Show("Error attempting to save accounts", "Error saving accounts", ex);
MessageBoxLib.ShowAdminAlert("Error attempting to save accounts", "Error saving accounts", ex);
}
}

View File

@@ -46,15 +46,16 @@ namespace LibationWinForms.Dialogs
var t = @$"
Title: {Book.Title}
Author(s): {Book.AuthorNames}
Narrator(s): {Book.NarratorNames}
Author(s): {Book.AuthorNames()}
Narrator(s): {Book.NarratorNames()}
Length: {(Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min")}
Category: {string.Join(" > ", Book.CategoriesNames)}
Category: {string.Join(" > ", Book.CategoriesNames())}
Purchase Date: {_libraryBook.DateAdded.ToString("d")}
".Trim();
if (!string.IsNullOrWhiteSpace(Book.SeriesNames))
t += $"\r\nSeries: {Book.SeriesNames}";
var seriesNames = Book.SeriesNames();
if (!string.IsNullOrWhiteSpace(seriesNames))
t += $"\r\nSeries: {seriesNames}";
var bookRating = Book.Rating?.ToStarString();
if (!string.IsNullOrWhiteSpace(bookRating))

View File

@@ -46,7 +46,7 @@ namespace LibationWinForms.Dialogs
if (template is null)
{
MessageBoxAlertAdmin.Show($"Programming error. {nameof(EditTemplateDialog)} was not created correctly", "Edit template error", new NullReferenceException($"{nameof(template)} is null"));
MessageBoxLib.ShowAdminAlert($"Programming error. {nameof(EditTemplateDialog)} was not created correctly", "Edit template error", new NullReferenceException($"{nameof(template)} is null"));
return;
}

View File

@@ -0,0 +1,120 @@
namespace LibationWinForms.Dialogs
{
partial class LiberatedStatusBatchDialog
{
/// <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.bookLiberatedCb = new System.Windows.Forms.ComboBox();
this.bookLiberatedLbl = new System.Windows.Forms.Label();
this.liberatedDescLbl = new System.Windows.Forms.Label();
this.cancelBtn = new System.Windows.Forms.Button();
this.saveBtn = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// bookLiberatedCb
//
this.bookLiberatedCb.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.bookLiberatedCb.FormattingEnabled = true;
this.bookLiberatedCb.Location = new System.Drawing.Point(52, 54);
this.bookLiberatedCb.Name = "bookLiberatedCb";
this.bookLiberatedCb.Size = new System.Drawing.Size(121, 23);
this.bookLiberatedCb.TabIndex = 7;
//
// bookLiberatedLbl
//
this.bookLiberatedLbl.AutoSize = true;
this.bookLiberatedLbl.Location = new System.Drawing.Point(12, 57);
this.bookLiberatedLbl.Name = "bookLiberatedLbl";
this.bookLiberatedLbl.Size = new System.Drawing.Size(34, 15);
this.bookLiberatedLbl.TabIndex = 6;
this.bookLiberatedLbl.Text = "Book";
//
// liberatedDescLbl
//
this.liberatedDescLbl.AutoSize = true;
this.liberatedDescLbl.Location = new System.Drawing.Point(12, 9);
this.liberatedDescLbl.Name = "liberatedDescLbl";
this.liberatedDescLbl.Size = new System.Drawing.Size(312, 30);
this.liberatedDescLbl.TabIndex = 5;
this.liberatedDescLbl.Text = "To download again next time: change to Not Downloaded\r\nTo not download: change to" +
" Downloaded";
//
// cancelBtn
//
this.cancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.cancelBtn.Location = new System.Drawing.Point(464, 79);
this.cancelBtn.Name = "cancelBtn";
this.cancelBtn.Size = new System.Drawing.Size(88, 27);
this.cancelBtn.TabIndex = 9;
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(346, 79);
this.saveBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.saveBtn.Name = "saveBtn";
this.saveBtn.Size = new System.Drawing.Size(88, 27);
this.saveBtn.TabIndex = 8;
this.saveBtn.Text = "Save";
this.saveBtn.UseVisualStyleBackColor = true;
this.saveBtn.Click += new System.EventHandler(this.saveBtn_Click);
//
// LiberatedStatusBatchDialog
//
this.AcceptButton = this.saveBtn;
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.cancelBtn;
this.ClientSize = new System.Drawing.Size(564, 118);
this.Controls.Add(this.cancelBtn);
this.Controls.Add(this.saveBtn);
this.Controls.Add(this.bookLiberatedCb);
this.Controls.Add(this.bookLiberatedLbl);
this.Controls.Add(this.liberatedDescLbl);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "LiberatedStatusBatchDialog";
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Liberated status: Whether the book has been downloaded";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.ComboBox bookLiberatedCb;
private System.Windows.Forms.Label bookLiberatedLbl;
private System.Windows.Forms.Label liberatedDescLbl;
private System.Windows.Forms.Button cancelBtn;
private System.Windows.Forms.Button saveBtn;
}
}

View File

@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using DataLayer;
using Dinah.Core;
using LibationFileManager;
namespace LibationWinForms.Dialogs
{
public partial class LiberatedStatusBatchDialog : Form
{
public LiberatedStatus BookLiberatedStatus { get; private set; }
public class liberatedComboBoxItem
{
public LiberatedStatus Status { get; set; }
public string Text { get; set; }
public override string ToString() => Text;
}
public LiberatedStatusBatchDialog()
{
InitializeComponent();
this.SetLibationIcon();
this.bookLiberatedCb.Items.Add(new liberatedComboBoxItem { Status = LiberatedStatus.Liberated, Text = "Downloaded" });
this.bookLiberatedCb.Items.Add(new liberatedComboBoxItem { Status = LiberatedStatus.NotLiberated, Text = "Not Downloaded" });
this.bookLiberatedCb.SelectedIndex = 0;
}
private void saveBtn_Click(object sender, EventArgs e)
{
BookLiberatedStatus = ((liberatedComboBoxItem)this.bookLiberatedCb.SelectedItem).Status;
this.DialogResult = DialogResult.OK;
}
private void cancelBtn_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}
}
}

View File

@@ -0,0 +1,60 @@
<root>
<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>
</root>

View File

@@ -77,7 +77,7 @@ namespace LibationWinForms.Dialogs
}
catch (Exception ex)
{
MessageBoxAlertAdmin.Show(
MessageBoxLib.ShowAdminAlert(
"Error scanning library. You may still manually select books to remove from Libation's library.",
"Error scanning library",
ex);
@@ -95,34 +95,22 @@ namespace LibationWinForms.Dialogs
if (selectedBooks.Count == 0)
return;
var titles = selectedBooks.Select(rge => "- " + rge.Title).ToList();
var titlesAgg = titles.Take(5).Aggregate((a, b) => $"{a}\r\n{b}");
if (titles.Count == 6)
titlesAgg += $"\r\n\r\nand 1 other";
else if (titles.Count > 6)
titlesAgg += $"\r\n\r\nand {titles.Count - 5} others";
var libraryBooks = selectedBooks.Select(rge => rge.LibraryBook).ToList();
var result = MessageBoxLib.ShowConfirmationDialog(
libraryBooks,
$"Are you sure you want to remove {0} from Libation's library?",
"Remove books from Libation?");
string thisThese = selectedBooks.Count > 1 ? "these" : "this";
string bookBooks = selectedBooks.Count > 1 ? "books" : "book";
if (result != DialogResult.Yes)
return;
var result = MessageBox.Show(
this,
$"Are you sure you want to remove {thisThese} {selectedBooks.Count} {bookBooks} from Libation's library?\r\n\r\n{titlesAgg}",
"Remove books from Libation?",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question,
MessageBoxDefaultButton.Button1);
var idsToRemove = libraryBooks.Select(lb => lb.Book.AudibleProductId).ToList();
var removeLibraryBooks = await LibraryCommands.RemoveBooksAsync(idsToRemove);
if (result == DialogResult.Yes)
{
var idsToRemove = selectedBooks.Select(rge => rge.AudibleProductId).ToList();
var removeLibraryBooks = await LibraryCommands.RemoveBooksAsync(idsToRemove);
foreach (var rEntry in selectedBooks)
_removableGridEntries.Remove(rEntry);
foreach (var rEntry in selectedBooks)
_removableGridEntries.Remove(rEntry);
UpdateSelection();
}
UpdateSelection();
}
private void UpdateSelection()

View File

File diff suppressed because it is too large Load Diff

View File

@@ -44,6 +44,7 @@ namespace LibationWinForms.Dialogs
this.retainAaxFileCbox.Text = desc(nameof(config.RetainAaxFile));
this.stripUnabridgedCbox.Text = desc(nameof(config.StripUnabridged));
this.createCueSheetCbox.Text = desc(nameof(config.CreateCueSheet));
this.downloadCoverArtCbox.Text = desc(nameof(config.DownloadCoverArt));
booksSelectControl.SetSearchTitle("books location");
booksSelectControl.SetDirectoryItems(
@@ -73,6 +74,7 @@ namespace LibationWinForms.Dialogs
lameConstantBitrateCbox.Checked = config.LameConstantBitrate;
LameMatchSourceBRCbox.Checked = config.LameMatchSourceBR;
lameVBRQualityTb.Value = config.LameVBRQuality;
downloadCoverArtCbox.Checked = config.DownloadCoverArt;
autoScanCb.Checked = config.AutoScan;
showImportedStatsCb.Checked = config.ShowImportedStats;
@@ -179,7 +181,7 @@ namespace LibationWinForms.Dialogs
// only warn if changed during this time. don't want to warn every time user happens to change settings while level is verbose
if (logLevelOld != logLevelNew)
MessageBoxVerboseLoggingWarning.ShowIfTrue();
MessageBoxLib.VerboseLoggingWarning_ShowIfTrue();
}
config.AllowLibationFixup = allowLibationFixupCbox.Checked;
@@ -196,6 +198,7 @@ namespace LibationWinForms.Dialogs
config.LameConstantBitrate = lameConstantBitrateCbox.Checked;
config.LameMatchSourceBR = LameMatchSourceBRCbox.Checked;
config.LameVBRQuality = lameVBRQualityTb.Value;
config.DownloadCoverArt = downloadCoverArtCbox.Checked;
config.AutoScan = autoScanCb.Checked;
config.ShowImportedStats = showImportedStatsCb.Checked;

View File

@@ -0,0 +1,112 @@
namespace LibationWinForms.Dialogs
{
partial class TagsBatchDialog
{
/// <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.tagsDescLbl = new System.Windows.Forms.Label();
this.newTagsTb = new System.Windows.Forms.TextBox();
this.cancelBtn = new System.Windows.Forms.Button();
this.saveBtn = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// tagsDescLbl
//
this.tagsDescLbl.AutoSize = true;
this.tagsDescLbl.Location = new System.Drawing.Point(13, 9);
this.tagsDescLbl.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.tagsDescLbl.Name = "tagsDescLbl";
this.tagsDescLbl.Size = new System.Drawing.Size(458, 15);
this.tagsDescLbl.TabIndex = 2;
this.tagsDescLbl.Text = "Tags are separated by a space. Each tag can contain letters, numbers, and undersc" +
"ores";
//
// newTagsTb
//
this.newTagsTb.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.newTagsTb.Location = new System.Drawing.Point(13, 30);
this.newTagsTb.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.newTagsTb.Name = "newTagsTb";
this.newTagsTb.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.newTagsTb.Size = new System.Drawing.Size(591, 23);
this.newTagsTb.TabIndex = 3;
//
// cancelBtn
//
this.cancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.cancelBtn.Location = new System.Drawing.Point(517, 71);
this.cancelBtn.Name = "cancelBtn";
this.cancelBtn.Size = new System.Drawing.Size(88, 27);
this.cancelBtn.TabIndex = 6;
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(399, 71);
this.saveBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.saveBtn.Name = "saveBtn";
this.saveBtn.Size = new System.Drawing.Size(88, 27);
this.saveBtn.TabIndex = 5;
this.saveBtn.Text = "Save";
this.saveBtn.UseVisualStyleBackColor = true;
this.saveBtn.Click += new System.EventHandler(this.saveBtn_Click);
//
// TagsBatchDialog
//
this.AcceptButton = this.saveBtn;
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.cancelBtn;
this.ClientSize = new System.Drawing.Size(617, 110);
this.Controls.Add(this.cancelBtn);
this.Controls.Add(this.saveBtn);
this.Controls.Add(this.tagsDescLbl);
this.Controls.Add(this.newTagsTb);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "TagsBatchDialog";
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Replace Tags";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label tagsDescLbl;
private System.Windows.Forms.TextBox newTagsTb;
private System.Windows.Forms.Button cancelBtn;
private System.Windows.Forms.Button saveBtn;
}
}

View File

@@ -0,0 +1,35 @@
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 LibationWinForms.Dialogs
{
public partial class TagsBatchDialog : Form
{
public string NewTags { get; private set; }
public TagsBatchDialog()
{
InitializeComponent();
this.SetLibationIcon();
}
private void saveBtn_Click(object sender, EventArgs e)
{
NewTags = this.newTagsTb.Text;
this.DialogResult = DialogResult.OK;
}
private void cancelBtn_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}
}
}

View File

@@ -0,0 +1,60 @@
<root>
<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>
</root>

View File

@@ -35,8 +35,8 @@
this.filterSearchTb = new System.Windows.Forms.TextBox();
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
this.importToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.noAccountsYetAddAccountToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.autoScanLibraryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.noAccountsYetAddAccountToolStripMenuItem = 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();
@@ -47,18 +47,24 @@
this.beginBookBackupsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.beginPdfBackupsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.convertAllM4bToMp3ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.liberateVisible2ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.exportToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.exportLibraryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.quickFiltersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.firstFilterIsDefaultToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.editQuickFiltersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
this.scanningToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.visibleBooksToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.liberateVisibleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.replaceTagsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.setDownloadedToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.removeToolStripMenuItem = 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.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.scanningToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.statusStrip1 = new System.Windows.Forms.StatusStrip();
this.visibleCountLbl = new System.Windows.Forms.ToolStripStatusLabel();
this.springLbl = new System.Windows.Forms.ToolStripStatusLabel();
@@ -74,18 +80,18 @@
this.gridPanel.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.gridPanel.Location = new System.Drawing.Point(14, 65);
this.gridPanel.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.gridPanel.Location = new System.Drawing.Point(34, 178);
this.gridPanel.Margin = new System.Windows.Forms.Padding(10, 8, 10, 8);
this.gridPanel.Name = "gridPanel";
this.gridPanel.Size = new System.Drawing.Size(979, 445);
this.gridPanel.Size = new System.Drawing.Size(2378, 1216);
this.gridPanel.TabIndex = 5;
//
// filterHelpBtn
//
this.filterHelpBtn.Location = new System.Drawing.Point(14, 31);
this.filterHelpBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.filterHelpBtn.Location = new System.Drawing.Point(34, 85);
this.filterHelpBtn.Margin = new System.Windows.Forms.Padding(10, 8, 10, 8);
this.filterHelpBtn.Name = "filterHelpBtn";
this.filterHelpBtn.Size = new System.Drawing.Size(26, 27);
this.filterHelpBtn.Size = new System.Drawing.Size(63, 74);
this.filterHelpBtn.TabIndex = 3;
this.filterHelpBtn.Text = "?";
this.filterHelpBtn.UseVisualStyleBackColor = true;
@@ -94,10 +100,10 @@
// filterBtn
//
this.filterBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.filterBtn.Location = new System.Drawing.Point(905, 31);
this.filterBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.filterBtn.Location = new System.Drawing.Point(2198, 85);
this.filterBtn.Margin = new System.Windows.Forms.Padding(10, 8, 10, 8);
this.filterBtn.Name = "filterBtn";
this.filterBtn.Size = new System.Drawing.Size(88, 27);
this.filterBtn.Size = new System.Drawing.Size(214, 74);
this.filterBtn.TabIndex = 2;
this.filterBtn.Text = "Filter";
this.filterBtn.UseVisualStyleBackColor = true;
@@ -107,26 +113,28 @@
//
this.filterSearchTb.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.filterSearchTb.Location = new System.Drawing.Point(217, 33);
this.filterSearchTb.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.filterSearchTb.Location = new System.Drawing.Point(527, 90);
this.filterSearchTb.Margin = new System.Windows.Forms.Padding(10, 8, 10, 8);
this.filterSearchTb.Name = "filterSearchTb";
this.filterSearchTb.Size = new System.Drawing.Size(681, 23);
this.filterSearchTb.Size = new System.Drawing.Size(1648, 47);
this.filterSearchTb.TabIndex = 1;
this.filterSearchTb.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.filterSearchTb_KeyPress);
//
// menuStrip1
//
this.menuStrip1.ImageScalingSize = new System.Drawing.Size(40, 40);
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.importToolStripMenuItem,
this.liberateToolStripMenuItem,
this.exportToolStripMenuItem,
this.quickFiltersToolStripMenuItem,
this.settingsToolStripMenuItem,
this.scanningToolStripMenuItem});
this.scanningToolStripMenuItem,
this.visibleBooksToolStripMenuItem,
this.settingsToolStripMenuItem});
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
this.menuStrip1.Name = "menuStrip1";
this.menuStrip1.Padding = new System.Windows.Forms.Padding(7, 2, 0, 2);
this.menuStrip1.Size = new System.Drawing.Size(1007, 24);
this.menuStrip1.Padding = new System.Windows.Forms.Padding(17, 5, 0, 5);
this.menuStrip1.Size = new System.Drawing.Size(2446, 58);
this.menuStrip1.TabIndex = 0;
this.menuStrip1.Text = "menuStrip1";
//
@@ -140,41 +148,41 @@
this.scanLibraryOfSomeAccountsToolStripMenuItem,
this.removeLibraryBooksToolStripMenuItem});
this.importToolStripMenuItem.Name = "importToolStripMenuItem";
this.importToolStripMenuItem.Size = new System.Drawing.Size(55, 20);
this.importToolStripMenuItem.Size = new System.Drawing.Size(132, 48);
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...";
this.noAccountsYetAddAccountToolStripMenuItem.Click += new System.EventHandler(this.noAccountsYetAddAccountToolStripMenuItem_Click);
//
// autoScanLibraryToolStripMenuItem
//
this.autoScanLibraryToolStripMenuItem.Name = "autoScanLibraryToolStripMenuItem";
this.autoScanLibraryToolStripMenuItem.Size = new System.Drawing.Size(247, 22);
this.autoScanLibraryToolStripMenuItem.Size = new System.Drawing.Size(613, 54);
this.autoScanLibraryToolStripMenuItem.Text = "A&uto Scan Library";
this.autoScanLibraryToolStripMenuItem.Click += new System.EventHandler(this.autoScanLibraryToolStripMenuItem_Click);
//
// noAccountsYetAddAccountToolStripMenuItem
//
this.noAccountsYetAddAccountToolStripMenuItem.Name = "noAccountsYetAddAccountToolStripMenuItem";
this.noAccountsYetAddAccountToolStripMenuItem.Size = new System.Drawing.Size(613, 54);
this.noAccountsYetAddAccountToolStripMenuItem.Text = "No accounts yet. A&dd Account...";
this.noAccountsYetAddAccountToolStripMenuItem.Click += new System.EventHandler(this.noAccountsYetAddAccountToolStripMenuItem_Click);
//
// scanLibraryToolStripMenuItem
//
this.scanLibraryToolStripMenuItem.Name = "scanLibraryToolStripMenuItem";
this.scanLibraryToolStripMenuItem.Size = new System.Drawing.Size(247, 22);
this.scanLibraryToolStripMenuItem.Size = new System.Drawing.Size(613, 54);
this.scanLibraryToolStripMenuItem.Text = "Scan &Library";
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.Size = new System.Drawing.Size(613, 54);
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.Size = new System.Drawing.Size(613, 54);
this.scanLibraryOfSomeAccountsToolStripMenuItem.Text = "Scan Library of &Some Accounts...";
this.scanLibraryOfSomeAccountsToolStripMenuItem.Click += new System.EventHandler(this.scanLibraryOfSomeAccountsToolStripMenuItem_Click);
//
@@ -184,21 +192,21 @@
this.removeAllAccountsToolStripMenuItem,
this.removeSomeAccountsToolStripMenuItem});
this.removeLibraryBooksToolStripMenuItem.Name = "removeLibraryBooksToolStripMenuItem";
this.removeLibraryBooksToolStripMenuItem.Size = new System.Drawing.Size(247, 22);
this.removeLibraryBooksToolStripMenuItem.Size = new System.Drawing.Size(613, 54);
this.removeLibraryBooksToolStripMenuItem.Text = "Remove Library Books";
this.removeLibraryBooksToolStripMenuItem.Click += new System.EventHandler(this.removeLibraryBooksToolStripMenuItem_Click);
//
// removeAllAccountsToolStripMenuItem
//
this.removeAllAccountsToolStripMenuItem.Name = "removeAllAccountsToolStripMenuItem";
this.removeAllAccountsToolStripMenuItem.Size = new System.Drawing.Size(157, 22);
this.removeAllAccountsToolStripMenuItem.Size = new System.Drawing.Size(390, 54);
this.removeAllAccountsToolStripMenuItem.Text = "All Accounts";
this.removeAllAccountsToolStripMenuItem.Click += new System.EventHandler(this.removeAllAccountsToolStripMenuItem_Click);
//
// removeSomeAccountsToolStripMenuItem
//
this.removeSomeAccountsToolStripMenuItem.Name = "removeSomeAccountsToolStripMenuItem";
this.removeSomeAccountsToolStripMenuItem.Size = new System.Drawing.Size(157, 22);
this.removeSomeAccountsToolStripMenuItem.Size = new System.Drawing.Size(390, 54);
this.removeSomeAccountsToolStripMenuItem.Text = "Some Accounts";
this.removeSomeAccountsToolStripMenuItem.Click += new System.EventHandler(this.removeSomeAccountsToolStripMenuItem_Click);
//
@@ -207,44 +215,52 @@
this.liberateToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.beginBookBackupsToolStripMenuItem,
this.beginPdfBackupsToolStripMenuItem,
this.convertAllM4bToMp3ToolStripMenuItem});
this.convertAllM4bToMp3ToolStripMenuItem,
this.liberateVisible2ToolStripMenuItem});
this.liberateToolStripMenuItem.Name = "liberateToolStripMenuItem";
this.liberateToolStripMenuItem.Size = new System.Drawing.Size(61, 20);
this.liberateToolStripMenuItem.Size = new System.Drawing.Size(148, 48);
this.liberateToolStripMenuItem.Text = "&Liberate";
//
// beginBookBackupsToolStripMenuItem
//
this.beginBookBackupsToolStripMenuItem.Name = "beginBookBackupsToolStripMenuItem";
this.beginBookBackupsToolStripMenuItem.Size = new System.Drawing.Size(293, 22);
this.beginBookBackupsToolStripMenuItem.Size = new System.Drawing.Size(728, 54);
this.beginBookBackupsToolStripMenuItem.Text = "Begin &Book and PDF Backups: {0}";
this.beginBookBackupsToolStripMenuItem.Click += new System.EventHandler(this.beginBookBackupsToolStripMenuItem_Click);
//
// beginPdfBackupsToolStripMenuItem
//
this.beginPdfBackupsToolStripMenuItem.Name = "beginPdfBackupsToolStripMenuItem";
this.beginPdfBackupsToolStripMenuItem.Size = new System.Drawing.Size(293, 22);
this.beginPdfBackupsToolStripMenuItem.Size = new System.Drawing.Size(728, 54);
this.beginPdfBackupsToolStripMenuItem.Text = "Begin &PDF Only Backups: {0}";
this.beginPdfBackupsToolStripMenuItem.Click += new System.EventHandler(this.beginPdfBackupsToolStripMenuItem_Click);
//
// convertAllM4bToMp3ToolStripMenuItem
//
this.convertAllM4bToMp3ToolStripMenuItem.Name = "convertAllM4bToMp3ToolStripMenuItem";
this.convertAllM4bToMp3ToolStripMenuItem.Size = new System.Drawing.Size(293, 22);
this.convertAllM4bToMp3ToolStripMenuItem.Text = "Convert all M4b to Mp3 [Long-running]...";
this.convertAllM4bToMp3ToolStripMenuItem.Size = new System.Drawing.Size(728, 54);
this.convertAllM4bToMp3ToolStripMenuItem.Text = "Convert all &M4b to Mp3 [Long-running]...";
this.convertAllM4bToMp3ToolStripMenuItem.Click += new System.EventHandler(this.convertAllM4bToMp3ToolStripMenuItem_Click);
//
// liberateVisible2ToolStripMenuItem
//
this.liberateVisible2ToolStripMenuItem.Name = "liberateVisible2ToolStripMenuItem";
this.liberateVisible2ToolStripMenuItem.Size = new System.Drawing.Size(728, 54);
this.liberateVisible2ToolStripMenuItem.Text = "Liberate &Visible Books: {0}";
this.liberateVisible2ToolStripMenuItem.Click += new System.EventHandler(this.liberateVisible);
//
// 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.Size = new System.Drawing.Size(127, 48);
this.exportToolStripMenuItem.Text = "E&xport";
//
// exportLibraryToolStripMenuItem
//
this.exportLibraryToolStripMenuItem.Name = "exportLibraryToolStripMenuItem";
this.exportLibraryToolStripMenuItem.Size = new System.Drawing.Size(156, 22);
this.exportLibraryToolStripMenuItem.Size = new System.Drawing.Size(387, 54);
this.exportLibraryToolStripMenuItem.Text = "E&xport Library...";
this.exportLibraryToolStripMenuItem.Click += new System.EventHandler(this.exportLibraryToolStripMenuItem_Click);
//
@@ -255,27 +271,76 @@
this.editQuickFiltersToolStripMenuItem,
this.toolStripSeparator1});
this.quickFiltersToolStripMenuItem.Name = "quickFiltersToolStripMenuItem";
this.quickFiltersToolStripMenuItem.Size = new System.Drawing.Size(84, 20);
this.quickFiltersToolStripMenuItem.Size = new System.Drawing.Size(204, 48);
this.quickFiltersToolStripMenuItem.Text = "Quick &Filters";
//
// firstFilterIsDefaultToolStripMenuItem
//
this.firstFilterIsDefaultToolStripMenuItem.Name = "firstFilterIsDefaultToolStripMenuItem";
this.firstFilterIsDefaultToolStripMenuItem.Size = new System.Drawing.Size(256, 22);
this.firstFilterIsDefaultToolStripMenuItem.Size = new System.Drawing.Size(639, 54);
this.firstFilterIsDefaultToolStripMenuItem.Text = "Start Libation with 1st filter &Default";
this.firstFilterIsDefaultToolStripMenuItem.Click += new System.EventHandler(this.FirstFilterIsDefaultToolStripMenuItem_Click);
//
// editQuickFiltersToolStripMenuItem
//
this.editQuickFiltersToolStripMenuItem.Name = "editQuickFiltersToolStripMenuItem";
this.editQuickFiltersToolStripMenuItem.Size = new System.Drawing.Size(256, 22);
this.editQuickFiltersToolStripMenuItem.Size = new System.Drawing.Size(639, 54);
this.editQuickFiltersToolStripMenuItem.Text = "&Edit quick filters...";
this.editQuickFiltersToolStripMenuItem.Click += new System.EventHandler(this.EditQuickFiltersToolStripMenuItem_Click);
//
// toolStripSeparator1
//
this.toolStripSeparator1.Name = "toolStripSeparator1";
this.toolStripSeparator1.Size = new System.Drawing.Size(253, 6);
this.toolStripSeparator1.Size = new System.Drawing.Size(636, 6);
//
// scanningToolStripMenuItem
//
this.scanningToolStripMenuItem.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right;
this.scanningToolStripMenuItem.Enabled = false;
this.scanningToolStripMenuItem.Image = global::LibationWinForms.Properties.Resources.import_16x16;
this.scanningToolStripMenuItem.Name = "scanningToolStripMenuItem";
this.scanningToolStripMenuItem.Size = new System.Drawing.Size(224, 48);
this.scanningToolStripMenuItem.Text = "Scanning...";
this.scanningToolStripMenuItem.Visible = false;
//
// visibleBooksToolStripMenuItem
//
this.visibleBooksToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.liberateVisibleToolStripMenuItem,
this.replaceTagsToolStripMenuItem,
this.setDownloadedToolStripMenuItem,
this.removeToolStripMenuItem});
this.visibleBooksToolStripMenuItem.Name = "visibleBooksToolStripMenuItem";
this.visibleBooksToolStripMenuItem.Size = new System.Drawing.Size(267, 48);
this.visibleBooksToolStripMenuItem.Text = "&Visible Books: {0}";
//
// liberateVisibleToolStripMenuItem
//
this.liberateVisibleToolStripMenuItem.Name = "liberateVisibleToolStripMenuItem";
this.liberateVisibleToolStripMenuItem.Size = new System.Drawing.Size(525, 54);
this.liberateVisibleToolStripMenuItem.Text = "&Liberate: {0}";
this.liberateVisibleToolStripMenuItem.Click += new System.EventHandler(this.liberateVisible);
//
// replaceTagsToolStripMenuItem
//
this.replaceTagsToolStripMenuItem.Name = "replaceTagsToolStripMenuItem";
this.replaceTagsToolStripMenuItem.Size = new System.Drawing.Size(525, 54);
this.replaceTagsToolStripMenuItem.Text = "Replace &Tags...";
this.replaceTagsToolStripMenuItem.Click += new System.EventHandler(this.replaceTagsToolStripMenuItem_Click);
//
// setDownloadedToolStripMenuItem
//
this.setDownloadedToolStripMenuItem.Name = "setDownloadedToolStripMenuItem";
this.setDownloadedToolStripMenuItem.Size = new System.Drawing.Size(525, 54);
this.setDownloadedToolStripMenuItem.Text = "Set \'&Downloaded\' status...";
this.setDownloadedToolStripMenuItem.Click += new System.EventHandler(this.setDownloadedToolStripMenuItem_Click);
//
// removeToolStripMenuItem
//
this.removeToolStripMenuItem.Name = "removeToolStripMenuItem";
this.removeToolStripMenuItem.Size = new System.Drawing.Size(525, 54);
this.removeToolStripMenuItem.Text = "&Remove from library...";
this.removeToolStripMenuItem.Click += new System.EventHandler(this.removeToolStripMenuItem_Click);
//
// settingsToolStripMenuItem
//
@@ -285,89 +350,80 @@
this.toolStripSeparator2,
this.aboutToolStripMenuItem});
this.settingsToolStripMenuItem.Name = "settingsToolStripMenuItem";
this.settingsToolStripMenuItem.Size = new System.Drawing.Size(61, 20);
this.settingsToolStripMenuItem.Size = new System.Drawing.Size(149, 48);
this.settingsToolStripMenuItem.Text = "&Settings";
//
// accountsToolStripMenuItem
//
this.accountsToolStripMenuItem.Name = "accountsToolStripMenuItem";
this.accountsToolStripMenuItem.Size = new System.Drawing.Size(133, 22);
this.accountsToolStripMenuItem.Size = new System.Drawing.Size(327, 54);
this.accountsToolStripMenuItem.Text = "&Accounts...";
this.accountsToolStripMenuItem.Click += new System.EventHandler(this.accountsToolStripMenuItem_Click);
//
// basicSettingsToolStripMenuItem
//
this.basicSettingsToolStripMenuItem.Name = "basicSettingsToolStripMenuItem";
this.basicSettingsToolStripMenuItem.Size = new System.Drawing.Size(133, 22);
this.basicSettingsToolStripMenuItem.Size = new System.Drawing.Size(327, 54);
this.basicSettingsToolStripMenuItem.Text = "&Settings...";
this.basicSettingsToolStripMenuItem.Click += new System.EventHandler(this.basicSettingsToolStripMenuItem_Click);
//
// toolStripSeparator2
//
this.toolStripSeparator2.Name = "toolStripSeparator2";
this.toolStripSeparator2.Size = new System.Drawing.Size(130, 6);
this.toolStripSeparator2.Size = new System.Drawing.Size(324, 6);
//
// aboutToolStripMenuItem
//
this.aboutToolStripMenuItem.Name = "aboutToolStripMenuItem";
this.aboutToolStripMenuItem.Size = new System.Drawing.Size(133, 22);
this.aboutToolStripMenuItem.Size = new System.Drawing.Size(327, 54);
this.aboutToolStripMenuItem.Text = "A&bout...";
this.aboutToolStripMenuItem.Click += new System.EventHandler(this.aboutToolStripMenuItem_Click);
//
// scanningToolStripMenuItem
//
this.scanningToolStripMenuItem.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right;
this.scanningToolStripMenuItem.Enabled = false;
this.scanningToolStripMenuItem.Image = global::LibationWinForms.Properties.Resources.import_16x16;
this.scanningToolStripMenuItem.Name = "scanningToolStripMenuItem";
this.scanningToolStripMenuItem.Size = new System.Drawing.Size(93, 20);
this.scanningToolStripMenuItem.Text = "Scanning...";
this.scanningToolStripMenuItem.Visible = false;
//
// statusStrip1
//
this.statusStrip1.ImageScalingSize = new System.Drawing.Size(40, 40);
this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.visibleCountLbl,
this.springLbl,
this.backupsCountsLbl,
this.pdfsCountsLbl});
this.statusStrip1.Location = new System.Drawing.Point(0, 517);
this.statusStrip1.Location = new System.Drawing.Point(0, 1419);
this.statusStrip1.Name = "statusStrip1";
this.statusStrip1.Padding = new System.Windows.Forms.Padding(1, 0, 16, 0);
this.statusStrip1.Size = new System.Drawing.Size(1007, 22);
this.statusStrip1.Padding = new System.Windows.Forms.Padding(2, 0, 39, 0);
this.statusStrip1.Size = new System.Drawing.Size(2446, 54);
this.statusStrip1.TabIndex = 6;
this.statusStrip1.Text = "statusStrip1";
//
// visibleCountLbl
//
this.visibleCountLbl.Name = "visibleCountLbl";
this.visibleCountLbl.Size = new System.Drawing.Size(53, 17);
this.visibleCountLbl.Size = new System.Drawing.Size(136, 41);
this.visibleCountLbl.Text = "Visible: 0";
//
// springLbl
//
this.springLbl.Name = "springLbl";
this.springLbl.Size = new System.Drawing.Size(548, 17);
this.springLbl.Size = new System.Drawing.Size(1299, 41);
this.springLbl.Spring = true;
//
// backupsCountsLbl
//
this.backupsCountsLbl.Name = "backupsCountsLbl";
this.backupsCountsLbl.Size = new System.Drawing.Size(218, 17);
this.backupsCountsLbl.Size = new System.Drawing.Size(544, 41);
this.backupsCountsLbl.Text = "[Calculating backed up book quantities]";
//
// pdfsCountsLbl
//
this.pdfsCountsLbl.Name = "pdfsCountsLbl";
this.pdfsCountsLbl.Size = new System.Drawing.Size(171, 17);
this.pdfsCountsLbl.Size = new System.Drawing.Size(426, 41);
this.pdfsCountsLbl.Text = "| [Calculating backed up PDFs]";
//
// addFilterBtn
//
this.addFilterBtn.Location = new System.Drawing.Point(47, 31);
this.addFilterBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.addFilterBtn.Location = new System.Drawing.Point(114, 85);
this.addFilterBtn.Margin = new System.Windows.Forms.Padding(10, 8, 10, 8);
this.addFilterBtn.Name = "addFilterBtn";
this.addFilterBtn.Size = new System.Drawing.Size(163, 27);
this.addFilterBtn.Size = new System.Drawing.Size(396, 74);
this.addFilterBtn.TabIndex = 4;
this.addFilterBtn.Text = "Add To Quick Filters";
this.addFilterBtn.UseVisualStyleBackColor = true;
@@ -375,9 +431,9 @@
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleDimensions = new System.Drawing.SizeF(17F, 41F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(1007, 539);
this.ClientSize = new System.Drawing.Size(2446, 1473);
this.Controls.Add(this.filterBtn);
this.Controls.Add(this.addFilterBtn);
this.Controls.Add(this.filterSearchTb);
@@ -387,7 +443,7 @@
this.Controls.Add(this.menuStrip1);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MainMenuStrip = this.menuStrip1;
this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.Margin = new System.Windows.Forms.Padding(10, 8, 10, 8);
this.Name = "Form1";
this.Text = "Libation: Liberate your Library";
this.Load += new System.EventHandler(this.Form1_Load);
@@ -438,5 +494,11 @@
private System.Windows.Forms.ToolStripMenuItem aboutToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem scanningToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem autoScanLibraryToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem visibleBooksToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem liberateVisibleToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem replaceTagsToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem setDownloadedToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem removeToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem liberateVisible2ToolStripMenuItem;
}
}

View File

@@ -18,21 +18,47 @@ namespace LibationWinForms
private string beginBookBackupsToolStripMenuItem_format { get; }
private string beginPdfBackupsToolStripMenuItem_format { get; }
private string visibleBooksToolStripMenuItem_format { get; }
private string liberateVisibleToolStripMenuItem_format { get; }
private string liberateVisible2ToolStripMenuItem_format { get; }
private ProductsGrid productsGrid { get; }
public Form1()
{
InitializeComponent();
if (this.DesignMode)
return;
productsGrid = new ProductsGrid { Dock = DockStyle.Fill };
productsGrid.VisibleCountChanged += (_, qty) => visibleCountLbl.Text = string.Format("Visible: {0}", qty);
gridPanel.Controls.Add(productsGrid);
this.Load += (_, __) =>
{
productsGrid.Display();
// also applies filter. ONLY call AFTER loading grid
loadInitialQuickFilterState();
};
// back up string formats
beginBookBackupsToolStripMenuItem_format = beginBookBackupsToolStripMenuItem.Text;
beginPdfBackupsToolStripMenuItem_format = beginPdfBackupsToolStripMenuItem.Text;
if (this.DesignMode)
return;
visibleBooksToolStripMenuItem_format = visibleBooksToolStripMenuItem.Text;
liberateVisibleToolStripMenuItem_format = liberateVisibleToolStripMenuItem.Text;
liberateVisible2ToolStripMenuItem_format = liberateVisible2ToolStripMenuItem.Text;
// independent UI updates
this.Load += (_, _) => this.RestoreSizeAndLocation(Configuration.Instance);
this.FormClosing += (_, _) => this.SaveSizeAndLocation(Configuration.Instance);
LibraryCommands.LibrarySizeChanged += reloadGridAndUpdateBottomNumbers;
LibraryCommands.LibrarySizeChanged += (_, __) =>
{
this.UIThreadSync(() => productsGrid.Display());
this.UIThreadAsync(() => doFilter(lastGoodFilter));
};
LibraryCommands.LibrarySizeChanged += setBackupCounts;
this.Load += setBackupCounts;
LibraryCommands.BookUserDefinedItemCommitted += setBackupCounts;
QuickFilters.Updated += updateFiltersMenu;
LibraryCommands.ScanBegin += LibraryCommands_ScanBegin;
@@ -44,6 +70,8 @@ namespace LibationWinForms
configAndInitAutoScan();
configVisibleBooksMenu();
// init default/placeholder cover art
var format = System.Drawing.Imaging.ImageFormat.Jpeg;
PictureStorage.SetDefaultImage(PictureSize._80x80, Properties.Resources.default_cover_80x80.ToBytes(format));
@@ -56,60 +84,9 @@ namespace LibationWinForms
if (this.DesignMode)
return;
// can't refactor into "this.Load => reloadGridAndUpdateBottomNumbers"
// because loadInitialQuickFilterState must follow it
reloadGridAndUpdateBottomNumbers();
// also applies filter. ONLY call AFTER loading grid
loadInitialQuickFilterState();
// I'm leaving this empty call here as a reminder that if we use this, it should probably be after DesignMode check
}
private void reloadGridAndUpdateBottomNumbers(object _ = null, object __ = null)
{
// suppressed filter while init'ing UI
var prev_isProcessingGridSelect = isProcessingGridSelect;
isProcessingGridSelect = true;
this.UIThreadSync(setGrid);
isProcessingGridSelect = prev_isProcessingGridSelect;
// UI init complete. now we can apply filter
this.UIThreadAsync(() => doFilter(lastGoodFilter));
setBackupCounts(null, null);
}
#region reload grid
private ProductsGrid productsGrid;
private void setGrid()
{
SuspendLayout();
{
// previous non-null grid with zero-count removes columns. remove/re-add grid to get columns back
if (productsGrid?.Count == 0)
{
gridPanel.Controls.Remove(productsGrid);
productsGrid.VisibleCountChanged -= setVisibleCount;
productsGrid.Dispose();
productsGrid = null;
}
if (productsGrid is null)
{
productsGrid = new ProductsGrid { Dock = DockStyle.Fill };
productsGrid.VisibleCountChanged += setVisibleCount;
gridPanel.UIThreadSync(() => gridPanel.Controls.Add(productsGrid));
}
productsGrid.Display();
}
ResumeLayout();
}
#endregion
#region bottom: qty books visible
private void setVisibleCount(object _, int qty) => visibleCountLbl.Text = string.Format("Visible: {0}", qty);
#endregion
#region bottom: backup counts
private System.ComponentModel.BackgroundWorker updateCountsBw;
private bool runBackupCountsAgain;
@@ -215,7 +192,6 @@ namespace LibationWinForms
}
private void filterBtn_Click(object sender, EventArgs e) => doFilter();
private bool isProcessingGridSelect = false;
private string lastGoodFilter = "";
private void doFilter(string filterString)
{
@@ -224,9 +200,6 @@ namespace LibationWinForms
}
private void doFilter()
{
if (isProcessingGridSelect || productsGrid is null)
return;
try
{
productsGrid.Filter(filterSearchTb.Text);
@@ -421,7 +394,7 @@ namespace LibationWinForms
}
catch (Exception ex)
{
MessageBoxAlertAdmin.Show(
MessageBoxLib.ShowAdminAlert(
"Error importing library. Please try again. If this still happens after 2 or 3 tries, stop and contact administrator",
"Error importing library",
ex);
@@ -485,7 +458,7 @@ namespace LibationWinForms
}
catch (Exception ex)
{
MessageBoxAlertAdmin.Show("Error attempting to export your library.", "Error exporting", ex);
MessageBoxLib.ShowAdminAlert("Error attempting to export your library.", "Error exporting", ex);
}
}
#endregion
@@ -537,6 +510,111 @@ namespace LibationWinForms
private void EditQuickFiltersToolStripMenuItem_Click(object sender, EventArgs e) => new EditQuickFilters(this).ShowDialog();
#endregion
#region Visible Books menu
private void configVisibleBooksMenu()
{
productsGrid.VisibleCountChanged += (_, qty) => {
visibleBooksToolStripMenuItem.Text = string.Format(visibleBooksToolStripMenuItem_format, qty);
visibleBooksToolStripMenuItem.Enabled = qty > 0;
var notLiberatedCount = productsGrid.GetVisible().Count(lb => lb.Book.UserDefinedItem.BookStatus == DataLayer.LiberatedStatus.NotLiberated);
};
productsGrid.VisibleCountChanged += setLiberatedVisibleMenuItemAsync;
LibraryCommands.BookUserDefinedItemCommitted += setLiberatedVisibleMenuItemAsync;
}
private async void setLiberatedVisibleMenuItemAsync(object _, int __)
=> await Task.Run(setLiberatedVisibleMenuItem);
private async void setLiberatedVisibleMenuItemAsync(object _, EventArgs __)
=> await Task.Run(setLiberatedVisibleMenuItem);
void setLiberatedVisibleMenuItem()
{
var notLiberated = productsGrid.GetVisible().Count(lb => lb.Book.UserDefinedItem.BookStatus == DataLayer.LiberatedStatus.NotLiberated);
this.UIThreadSync(() =>
{
if (notLiberated > 0)
{
liberateVisibleToolStripMenuItem.Text = string.Format(liberateVisibleToolStripMenuItem_format, notLiberated);
liberateVisibleToolStripMenuItem.Enabled = true;
liberateVisible2ToolStripMenuItem.Text = string.Format(liberateVisible2ToolStripMenuItem_format, notLiberated);
liberateVisible2ToolStripMenuItem.Enabled = true;
}
else
{
liberateVisibleToolStripMenuItem.Text = "All visible books are liberated";
liberateVisibleToolStripMenuItem.Enabled = false;
liberateVisible2ToolStripMenuItem.Text = "All visible books are liberated";
liberateVisible2ToolStripMenuItem.Enabled = false;
}
});
}
private async void liberateVisible(object sender, EventArgs e)
=> await BookLiberation.ProcessorAutomationController.BackupAllBooksAsync(productsGrid.GetVisible());
private void replaceTagsToolStripMenuItem_Click(object sender, EventArgs e)
{
var dialog = new TagsBatchDialog();
var result = dialog.ShowDialog();
if (result != DialogResult.OK)
return;
var visibleLibraryBooks = productsGrid.GetVisible();
var confirmationResult = MessageBoxLib.ShowConfirmationDialog(
visibleLibraryBooks,
$"Are you sure you want to replace tags in {0}?",
"Replace tags?");
if (confirmationResult != DialogResult.Yes)
return;
foreach (var libraryBook in visibleLibraryBooks)
libraryBook.Book.UserDefinedItem.Tags = dialog.NewTags;
LibraryCommands.UpdateUserDefinedItem(visibleLibraryBooks.Select(lb => lb.Book));
}
private void setDownloadedToolStripMenuItem_Click(object sender, EventArgs e)
{
var dialog = new LiberatedStatusBatchDialog();
var result = dialog.ShowDialog();
if (result != DialogResult.OK)
return;
var visibleLibraryBooks = productsGrid.GetVisible();
var confirmationResult = MessageBoxLib.ShowConfirmationDialog(
visibleLibraryBooks,
$"Are you sure you want to replace downloaded status in {0}?",
"Replace downloaded status?");
if (confirmationResult != DialogResult.Yes)
return;
foreach (var libraryBook in visibleLibraryBooks)
libraryBook.Book.UserDefinedItem.BookStatus = dialog.BookLiberatedStatus;
LibraryCommands.UpdateUserDefinedItem(visibleLibraryBooks.Select(lb => lb.Book));
}
private async void removeToolStripMenuItem_Click(object sender, EventArgs e)
{
var visibleLibraryBooks = productsGrid.GetVisible();
var confirmationResult = MessageBoxLib.ShowConfirmationDialog(
visibleLibraryBooks,
$"Are you sure you want to remove {0} from Libation's library?",
"Remove books from Libation?");
if (confirmationResult != DialogResult.Yes)
return;
var visibleIds = visibleLibraryBooks.Select(lb => lb.Book.AudibleProductId).ToList();
await LibraryCommands.RemoveBooksAsync(visibleIds);
}
#endregion
#region Settings menu
private void accountsToolStripMenuItem_Click(object sender, EventArgs e) => new AccountsDialog(this).ShowDialog();

View File

@@ -1,27 +0,0 @@
using System;
using LibationWinForms.Dialogs;
namespace LibationWinForms
{
public static class MessageBoxAlertAdmin
{
/// <summary>
/// Logs error. Displays a message box dialog with specified text and caption.
/// </summary>
/// <param name="text">The text to display in the message box.</param>
/// <param name="caption">The text to display in the title bar of the message box.</param>
/// <param name="exception">Exception to log</param>
/// <returns>One of the System.Windows.Forms.DialogResult values.</returns>
public static System.Windows.Forms.DialogResult Show(string text, string caption, Exception exception)
{
try
{
Serilog.Log.Logger.Error(exception, "Alert admin error: {@DebugText}", new { text, caption });
}
catch { }
using var form = new MessageBoxAlertAdminDialog(text, caption, exception);
return form.ShowDialog();
}
}
}

View File

@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using DataLayer;
using Dinah.Core.Logging;
using LibationWinForms.Dialogs;
using Serilog;
namespace LibationWinForms
{
public static class MessageBoxLib
{
/// <summary>
/// Logs error. Displays a message box dialog with specified text and caption.
/// </summary>
/// <param name="text">The text to display in the message box.</param>
/// <param name="caption">The text to display in the title bar of the message box.</param>
/// <param name="exception">Exception to log</param>
/// <returns>One of the System.Windows.Forms.DialogResult values.</returns>
public static DialogResult ShowAdminAlert(string text, string caption, Exception exception)
{
try
{
Serilog.Log.Logger.Error(exception, "Alert admin error: {@DebugText}", new { text, caption });
}
catch { }
using var form = new MessageBoxAlertAdminDialog(text, caption, exception);
return form.ShowDialog();
}
public static void VerboseLoggingWarning_ShowIfTrue()
{
// when turning on debug (and especially Verbose) to share logs, some privacy settings may not be obscured
if (Log.Logger.IsVerboseEnabled())
MessageBox.Show(@"
Warning: verbose logging is enabled.
This should be used for debugging only. It creates many
more logs and debug files, neither of which are as
strictly anonymous.
When you are finished debugging, it's highly recommended
to set your debug MinimumLevel to Information and restart
Libation.
".Trim(), "Verbose logging enabled", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
public static DialogResult ShowConfirmationDialog(IEnumerable<LibraryBook> libraryBooks, string format, string title)
{
if (libraryBooks is null || !libraryBooks.Any())
return DialogResult.Cancel;
var count = libraryBooks.Count();
string thisThese = count > 1 ? "these" : "this";
string bookBooks = count > 1 ? "books" : "book";
string titlesAgg = libraryBooks.AggregateTitles();
var message
= string.Format(format, $"{thisThese} {count} {bookBooks}")
+ $"\r\n\r\n{titlesAgg}";
return MessageBox.Show(
message,
title,
MessageBoxButtons.YesNo,
MessageBoxIcon.Question,
MessageBoxDefaultButton.Button1);
}
}
}

View File

@@ -1,28 +0,0 @@
using System;
using System.Linq;
using Dinah.Core.Logging;
using Serilog;
using System.Windows.Forms;
namespace LibationWinForms
{
public static class MessageBoxVerboseLoggingWarning
{
public static void ShowIfTrue()
{
// when turning on debug (and especially Verbose) to share logs, some privacy settings may not be obscured
if (Log.Logger.IsVerboseEnabled())
MessageBox.Show(@"
Warning: verbose logging is enabled.
This should be used for debugging only. It creates many
more logs and debug files, neither of which are as
strictly anonymous.
When you are finished debugging, it's highly recommended
to set your debug MinimumLevel to Information and restart
Libation.
".Trim(), "Verbose logging enabled", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
}

View File

@@ -49,7 +49,7 @@ namespace LibationWinForms
// migrations which require Forms or are long-running
RunWindowsOnlyMigrations(config);
MessageBoxVerboseLoggingWarning.ShowIfTrue();
MessageBoxLib.VerboseLoggingWarning_ShowIfTrue();
#if !DEBUG
checkForUpdate();
@@ -63,7 +63,7 @@ namespace LibationWinForms
var body = "An unrecoverable error occurred. Since this error happened before logging could be initialized, this error can not be written to the log file.";
try
{
MessageBoxAlertAdmin.Show(body, title, ex);
MessageBoxLib.ShowAdminAlert(body, title, ex);
}
catch
{
@@ -178,7 +178,7 @@ namespace LibationWinForms
}
catch (Exception ex)
{
MessageBoxAlertAdmin.Show("Error checking for update", "Error checking for update", ex);
MessageBoxLib.ShowAdminAlert("Error checking for update", "Error checking for update", ex);
return;
}
@@ -203,7 +203,7 @@ namespace LibationWinForms
}
catch (Exception ex)
{
MessageBoxAlertAdmin.Show("Error downloading update", "Error downloading update", ex);
MessageBoxLib.ShowAdminAlert("Error downloading update", "Error downloading update", ex);
}
}
}

View File

@@ -128,14 +128,14 @@ namespace LibationWinForms
// Immutable properties
{
Title = Book.Title;
Series = Book.SeriesNames;
Series = Book.SeriesNames();
Length = Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min";
MyRating = Book.UserDefinedItem.Rating?.ToStarString()?.DefaultIfNullOrWhiteSpace("");
PurchaseDate = libraryBook.DateAdded.ToString("d");
ProductRating = Book.Rating?.ToStarString()?.DefaultIfNullOrWhiteSpace("");
Authors = Book.AuthorNames;
Narrators = Book.NarratorNames;
Category = string.Join(" > ", Book.CategoriesNames);
Authors = Book.AuthorNames();
Narrators = Book.NarratorNames();
Category = string.Join(" > ", Book.CategoriesNames());
Misc = GetMiscDisplay(libraryBook);
LongDescription = GetDescriptionDisplay(Book);
Description = TrimTextToWord(LongDescription, 62);
@@ -232,12 +232,12 @@ namespace LibationWinForms
/// </summary>
private Dictionary<string, Func<object>> CreateMemberValueDictionary() => new()
{
{ nameof(Title), () => Book.TitleSortable },
{ nameof(Series), () => Book.SeriesSortable },
{ nameof(Title), () => Book.TitleSortable() },
{ nameof(Series), () => Book.SeriesSortable() },
{ nameof(Length), () => Book.LengthInMinutes },
{ nameof(MyRating), () => Book.UserDefinedItem.Rating.FirstScore },
{ nameof(MyRating), () => Book.UserDefinedItem.Rating.FirstScore() },
{ nameof(PurchaseDate), () => LibraryBook.DateAdded },
{ nameof(ProductRating), () => Book.Rating.FirstScore },
{ nameof(ProductRating), () => Book.Rating.FirstScore() },
{ nameof(Authors), () => Authors },
{ nameof(Narrators), () => Narrators },
{ nameof(Description), () => Description },
@@ -292,7 +292,7 @@ namespace LibationWinForms
details.Add($"Account: {locale} - {acct}");
if (libraryBook.Book.HasPdf)
if (libraryBook.Book.HasPdf())
details.Add("Has PDF");
if (libraryBook.Book.IsAbridged)
details.Add("Abridged");

View File

@@ -0,0 +1,86 @@
namespace LibationWinForms
{
partial class ImageDisplay
{
/// <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.components = new System.ComponentModel.Container();
this.pictureBox1 = new System.Windows.Forms.PictureBox();
this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components);
this.savePictureToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
this.contextMenuStrip1.SuspendLayout();
this.SuspendLayout();
//
// pictureBox1
//
this.pictureBox1.ContextMenuStrip = this.contextMenuStrip1;
this.pictureBox1.Dock = System.Windows.Forms.DockStyle.Fill;
this.pictureBox1.Location = new System.Drawing.Point(0, 0);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(522, 450);
this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
this.pictureBox1.TabIndex = 0;
this.pictureBox1.TabStop = false;
//
// contextMenuStrip1
//
this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.savePictureToolStripMenuItem});
this.contextMenuStrip1.Name = "contextMenuStrip1";
this.contextMenuStrip1.Size = new System.Drawing.Size(139, 26);
//
// savePictureToolStripMenuItem
//
this.savePictureToolStripMenuItem.Name = "savePictureToolStripMenuItem";
this.savePictureToolStripMenuItem.Size = new System.Drawing.Size(138, 22);
this.savePictureToolStripMenuItem.Text = "Save Picture";
this.savePictureToolStripMenuItem.Click += new System.EventHandler(this.savePictureToolStripMenuItem_Click);
//
// ImageDisplay
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(522, 450);
this.Controls.Add(this.pictureBox1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
this.Name = "ImageDisplay";
this.Text = "ImageDisplay";
this.Shown += new System.EventHandler(this.ImageDisplay_Shown);
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
this.contextMenuStrip1.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
public System.Windows.Forms.PictureBox pictureBox1;
private System.Windows.Forms.ContextMenuStrip contextMenuStrip1;
private System.Windows.Forms.ToolStripMenuItem savePictureToolStripMenuItem;
}
}

View File

@@ -0,0 +1,120 @@
using FileLiberator;
using LibationFileManager;
using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
namespace LibationWinForms
{
public partial class ImageDisplay : Form
{
public string PictureFileName { get; set; }
public string BookSaveDirectory { get; set; }
public byte[] CoverPicture { get => _coverBytes; set => pictureBox1.Image = Dinah.Core.Drawing.ImageReader.ToImage(_coverBytes = value); }
private byte[] _coverBytes;
public ImageDisplay()
{
InitializeComponent();
lastWidth = Width;
lastHeight = Height;
}
#region Make the form's aspect ratio always match the picture's aspect ratio.
private bool detectedResizeDirection = false;
private bool resizingWidth = false;
private bool resizingHeight = false;
private int lastWidth;
private int lastHeight;
private int formExtraWidth;
private int formExtraHeight;
private double pictureAR = 1;
protected override void OnResizeBegin(EventArgs e)
{
detectedResizeDirection = false;
base.OnResizeBegin(e);
}
protected override void OnResizeEnd(EventArgs e)
{
base.OnResize(e);
base.OnResizeEnd(e);
}
protected override void OnResize(EventArgs e)
{
if (WindowState != FormWindowState.Normal)
{
base.OnResize(e);
return;
}
int width = this.Width, height = this.Height;
if (!detectedResizeDirection)
{
resizingWidth = lastWidth != width;
resizingHeight = lastHeight != height;
detectedResizeDirection = true;
}
if (resizingWidth && !resizingHeight)
height = CalculateARHeight(width);
else
width = CalculateARWidth(height);
pictureBox1.Size = new Size(width - formExtraWidth, height - formExtraHeight);
lastWidth = width;
lastHeight = height;
SetBoundsCore(Location.X, Location.Y, width, height, BoundsSpecified.Width | BoundsSpecified.Height);
}
private int CalculateARHeight(int width)
{
return (int)((width - formExtraWidth) * pictureAR) + formExtraHeight;
}
private int CalculateARWidth(int height)
{
return (int)((height - formExtraHeight) * pictureAR) + formExtraWidth;
}
#endregion
private void ImageDisplay_Shown(object sender, EventArgs e)
{
formExtraWidth = Width - pictureBox1.Width;
formExtraHeight = Height - pictureBox1.Height;
OnResize(e);
}
private void savePictureToolStripMenuItem_Click(object sender, EventArgs e)
{
SaveFileDialog saveFileDialog = new();
saveFileDialog.Filter = "jpeg|*.jpg";
saveFileDialog.InitialDirectory = Directory.Exists(BookSaveDirectory) ? BookSaveDirectory : Path.GetDirectoryName(BookSaveDirectory);
saveFileDialog.FileName = PictureFileName;
if (saveFileDialog.ShowDialog() != DialogResult.OK)
return;
try
{
File.WriteAllBytes(saveFileDialog.FileName, CoverPicture);
}
catch (Exception ex)
{
Serilog.Log.Logger.Error(ex, $"Failed to save picture to {saveFileDialog.FileName}");
MessageBox.Show(this, $"An error was encountered while trying to save the picture\r\n\r\n{ex.Message}", "Failed to save picture", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
}
}
}
}

View File

@@ -0,0 +1,63 @@
<root>
<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="contextMenuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

View File

@@ -5,15 +5,18 @@ using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using ApplicationServices;
using DataLayer;
using Dinah.Core;
using Dinah.Core.DataBinding;
using Dinah.Core.Threading;
using Dinah.Core.Windows.Forms;
using FileLiberator;
using LibationFileManager;
using LibationWinForms.Dialogs;
namespace LibationWinForms
{
#region // legacy instructions to update data_grid_view
// INSTRUCTIONS TO UPDATE DATA_GRID_VIEW
// - delete current DataGridView
// - view > other windows > data sources
@@ -27,10 +30,14 @@ namespace LibationWinForms
// - go to Design view
// - click on Data Sources > ProductItem. dropdown: DataGridView
// - drag/drop ProductItem on design surface
// AS OF AUGUST 2021 THIS DOES NOT WORK IN VS2019 WITH .NET-5 PROJECTS
//
// as of august 2021 this does not work in vs2019 with .net5 projects
// VS has improved since then with .net6+ but I haven't checked again
#endregion
public partial class ProductsGrid : UserControl
{
/// <summary>Number of visible rows has changed</summary>
public event EventHandler<int> VisibleCountChanged;
// alias
@@ -44,10 +51,12 @@ namespace LibationWinForms
_dataGridView.Sorted += Filter;
_dataGridView.CellContentClick += DataGridView_CellContentClick;
this.Load += ProductsGrid_Load;
EnableDoubleBuffering();
}
private void EnableDoubleBuffering()
private void EnableDoubleBuffering()
{
var propertyInfo = _dataGridView.GetType().GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
@@ -62,14 +71,38 @@ namespace LibationWinForms
if (e.RowIndex < 0)
return;
var clickedColumn = _dataGridView.Columns[e.ColumnIndex];
if (clickedColumn == liberateGVColumn)
if (e.ColumnIndex == liberateGVColumn.Index)
await Liberate_Click(getGridEntry(e.RowIndex));
else if (clickedColumn == tagAndDetailsGVColumn)
else if (e.ColumnIndex == tagAndDetailsGVColumn.Index)
Details_Click(getGridEntry(e.RowIndex));
else if (clickedColumn == descriptionGVColumn)
else if (e.ColumnIndex == descriptionGVColumn.Index)
Description_Click(getGridEntry(e.RowIndex), _dataGridView.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false));
else if (e.ColumnIndex == coverGVColumn.Index)
await Cover_Click(getGridEntry(e.RowIndex));
}
private ImageDisplay imageDisplay;
private async Task Cover_Click(GridEntry liveGridEntry)
{
var picDefinition = new PictureDefinition(liveGridEntry.LibraryBook.Book.PictureLarge, PictureSize.Native);
var picDlTask = Task.Run(() => PictureStorage.GetPictureSynchronously(picDefinition));
(_, byte[] initialImageBts) = PictureStorage.GetPicture(new PictureDefinition(liveGridEntry.LibraryBook.Book.PictureId, PictureSize._80x80));
var windowTitle = $"{liveGridEntry.Title} - Cover";
if (imageDisplay is null || imageDisplay.IsDisposed || !imageDisplay.Visible)
{
imageDisplay = new ImageDisplay();
imageDisplay.RestoreSizeAndLocation(Configuration.Instance);
imageDisplay.FormClosed += (_, _) => imageDisplay.SaveSizeAndLocation(Configuration.Instance);
imageDisplay.Show(this);
}
imageDisplay.BookSaveDirectory = AudibleFileStorage.Audio.GetDestinationDirectory(liveGridEntry.LibraryBook);
imageDisplay.PictureFileName = System.IO.Path.GetFileName(AudibleFileStorage.Audio.GetBooksDirectoryFilename(liveGridEntry.LibraryBook, ".jpg"));
imageDisplay.Text = windowTitle;
imageDisplay.CoverPicture = initialImageBts;
imageDisplay.CoverPicture = await picDlTask;
}
private void Description_Click(GridEntry liveGridEntry, Rectangle cellDisplay)
@@ -81,13 +114,13 @@ namespace LibationWinForms
BorderThickness = 2,
};
void CloseWindow (object o, EventArgs e)
void CloseWindow(object o, EventArgs e)
{
displayWindow.Close();
}
_dataGridView.Scroll += CloseWindow;
displayWindow.FormClosed += (_,_) => _dataGridView.Scroll -= CloseWindow;
displayWindow.FormClosed += (_, _) => _dataGridView.Scroll -= CloseWindow;
displayWindow.Show(this);
}
@@ -96,7 +129,7 @@ namespace LibationWinForms
var libraryBook = liveGridEntry.LibraryBook;
// liberated: open explorer to file
if (libraryBook.Book.Audio_Exists)
if (libraryBook.Book.Audio_Exists())
{
var filePath = AudibleFileStorage.Audio.GetPath(libraryBook.Book.AudibleProductId);
if (!Go.To.File(filePath))
@@ -122,24 +155,13 @@ namespace LibationWinForms
#region UI display functions
public int Count { get; private set; }
private SortableBindingList<GridEntry> bindingList;
public void Display()
{
// don't return early if lib size == 0. this will not update correctly if all books are removed
var lib = DbContexts.GetLibrary_Flat_NoTracking();
Count = lib.Count;
// if no data. hide all columns. return
if (!lib.Any())
{
for (var i = _dataGridView.ColumnCount - 1; i >= 0; i--)
_dataGridView.Columns.RemoveAt(i);
return;
}
var orderedBooks = lib
// default load order
.OrderByDescending(lb => lb.DateAdded)
@@ -150,49 +172,49 @@ namespace LibationWinForms
.ToList();
// BIND
if (bindingList is null)
bindToGrid(orderedBooks);
else
if (bindingList?.Count > 0)
updateGrid(orderedBooks);
else
bindToGrid(orderedBooks);
// FILTER
Filter();
}
private void bindToGrid(List<DataLayer.LibraryBook> orderedBooks)
{
bindingList = new SortableBindingList<GridEntry>(orderedBooks.Select(lb => toGridEntry(lb)));
gridEntryBindingSource.DataSource = bindingList;
}
private void bindToGrid(List<DataLayer.LibraryBook> orderedBooks)
{
bindingList = new SortableBindingList<GridEntry>(orderedBooks.Select(lb => toGridEntry(lb)));
gridEntryBindingSource.DataSource = bindingList;
}
private void updateGrid(List<DataLayer.LibraryBook> orderedBooks)
{
for (var i = orderedBooks.Count - 1; i >= 0; i--)
{
var libraryBook = orderedBooks[i];
var existingItem = bindingList.FirstOrDefault(i => i.AudibleProductId == libraryBook.Book.AudibleProductId);
private void updateGrid(List<DataLayer.LibraryBook> orderedBooks)
{
for (var i = orderedBooks.Count - 1; i >= 0; i--)
{
var libraryBook = orderedBooks[i];
var existingItem = bindingList.FirstOrDefault(i => i.AudibleProductId == libraryBook.Book.AudibleProductId);
// add new to top
if (existingItem is null)
bindingList.Insert(0, toGridEntry(libraryBook));
// update existing
else
existingItem.UpdateLibraryBook(libraryBook);
}
// add new to top
if (existingItem is null)
bindingList.Insert(0, toGridEntry(libraryBook));
// update existing
else
existingItem.UpdateLibraryBook(libraryBook);
}
// remove deleted from grid. note: actual deletion from db must still occur via the RemoveBook feature. deleting from audible will not trigger this
var oldIds = bindingList.Select(ge => ge.AudibleProductId).ToList();
var newIds = orderedBooks.Select(lb => lb.Book.AudibleProductId).ToList();
var remove = oldIds.Except(newIds).ToList();
foreach (var id in remove)
{
var oldItem = bindingList.FirstOrDefault(ge => ge.AudibleProductId == id);
if (oldItem is not null)
bindingList.Remove(oldItem);
}
}
// remove deleted from grid. note: actual deletion from db must still occur via the RemoveBook feature. deleting from audible will not trigger this
var oldIds = bindingList.Select(ge => ge.AudibleProductId).ToList();
var newIds = orderedBooks.Select(lb => lb.Book.AudibleProductId).ToList();
var remove = oldIds.Except(newIds).ToList();
foreach (var id in remove)
{
var oldItem = bindingList.FirstOrDefault(ge => ge.AudibleProductId == id);
if (oldItem is not null)
bindingList.Remove(oldItem);
}
}
private GridEntry toGridEntry(DataLayer.LibraryBook libraryBook)
private GridEntry toGridEntry(DataLayer.LibraryBook libraryBook)
{
var entry = new GridEntry(libraryBook);
entry.Committed += Filter;
@@ -200,11 +222,11 @@ namespace LibationWinForms
return entry;
}
#endregion
#endregion
#region Filter
#region Filter
private string _filterSearchString;
private string _filterSearchString;
private void Filter(object _ = null, EventArgs __ = null) => Filter(_filterSearchString);
public void Filter(string searchString)
{
@@ -213,6 +235,8 @@ namespace LibationWinForms
if (_dataGridView.Rows.Count == 0)
return;
var initVisible = getVisible().Count();
var searchResults = SearchEngineCommands.Search(searchString);
var productIds = searchResults.Docs.Select(d => d.ProductId).ToList();
@@ -229,15 +253,21 @@ namespace LibationWinForms
// Causes repainting of the DataGridView
bindingContext.ResumeBinding();
VisibleCountChanged?.Invoke(this, GetVisible().Count());
var endVisible = getVisible().Count();
if (initVisible != endVisible)
VisibleCountChanged?.Invoke(this, endVisible);
}
#endregion
internal IEnumerable<DataLayer.LibraryBook> GetVisible()
private IEnumerable<DataGridViewRow> getVisible()
=> _dataGridView
.AsEnumerable()
.Where(row => row.Visible)
.Where(row => row.Visible);
internal List<DataLayer.LibraryBook> GetVisible()
=> getVisible()
.Select(row => ((GridEntry)row.DataBoundItem).LibraryBook)
.ToList();
@@ -245,7 +275,8 @@ namespace LibationWinForms
#region Column Customizations
protected override void OnVisibleChanged(EventArgs e)
// to ensure this is only ever called once: Load instead of 'override OnVisibleChanged'
private void ProductsGrid_Load(object sender, EventArgs e)
{
contextMenuStrip1.Items.Add(new ToolStripLabel("Show / Hide Columns"));
contextMenuStrip1.Items.Add(new ToolStripSeparator());
@@ -342,6 +373,8 @@ namespace LibationWinForms
{
if (e.ColumnIndex == descriptionGVColumn.Index)
e.ToolTipText = "Click to see full description";
else if (e.ColumnIndex == coverGVColumn.Index)
e.ToolTipText = "Click to see full size";
}
#endregion