using DataLayer; using Dinah.Core; using System; using System.Collections.Generic; using System.Linq; namespace ApplicationServices; /// What to tell the user about titles a run held back, instead of the full warning per title per run. public static class DeferredDownloadUserMessage { /// /// A compact breakdown for the log, eg: /// "Audible denied a download license: 3, Audible has no downloadable audio yet: 1". /// public static string BuildLogBreakdown(IEnumerable deferred) { var breakdown = string.Join(", ", GroupByKind(deferred).Select(g => $"{g.First().KindLabel}: {g.Count()}")); return breakdown is "" ? "none" : breakdown; } /// /// The lines a CLI run prints in place of a full warning per title. Says how many were held back, why, /// when the soonest will be attempted again, and how to override. /// public static IEnumerable BuildCliSkippedLines(IReadOnlyCollection skipped, DateTimeOffset now) { if (skipped.Count == 0) yield break; yield return $"Skipped {"title".PluralizeWithCount(skipped.Count)} that recently failed to download. Libation will try again by itself."; foreach (var group in GroupByKind(skipped)) yield return $" {group.First().KindLabel}: {group.Count()} (next attempt {DescribeWhen(group.Min(d => d.RetryAfter), now)})"; yield return " To try one now: libationcli liberate . For all of them: libationcli liberate --force."; } /// "in about 3 hours" / "in about 12 days (9/14/2026)" - a summary should not need a clock to read. public static string DescribeWhen(DateTimeOffset when, DateTimeOffset now) { var wait = when - now; if (wait <= TimeSpan.Zero) return "on the next run"; if (wait < TimeSpan.FromHours(1)) return $"in about {"minute".PluralizeWithCount(Math.Max(1, (int)wait.TotalMinutes))}"; // Rounded to hours first, so a wait of exactly one day does not read as 23 or 24 hours depending on // how long the run took to reach this line. var hours = (int)Math.Round(wait.TotalHours); return hours < 24 ? $"in about {"hour".PluralizeWithCount(hours)}" : $"in about {"day".PluralizeWithCount((int)Math.Round(hours / 24d))} ({when.ToLocalTime():d})"; } private static IEnumerable> GroupByKind(IEnumerable deferred) => deferred.GroupBy(d => d.Kind).OrderBy(g => g.Key); }