Compare commits

...

15 Commits

Author SHA1 Message Date
Robert
042f2e135a incr ver 2026-01-06 07:28:33 -05:00
Robert
930fc3da58 Merge branch 'master' of https://github.com/rmcrackan/Libation 2026-01-06 07:26:40 -05:00
Robert
90e8d03590 add github doc contributor 2026-01-06 07:26:35 -05:00
rmcrackan
ee908a4f13 Merge pull request #1534 from Mbucari/master
Improve LocateAudiobooksDialog and fix Classic upgrade dialog
2026-01-06 07:26:08 -05:00
rmcrackan
70ec31303b Merge pull request #1535 from niontrix/patch-1
Document Pacstall for program installation on Linux
2026-01-06 07:16:14 -05:00
Tobias Heinlein
319d547aa0 Document Pacstall for program installation on Linux
Added information about Pacstall for easier program installation.
2026-01-06 10:33:20 +01:00
Mbucari
a59e42e7c9 Merge branch 'rmcrackan:master' into master 2026-01-05 18:50:51 -07:00
Michael Bucari-Tovo
af2e89dd1e Remove UpgradeNotificationDialog form positioning
Hopefully address #1531
2026-01-05 18:50:28 -07:00
rmcrackan
b2c5884e11 Merge pull request #1533 from rmcrackan/rmcrackan/log-friendly2
Rmcrackan/log friendly2
2026-01-05 19:30:43 -05:00
Robert
11d9cdefe2 typo 2026-01-05 19:30:03 -05:00
Robert
54485ae150 LogFriendly 2026-01-05 19:28:50 -05:00
Michael Bucari-Tovo
4bd641ee50 Improve LocateAudiobooksDialog
- Move LocatedAudiobooksViewModel to LibationUiBase
- Refactor Avalonia and Classic displays to use same view model.
- Do scan on background task
2026-01-05 15:38:15 -07:00
Robert
6e56297434 LogFriendly 2026-01-05 14:00:43 -05:00
rmcrackan
e1f59eadbd Merge pull request #1529 from rmcrackan/rmcrackan/doc-github-sources
Doc re-directs to include pointers to github sources
2026-01-04 09:49:57 -05:00
Robert
cfda065219 Doc re-directs to include pointers to github sources 2026-01-04 09:48:14 -05:00
21 changed files with 278 additions and 206 deletions

View File

