mirror of
https://github.com/rmcrackan/Libation.git
synced 2026-09-12 21:57:19 -04:00
'Not Downloaded' reads as a claim about the file on disk, but the status it names is really an instruction about the future. Telling someone with a finished audiobook to set it 'Not Downloaded' asks them to assert something they know is false, which is why the question keeps coming up. 'Download Pending' says the same thing about intent without saying anything untrue about the file, and stands alone in a dropdown or a support reply where bare 'Pending' would not. The context menu carriers move from "Set Download status to 'X'" to "Mark as 'X'" so the word does not land twice in one breath, with the accelerator on P to stay clear of Downloaded's D. The persisted enum, the --not-downloaded CLI flag and the IsLiberated search tags are unchanged, so scripts and saved quick filters keep working. WinForms status combo boxes grow from 121 to 150px and the better-quality Mark button from 210 to 240px to fit the longer label. Docs carry 'previously "Not Downloaded"' on first mention, since years of Reddit and GitHub answers use the old name. Co-authored-by: rmcrackan <rmcrackan@gmail.com>
164 lines
5.1 KiB
C#
164 lines
5.1 KiB
C#
using ApplicationServices;
|
|
using DataLayer;
|
|
using Dinah.Core;
|
|
using LibationFileManager;
|
|
using System;
|
|
using System.Linq;
|
|
using System.Windows.Forms;
|
|
|
|
namespace LibationWinForms.Dialogs;
|
|
|
|
public partial class BookDetailsDialog : Form
|
|
{
|
|
public class liberatedComboBoxItem
|
|
{
|
|
public LiberatedStatus Status { get; set; }
|
|
public string? Text { get; set; }
|
|
public override string? ToString() => Text;
|
|
}
|
|
|
|
public string? NewTags { get; private set; }
|
|
public LiberatedStatus BookLiberatedStatus { get; private set; }
|
|
public LiberatedStatus? PdfLiberatedStatus { get; private set; }
|
|
|
|
private Book? Book => LibraryBook?.Book;
|
|
|
|
public BookDetailsDialog()
|
|
{
|
|
InitializeComponent();
|
|
this.SetLibationIcon();
|
|
audibleLink.SetLinkLabelColors();
|
|
}
|
|
|
|
public LibraryBook? LibraryBook
|
|
{
|
|
get => field;
|
|
set
|
|
{
|
|
field = value;
|
|
initDetails();
|
|
initTags();
|
|
initLiberated();
|
|
}
|
|
}
|
|
|
|
// 1st draft: lazily cribbed from GridEntry.ctor()
|
|
private void initDetails()
|
|
{
|
|
if (Book is null)
|
|
return;
|
|
audibleLink.LinkVisited = false;
|
|
this.Text = Book.TitleWithSubtitle;
|
|
dolbyAtmosPb.Visible = Book.IsSpatial;
|
|
dolbyAtmosPb.Image = Application.IsDarkModeEnabled ? Properties.Resources.Dolby_Atmos_Vertical_80_dark : Properties.Resources.Dolby_Atmos_Vertical_80;
|
|
|
|
var picture = (Book.PictureId ?? Book.PictureLarge) is not string picId ? null
|
|
: PictureStorage.GetPicture(new(picId, PictureSize._80x80)).bytes;
|
|
this.coverPb.Image = WinFormsUtil.TryLoadImageOrDefault(picture, PictureSize._80x80);
|
|
|
|
var title = string.IsNullOrEmpty(Book.Subtitle) ? Book.Title : $"{Book.Title}\r\n {Book.Subtitle}";
|
|
var t = $"""
|
|
Title: {title}
|
|
Author(s): {Book.AuthorNames}
|
|
Narrator(s): {Book.NarratorNames}
|
|
Length: {(Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min")}
|
|
Category: {string.Join(", ", Book.LowestCategoryNames())}
|
|
Purchase Date: {LibraryBook!.DateAdded:d}
|
|
Language: {Book.Language}
|
|
Audible ID: {Book.AudibleProductId}
|
|
""";
|
|
|
|
var seriesNames = Book.SeriesNames();
|
|
if (!string.IsNullOrWhiteSpace(seriesNames))
|
|
t += $"\r\nSeries: {seriesNames}";
|
|
|
|
var bookRating = Book.Rating?.ToStarString();
|
|
if (!string.IsNullOrWhiteSpace(bookRating))
|
|
t += $"\r\nBook Rating:\r\n{bookRating}";
|
|
|
|
var myRating = Book.UserDefinedItem.Rating?.ToStarString();
|
|
if (!string.IsNullOrWhiteSpace(myRating))
|
|
t += $"\r\nMy Rating:\r\n{myRating}";
|
|
|
|
this.detailsTb.Text = t;
|
|
}
|
|
private void initTags() => this.newTagsTb.Text = Book?.UserDefinedItem.Tags;
|
|
private void initLiberated()
|
|
{
|
|
if (Book is null)
|
|
return;
|
|
{
|
|
var status = Book.UserDefinedItem.BookStatus;
|
|
this.bookLiberatedCb.Items.Clear();
|
|
this.bookLiberatedCb.Items.Add(new liberatedComboBoxItem { Status = LiberatedStatus.Liberated, Text = "Downloaded" });
|
|
this.bookLiberatedCb.Items.Add(new liberatedComboBoxItem { Status = LiberatedStatus.NotLiberated, Text = "Download Pending" });
|
|
|
|
// this should only appear if is already an error. User should not be able to set status to error, only away from error
|
|
if (status == LiberatedStatus.Error)
|
|
this.bookLiberatedCb.Items.Add(new liberatedComboBoxItem { Status = LiberatedStatus.Error, Text = "Error" });
|
|
|
|
setDefaultComboBox(this.bookLiberatedCb, status);
|
|
}
|
|
|
|
{
|
|
var status = Book.UserDefinedItem.PdfStatus;
|
|
this.pdfLiberatedCb.Items.Clear();
|
|
this.pdfLiberatedCb.Enabled = status is not null;
|
|
if (status is not null)
|
|
{
|
|
this.pdfLiberatedCb.Items.Add(new liberatedComboBoxItem { Status = LiberatedStatus.Liberated, Text = "Downloaded" });
|
|
this.pdfLiberatedCb.Items.Add(new liberatedComboBoxItem { Status = LiberatedStatus.NotLiberated, Text = "Download Pending" });
|
|
|
|
setDefaultComboBox(this.pdfLiberatedCb, status);
|
|
}
|
|
}
|
|
}
|
|
private static void setDefaultComboBox(ComboBox comboBox, LiberatedStatus? status)
|
|
{
|
|
if (!status.HasValue)
|
|
{
|
|
comboBox.SelectedIndex = 0;
|
|
return;
|
|
}
|
|
|
|
var item = comboBox.Items.Cast<liberatedComboBoxItem>().SingleOrDefault(item => item.Status == status.Value);
|
|
if (item is not null)
|
|
comboBox.SelectedItem = item;
|
|
else
|
|
comboBox.SelectedIndex = 0;
|
|
}
|
|
|
|
private async void saveBtn_Click(object sender, EventArgs e)
|
|
{
|
|
NewTags = this.newTagsTb.Text;
|
|
if (bookLiberatedCb.SelectedItem is liberatedComboBoxItem bStatus)
|
|
BookLiberatedStatus = bStatus.Status;
|
|
|
|
if (pdfLiberatedCb.Enabled && pdfLiberatedCb.SelectedItem is liberatedComboBoxItem pStatus)
|
|
PdfLiberatedStatus = pStatus.Status;
|
|
|
|
if (LibraryBook is null)
|
|
return;
|
|
|
|
Invoke(() => saveBtn.Enabled = cancelBtn.Enabled = false);
|
|
await LibraryBook.UpdateUserDefinedItemAsync(NewTags, BookLiberatedStatus, PdfLiberatedStatus);
|
|
Invoke(() => saveBtn.Enabled = cancelBtn.Enabled = true);
|
|
}
|
|
|
|
private void cancelBtn_Click(object sender, EventArgs e)
|
|
{
|
|
this.DialogResult = DialogResult.Cancel;
|
|
this.Close();
|
|
}
|
|
|
|
private void audibleLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
|
|
{
|
|
if (Book is null)
|
|
return;
|
|
var locale = AudibleApi.Localization.Get(Book.Locale);
|
|
var link = $"https://www.audible.{locale.TopDomain}/pd/{Book.AudibleProductId}";
|
|
Go.To.Url(link);
|
|
e.Link?.Visited = true;
|
|
}
|
|
}
|