Compare commits

...

9 Commits

Author SHA1 Message Date
Robert
cf571148bc incr ver 2025-11-25 23:19:49 -05:00
rmcrackan
2c2a720ba9 Merge pull request #1458 from Mbucari/master
Bug fixes in the downloader
2025-11-25 22:47:48 -05:00
Michael Bucari-Tovo
b577ef7187 Improve SetBeckupCounts
Change Avalonia's Task-based approach to WinForms' BackgroundWorker approach.
- Reduce number of calls to GetLibrary by adding the Library to the LibraryStats record.
2025-11-25 14:59:48 -07:00
Michael Bucari-Tovo
ffbb3c3516 Make namespace name match assembly name 2025-11-25 13:34:12 -07:00
Michael Bucari-Tovo
2a6cf38677 Fix book details dialog not saving 2025-11-25 13:33:40 -07:00
Michael Bucari-Tovo
d8104a4d7c Check is stream is disposed before reading position. 2025-11-25 12:34:20 -07:00
Michael Bucari-Tovo
af85ea9219 Fix exception being throw in Dispose() 2025-11-25 12:24:07 -07:00
rmcrackan
c30e149a36 Merge pull request #1456 from Mbucari/master
Fix database lock from -wal and -whm files
2025-11-24 22:57:12 -05:00
MBucari
050a4867b7 Fix database lock from -wal and -whm files
Delete LibationContext.db-shm and LibationContext.db-wal files as part of startup routine.
2025-11-24 20:45:55 -07:00
12 changed files with 102 additions and 32 deletions

View File

@@ -26,7 +26,17 @@ namespace AaxDecrypter
protected string OutputDirectory { get; }
public IDownloadOptions DownloadOptions { get; }
protected NetworkFileStream InputFileStream => NfsPersister.NetworkFileStream;
protected virtual long InputFilePosition => InputFileStream.Position;
protected virtual long InputFilePosition
{
get
{
//Use try/catch instread of checking CanRead to avoid
//a race with the background download completing
//between the check and the Position call.
try { return InputFileStream.Position; }
catch { return InputFileStream.Length; }
}
}
private bool downloadFinished;
private NetworkFileStreamPersister? m_nfsPersister;

View File

@@ -209,6 +209,12 @@ namespace AaxDecrypter
}
}
}
catch (Exception ex)
{
//Don't throw from DownloadTask.
//This task gets awaited in Dispose() and we don't want to have an unhandled exception there.
Serilog.Log.Error(ex, "An error was encountered during the download process.");
}
finally
{
_writeFile.Dispose();

View File

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

View File

@@ -90,6 +90,7 @@ namespace AppScaffolding
{
config.LoadPersistentSettings(config.LibationFiles.SettingsFilePath);
}
DeleteOpenSqliteFiles(config);
AudibleApiStorage.EnsureAccountsSettingsFileExists();
//
@@ -102,6 +103,19 @@ namespace AppScaffolding
Migrations.migrate_to_v12_0_1(config);
}
/// <summary>
/// Delete shared memory and write-ahead log SQLite database files which may prevent access to the database.
/// </summary>
private static void DeleteOpenSqliteFiles(Configuration config)
{
var walFile = SqliteStorage.DatabasePath + "-wal";
var shmFile = SqliteStorage.DatabasePath + "-shm";
if (File.Exists(walFile))
FileManager.FileUtility.SaferDelete(walFile);
if (File.Exists(shmFile))
FileManager.FileUtility.SaferDelete(shmFile);
}
/// <summary>Initialize logging. Wire-up events. Run after migration</summary>
public static void RunPostMigrationScaffolding(Variety variety, Configuration config)
{

View File

@@ -586,7 +586,7 @@ namespace ApplicationServices
// below are queries, not commands. maybe I should make a LibraryQueries. except there's already one of those...
public record LibraryStats(int booksFullyBackedUp, int booksDownloadedOnly, int booksNoProgress, int booksError, int booksUnavailable, int pdfsDownloaded, int pdfsNotDownloaded, int pdfsUnavailable)
public record LibraryStats(int booksFullyBackedUp, int booksDownloadedOnly, int booksNoProgress, int booksError, int booksUnavailable, int pdfsDownloaded, int pdfsNotDownloaded, int pdfsUnavailable, IEnumerable<LibraryBook> LibraryBooks)
{
public int PendingBooks => booksNoProgress + booksDownloadedOnly;
public bool HasPendingBooks => PendingBooks > 0;
@@ -655,7 +655,7 @@ namespace ApplicationServices
Log.Logger.Information("PDF counts. {@DebugInfo}", new { total = pdfResults.Count, pdfsDownloaded, pdfsNotDownloaded, pdfsUnavailable });
return new(booksFullyBackedUp, booksDownloadedOnly, booksNoProgress, booksError, booksUnavailable, pdfsDownloaded, pdfsNotDownloaded, pdfsUnavailable);
return new(booksFullyBackedUp, booksDownloadedOnly, booksNoProgress, booksError, booksUnavailable, pdfsDownloaded, pdfsNotDownloaded, pdfsUnavailable, libraryBooks);
}
}
}

