diff --git a/Source/AppScaffolding/LibationScaffolding.cs b/Source/AppScaffolding/LibationScaffolding.cs index bb51d155..395d14d8 100644 --- a/Source/AppScaffolding/LibationScaffolding.cs +++ b/Source/AppScaffolding/LibationScaffolding.cs @@ -225,6 +225,11 @@ public static class LibationScaffolding private static void configureLogging(Configuration config) { config.ConfigureLogging(); + Log.Information( + "Paths: LibationFiles={LibationFiles} AppsettingsJson={AppsettingsJson} SQLiteDb={SqliteDb}", + config.LibationFiles.Location, + config.LibationFiles.AppsettingsJsonFile ?? "(null, LIBATION_FILES_DIR may be set)", + Path.Combine(config.LibationFiles.Location, "LibationContext.db")); DbContexts.TryEmitPendingInitialDatabaseStatistics(); // capture most Console.WriteLine() and write to serilog. See below tests for details. diff --git a/Source/ApplicationServices/DbContexts.cs b/Source/ApplicationServices/DbContexts.cs index f4a468e0..78d2a9ab 100644 --- a/Source/ApplicationServices/DbContexts.cs +++ b/Source/ApplicationServices/DbContexts.cs @@ -3,6 +3,7 @@ using LibationFileManager; using Serilog; using System; using System.Collections.Generic; +using System.IO; using System.Threading; namespace ApplicationServices; @@ -35,9 +36,22 @@ public static class DbContexts var context = !string.IsNullOrEmpty(Configuration.Instance.PostgresqlConnectionString) ? LibationContextFactory.CreatePostgres(Configuration.Instance.PostgresqlConnectionString) : LibationContextFactory.CreateSqlite(SqliteStorage.ConnectionString); - LibationContextFactory.ApplyMigrations( - context, - string.IsNullOrEmpty(Configuration.Instance.PostgresqlConnectionString) ? SqliteStorage.DatabasePath : null); + try + { + LibationContextFactory.ApplyMigrations( + context, + string.IsNullOrEmpty(Configuration.Instance.PostgresqlConnectionString) ? SqliteStorage.DatabasePath : null); + } + catch (InvalidOperationException ex) when (ex.Message.Contains("Libation cannot write its SQLite database", StringComparison.Ordinal)) + { + Log.Error( + ex, + "SQLite migrations failed (read-only or blocked). LibationFiles={LibationFiles} DatabasePath={DatabasePath} AppsettingsJson={AppsettingsJson}", + Configuration.Instance.LibationFiles.Location, + SqliteStorage.DatabasePath, + Configuration.Instance.LibationFiles.AppsettingsJsonFile ?? "(null, LIBATION_FILES_DIR may be set)"); + throw; + } // Validate SQLite DB file was created and is accessible (once per process; OS may delay availability) if (!_sqliteDbValidated && string.IsNullOrEmpty(Configuration.Instance.PostgresqlConnectionString)) diff --git a/Source/DataLayer/LibationContextFactory.cs b/Source/DataLayer/LibationContextFactory.cs index c4e4b383..451caea7 100644 --- a/Source/DataLayer/LibationContextFactory.cs +++ b/Source/DataLayer/LibationContextFactory.cs @@ -76,7 +76,7 @@ public class LibationContextFactory { // Match LibationFileManager.Configuration.IsLinux (OperatingSystem.IsLinux); avoid referencing that project from DataLayer. var linuxSection = OperatingSystem.IsLinux() - ? "\n\nOn Linux: check ownership and permissions on that folder (chmod/chown). Include LibationContext.db-wal and LibationContext.db-shm if they exist. Snap data is often under ~/snap/libation//.local/share/Libation — that entire tree must be writable.\n\nIf Libation will not start, set environment variable LIBATION_FILES_DIR to an existing writable directory you own, then launch again. After Libation starts, you can also change the Libation Files folder in Settings.\n\nIf this persists on Snap and permissions look correct, try the non-Snap build to rule out confinement blocking writes." + ? "\n\nOn Linux: check ownership and permissions on that folder (chmod/chown). Include LibationContext.db-wal and LibationContext.db-shm if they exist. Snap data is often under ~/snap/libation//.local/share/Libation - that entire tree must be writable.\n\nSnap: after a refresh, appsettings.json under the new revision folder may still list LibationFiles pointing at an older ~/snap/libation// path. The crash path may still show that old revision. Edit appsettings.json so LibationFiles matches the folder that contains that file (same revision as current), or set LIBATION_FILES_DIR. See docs/installation/linux.md (Snap) and https://github.com/rmcrackan/Libation/issues/1776.\n\nIf Libation will not start, set environment variable LIBATION_FILES_DIR to an existing writable directory you own, then launch again. After Libation starts, you can also change the Libation Files folder in Settings.\n\nIf this persists on Snap and permissions look correct, try the non-Snap build to rule out confinement blocking writes." : "\n\nAfter Libation starts, you can change the Libation Files folder in Settings. If Libation will not start, set environment variable LIBATION_FILES_DIR to an existing writable directory you own, then launch again."; return new InvalidOperationException( diff --git a/Source/LibationFileManager/LibationFiles.cs b/Source/LibationFileManager/LibationFiles.cs index e0a593c6..bfd43648 100644 --- a/Source/LibationFileManager/LibationFiles.cs +++ b/Source/LibationFileManager/LibationFiles.cs @@ -38,7 +38,7 @@ public class LibationFiles /// /// Found Location of appsettings.json. This file must exist or be able to be created for Libation to start. /// - internal string? AppsettingsJsonFile { get; } + public string? AppsettingsJsonFile { get; } /// /// File path to Settings.json inside /// diff --git a/docs/advanced/troubleshoot.md b/docs/advanced/troubleshoot.md index 086f4acd..f91610f5 100644 --- a/docs/advanced/troubleshoot.md +++ b/docs/advanced/troubleshoot.md @@ -53,3 +53,15 @@ There are two possible causes of this error. 2. The database's journaling mode is incompatible with your environment. Change the journaling mode to `DELETE` by one of two methods. 1. [Run hangover](#how-to-run-the-hangover-app) and execute the following command in the "Database" tab: `PRAGMA journal_mode=DELETE` 2. run this command in your terminal: `sqlite3 "path/to/libation/files/LibationContext.db" "PRAGMA journal_mode=DELETE;"` + +## Linux Snap and SQLite write failures + +Symptoms include a crash on startup that mentions `LibationContext.db` under a path like `~/snap/libation//.local/share/Libation/`. + +1. **Permissions** - The whole Libation data directory must be writable by your user, including `LibationContext.db`, `LibationContext.db-wal`, and `LibationContext.db-shm` when they exist. Fix ownership with `chown` if needed. + +2. **Stale `LibationFiles` after a Snap refresh** - Snap may install a new revision folder (new ``) while `appsettings.json` inside the **new** folder still points `LibationFiles` at the **previous** revision path. Libation then targets the old path while the app runs from the new revision, which often surfaces as a read-only or migration failure even when permissions on both trees look fine. + + **Fix:** edit `appsettings.json` in the active revision (for example under `~/snap/libation/current/...`) so the `LibationFiles` value uses the **same** `.../snap/libation//...` as that file, or use `LIBATION_FILES_DIR`. Step-by-step context: [Install on Linux - Snap](/docs/installation/linux#snap) and [issue #1776](https://github.com/rmcrackan/Libation/issues/1776). + +3. **Non-Snap build** - If you still suspect Snap confinement after the above, try a `.deb` / `.rpm` / AppImage build from [Releases](https://github.com/rmcrackan/Libation/releases) to compare behavior. diff --git a/docs/frequently-asked-questions.md b/docs/frequently-asked-questions.md index 2fce6cd4..fd988ea1 100644 --- a/docs/frequently-asked-questions.md +++ b/docs/frequently-asked-questions.md @@ -43,6 +43,12 @@ Spatial audiobooks are delivered in two formats: [E-AC-3](./features/audio-file- For reasons known only to Jeff Bezos and God, amazon and audible brazil handle logins slightly differently. The external browser login option is not possible for Brazil. [See this ticket for more details.](https://github.com/rmcrackan/Libation/issues/1103) +## Snap refreshed and Libation crashes on the database - what should I check? + +Snap keeps per-version folders under `~/snap/libation//`. After an update, `appsettings.json` in the **new** folder may still list `"LibationFiles"` with an **old** revision path. Libation then opens the wrong directory and you can see errors about SQLite or migrations even when permissions look fine. + +Open `appsettings.json` next to your running install (often under `~/snap/libation/current/...`), set `LibationFiles` to the Libation data path **inside that same revision**, save, and launch again. Details: [Linux install - Snap](/docs/installation/linux#snap), [Troubleshooting - Snap](/docs/advanced/troubleshoot#linux-snap-and-sqlite-write-failures), and [issue #1776](https://github.com/rmcrackan/Libation/issues/1776). + ## How Do I Use Libation With a South Africa Account? Like many countries, amazon gives South Africa it's own amazon site. [Unlike many other regions](https://www.audible.com/ep/country-selector) there is not South Africa specific audible site. Use `US` for your region -- ie: audible.com. diff --git a/docs/getting-started.md b/docs/getting-started.md index 1a35d0b8..c1788b96 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -18,7 +18,7 @@ Nearly 100% of the difference is look and feel -- it's a matter of preference. Extract the zip file to a folder and then run `Libation.exe` from inside of that folder. Do not put it in Program Files. The inability to edit files from there causes problems with configuration and updating. -- [Linux](./installation/linux.md) +- [Linux](./installation/linux.md) (if you use **Snap**, read the [Snap](./installation/linux.md#snap) section after each refresh so `appsettings.json` and `LibationFiles` stay on the same revision) - [MacOS](./installation/mac.md) ## Create Accounts diff --git a/docs/index.md b/docs/index.md index 94ad889a..6b21ec0b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -11,7 +11,7 @@ Welcome to the Libation documentation! Here you'll find everything you need to g Platform-specific installation guides: -- **[Linux Installation](/docs/installation/linux)** - Install Libation on Linux +- **[Linux Installation](/docs/installation/linux)** - Install Libation on Linux (includes [Snap](/docs/installation/linux#snap) notes) - **[Mac Installation](/docs/installation/mac)** - Install Libation on macOS - **[Docker Installation](/docs/installation/docker)** - Run Libation in a Docker container diff --git a/docs/installation/linux.md b/docs/installation/linux.md index 8dad6388..5a97c53d 100644 --- a/docs/installation/linux.md +++ b/docs/installation/linux.md @@ -6,6 +6,16 @@ New Libation releases are automatically packed into `.deb` and `.rpm` package and are available from the [Libation repository's releases page](https://github.com/rmcrackan/Libation/releases). +## Snap + +If you install Libation from [Snapcraft](https://snapcraft.io/) (or another Snap channel), data lives under your home directory in versioned folders, for example `~/snap/libation/177/.local/share/Libation/`. Snap keeps a `current` symlink that points at the active revision. Each refresh can add a new numeric folder while copying config into it. + +`appsettings.json` in that tree contains a `LibationFiles` string. It must point at the **same** revision directory as the `appsettings.json` file you are editing. If it still says an older path (for example `.../snap/libation/174/...` while you run from `177`), Libation may try to open the database on the old path and fail with a read-only or migration error even when file permissions look correct. + +**What to do:** open `appsettings.json` next to the running install (under `~/snap/libation/current/...` or the new revision folder), set `LibationFiles` to the Libation data directory **inside that same revision**, save, and start Libation again. Alternatively set the `LIBATION_FILES_DIR` environment variable to a directory you control (see advanced docs). A user-written walkthrough of this situation is in [GitHub issue #1776](https://github.com/rmcrackan/Libation/issues/1776). + +For removable drives and Snap interfaces, your distro's Snap docs apply. Libation also documents common Linux problems in [Troubleshooting](/docs/advanced/troubleshoot). + ## Runtime dependencies (Audible sign-in) The Chardonnay desktop build can log into Audible inside the app when the setting to use Libation's built-in web browser for sign-in is enabled (Import / library settings). On Linux that embedded flow uses WebKit2GTK; the native library is usually exposed as `libwebkit2gtk` (exact package names vary by distro). It is required for in-app OAuth when adding an account or signing in again through that path.