* Default Scan library to on for new accounts from Upsert / Mkb79 import (matches GUI)

* CLI liberate: print short license-denial reasons to stderr
* GUI: message when stoplight can’t queue (e.g. absent from last scan)
This commit is contained in:
rmcrackan
2026-05-05 13:32:28 -04:00
parent 78371e3522
commit 0cc2ef773d
8 changed files with 66 additions and 5 deletions

View File

@@ -88,7 +88,8 @@ public class AccountsSettings : IUpdatable
var l = Localization.Get(locale);
var id = new Identity(l);
var account = new Account(accountId) { IdentityTokens = id };
// Match GUI default for new rows (WinForms/Avalonia): include account in library scans.
var account = new Account(accountId) { IdentityTokens = id, LibraryScan = true };
Add(account);
return account;
}

View File

@@ -217,7 +217,8 @@ public partial class Mkb79Auth
{
DecryptKey = ActivationBytes,
AccountName = $"{email} - {Locale.Name}",
IdentityTokens = new Identity(Locale)
IdentityTokens = new Identity(Locale),
LibraryScan = true,
};
account.IdentityTokens.Update(

View File

@@ -0,0 +1,26 @@
using AudibleApi;
using System;
using System.Collections.Generic;
namespace LibationCli;
internal static class ContentLicenseDeniedCliSummary
{
/// <summary>Short lines for stderr when Audible denies a download license; mirrors log detail without dumping the full JSON.</summary>
public static IEnumerable<string> Lines(ContentLicenseDeniedException ex)
{
ArgumentNullException.ThrowIfNull(ex);
yield return "Audible denied a content license (download not allowed for this account/title).";
yield return ex.Message;
if (ex.Ownership?.Message is { } own && !string.IsNullOrWhiteSpace(own))
yield return $"Ownership: {own}";
if (ex.Client?.Message is { } cli && !string.IsNullOrWhiteSpace(cli))
yield return $"Client: {cli}";
if (ex.Membership?.Message is { } mem && !string.IsNullOrWhiteSpace(mem))
yield return $"Membership: {mem}";
if (ex.AYCL?.Message is { } aycl && !string.IsNullOrWhiteSpace(aycl))
yield return $"AYCL (aka: Plus catalog): {aycl}";
}
}

View File

@@ -6,7 +6,7 @@ using System.Threading.Tasks;
namespace LibationCli;
[Verb("list-accounts", HelpText = "List configured Audible accounts, locale, scan flag, and whether stored credentials are valid.")]
[Verb("list-accounts", HelpText = "List configured Audible accounts: locale, whether the account is included in automatic GUI scans ('Scan library'), and whether stored credentials are valid.")]
internal class ListAccountsOptions : OptionsBase
{
[Option('b', "bare", HelpText = "Print tab-separated values without table borders (account id, name, locale, scan library, authenticated).")]

View File

@@ -1,4 +1,5 @@
using ApplicationServices;
using AudibleApi;
using CommandLine;
using DataLayer;
using FileLiberator;
@@ -121,6 +122,12 @@ public abstract class ProcessableOptionsBase : OptionsBase
Serilog.Log.Logger.Error(errorMessage);
}
}
catch (ContentLicenseDeniedException clEx)
{
foreach (var line in ContentLicenseDeniedCliSummary.Lines(clEx))
Console.Error.WriteLine(line);
Serilog.Log.Logger.Error(clEx, "Content license denied {@DebugInfo}", new { Book = libraryBook.LogFriendly() });
}
catch (Exception ex)
{
var msg = "Error processing book. Skipping. This book will be tried again on next attempt. For options of skipping or marking as error, retry with main Libation app.";

View File

@@ -1,5 +1,6 @@
using ApplicationServices;
using DataLayer;
using FileLiberator;
using LibationFileManager;
using LibationUiBase.Forms;
using LibationUiBase;
@@ -137,21 +138,43 @@ public class ProcessQueueViewModel : ReactiveObject
var item = libraryBooks[0];
if (item.AbsentFromLastScan)
{
Serilog.Log.Logger.Warning("Download not queued: {libraryBook} is absent from the last library scan.", item.LogFriendly());
MessageBoxBase.Show(
"This title is marked absent from your last library scan.\n\nRun Scan (or `libationcli scan`) so Libation can refresh your library, then try again.",
"Library scan required",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
return false;
else if (item.NeedsBookDownload)
}
if (item.NeedsBookDownload)
{
RemoveCompleted(item);
Serilog.Log.Logger.Information("Begin single library book backup of {libraryBook}", item);
AddDownloadDecrypt([item], config);
return true;
}
else if (item.NeedsPdfDownload)
if (item.NeedsPdfDownload)
{
RemoveCompleted(item);
Serilog.Log.Logger.Information("Begin single pdf backup of {libraryBook}", item);
AddDownloadPdf([item], config);
return true;
}
Serilog.Log.Logger.Warning(
"Download not queued: single-item backup not applicable for {libraryBook} (book status or type does not request download).",
item.LogFriendly());
if (!item.Book.AudioExists)
{
MessageBoxBase.Show(
"Libation could not queue a download for this title.\n\n"
+ "If it should be downloadable: confirm it is not already liberated, try \"Set download status\" to Not downloaded, or check whether a library scan is required.",
"Download not queued",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
return false;
}
else
{

View File

@@ -425,6 +425,7 @@ public class upsert : AccountsTestBase
var acct = accountsSettings.GetAccount("cng", "us");
acct.BeNotNull();
acct.AccountId.Should().Be("cng");
acct.LibraryScan.Should().BeTrue();
}
[TestMethod]

View File

@@ -102,6 +102,8 @@ libationcli list-accounts --bare
`--bare` (`-b`) prints tab-separated values with no table: account id, name, locale, scan library (`yes` / `no`), authenticated (`yes` / `no`), for scripts and `cut` / `awk`.
**Scan library** (`yes` / `no`) is the same checkbox as "Include in library scan?" in Accounts: it controls whether the main Libation app includes that account in automatic scans (startup / periodic scan behavior). It does **not** restrict `libationcli scan` with no arguments, which still imports from every configured account unless you pass specific account nicknames or ids.
If no accounts exist yet, the CLI prints `No accounts configured.` and exits successfully.
## Scan All Libraries