View File

@@ -1,6 +1,6 @@
using Microsoft.EntityFrameworkCore.Design;
namespace DataLayer.Postgres
namespace DataLayer.Sqlite
{
public class SqliteContextFactory : IDesignTimeDbContextFactory<LibationContext>
{

View File

@@ -73,8 +73,8 @@ namespace LibationAvalonia.Dialogs
}
}
public void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
=> SaveAndClose();
public async void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
=> await SaveAndCloseAsync();
public class liberatedComboBoxItem
{
public LiberatedStatus Status { get; set; }

View File

@@ -1,5 +1,4 @@
using ApplicationServices;
using Avalonia.Threading;
using DataLayer;
using LibationFileManager;
using ReactiveUI;
@@ -11,7 +10,7 @@ namespace LibationAvalonia.ViewModels
{
partial class MainVM
{
private Task<LibraryCommands.LibraryStats>? updateCountsTask;
private System.ComponentModel.BackgroundWorker updateCountsBw = new();
/// <summary> The "Begin Book and PDF Backup" menu item header text </summary>
public string BookBackupsToolStripText { get; private set; } = "Begin Book and PDF Backups: 0";
@@ -46,20 +45,40 @@ namespace LibationAvalonia.ViewModels
//Pass null to the setup count to get the whole library.
LibraryCommands.BookUserDefinedItemCommitted += async (_, _)
=> await SetBackupCountsAsync(null);
updateCountsBw.DoWork += UpdateCountsBw_DoWork;
updateCountsBw.RunWorkerCompleted += UpdateCountsBw_Completed; ;
}
private bool runBackupCountsAgain;
public async Task SetBackupCountsAsync(IEnumerable<LibraryBook>? libraryBooks)
{
if (updateCountsTask?.IsCompleted ?? true)
{
updateCountsTask = Task.Run(() => LibraryCommands.GetCounts(libraryBooks));
var stats = await updateCountsTask;
await Dispatcher.UIThread.InvokeAsync(() => LibraryStats = stats);
runBackupCountsAgain = true;
if (Configuration.Instance.AutoDownloadEpisodes
&& stats.PendingBooks + stats.pdfsNotDownloaded > 0)
await Dispatcher.UIThread.InvokeAsync(BackupAllBooks);
if (!updateCountsBw.IsBusy)
updateCountsBw.RunWorkerAsync(libraryBooks);
}
private void UpdateCountsBw_DoWork(object? sender, System.ComponentModel.DoWorkEventArgs e)
{
while (runBackupCountsAgain)
{
runBackupCountsAgain = false;
e.Result = LibraryCommands.GetCounts(e.Argument as IEnumerable<LibraryBook>);
}
}
private void UpdateCountsBw_Completed(object? sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
if (e.Result is not LibraryCommands.LibraryStats stats)
return;
LibraryStats = stats;
if (Configuration.Instance.AutoDownloadEpisodes
&& stats.PendingBooks + stats.pdfsNotDownloaded > 0)
BackupAllBooks(stats.LibraryBooks);
}
}
}

View File

@@ -7,6 +7,7 @@ using DataLayer;
using LibationUiBase.Forms;
using LibationUiBase;
using System.Collections.Generic;
using Avalonia.Threading;
#nullable enable
namespace LibationAvalonia.ViewModels
@@ -15,14 +16,24 @@ namespace LibationAvalonia.ViewModels
{
public void Configure_Liberate() { }
/// <summary> This gets called by the "Begin Book and PDF Backups" menu item. </summary>
public async Task BackupAllBooks()
{
var books = await Task.Run(() => DbContexts.GetLibrary_Flat_NoTracking());
BackupAllBooks(books);
}
private void BackupAllBooks(IEnumerable<LibraryBook> books)
{
try
{
var unliberated = await Task.Run(() => DbContexts.GetLibrary_Flat_NoTracking().UnLiberated().ToArray());
var unliberated = books.UnLiberated().ToArray();
if (ProcessQueue.QueueDownloadDecrypt(unliberated))
setQueueCollapseState(false);
Dispatcher.UIThread.Invoke(() =>
{
if (ProcessQueue.QueueDownloadDecrypt(unliberated))
setQueueCollapseState(false);
});
}
catch (Exception ex)
{
@@ -30,9 +41,11 @@ namespace LibationAvalonia.ViewModels
}
}
/// <summary> This gets called by the "Begin PDF Only Backups" menu item. </summary>
public async Task BackupAllPdfs()
{
if (ProcessQueue.QueueDownloadPdf(await Task.Run(() => DbContexts.GetLibrary_Flat_NoTracking())))
var books = await Task.Run(() => DbContexts.GetLibrary_Flat_NoTracking());
if (ProcessQueue.QueueDownloadPdf(books))
setQueueCollapseState(false);
}

View File

@@ -5,7 +5,7 @@ namespace LibationFileManager
public static class SqliteStorage
{
// not customizable. don't move to config
private static string databasePath => Path.Combine(Configuration.Instance.LibationFiles.Location, "LibationContext.db");
public static string ConnectionString => $"Data Source={databasePath};Foreign Keys=False;Pooling=False;";
public static string DatabasePath => Path.Combine(Configuration.Instance.LibationFiles.Location, "LibationContext.db");
public static string ConnectionString => $"Data Source={DatabasePath};Foreign Keys=False;Pooling=False;";
}
}

View File

@@ -1,6 +1,7 @@
using DataLayer;
using LibationUiBase;
using ApplicationServices;
using DataLayer;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
@@ -13,12 +14,21 @@ namespace LibationWinForms
//GetLibrary_Flat_NoTracking() may take a long time on a hugh library. so run in new thread
private async void beginBookBackupsToolStripMenuItem_Click(object _ = null, EventArgs __ = null)
{
var library = await Task.Run(() => DbContexts.GetLibrary_Flat_NoTracking());
BackupAllBooks(library);
}
private void BackupAllBooks(IEnumerable<LibraryBook> books)
{
try
{
var unliberated = await Task.Run(() => ApplicationServices.DbContexts.GetLibrary_Flat_NoTracking().UnLiberated().ToArray());
if (processBookQueue1.ViewModel.QueueDownloadDecrypt(unliberated))
SetQueueCollapseState(false);
var unliberated = books.UnLiberated().ToArray();
Invoke(() =>
{
if (processBookQueue1.ViewModel.QueueDownloadDecrypt(unliberated))
SetQueueCollapseState(false);
});
}
catch (Exception ex)
{

View File

@@ -28,13 +28,11 @@ namespace LibationWinForms
// winforms only. this should NOT be allowed in cli
updateCountsBw.RunWorkerCompleted += (object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) =>
{
if (!Configuration.Instance.AutoDownloadEpisodes)
if (!Configuration.Instance.AutoDownloadEpisodes || e.Result is not LibraryCommands.LibraryStats libraryStats)
return;
var libraryStats = e.Result as LibraryCommands.LibraryStats;
if ((libraryStats.PendingBooks + libraryStats.pdfsNotDownloaded) > 0)
Invoke(() => beginBookBackupsToolStripMenuItem_Click(null, System.EventArgs.Empty));
BackupAllBooks(libraryStats.LibraryBooks);
};
}