@@ -1 +1,3 @@
# This page has been moved to https://getlibation.com/docs/advanced/advanced
# This page has been moved to https://getlibation.com/docs/advanced/advanced
([page in github](https://github.com/rmcrackan/Libation/blob/master/docs/advanced/advanced.md))

View File

@@ -1 +1,3 @@
# This page has been moved to https://getlibation.com/docs/features/audio-file-formats
# This page has been moved to https://getlibation.com/docs/features/audio-file-formats
([page in github](https://github.com/rmcrackan/Libation/blob/master/docs/features/audio-file-formats.md))

View File

@@ -1 +1,3 @@
# This page has been moved to https://getlibation.com/docs/installation/docker
# This page has been moved to https://getlibation.com/docs/installation/docker
([page in github](https://github.com/rmcrackan/Libation/blob/master/docs/installation/docker.md))

View File

@@ -1 +1,3 @@
# This page has been moved to https://getlibation.com/docs/frequently-asked-questions
# This page has been moved to https://getlibation.com/docs/frequently-asked-questions
([page in github](https://github.com/rmcrackan/Libation/blob/master/docs/frequently-asked-questions.md))

View File

@@ -1 +1,3 @@
# This page has been moved to https://getlibation.com/docs/getting-started
# This page has been moved to https://getlibation.com/docs/getting-started
([page in github](https://github.com/rmcrackan/Libation/blob/master/docs/getting-started.md))

View File

@@ -1 +1,3 @@
# This page has been moved to https://getlibation.com/docs/installation/linux
# This page has been moved to https://getlibation.com/docs/installation/linux
([page in github](https://github.com/rmcrackan/Libation/blob/master/docs/installation/linux.md))

View File

@@ -1 +1,3 @@
# This page has been moved to https://getlibation.com/docs/installation/mac
# This page has been moved to https://getlibation.com/docs/installation/mac
([page in github](https://github.com/rmcrackan/Libation/blob/master/docs/installation/mac.md))

View File

@@ -1 +1,3 @@
# This page has been moved to https://getlibation.com/docs/development/nix-linux-setup
# This page has been moved to https://getlibation.com/docs/development/nix-linux-setup
([page in github](https://github.com/rmcrackan/Libation/blob/master/docs/development/nix-linux-setup.md))

View File

@@ -1 +1,3 @@
# This page has been moved to https://getlibation.com/docs/features/naming-templates
# This page has been moved to https://getlibation.com/docs/features/naming-templates
([page in github](https://github.com/rmcrackan/Libation/blob/master/docs/features/naming-templates.md))

View File

@@ -1 +1,3 @@
# This page has been moved to https://getlibation.com/docs/features/searching-and-filtering
# This page has been moved to https://getlibation.com/docs/features/searching-and-filtering
([page in github](https://github.com/rmcrackan/Libation/blob/master/docs/features/searching-and-filtering.md))

View File

@@ -2,7 +2,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Version>13.1.0.1</Version>
<Version>13.1.1.1</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Octokit" Version="14.0.0" />

View File

@@ -322,7 +322,7 @@ namespace FileLiberator
catch (Exception ex)
{
//Failure to determine output audio format should not be considered a failure to download the book
Serilog.Log.Logger.Error(ex, "Error determining output audio format for {@Book}. File = '{@audioFile}'", options.LibraryBook, firstAudioFile);
Serilog.Log.Logger.Error(ex, "Error determining output audio format for {@Book}. File = '{@audioFile}'", options.LibraryBook.LogFriendly(), firstAudioFile);
return AudioFormat.Default;
}
@@ -427,7 +427,7 @@ namespace FileLiberator
{
//Failure to download cover art should not be considered a failure to download the book
if (!cancellationToken.IsCancellationRequested)
Serilog.Log.Logger.Error(ex, "Error downloading cover art for {@Book} to {@metadataFile}.", options.LibraryBook, coverPath);
Serilog.Log.Logger.Error(ex, "Error downloading cover art for {@Book} to {@metadataFile}.", options.LibraryBook.LogFriendly(), coverPath);
throw;
}
}
@@ -476,7 +476,7 @@ namespace FileLiberator
{
//Failure to download records should not be considered a failure to download the book
if (!cancellationToken.IsCancellationRequested)
Serilog.Log.Logger.Error(ex, "Error downloading clips and bookmarks for {@Book} to {@recordsPath}.", options.LibraryBook, recordsPath);
Serilog.Log.Logger.Error(ex, "Error downloading clips and bookmarks for {@Book} to {@recordsPath}.", options.LibraryBook.LogFriendly(), recordsPath);
throw;
}
}
@@ -512,7 +512,7 @@ namespace FileLiberator
{
//Failure to download metadata should not be considered a failure to download the book
if (!cancellationToken.IsCancellationRequested)
Serilog.Log.Logger.Error(ex, "Error downloading metdatat of {@Book} to {@metadataFile}.", options.LibraryBook, metadataPath);
Serilog.Log.Logger.Error(ex, "Error downloading metadata of {@Book} to {@metadataFile}.", options.LibraryBook.LogFriendly(), metadataPath);
throw;
}
}

View File

@@ -3,6 +3,9 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="450"
xmlns:uibase="clr-namespace:LibationUiBase;assembly=LibationUiBase"
x:DataType="uibase:LocatedAudiobooksViewModel"
x:CompileBindings="True"
Width="600" Height="450"
x:Class="LibationAvalonia.Dialogs.LocateAudiobooksDialog"
Title="Locate Audiobooks"
@@ -13,17 +16,34 @@
<StackPanel Grid.Column="1" Orientation="Horizontal">
<TextBlock Text="IDs Found: " />
<TextBlock Text="{Binding FoundAsins}" />
<TextBlock Text="{Binding FoundAsinCount}" />
</StackPanel>
<ListBox Margin="0,5,0,0" Grid.Row="1" Grid.ColumnSpan="2" Name="foundAudiobooksLB" ItemsSource="{Binding FoundFiles}" AutoScrollToSelectedItem="true">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid ColumnDefinitions="Auto,*">
<TextBlock Grid.Column="0" Margin="0,0,10,0" Text="{Binding Item1}" />
<TextBlock Grid.Column="1" Text="{Binding Item2}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<DataGrid
Margin="0,5,0,0"
Grid.Row="1"
Grid.ColumnSpan="2"
IsReadOnly="True"
CanUserSortColumns="True"
CanUserResizeColumns="True"
GridLinesVisibility="All"
DoubleTapped="foundFilesDataGrid_DoubleTapped"
Name="foundFilesDataGrid"
ItemsSource="{Binding FoundFiles}">
<DataGrid.Styles>
<Style Selector="DataGridCell TextBlock">
<Setter Property="ToolTip.Tip" Value="Double-click to open containing folder."/>
</Style>
</DataGrid.Styles>
<DataGrid.Columns>
<DataGridTextColumn
Header="Found ID"
Width="Auto"
Binding="{Binding ID}" />
<DataGridTextColumn
Header="Found File"
Width="*"
Binding="{Binding FileName}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>

View File

@@ -1,41 +1,39 @@
using ApplicationServices;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Platform.Storage;
using DataLayer;
using LibationAvalonia.ViewModels;
using Dinah.Core;
using LibationFileManager;
using ReactiveUI;
using LibationUiBase;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reactive.Linq;
using System.Threading;
namespace LibationAvalonia.Dialogs
{
public partial class LocateAudiobooksDialog : DialogWindow
{
private event EventHandler<FilePathCache.CacheEntry>? FileFound;
private readonly CancellationTokenSource tokenSource = new();
private readonly List<string> foundAsins = new();
private readonly LocatedAudiobooksViewModel _viewModel;
public LocateAudiobooksDialog()
{
InitializeComponent();
DataContext = _viewModel = new();
var list = new AvaloniaList<FoundAudiobook>();
DataContext = _viewModel = new(list);
list.CollectionChanged += (_, _) => foundFilesDataGrid.ScrollIntoView(list[^1], foundFilesDataGrid.Columns[0]);
this.RestoreSizeAndLocation(Configuration.Instance);
if (Design.IsDesignMode)
{
_viewModel.FoundFiles.Add(new("[0000001]", "Filename 1.m4b"));
_viewModel.FoundFiles.Add(new("[0000002]", "Filename 2.m4b"));
_viewModel.AddFoundFile(new("0000000001", FileType.Audio, "Filename 1.m4b"));
_viewModel.AddFoundFile(new("0000000002", FileType.Audio, "Filename 2.m4b"));
}
else
{
Opened += LocateAudiobooksDialog_Opened;
FileFound += LocateAudiobooks_FileFound;
Closing += LocateAudiobooksDialog_Closing;
}
}
@@ -49,19 +47,6 @@ namespace LibationAvalonia.Dialogs
this.SaveSizeAndLocation(Configuration.Instance);
}
private void LocateAudiobooks_FileFound(object? sender, FilePathCache.CacheEntry e)
{
var newItem = new Tuple<string, string>($"[{e.Id}]", Path.GetFileName(e.Path));
_viewModel.FoundFiles.Add(newItem);
foundAudiobooksLB.SelectedItem = newItem;
if (!foundAsins.Any(asin => asin == e.Id))
{
foundAsins.Add(e.Id);
_viewModel.FoundAsins = foundAsins.Count;
}
}
private async void LocateAudiobooksDialog_Opened(object? sender, EventArgs e)
{
var folderPicker = new FolderPickerOpenOptions
@@ -76,37 +61,18 @@ namespace LibationAvalonia.Dialogs
if (selectedFolder is null || !Directory.Exists(selectedFolder))
{
await CancelAndCloseAsync();
return;
}
await foreach (var book in AudioFileStorage.FindAudiobooksAsync(selectedFolder, tokenSource.Token))
else
{
try
{
FilePathCache.Insert(book);
var lb = DbContexts.GetLibraryBook_Flat_NoTracking(book.Id);
if (lb is not null && lb.Book?.UserDefinedItem.BookStatus is not LiberatedStatus.Liberated)
await lb.UpdateBookStatusAsync(LiberatedStatus.Liberated);
tokenSource.Token.ThrowIfCancellationRequested();
FileFound?.Invoke(this, book);
}
catch (OperationCanceledException) { }
catch (Exception ex)
{
Serilog.Log.Error(ex, "Error adding found audiobook file to Libation. {@audioFile}", book);
}
await _viewModel.FindAndAddBooksAsync(selectedFolder, tokenSource.Token);
await MessageBox.Show(this, $"Libation has found {_viewModel.FoundAsinCount} unique audiobooks and added them to its database. ", $"Found {_viewModel.FoundAsinCount} Audiobooks");
}
}
await MessageBox.Show(this, $"Libation has found {foundAsins.Count} unique audiobooks and added them to its database. ", $"Found {foundAsins.Count} Audiobooks");
await SaveAndCloseAsync();
private void foundFilesDataGrid_DoubleTapped(object? sender, Avalonia.Input.TappedEventArgs e)
{
if (sender is DataGrid dg && dg.SelectedItem is FoundAudiobook foundAudiobook)
Go.To.File(foundAudiobook.Entry.Path);
}
}
public class LocatedAudiobooksViewModel : ViewModelBase
{
public AvaloniaList<Tuple<string, string>> FoundFiles { get; } = new();
public int FoundAsins { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
}
}

View File

@@ -47,6 +47,7 @@ public class LibationContributor
GitHubUser("twsouthwick"),
GitHubUser("radiorambo"),
GitHubUser("Youssef1313"),
GitHubUser("niontrix"),
]);
private LibationContributor(string name, LibationContributorType type,Uri link)

View File

@@ -0,0 +1,71 @@
using ApplicationServices;
using DataLayer;
using LibationFileManager;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace LibationUiBase;
public class FoundAudiobook
{
public string ID => Entry.Id;
public string FileName { get; }
public FilePathCache.CacheEntry Entry { get; }
public FoundAudiobook(FilePathCache.CacheEntry entry)
{
Entry = entry;
FileName = Path.GetFileName(entry.Path);
}
}
public class LocatedAudiobooksViewModel : ReactiveObject
{
public IList<FoundAudiobook> FoundFiles { get; }
public int FoundAsinCount { get => field; private set => RaiseAndSetIfChanged(ref field, value); }
private readonly HashSet<string> foundAsinsSet = [];
public LocatedAudiobooksViewModel(IList<FoundAudiobook> fileList)
{
FoundFiles = fileList;
}
public void AddFoundFile(FilePathCache.CacheEntry entry)
{
FoundAudiobook foundFile = new(entry);
Invoke(() => FoundFiles?.Add(foundFile));
if (!foundAsinsSet.Contains(entry.Id))
{
foundAsinsSet.Add(entry.Id);
FoundAsinCount = foundAsinsSet.Count;
}
}
public async Task FindAndAddBooksAsync(string searchdir, CancellationToken cancellation)
{
await Task.Run(() => FindAndAddBooksInternal(searchdir, cancellation), cancellation).ConfigureAwait(false);
}
private async Task FindAndAddBooksInternal(string searchdir, CancellationToken cancellation)
{
await foreach (var book in AudioFileStorage.FindAudiobooksAsync(searchdir, cancellation))
{
try
{
FilePathCache.Insert(book);
var lb = DbContexts.GetLibraryBook_Flat_NoTracking(book.Id);
if (lb is not null && lb.Book?.UserDefinedItem.BookStatus is not LiberatedStatus.Liberated)
await lb.UpdateBookStatusAsync(LiberatedStatus.Liberated);
cancellation.ThrowIfCancellationRequested();
AddFoundFile(book);
}
catch (OperationCanceledException) { }
catch (Exception ex)
{
Serilog.Log.Error(ex, "Error adding found audiobook file to Libation. {@audioFile}", book);
}
}
}
}

View File

@@ -28,80 +28,111 @@
/// </summary>
private void InitializeComponent()
{
this.label1 = new System.Windows.Forms.Label();
this.foundAudiobooksLV = new System.Windows.Forms.ListView();
this.columnHeader1 = new System.Windows.Forms.ColumnHeader();
this.columnHeader2 = new System.Windows.Forms.ColumnHeader();
this.booksFoundLbl = new System.Windows.Forms.Label();
this.SuspendLayout();
components = new System.ComponentModel.Container();
label1 = new System.Windows.Forms.Label();
booksFoundLbl = new System.Windows.Forms.Label();
dataGridView1 = new System.Windows.Forms.DataGridView();
foundAudiobookBindingSource = new System.Windows.Forms.BindingSource(components);
iDDataGridViewTextBoxColumn = new AccessibleDataGridViewColumn();
fileNameDataGridViewTextBoxColumn = new AccessibleDataGridViewColumn();
((System.ComponentModel.ISupportInitialize)dataGridView1).BeginInit();
((System.ComponentModel.ISupportInitialize)foundAudiobookBindingSource).BeginInit();
SuspendLayout();
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(12, 9);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(108, 15);
this.label1.TabIndex = 1;
this.label1.Text = "Found Audiobooks";
//
// foundAudiobooksLV
//
this.foundAudiobooksLV.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.foundAudiobooksLV.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.columnHeader1,
this.columnHeader2});
this.foundAudiobooksLV.FullRowSelect = true;
this.foundAudiobooksLV.Location = new System.Drawing.Point(12, 33);
this.foundAudiobooksLV.Name = "foundAudiobooksLV";
this.foundAudiobooksLV.Size = new System.Drawing.Size(321, 261);
this.foundAudiobooksLV.TabIndex = 2;
this.foundAudiobooksLV.UseCompatibleStateImageBehavior = false;
this.foundAudiobooksLV.View = System.Windows.Forms.View.Details;
//
// columnHeader1
//
this.columnHeader1.Text = "Book ID";
this.columnHeader1.Width = 85;
//
// columnHeader2
//
this.columnHeader2.Text = "Title";
label1.AutoSize = true;
label1.Location = new System.Drawing.Point(12, 9);
label1.Name = "label1";
label1.Size = new System.Drawing.Size(108, 15);
label1.TabIndex = 1;
label1.Text = "Found Audiobooks";
//
// booksFoundLbl
//
this.booksFoundLbl.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.booksFoundLbl.AutoSize = true;
this.booksFoundLbl.Location = new System.Drawing.Point(253, 9);
this.booksFoundLbl.Name = "booksFoundLbl";
this.booksFoundLbl.Size = new System.Drawing.Size(80, 15);
this.booksFoundLbl.TabIndex = 3;
this.booksFoundLbl.Text = "IDs Found: {0}";
this.booksFoundLbl.TextAlign = System.Drawing.ContentAlignment.TopRight;
booksFoundLbl.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right;
booksFoundLbl.AutoSize = true;
booksFoundLbl.Location = new System.Drawing.Point(253, 9);
booksFoundLbl.Name = "booksFoundLbl";
booksFoundLbl.Size = new System.Drawing.Size(72, 15);
booksFoundLbl.TabIndex = 3;
booksFoundLbl.Text = "IDs Found: 0";
booksFoundLbl.TextAlign = System.Drawing.ContentAlignment.TopRight;
//
// dataGridView1
//
dataGridView1.AllowUserToAddRows = false;
dataGridView1.AllowUserToDeleteRows = false;
dataGridView1.AllowUserToResizeRows = false;
dataGridView1.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
dataGridView1.AutoGenerateColumns = false;
dataGridView1.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.AllCells;
dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { iDDataGridViewTextBoxColumn, fileNameDataGridViewTextBoxColumn });
dataGridView1.DataSource = foundAudiobookBindingSource;
dataGridView1.Location = new System.Drawing.Point(12, 27);
dataGridView1.Name = "dataGridView1";
dataGridView1.RowHeadersVisible = false;
dataGridView1.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.CellSelect;
dataGridView1.Size = new System.Drawing.Size(321, 267);
dataGridView1.TabIndex = 4;
dataGridView1.CellDoubleClick += dataGridView1_CellDoubleClick;
//
// foundAudiobookBindingSource
//
foundAudiobookBindingSource.DataSource = typeof(LibationUiBase.FoundAudiobook);
//
// iDDataGridViewTextBoxColumn
//
iDDataGridViewTextBoxColumn.AccessibilityDescription = "Audiobook's Audible product ID forund by the scan.";
iDDataGridViewTextBoxColumn.AccessibilityName = "Found ASIN";
iDDataGridViewTextBoxColumn.DataPropertyName = "ID";
iDDataGridViewTextBoxColumn.HeaderText = "Found ID";
iDDataGridViewTextBoxColumn.Name = "iDDataGridViewTextBoxColumn";
iDDataGridViewTextBoxColumn.ReadOnly = true;
iDDataGridViewTextBoxColumn.Resizable = System.Windows.Forms.DataGridViewTriState.True;
iDDataGridViewTextBoxColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic;
iDDataGridViewTextBoxColumn.Width = 80;
//
// fileNameDataGridViewTextBoxColumn
//
fileNameDataGridViewTextBoxColumn.AccessibilityDescription = "Audiobook file found. Double-click to open containing folder.";
fileNameDataGridViewTextBoxColumn.AccessibilityName = "Found File";
fileNameDataGridViewTextBoxColumn.DataPropertyName = "FileName";
fileNameDataGridViewTextBoxColumn.HeaderText = "Found File";
fileNameDataGridViewTextBoxColumn.Name = "fileNameDataGridViewTextBoxColumn";
fileNameDataGridViewTextBoxColumn.ReadOnly = true;
fileNameDataGridViewTextBoxColumn.Resizable = System.Windows.Forms.DataGridViewTriState.True;
fileNameDataGridViewTextBoxColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic;
fileNameDataGridViewTextBoxColumn.Width = 87;
//
// LocateAudiobooksDialog
//
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.ClientSize = new System.Drawing.Size(345, 306);
this.Controls.Add(this.booksFoundLbl);
this.Controls.Add(this.foundAudiobooksLV);
this.Controls.Add(this.label1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
this.Name = "LocateAudiobooksDialog";
this.Text = "Locate Audiobooks";
this.ResumeLayout(false);
this.PerformLayout();
AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
ClientSize = new System.Drawing.Size(345, 306);
Controls.Add(dataGridView1);
Controls.Add(booksFoundLbl);
Controls.Add(label1);
FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
Name = "LocateAudiobooksDialog";
Text = "Locate Audiobooks";
FormClosing += LocateAudiobooks_FormClosing;
Shown += LocateAudiobooks_Shown;
((System.ComponentModel.ISupportInitialize)dataGridView1).EndInit();
((System.ComponentModel.ISupportInitialize)foundAudiobookBindingSource).EndInit();
ResumeLayout(false);
PerformLayout();
}
#endregion
private System.Windows.Forms.Label label1;
private System.Windows.Forms.ListView foundAudiobooksLV;
private System.Windows.Forms.ColumnHeader columnHeader1;
private System.Windows.Forms.ColumnHeader columnHeader2;
private System.Windows.Forms.Label booksFoundLbl;
private System.Windows.Forms.DataGridView dataGridView1;
private System.Windows.Forms.BindingSource foundAudiobookBindingSource;
private AccessibleDataGridViewColumn iDDataGridViewTextBoxColumn;
private AccessibleDataGridViewColumn fileNameDataGridViewTextBoxColumn;
}
}

View File

@@ -1,53 +1,35 @@
using ApplicationServices;
using DataLayer;
using DataLayer;
using Dinah.Core;
using LibationFileManager;
using LibationUiBase;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace LibationWinForms.Dialogs
{
public partial class LocateAudiobooksDialog : Form
{
private event EventHandler<FilePathCache.CacheEntry> FileFound;
private readonly CancellationTokenSource tokenSource = new();
private readonly List<string> foundAsins = new();
private readonly string labelFormatText;
private readonly LocatedAudiobooksViewModel _viewModel;
public LocateAudiobooksDialog()
{
InitializeComponent();
labelFormatText = booksFoundLbl.Text;
setFoundBookCount(0);
this.SetLibationIcon();
this.RestoreSizeAndLocation(Configuration.Instance);
Shown += LocateAudiobooks_Shown;
FileFound += LocateAudiobooks_FileFound;
FormClosing += LocateAudiobooks_FormClosing;
_viewModel = new LocatedAudiobooksViewModel(new SortBindingList<FoundAudiobook>());
dataGridView1.EnableHeadersVisualStyles = !Application.IsDarkModeEnabled;
dataGridView1.RowsAdded += DataGridView1_RowsAdded;
foundAudiobookBindingSource.DataSource = _viewModel.FoundFiles;
booksFoundLbl.DataBindings.Add(new Binding(nameof(booksFoundLbl.Text), _viewModel, nameof(_viewModel.FoundAsinCount), true, DataSourceUpdateMode.OnPropertyChanged, 0, booksFoundLbl.Text));
}
private void setFoundBookCount(int count)
=> booksFoundLbl.Text = string.Format(labelFormatText, count);
private void LocateAudiobooks_FileFound(object sender, FilePathCache.CacheEntry e)
private void DataGridView1_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
{
foundAudiobooksLV.Items
.Add(new ListViewItem(new string[] { $"[{e.Id}]", Path.GetFileName(e.Path) }))
.EnsureVisible();
foundAudiobooksLV.AutoResizeColumn(1, ColumnHeaderAutoResizeStyle.ColumnContent);
if (!foundAsins.Any(asin => asin == e.Id))
{
foundAsins.Add(e.Id);
setFoundBookCount(foundAsins.Count);
}
dataGridView1.FirstDisplayedScrollingRowIndex = e.RowIndex;
}
private void LocateAudiobooks_FormClosing(object sender, FormClosingEventArgs e)
@@ -65,34 +47,22 @@ namespace LibationWinForms.Dialogs
InitialDirectory = Configuration.Instance.Books
};
if (fbd.ShowDialog() != DialogResult.OK || !Directory.Exists(fbd.SelectedPath))
var result = fbd.ShowDialog(this);
if (result != DialogResult.OK || !Directory.Exists(fbd.SelectedPath))
{
Close();
return;
DialogResult = result;
}
await foreach (var book in AudioFileStorage.FindAudiobooksAsync(fbd.SelectedPath, tokenSource.Token))
else
{
try
{
FilePathCache.Insert(book);
var lb = DbContexts.GetLibraryBook_Flat_NoTracking(book.Id);
if (lb.Book.UserDefinedItem.BookStatus is not LiberatedStatus.Liberated)
await lb.UpdateBookStatusAsync(LiberatedStatus.Liberated);
tokenSource.Token.ThrowIfCancellationRequested();
this.Invoke(FileFound, this, book);
}
catch (OperationCanceledException) { }
catch (Exception ex)
{
Serilog.Log.Error(ex, "Error adding found audiobook file to Libation. {@audioFile}", book);
}
await _viewModel.FindAndAddBooksAsync(fbd.SelectedPath, tokenSource.Token);
MessageBox.Show(this, $"Libation has found {_viewModel.FoundAsinCount} unique audiobooks and added them to its database. ", $"Found {_viewModel.FoundAsinCount} Audiobooks");
}
}
MessageBox.Show(this, $"Libation has found {foundAsins.Count} unique audiobooks and added them to its database. ", $"Found {foundAsins.Count} Audiobooks");
Close();
private void dataGridView1_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex >= 0 && e.RowIndex < _viewModel.FoundFiles.Count)
Go.To.File(_viewModel.FoundFiles[e.RowIndex].Entry.Path);
}
}
}

View File

@@ -24,21 +24,6 @@ namespace LibationWinForms.Dialogs
releaseNotesTbox.Text = upgradeProperties.Notes;
Shown += (_, _) => yesBtn.Focus();
Load += UpgradeNotificationDialog_Load;
}
private void UpgradeNotificationDialog_Load(object sender, EventArgs e)
{
//This dialog starts before Form1, soposition it at the center of where Form1 will be.
var savedState = Configuration.Instance.GetNonString<FormSizeAndPosition>(defaultValue: null, nameof(Form1));
if (savedState is null) return;
int x = savedState.X + (savedState.Width - Width) / 2;
int y = savedState.Y + (savedState.Height - Height) / 2;
Location = new(x, y);
TopMost = true;
}
private void PackageDlLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)

View File

@@ -11,14 +11,12 @@ namespace LibationWinForms
{
setProgressVisible(false);
#pragma warning disable CS8321 // Local function is declared but never used
Task upgradeAvailable(UpgradeEventArgs e)
async Task upgradeAvailable(UpgradeEventArgs e)
{
var notificationResult = new UpgradeNotificationDialog(e.UpgradeProperties).ShowDialog(this);
var notificationResult = await new UpgradeNotificationDialog(e.UpgradeProperties).ShowDialogAsync(this);
e.Ignore = notificationResult == DialogResult.Ignore;
e.InstallUpgrade = notificationResult == DialogResult.Yes;
return Task.CompletedTask;
}
#pragma warning restore CS8321 // Local function is declared but never used

View File

@@ -69,6 +69,16 @@ Thanks to [mhdi](https://aur.archlinux.org/account/mhdi) for taking care of AUR
Thanks to [TomaSajt](https://github.com/tomasajt) for taking care of Nix package maintenance.
### Pacstall
Pacstall is the AUR Ubuntu wishes it had. It takes the concept of the AUR and puts a spin on it, making it easier to install programs without scouring github repos and the likes. See the [Pacstall](https://pacstall.dev/) project for more information.
```bash
pacstall -I libation-deb
```
---
If your desktop uses gtk, you should now see Libation among your applications.
Additionally, you may launch Libation, LibationCli, and Hangover (the Libation recovery app) via the command line using 'libation, libationcli', and 'hangover' aliases respectively.