Commit 067d50eb593e

Vincent Demeester <vincent@sbr.pm>
2026-09-10 14:50:21
feat: replaced Calibre with Liseur Sync
Replaced the unauthenticated ebook server with authenticated library and reading-state synchronization backed by the existing Calibre library. Signed-off-by: Vincent Demeester <vincent@sbr.pm>
1 parent d2b5610
Changed files (2)
systems/rhea/extra.nix
@@ -244,8 +244,8 @@ in
               altHosts = [ "t.sbr.pm" ];
             };
             immich.port = 2283;
-            calibre = {
-              port = 8083;
+            liseur = {
+              port = 8585;
               altHosts = [ "books.sbr.pm" ];
             };
             dav.port = 6065;
@@ -744,6 +744,7 @@ in
         "/var/lib/readarr" # Readarr database and config (~6MB)
         "/var/lib/prowlarr" # Prowlarr database and config
         "/var/lib/jellyfin" # Jellyfin database and config
+        "/var/lib/liseur-sync" # Liseur Sync database, accounts, and reading state
         # "/var/lib/immich" # Immich app data # Already handled in aion
         # "/var/lib/traefik" # Traefik acme.json (Let's Encrypt certs)
       ];
@@ -829,21 +830,33 @@ in
     $PSQL immich -tAc "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO vincent"
   '';
 
-  # Calibre Content Server for ebook library
-  systemd.services.calibre-server = {
-    description = "Calibre Content Server";
-    after = [ "network.target" ];
-    wantedBy = [ "multi-user.target" ];
-
-    serviceConfig = {
-      Type = "simple";
-      ExecStart = "${pkgs.calibre}/bin/calibre-server --port=8083 /zion/ebooks";
-      Restart = "on-failure";
-      User = "vincent";
-      Group = "users";
+  # Liseur Sync indexes the Calibre library and synchronizes reader state.
+  virtualisation.podman.enable = true;
+  virtualisation.oci-containers = {
+    backend = "podman";
+    containers.liseur-sync = {
+      image = "ghcr.io/chmouel/liseur-sync@sha256:5015b61a6ac32eea9fdaa641b15c9a6234a4700b9d2a8a07ffcba2485efdaf24";
+      pull = "newer";
+      environment = {
+        LISEUR_LISTEN_ADDR = "127.0.0.1:8585";
+        LISEUR_DATABASE_DRIVER = "sqlite";
+        LISEUR_DATABASE_URL = "/data/liseur-sync.db";
+        LISEUR_CACHE_DIR = "/data/cache";
+        LISEUR_FOLDER_ROOTS = "/books";
+        LISEUR_TRUSTED_PROXIES = "127.0.0.1/32,::1/128";
+      };
+      extraOptions = [ "--network=host" ];
+      volumes = [
+        "/var/lib/liseur-sync:/data"
+        "/zion/ebooks:/books:ro"
+      ];
     };
   };
 
+  systemd.tmpfiles.rules = [
+    "d /var/lib/liseur-sync 0750 65532 65532 -"
+  ];
+
   networking.useDHCP = lib.mkDefault true;
 
   # Open firewall for Traefik and NFS
globals.nix
@@ -778,9 +778,11 @@ _: {
     # Podcast and audiobook management on aion (routed through rhea/traefik)
     audiobookshelf.host = "rhea";
     podcasts.host = "rhea";
-    # Ebook library management on rhea
-    calibre.host = "rhea";
-    books.host = "rhea";
+    # Ebook library and reading-position synchronization on rhea
+    liseur = {
+      host = "rhea";
+      aliases = [ "books" ];
+    };
     # WebDAV on rhea
     dav.host = "rhea";
     # MQTT on demeter (routed through rhea/traefik)