Next iterative step toward replacing live scanning with db state. FilePaths.json => db

This commit is contained in:
Robert McRackan 2021-07-28 09:40:27 -04:00
parent 5c6bf300c6
commit 75f1d987fc
7 changed files with 85 additions and 14 deletions

View File

@ -204,7 +204,7 @@ namespace AaxDecrypter
_networkStream = response.GetResponseStream();
//Download the file in the background.
Thread downloadThread = new Thread(() => DownloadFile());
Thread downloadThread = new Thread(() => DownloadFile()) { IsBackground = true };
downloadThread.Start();
hasBegunDownloading = true;

View File

@ -25,6 +25,7 @@ namespace DataLayer
.GetBooks()
.Where(predicate);
/// <summary>This is still IQueryable. YOU MUST CALL ToList() YOURSELF</summary>
public static IQueryable<Book> GetBooks(this IQueryable<Book> books)
=> books
// owned items are always loaded. eg: book.UserDefinedItem, book.Supplements

View File

@ -16,16 +16,16 @@ namespace FileManager
public string Path { get; set; }
}
static Cache<CacheEntry> cache { get; } = new Cache<CacheEntry>();
private static Cache<CacheEntry> cache { get; } = new Cache<CacheEntry>();
public static string JsonFile => Path.Combine(Configuration.Instance.LibationFiles, "FilePaths.json");
private static string jsonFile => Path.Combine(Configuration.Instance.LibationFiles, "FilePaths.json");
static FilePathCache()
{
// load json into memory. if file doesn't exist, nothing to do. save() will create if needed
if (File.Exists(JsonFile))
if (File.Exists(jsonFile))
{
var list = JsonConvert.DeserializeObject<List<CacheEntry>>(File.ReadAllText(JsonFile));
var list = JsonConvert.DeserializeObject<List<CacheEntry>>(File.ReadAllText(jsonFile));
cache = new Cache<CacheEntry>(list);
}
}
@ -74,7 +74,7 @@ namespace FileManager
private static void save()
{
// create json if not exists
static void resave() => File.WriteAllText(JsonFile, JsonConvert.SerializeObject(cache.ToList(), Formatting.Indented));
static void resave() => File.WriteAllText(jsonFile, JsonConvert.SerializeObject(cache.ToList(), Formatting.Indented));
lock (locker)
{

View File

@ -13,7 +13,7 @@
<!-- <PublishSingleFile>true</PublishSingleFile> -->
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<Version>5.4.0.5</Version>
<Version>5.4.1.1</Version>
</PropertyGroup>
<ItemGroup>

View File

@ -1,9 +1,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using AudibleApi;
using AudibleApi.Authorization;
using DataLayer;
using Microsoft.EntityFrameworkCore;
using Dinah.Core.IO;
using Dinah.Core.Logging;
using FileManager;
@ -51,6 +54,7 @@ namespace LibationLauncher
migrate_to_v5_0_0(config);
migrate_to_v5_2_0__post_config(config);
migrate_to_v5_4_1(config);
ensureSerilogConfig(config);
configureLogging(config);
@ -141,7 +145,7 @@ namespace LibationLauncher
CancelInstallation();
}
#region migrate_to_v5_0_0 re-register device if device info not in settings
#region migrate to v5.0.0 re-register device if device info not in settings
private static void migrate_to_v5_0_0(Configuration config)
{
if (!config.Exists(nameof(config.AllowLibationFixup)))
@ -229,6 +233,72 @@ namespace LibationLauncher
}
#endregion
#region migrate to v5.4.1 see comment
// this 'migration' is a bit different. it intentionally runs each time Libation is started. its job will be fulfilled when I eventually
// implement the portion which removes FilePaths.json, at which time this method will be a proper migration
//
// I'm iterating through safe steps toward getting rid of the live scanner except to track audiobook files as a convenience
// such as clicking the stop light to open its location. live scanning will be replaced with state tracking in the database.
// FilePaths.json => db. long running. fire and forget
private static void migrate_to_v5_4_1(Configuration config)
=> new System.Threading.Thread(() => migrate_to_v5_4_1_thread(config)) { IsBackground = true }.Start();
private static void migrate_to_v5_4_1_thread(Configuration config)
{
var debugStopwatch = System.Diagnostics.Stopwatch.StartNew();
try
{
var filePaths = Path.Combine(config.LibationFiles, "FilePaths.json");
if (!File.Exists(filePaths))
return;
using var context = ApplicationServices.DbContexts.GetContext();
context.Books.Load();
var jArr = JArray.Parse(File.ReadAllText(filePaths));
foreach (var jToken in jArr)
{
var asinToken = jToken["Id"];
var fileTypeToken = jToken["FileType"];
var pathToken = jToken["Path"];
if (asinToken is null || fileTypeToken is null || pathToken is null ||
asinToken.Type != JTokenType.String || fileTypeToken.Type != JTokenType.Integer || pathToken.Type != JTokenType.String)
continue;
var asin = asinToken.Value<string>();
var fileType = (FileType)fileTypeToken.Value<int>();
var path = pathToken.Value<string>();
if (fileType == FileType.Unknown || fileType == FileType.AAXC)
continue;
var book = context.Books.Local.FirstOrDefault(b => b.AudibleProductId == asin);
if (book is null)
continue;
// assign these strings and enums/ints unconditionally. EFCore will only update if changed
if (fileType == FileType.PDF)
book.UserDefinedItem.PdfStatus = LiberatedStatus.Liberated;
if (fileType == FileType.Audio)
{
book.UserDefinedItem.BookStatus = LiberatedStatus.Liberated;
book.UserDefinedItem.BookLocation = path;
}
}
context.SaveChanges();
}
catch (Exception ex)
{
Log.Logger.Error(ex, "Error attempting to insert FilePaths into db");
}
debugStopwatch.Stop();
var debugTotal = debugStopwatch.Elapsed;
}
#endregion
private static void ensureSerilogConfig(Configuration config)
{
if (config.GetObject("Serilog") != null)

View File

@ -176,7 +176,8 @@ namespace LibationWinForms.BookLiberation
downloadDialog.UpdateFilename(destination);
downloadDialog.Show();
new System.Threading.Thread(() => {
new System.Threading.Thread(() =>
{
var downloadFile = new DownloadFile();
downloadFile.DownloadProgressChanged += (_, progress) => downloadDialog.UIThread(() =>
@ -190,7 +191,9 @@ namespace LibationWinForms.BookLiberation
});
downloadFile.PerformDownloadFileAsync(url, destination).GetAwaiter().GetResult();
}).Start();
})
{ IsBackground = true }
.Start();
}
// subscribed to Begin event because a new form should be created+processed+closed on each iteration

View File

@ -9,14 +9,11 @@ namespace LibationWinForms
{
internal class GridEntry
{
private LibraryBook libraryBook;
private LibraryBook libraryBook { get; }
private Book book => libraryBook.Book;
public Book GetBook() => book;
// this special case is obvious and ugly
public void REPLACE_Library_Book(LibraryBook libraryBook) => this.libraryBook = libraryBook;
public GridEntry(LibraryBook libraryBook) => this.libraryBook = libraryBook;
// hide from public fields from Data Source GUI with [Browsable(false)]