about summary refs log tree commit diff stats
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--modules/by-name/lf/lf/ctpv/default.nix458
-rw-r--r--modules/by-name/lf/lf/ctpv/helpers.sh23
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/any.sh81
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/application/archive/atool.sh (renamed from modules/by-name/lf/lf/ctpv/prev/atool.sh)0
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/application/archive/default.nix89
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/application/dll/default.nix14
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/application/dll/dll.sh19
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/application/epub/default.nix14
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/application/epub/epub.sh27
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/application/pdf/default.nix12
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/application/pdf/pdf.sh (renamed from modules/by-name/lf/lf/ctpv/prev/pdf.sh)0
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/application/pgp/default.nix17
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/application/pgp/pgp.sh13
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/application/sqlite/default.nix13
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/application/sqlite/sqlite.sh (renamed from modules/by-name/lf/lf/ctpv/prev/bat.sh)9
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/application/x-bittorrent/default.nix11
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/application/x-bittorrent/torrent.sh (renamed from modules/by-name/lf/lf/ctpv/prev/torrent.sh)0
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/audio/audio.sh (renamed from modules/by-name/lf/lf/ctpv/prev/audio.sh)0
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/audio/default.nix12
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/default.nix23
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/font/default.nix17
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/font/font.sh (renamed from modules/by-name/lf/lf/ctpv/prev/font.sh)0
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/image/default.nix11
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/image/image.sh (renamed from modules/by-name/lf/lf/ctpv/prev/image.sh)0
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/image/svg+xml/default.nix12
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/image/svg+xml/svg.sh (renamed from modules/by-name/lf/lf/ctpv/prev/svg.sh)2
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/image/x-xcf/default.nix13
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/image/x-xcf/xcf.sh (renamed from modules/by-name/lf/lf/ctpv/prev/xcf.sh)0
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/inode/default.nix16
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/inode/ls.sh (renamed from modules/by-name/lf/lf/ctpv/prev/ls.sh)0
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/inode/symlink.sh (renamed from modules/by-name/lf/lf/ctpv/prev/symlink.sh)0
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/remove_default_previews.nix30
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/text/bat.sh (renamed from modules/by-name/lf/lf/ctpv/prev/gpg.sh)2
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/text/default.nix21
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/text/diff/default.nix12
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/text/diff/delta.sh (renamed from modules/by-name/lf/lf/ctpv/prev/delta.sh)0
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/text/glow.sh (renamed from modules/by-name/lf/lf/ctpv/prev/glow.sh)0
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/text/html/default.nix13
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/text/html/elinks.sh (renamed from modules/by-name/lf/lf/ctpv/prev/elinks.sh)0
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/text/json/default.nix12
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/text/json/jq.sh (renamed from modules/by-name/lf/lf/ctpv/prev/jq.sh)0
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/video/default.nix11
-rw-r--r--modules/by-name/lf/lf/ctpv/prev/video/video.sh (renamed from modules/by-name/lf/lf/ctpv/prev/video.sh)0
-rw-r--r--modules/by-name/lf/lf/module.nix5
44 files changed, 663 insertions, 349 deletions
diff --git a/modules/by-name/lf/lf/ctpv/default.nix b/modules/by-name/lf/lf/ctpv/default.nix
index f61b65f1..e89ae453 100644
--- a/modules/by-name/lf/lf/ctpv/default.nix
+++ b/modules/by-name/lf/lf/ctpv/default.nix
@@ -2,101 +2,68 @@
   pkgs,
   sysLib,
   lib,
+  config,
   ...
 }: let
   functionCall = {
     name,
     dependencies,
     replacementStrings,
+    scriptPath,
     ...
   }:
     sysLib.writeShellScript {
       inherit name;
-      src = ./prev/${name}.sh;
+      src = scriptPath;
       keepPath = false;
-      dependencies =
-        dependencies
-        ++ (
-          with pkgs; [
-            dash
-            coreutils
-            # Dependencies of the helpers.sh script
-            chafa
-            gnused
-            gnugrep
-            unixtools.script
-          ]
-        );
-      inherit replacementStrings;
+      inherit replacementStrings dependencies;
     }
     + "/bin/${name}";
 
-  default_previews = [
-    "any"
-    "atool"
-    "audio"
-    "bat"
-    "cat"
-    "colordiff"
-    "delta"
-    "diff_so_fancy"
-    "elinks"
-    "font"
-    "glow"
-    "gpg"
-    "highlight"
-    "image"
-    "jq"
-    "libreoffice"
-    "ls"
-    "lynx"
-    "mdcat"
-    "pdf"
-    "source_highlight"
-    "svg"
-    "symlink"
-    "torrent"
-    "video"
-    "w3m"
-  ];
-
   mkPreview = name: {
-    escape ? true,
-    mime_matches ? [],
-    extension_matches ? [],
-    priority ? 0,
-    dependencies ? [],
-    replacementStrings ? {},
+    matches,
+    priority,
+    dependencies,
+    replacementStrings,
+    previewer,
   }: let
     mkMimePath = val: let
       split = lib.strings.splitString "/" val;
       rhs = builtins.elemAt split 0;
       lhs = builtins.elemAt split 1;
+
+      e_rhs =
+        if rhs == "*"
+        then rhs
+        else "\"${rhs}\"";
+      e_lhs =
+        if lhs == "*"
+        then lhs
+        else "\"${lhs}\"";
     in
-      assert builtins.length split == 2; ''"${rhs}"/"${lhs}"'';
+      assert builtins.length split == 2; "${e_rhs}/${e_lhs}";
 
-    extensions = lib.strings.concatStringsSep " " (builtins.map (val: ''."${val}"'') extension_matches);
-    mimes = lib.strings.concatStringsSep " " (
-      if escape
-      then builtins.map mkMimePath mime_matches
-      else mime_matches
-    );
+    extensions = lib.strings.concatStringsSep " " (builtins.map (val: ''."${val}"'') matches.extension);
+    mimes = lib.strings.concatStringsSep " " (builtins.map mkMimePath matches.mime);
     function = functionCall {
-      inherit name dependencies;
-      replacementStrings = {HELPERS = ./helpers.sh;} // replacementStrings;
-    };
-  in
-    lib.optionalString (builtins.elem name default_previews) "remove ${name}\n"
-    + ''
-      preview ${name} ${extensions} ${mimes} {{
-        ${function}
-
-        # Always exit with success, when the function didn't exit,
-        # to ensure that error messages come through
-        true
-      }}
-      priority ${name} ${builtins.toString priority}
-    '';
+      inherit
+        name
+        dependencies
+        replacementStrings
+        ;
+      scriptPath = previewer;
+    };
+  in ''
+    preview ${name} ${extensions} ${mimes} {{
+      # When the function exits with code 127 (no command found), ctpv will treat it as
+      # signal to contiue with the next previewer and hide this failure.
+      # As we wrap them in their dependencies, they should never fail and as such we want
+      # to know about the problem.
+
+      ${function} || exit 1
+    }}
+    priority ${name} ${builtins.toString priority}
+  '';
 
   mkRemove = name: ''
     remove ${name}
@@ -110,226 +77,131 @@
         then mkRemove name
         else mkPreview name value)
       attrs));
-in
-  mkConfigFile
-  {
-    # Remove all the default ones.
-    "cat" = null;
-    "colordiff" = null;
-    "diff_so_fancy" = null;
-    "highlight" = null;
-    "lynx" = null;
-    "mdcat" = null;
-    "source_highlight" = null;
-    "w3m" = null;
 
-    any = {
-      escape = false;
-      mime_matches = ["*/*"];
-      priority = -1;
-      dependencies = [
-        pkgs.exiftool
-        pkgs.tinyxxd # For xxd
-      ];
-    };
+  previewSubmodule = lib.types.submodule {
+    options = {
+      matches = {
+        mime = lib.mkOption {
+          type = lib.types.listOf (lib.types.strMatching "([a-z0-9A-Z_-]+|\\*)/([a-z0-9A-Z_.+-]+|\\*)");
+          default = [];
+          description = "All mime types to match.";
+        };
+        extension = lib.mkOption {
+          type = lib.types.listOf (lib.types.strMatching "[a-z0-9A-Z].?+");
+          default = [];
+          description = "All extensions to match.";
+        };
+      };
+
+      previewer = lib.mkOption {
+        type = lib.types.pathInStore;
+        description = "The path to the preview script or binary";
+      };
+
+      priority = lib.mkOption {
+        # Priority is a c int in ctpv, so a 16 bits should be enough.
+        type = lib.types.ints.s16;
+        default = 0;
+        description = "The priority to use this previewer.";
+      };
+      replacementStrings = lib.mkOption {
+        type = lib.types.attrsOf (lib.types.either lib.types.str lib.types.pathInStore);
+        default = {
+          HELPERS = ./helpers.sh;
+        };
+        description = "Arbitrary strings to replace in the shell script.";
+      };
+      dependencies = lib.mkOption {
+        type = lib.types.listOf lib.types.package;
+        default = with pkgs; [
+          dash
+          coreutils
+          # Dependencies of the helpers.sh script
+          chafa
+          gnused
+          gnugrep
+          unixtools.script
+        ];
+        description = "The packages to provide to the script";
+      };
+    };
+  };
+
+  other_files = filter_nulls (import_dir ./prev);
+
+  filter_nulls = input:
+    builtins.concatMap (val:
+      if val == null
+      then []
+      else if builtins.isList val
+      then filter_nulls val
+      else [val])
+    input;
+
+  import_dir = path:
+    lib.attrsets.mapAttrsToList (file: type:
+      if type == "directory"
+      then import_dir "${path}/${file}"
+      else if builtins.match "[^.]+\\.nix" file != null
+      then "${path}/${file}"
+      else null) (builtins.readDir path);
+
+  cfg = config.soispha.programs.lf;
+in {
+  imports = other_files;
+
+  options.soispha.programs.lf.ctpv = {
+    xdgDataHome = lib.mkOption {
+      description = ''
+        The directory to treat as the XDG_DATA_HOME.
+      '';
+      type = lib.types.str;
+      default = config.home-manager.users.soispha.xdg.dataHome;
+    };
+
+    previewers = lib.mkOption {
+      description = ''
+        The previewers to add to the config file.
+      '';
+      type = lib.types.attrsOf (lib.types.nullOr previewSubmodule);
+      default = {};
+    };
+    extraConfigPre = lib.mkOption {
+      description = ''
+        A string that is passed first in the config file.
+      '';
+      type = lib.types.lines;
+      default = "";
+    };
+  };
+
+  config.home-manager.users.soispha = lib.mkIf cfg.enable {
+    xdg.configFile = {
+      "ctpv/config".text = cfg.ctpv.extraConfigPre + "\n" + (mkConfigFile cfg.ctpv.previewers);
+    };
+  };
+}
+# TODO: Add these. <2024-12-08>
+#     libreoffice = {
+#       extension_matches = [
+#         "csv"
+#         "doc"
+#         "docx"
+#         "fodp"
+#         "fods"
+#         "fodt"
+#         "odp"
+#         "ods"
+#         "odt"
+#         "ppt"
+#         "pptx"
+#         "xls"
+#         "xlsx"
+#       ];
+#       dependencies = [
+#         pkgs.libreoffice
+#       ];
+#     };
+#
+# )
 
-    audio = {
-      mime_matches = ["audio/*"];
-      dependencies = [
-        pkgs.ffmpegthumbnailer
-        pkgs.ffmpeg
-      ];
-    };
-
-    delta = {
-      priority = 1;
-      mime_matches = ["text/x-diff" "text/x-patch"];
-      dependencies = [
-        pkgs.delta
-      ];
-    };
-    # TODO: I might want to use lynx/w3m instead <2024-11-24>
-    elinks = {
-      priority = 1;
-      mime_matches = ["text/html"];
-      dependencies = [
-        pkgs.elinks
-      ];
-    };
-    bat = {
-      mime_matches = ["text/*"];
-      dependencies = [
-        pkgs.bat
-      ];
-    };
-
-    xcf = {
-      priority = 1;
-      mime_matches = ["image/x-xcf"];
-      dependencies = [
-        # pkgs.gimp
-        pkgs.exiftool
-      ];
-    };
-    svg = {
-      priority = 1;
-      mime_matches = ["image/svg+xml"];
-      dependencies = [
-        pkgs.imagemagick
-      ];
-    };
-    image = {
-      mime_matches = ["image/*"];
-      dependencies = [
-      ];
-    };
-
-    jq = {
-      priority = 1;
-      mime_matches = ["application/json"];
-      dependencies = [
-        pkgs.jq
-      ];
-    };
-    pdf = {
-      priority = 1;
-      mime_matches = ["application/pdf"];
-      dependencies = [
-        pkgs.poppler_utils # for `pdftoppm`
-      ];
-    };
-
-    ls = {
-      priority = 1;
-      mime_matches = ["inode/directory"];
-      dependencies = [];
-    };
-    symlink = {
-      priority = 1;
-      mime_matches = ["inode/symlink"];
-      dependencies = [];
-    };
-
-    font = {
-      mime_matches = ["font/*"];
-      dependencies = [
-        pkgs.fontforge # for `fontimage`
-      ];
-    };
-    video = {
-      mime_matches = ["video/*"];
-      dependencies = [
-        pkgs.ffmpegthumbnailer
-      ];
-    };
-
-    libreoffice = {
-      extension_matches = [
-        "csv"
-        "doc"
-        "docx"
-        "fodp"
-        "fods"
-        "fodt"
-        "odp"
-        "ods"
-        "odt"
-        "ppt"
-        "pptx"
-        "xls"
-        "xlsx"
-      ];
-      dependencies = [
-        pkgs.libreoffice
-      ];
-    };
-
-    atool = {
-      extension_matches = [
-        "7z"
-        "Z"
-        "a"
-        "ace"
-        "alz"
-        "arc"
-        "arj"
-        "bz"
-        "bz2"
-        "cab"
-        "cpio"
-        "deb"
-        "gz"
-        "jar"
-        "lha"
-        "lrz"
-        "lz"
-        "lzh"
-        "lzma"
-        "lzo"
-        "rar"
-        "rpm"
-        "rz"
-        "t7z"
-        "tZ"
-        "tar"
-        "tar.7z"
-        "tar.Z"
-        "tar.bz"
-        "tar.bz2"
-        "tar.gz"
-        "tar.lz"
-        "tar.lzo"
-        "tar.xz"
-        "tbz"
-        "tbz2"
-        "tgz"
-        "tlz"
-        "txz"
-        "tzo"
-        "war"
-        "xz"
-        "zip"
-      ];
-      dependencies = [
-        pkgs.atool
-
-        # archive tools
-        pkgs.file
-        pkgs.gnutar
-        pkgs.gzip
-        pkgs.pbzip2
-        pkgs.plzip
-        pkgs.lzop
-        pkgs.xz
-        pkgs.zip
-        pkgs.unzip
-        pkgs.arj
-        pkgs.rpm
-        pkgs.cpio
-        pkgs.archiver # for arc
-        pkgs.p7zip
-
-        # Unfree stuff
-        # pkgs.rar
-        # pkgs.lha
-      ];
-    };
-    glow = {
-      extension_matches = ["md"];
-      dependencies = [
-        pkgs.glow
-      ];
-    };
-    gpg = {
-      extension_matches = ["gpg" "asc" "key" "pgp" "sig"];
-      dependencies = [
-        pkgs.sequoia-sq
-      ];
-    };
-    torrent = {
-      extension_matches = ["torrent"];
-      dependencies = [
-        pkgs.transmission_4 # for `transmission-show`
-      ];
-    };
-  }
diff --git a/modules/by-name/lf/lf/ctpv/helpers.sh b/modules/by-name/lf/lf/ctpv/helpers.sh
index dacda766..c28defce 100644
--- a/modules/by-name/lf/lf/ctpv/helpers.sh
+++ b/modules/by-name/lf/lf/ctpv/helpers.sh
@@ -32,10 +32,6 @@ convert_and_show_image() {
     send_image "$cache_f"
 }
 
-isBinary() {
-    LC_ALL=C LC_MESSAGES=C grep --with-filename --max-count=1 '^' "$1" 2>&1 | grep --quiet 'binary file matches$'
-}
-
 hide_script_env() {
     cmd=""
     for part in "$@"; do
@@ -48,3 +44,22 @@ hide_script_env() {
 
     script --command "$cmd" --quiet --return /dev/null
 }
+
+preview_bat() {
+    bat \
+        --color always \
+        --style plain \
+        --paging never \
+        --terminal-width "$w" \
+        --wrap character \
+        -- "$1"
+}
+
+preview_xxd() {
+    # This has been derived mathematically.
+    octet_columns=$(((2 * w - 22) / 7))
+
+    # -R: colorization
+    # -u: Uppercase letters for hexadecimal
+    xxd -R always -u -cols "$octet_columns" -len "$((h * octet_columns))" -- "$1"
+}
diff --git a/modules/by-name/lf/lf/ctpv/prev/any.sh b/modules/by-name/lf/lf/ctpv/prev/any.sh
index bb25d90b..ecb18e02 100644
--- a/modules/by-name/lf/lf/ctpv/prev/any.sh
+++ b/modules/by-name/lf/lf/ctpv/prev/any.sh
@@ -5,54 +5,43 @@ SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH
 
 # shellcheck disable=SC2269
 f="$f"
+# shellcheck disable=SC2269
+w="$w"
+# shellcheck disable=SC2269
+e="$e"
+# shellcheck disable=SC2269
+m="$m"
+# shellcheck disable=SC2269
+h="$h"
+
+# shellcheck disable=SC2269
+extension="$e"
+# shellcheck disable=SC2269
+mime="$m"
 
 . %HELPERS
 
-# FIXME: Fix the problem, that `???.log.gpg` won't trigger the gpg
-# preview, as `cptv` thinks, the extension is `log.gpg` <2024-11-24>
-#
-# # Before running things on the last extension, ctpv tries to run a preview on the full
-# # extension. Therefore, we need to filter these out, to allow the real display to run on
-# # them.
-#
-# full_extension="${f#*.}"
-#
-# # contains(string, substring)
-# #
-# # Returns 0 if the specified string contains the specified substring,
-# # otherwise returns 1.
-# contains() {
-#     string="$1"
-#     substring="$2"
-#     if [ "${string#*"$substring"}" != "$string" ]; then
-#         return 0 # $substring is in $string
-#     else
-#         return 1 # $substring is not in $string
-#     fi
-# }
-#
-# if contains "$full_extension" "."; then
-#     sub_extension="${full_extension#*.}"
-#     base_extension="${full_extension%.*}"
-#
-#     case "$base_extension" in
-#     "tar")
-#         # delegate to the archive tool
-#         exit 127
-#         ;;
-#     esac
-#
-#     case "$sub_extension" in
-#     "gpg" | "sig" | "key" | "cert" | "asc")
-#         # delegate to the gpg tool
-#         exit 127
-#         ;;
-#     esac
-# fi
-
-if exiftool "$f" >/dev/null 2>&1; then
-    exiftool -- "$f"
+is_not_printable() {
+    grep --text --quiet '[^[:print:]]' "$1"
+}
+
+case "$mime" in
+"application/octet-stream")
+    # Ignore this mime type in the warning, as can't really find a good way to preview it.
+    ;;
+
+*)
+    echo "(ctpv did not recognize this file, with extension: '$extension' and mime: '$mime')"
+
+    directory_storage="%STORAGE_DIRECTORY/$mime"
+    mkdir --parents "$(dirname "%STORAGE_DIRECTORY/$mime")"
+
+    printf "%s -- %s\n" "$f" "$extension" >>"$directory_storage"
+    ;;
+esac
+
+if is_not_printable "$f"; then
+    perview_xxd "$f"
 else
-    # `exiftool` did not recognize the file.
-    hide_script_env xxd -- "$f"
+    preview_bat "$f"
 fi
diff --git a/modules/by-name/lf/lf/ctpv/prev/atool.sh b/modules/by-name/lf/lf/ctpv/prev/application/archive/atool.sh
index 5f4baac7..5f4baac7 100644
--- a/modules/by-name/lf/lf/ctpv/prev/atool.sh
+++ b/modules/by-name/lf/lf/ctpv/prev/application/archive/atool.sh
diff --git a/modules/by-name/lf/lf/ctpv/prev/application/archive/default.nix b/modules/by-name/lf/lf/ctpv/prev/application/archive/default.nix
new file mode 100644
index 00000000..befed06c
--- /dev/null
+++ b/modules/by-name/lf/lf/ctpv/prev/application/archive/default.nix
@@ -0,0 +1,89 @@
+{pkgs, ...}: {
+  soispha.programs.lf.ctpv.previewers = {
+    atool = {
+      previewer = ./atool.sh;
+      matches.mime = [
+        "application/gzip"
+        "application/java-archive"
+        "application/vnd.debian.binary-package"
+        "application/x-7z-compressed"
+        "application/x-bzip2"
+        "application/x-rar"
+        "application/x-tar"
+        "application/x-xz"
+        "application/zip"
+        "application/zlib"
+      ];
+
+      matches.extension = [
+        "7z"
+        "Z"
+        "a"
+        "ace"
+        "alz"
+        "arc"
+        "arj"
+        "bz"
+        "bz2"
+        "cab"
+        "cpio"
+        "deb"
+        "gz"
+        "jar"
+        "lha"
+        "lrz"
+        "lz"
+        "lzh"
+        "lzma"
+        "lzo"
+        "rar"
+        "rpm"
+        "rz"
+        "t7z"
+        "tZ"
+        "tar"
+        "tar.7z"
+        "tar.Z"
+        "tar.bz"
+        "tar.bz2"
+        "tar.gz"
+        "tar.lz"
+        "tar.lzo"
+        "tar.xz"
+        "tbz"
+        "tbz2"
+        "tgz"
+        "tlz"
+        "txz"
+        "tzo"
+        "war"
+        "xz"
+        "zip"
+      ];
+      dependencies = [
+        pkgs.atool
+
+        # archive tools
+        pkgs.archiver # for arc
+        pkgs.arj
+        pkgs.cpio
+        pkgs.dpkg
+        pkgs.file
+        pkgs.gnutar
+        pkgs.gzip
+        pkgs.lzop
+        pkgs.p7zip
+        pkgs.pbzip2
+        pkgs.plzip
+        pkgs.rpm
+        pkgs.unzip
+        pkgs.xz
+        pkgs.zip
+
+        # Unfree stuff
+        # pkgs.lha
+        # pkgs.rar
+      ];
+    };
+  };
+}
diff --git a/modules/by-name/lf/lf/ctpv/prev/application/dll/default.nix b/modules/by-name/lf/lf/ctpv/prev/application/dll/default.nix
new file mode 100644
index 00000000..00c7e389
--- /dev/null
+++ b/modules/by-name/lf/lf/ctpv/prev/application/dll/default.nix
@@ -0,0 +1,14 @@
+{pkgs, ...}: {
+  soispha.programs.lf.ctpv.previewers = {
+    dll = {
+      previewer = ./dll.sh;
+      matches.mime = [
+        "application/vnd.microsoft.portable-executable"
+      ];
+      priority = 1;
+      dependencies = [
+        pkgs.tinyxxd
+      ];
+    };
+  };
+}
diff --git a/modules/by-name/lf/lf/ctpv/prev/application/dll/dll.sh b/modules/by-name/lf/lf/ctpv/prev/application/dll/dll.sh
new file mode 100644
index 00000000..678506eb
--- /dev/null
+++ b/modules/by-name/lf/lf/ctpv/prev/application/dll/dll.sh
@@ -0,0 +1,19 @@
+#! /usr/bin/env dash
+
+# shellcheck source=/dev/null
+SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH
+
+# shellcheck disable=SC2269
+f="$f"
+# shellcheck disable=SC2269
+w="$w"
+# shellcheck disable=SC2269
+e="$e"
+# shellcheck disable=SC2269
+m="$m"
+# shellcheck disable=SC2269
+h="$h"
+
+. %HELPERS
+
+preview_xxd "$f"
diff --git a/modules/by-name/lf/lf/ctpv/prev/application/epub/default.nix b/modules/by-name/lf/lf/ctpv/prev/application/epub/default.nix
new file mode 100644
index 00000000..33c51352
--- /dev/null
+++ b/modules/by-name/lf/lf/ctpv/prev/application/epub/default.nix
@@ -0,0 +1,14 @@
+{pkgs, ...}: {
+  soispha.programs.lf.ctpv.previewers = {
+    epub = {
+      previewer = ./epub.sh;
+      matches.mime = ["application/epub+zip"];
+      matches.extension = ["epub"];
+      priority = 1;
+      dependencies = with pkgs; [
+        bk
+        epub-thumbnailer
+      ];
+    };
+  };
+}
diff --git a/modules/by-name/lf/lf/ctpv/prev/application/epub/epub.sh b/modules/by-name/lf/lf/ctpv/prev/application/epub/epub.sh
new file mode 100644
index 00000000..703e7dad
--- /dev/null
+++ b/modules/by-name/lf/lf/ctpv/prev/application/epub/epub.sh
@@ -0,0 +1,27 @@
+#! /usr/bin/env dash
+
+# shellcheck source=/dev/null
+SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH
+
+# shellcheck disable=SC2269
+f="$f"
+# shellcheck disable=SC2269
+w="$w"
+# shellcheck disable=SC2269
+e="$e"
+# shellcheck disable=SC2269
+m="$m"
+# shellcheck disable=SC2269
+h="$h"
+# shellcheck disable=SC2269
+cache_f="$cache_f"
+
+. %HELPERS
+
+epub() {
+    epub-thumbnailer "$f" "$cache_f" 20000
+}
+
+convert_and_show_image epub
+
+bk --meta "$f"
diff --git a/modules/by-name/lf/lf/ctpv/prev/application/pdf/default.nix b/modules/by-name/lf/lf/ctpv/prev/application/pdf/default.nix
new file mode 100644
index 00000000..b27e3ef9
--- /dev/null
+++ b/modules/by-name/lf/lf/ctpv/prev/application/pdf/default.nix
@@ -0,0 +1,12 @@
+{pkgs, ...}: {
+  soispha.programs.lf.ctpv.previewers = {
+    pdf = {
+      previewer = ./pdf.sh;
+      matches.mime = ["application/pdf"];
+      priority = 1;
+      dependencies = [
+        pkgs.poppler_utils # for `pdftoppm`
+      ];
+    };
+  };
+}
diff --git a/modules/by-name/lf/lf/ctpv/prev/pdf.sh b/modules/by-name/lf/lf/ctpv/prev/application/pdf/pdf.sh
index 2f807b1a..2f807b1a 100644
--- a/modules/by-name/lf/lf/ctpv/prev/pdf.sh
+++ b/modules/by-name/lf/lf/ctpv/prev/application/pdf/pdf.sh
diff --git a/modules/by-name/lf/lf/ctpv/prev/application/pgp/default.nix b/modules/by-name/lf/lf/ctpv/prev/application/pgp/default.nix
new file mode 100644
index 00000000..c219a1ed
--- /dev/null
+++ b/modules/by-name/lf/lf/ctpv/prev/application/pgp/default.nix
@@ -0,0 +1,17 @@
+{pkgs, ...}: {
+  soispha.programs.lf.ctpv.previewers = {
+    pgp = {
+      previewer = ./pgp.sh;
+      matches.extension = ["gpg" "asc" "key" "pgp" "sig"];
+      matches.mime = [
+        "application/pgp-encrypted"
+        "application/pgp-keys"
+        "application/pgp-signature"
+      ];
+      priority = 1;
+      dependencies = [
+        pkgs.sequoia-sq
+      ];
+    };
+  };
+}
diff --git a/modules/by-name/lf/lf/ctpv/prev/application/pgp/pgp.sh b/modules/by-name/lf/lf/ctpv/prev/application/pgp/pgp.sh
new file mode 100644
index 00000000..a4eefd96
--- /dev/null
+++ b/modules/by-name/lf/lf/ctpv/prev/application/pgp/pgp.sh
@@ -0,0 +1,13 @@
+#! /usr/bin/env dash
+
+# shellcheck source=/dev/null
+SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH
+
+# shellcheck disable=SC2269
+f="$f"
+# shellcheck disable=SC2269
+w="$w"
+
+. %HELPERS
+
+hide_script_env sq inspect --certifications -- "$f"
diff --git a/modules/by-name/lf/lf/ctpv/prev/application/sqlite/default.nix b/modules/by-name/lf/lf/ctpv/prev/application/sqlite/default.nix
new file mode 100644
index 00000000..54b8c29e
--- /dev/null
+++ b/modules/by-name/lf/lf/ctpv/prev/application/sqlite/default.nix
@@ -0,0 +1,13 @@
+{pkgs, ...}: {
+  soispha.programs.lf.ctpv.previewers = {
+    sqlite = {
+      previewer = ./sqlite.sh;
+      matches.mime = ["application/vnd.sqlite3"];
+      priority = 1;
+      dependencies = [
+        pkgs.sqlite
+        pkgs.bat
+      ];
+    };
+  };
+}
diff --git a/modules/by-name/lf/lf/ctpv/prev/bat.sh b/modules/by-name/lf/lf/ctpv/prev/application/sqlite/sqlite.sh
index 760ad50e..07e77a93 100644
--- a/modules/by-name/lf/lf/ctpv/prev/bat.sh
+++ b/modules/by-name/lf/lf/ctpv/prev/application/sqlite/sqlite.sh
@@ -10,10 +10,13 @@ w="$w"
 
 . %HELPERS
 
-bat \
+echo "SQLite database. Schema:"
+echo
+
+sqlite3 "$f" ".schema --indent" | bat \
     --color always \
     --style plain \
     --paging never \
+    --language SQL \
     --terminal-width "$w" \
-    --wrap character \
-    -- "$f"
+    --wrap character
diff --git a/modules/by-name/lf/lf/ctpv/prev/application/x-bittorrent/default.nix b/modules/by-name/lf/lf/ctpv/prev/application/x-bittorrent/default.nix
new file mode 100644
index 00000000..504623a0
--- /dev/null
+++ b/modules/by-name/lf/lf/ctpv/prev/application/x-bittorrent/default.nix
@@ -0,0 +1,11 @@
+{pkgs, ...}: {
+  soispha.programs.lf.ctpv.previewers = {
+    torrent = {
+      previewer = ./torrent.sh;
+      matches.mime = ["application/x-bittorrent"];
+      dependencies = [
+        pkgs.transmission_4 # for `transmission-show`
+      ];
+    };
+  };
+}
diff --git a/modules/by-name/lf/lf/ctpv/prev/torrent.sh b/modules/by-name/lf/lf/ctpv/prev/application/x-bittorrent/torrent.sh
index 16cfcbcd..16cfcbcd 100644
--- a/modules/by-name/lf/lf/ctpv/prev/torrent.sh
+++ b/modules/by-name/lf/lf/ctpv/prev/application/x-bittorrent/torrent.sh
diff --git a/modules/by-name/lf/lf/ctpv/prev/audio.sh b/modules/by-name/lf/lf/ctpv/prev/audio/audio.sh
index c5abc646..c5abc646 100644
--- a/modules/by-name/lf/lf/ctpv/prev/audio.sh
+++ b/modules/by-name/lf/lf/ctpv/prev/audio/audio.sh
diff --git a/modules/by-name/lf/lf/ctpv/prev/audio/default.nix b/modules/by-name/lf/lf/ctpv/prev/audio/default.nix
new file mode 100644
index 00000000..97731daf
--- /dev/null
+++ b/modules/by-name/lf/lf/ctpv/prev/audio/default.nix
@@ -0,0 +1,12 @@
+{pkgs, ...}: {
+  soispha.programs.lf.ctpv.previewers = {
+    audio = {
+      previewer = ./audio.sh;
+      matches.mime = ["audio/*"];
+      dependencies = [
+        pkgs.ffmpegthumbnailer
+        pkgs.ffmpeg
+      ];
+    };
+  };
+}
diff --git a/modules/by-name/lf/lf/ctpv/prev/default.nix b/modules/by-name/lf/lf/ctpv/prev/default.nix
new file mode 100644
index 00000000..d8ab36c4
--- /dev/null
+++ b/modules/by-name/lf/lf/ctpv/prev/default.nix
@@ -0,0 +1,23 @@
+{
+  pkgs,
+  config,
+  ...
+}: let
+  cfg = config.soispha.programs.lf;
+in {
+  soispha.programs.lf.ctpv.previewers = {
+    any = {
+      previewer = ./any.sh;
+      mime_matches = ["*/*"];
+      priority = -1;
+      replacementStrings = {
+        STORAGE_DIRECTORY = "${cfg.ctpv.xdgDataHome}/ctpv/missing_previews";
+      };
+
+      dependencies = [
+        pkgs.tinyxxd # For xxd
+        pkgs.bat
+      ];
+    };
+  };
+}
diff --git a/modules/by-name/lf/lf/ctpv/prev/font/default.nix b/modules/by-name/lf/lf/ctpv/prev/font/default.nix
new file mode 100644
index 00000000..f5301008
--- /dev/null
+++ b/modules/by-name/lf/lf/ctpv/prev/font/default.nix
@@ -0,0 +1,17 @@
+{pkgs, ...}: {
+  soispha.programs.lf.ctpv.previewers = {
+    font = {
+      previewer = ./font.sh;
+      matches.mime = [
+        "font/*"
+        "application/vnd.ms-opentype"
+
+        # TODO: This should be added (ext: 'eot') <2024-12-04>
+        # "application/vnd.ms-fontobject"
+      ];
+      dependencies = [
+        pkgs.fontforge # for `fontimage`
+      ];
+    };
+  };
+}
diff --git a/modules/by-name/lf/lf/ctpv/prev/font.sh b/modules/by-name/lf/lf/ctpv/prev/font/font.sh
index 4065557e..4065557e 100644
--- a/modules/by-name/lf/lf/ctpv/prev/font.sh
+++ b/modules/by-name/lf/lf/ctpv/prev/font/font.sh
diff --git a/modules/by-name/lf/lf/ctpv/prev/image/default.nix b/modules/by-name/lf/lf/ctpv/prev/image/default.nix
new file mode 100644
index 00000000..36700fec
--- /dev/null
+++ b/modules/by-name/lf/lf/ctpv/prev/image/default.nix
@@ -0,0 +1,11 @@
+{pkgs, ...}: {
+  soispha.programs.lf.ctpv.previewers = {
+    image = {
+      previewer = ./image.sh;
+      matches.mime = ["image/*"];
+      dependencies = [
+        # Everything is already in the default dependencies.
+      ];
+    };
+  };
+}
diff --git a/modules/by-name/lf/lf/ctpv/prev/image.sh b/modules/by-name/lf/lf/ctpv/prev/image/image.sh
index b5b97668..b5b97668 100644
--- a/modules/by-name/lf/lf/ctpv/prev/image.sh
+++ b/modules/by-name/lf/lf/ctpv/prev/image/image.sh
diff --git a/modules/by-name/lf/lf/ctpv/prev/image/svg+xml/default.nix b/modules/by-name/lf/lf/ctpv/prev/image/svg+xml/default.nix
new file mode 100644
index 00000000..5b965fd7
--- /dev/null
+++ b/modules/by-name/lf/lf/ctpv/prev/image/svg+xml/default.nix
@@ -0,0 +1,12 @@
+{pkgs, ...}: {
+  soispha.programs.lf.ctpv.previewers = {
+    svg = {
+      previewer = ./svg.sh;
+      priority = 1;
+      matches.mime = ["image/svg+xml"];
+      dependencies = [
+        pkgs.imagemagick
+      ];
+    };
+  };
+}
diff --git a/modules/by-name/lf/lf/ctpv/prev/svg.sh b/modules/by-name/lf/lf/ctpv/prev/image/svg+xml/svg.sh
index 0c82025f..04a06f56 100644
--- a/modules/by-name/lf/lf/ctpv/prev/svg.sh
+++ b/modules/by-name/lf/lf/ctpv/prev/image/svg+xml/svg.sh
@@ -11,7 +11,7 @@ cache_f="$cache_f"
 . %HELPERS
 
 svg() {
-    magick convert "$f" "jpg:$cache_f"
+    magick "$f" "jpg:$cache_f"
 }
 
 convert_and_show_image svg
diff --git a/modules/by-name/lf/lf/ctpv/prev/image/x-xcf/default.nix b/modules/by-name/lf/lf/ctpv/prev/image/x-xcf/default.nix
new file mode 100644
index 00000000..a3394ef4
--- /dev/null
+++ b/modules/by-name/lf/lf/ctpv/prev/image/x-xcf/default.nix
@@ -0,0 +1,13 @@
+{pkgs, ...}: {
+  soispha.programs.lf.ctpv.previewers = {
+    xcf = {
+      previewer = ./xcf.sh;
+      priority = 1;
+      matches.mime = ["image/x-xcf"];
+      dependencies = [
+        # pkgs.gimp
+        pkgs.exiftool
+      ];
+    };
+  };
+}
diff --git a/modules/by-name/lf/lf/ctpv/prev/xcf.sh b/modules/by-name/lf/lf/ctpv/prev/image/x-xcf/xcf.sh
index 1603e337..1603e337 100644
--- a/modules/by-name/lf/lf/ctpv/prev/xcf.sh
+++ b/modules/by-name/lf/lf/ctpv/prev/image/x-xcf/xcf.sh
diff --git a/modules/by-name/lf/lf/ctpv/prev/inode/default.nix b/modules/by-name/lf/lf/ctpv/prev/inode/default.nix
new file mode 100644
index 00000000..1261727e
--- /dev/null
+++ b/modules/by-name/lf/lf/ctpv/prev/inode/default.nix
@@ -0,0 +1,16 @@
+{ ...}: {
+  soispha.programs.lf.ctpv.previewers = {
+    ls = {
+      previewer = ./ls.sh;
+      priority = 1;
+      mime_matches = ["inode/directory"];
+      dependencies = [];
+    };
+    symlink = {
+      previewer = ./symlink.sh;
+      priority = 1;
+      mime_matches = ["inode/symlink"];
+      dependencies = [];
+    };
+  };
+}
diff --git a/modules/by-name/lf/lf/ctpv/prev/ls.sh b/modules/by-name/lf/lf/ctpv/prev/inode/ls.sh
index f73bd1c2..f73bd1c2 100644
--- a/modules/by-name/lf/lf/ctpv/prev/ls.sh
+++ b/modules/by-name/lf/lf/ctpv/prev/inode/ls.sh
diff --git a/modules/by-name/lf/lf/ctpv/prev/symlink.sh b/modules/by-name/lf/lf/ctpv/prev/inode/symlink.sh
index b30957d0..b30957d0 100644
--- a/modules/by-name/lf/lf/ctpv/prev/symlink.sh
+++ b/modules/by-name/lf/lf/ctpv/prev/inode/symlink.sh
diff --git a/modules/by-name/lf/lf/ctpv/prev/remove_default_previews.nix b/modules/by-name/lf/lf/ctpv/prev/remove_default_previews.nix
new file mode 100644
index 00000000..1407386b
--- /dev/null
+++ b/modules/by-name/lf/lf/ctpv/prev/remove_default_previews.nix
@@ -0,0 +1,30 @@
+{...}: {
+  soispha.programs.lf.ctpv.extraConfigPre = ''
+    remove any
+    remove atool
+    remove audio
+    remove bat
+    remove cat
+    remove colordiff
+    remove delta
+    remove diff_so_fancy
+    remove elinks
+    remove font
+    remove glow
+    remove gpg
+    remove highlight
+    remove image
+    remove jq
+    remove libreoffice
+    remove ls
+    remove lynx
+    remove mdcat
+    remove pdf
+    remove source_highlight
+    remove svg
+    remove symlink
+    remove torrent
+    remove video
+    remove w3m
+  '';
+}
diff --git a/modules/by-name/lf/lf/ctpv/prev/gpg.sh b/modules/by-name/lf/lf/ctpv/prev/text/bat.sh
index f162c565..be952aea 100644
--- a/modules/by-name/lf/lf/ctpv/prev/gpg.sh
+++ b/modules/by-name/lf/lf/ctpv/prev/text/bat.sh
@@ -10,4 +10,4 @@ w="$w"
 
 . %HELPERS
 
-hide_script_env sq inspect -- "$f"
+preview_bat "$f"
diff --git a/modules/by-name/lf/lf/ctpv/prev/text/default.nix b/modules/by-name/lf/lf/ctpv/prev/text/default.nix
new file mode 100644
index 00000000..14e74a4d
--- /dev/null
+++ b/modules/by-name/lf/lf/ctpv/prev/text/default.nix
@@ -0,0 +1,21 @@
+{pkgs, ...}: {
+  soispha.programs.lf.ctpv.previewers = {
+    glow = {
+      previewer = ./glow.sh;
+      priority = 1;
+      matches.extension = ["md"];
+      dependencies = [
+        pkgs.glow
+      ];
+    };
+
+    bat = {
+      priority = 0;
+      previewer = ./bat.sh;
+      matches.mime = ["text/*"];
+      dependencies = [
+        pkgs.bat
+      ];
+    };
+  };
+}
diff --git a/modules/by-name/lf/lf/ctpv/prev/text/diff/default.nix b/modules/by-name/lf/lf/ctpv/prev/text/diff/default.nix
new file mode 100644
index 00000000..e0fa15bb
--- /dev/null
+++ b/modules/by-name/lf/lf/ctpv/prev/text/diff/default.nix
@@ -0,0 +1,12 @@
+{pkgs, ...}: {
+  soispha.programs.lf.ctpv.previewers = {
+    delta = {
+      previewer = ./delta.sh;
+      priority = 1;
+      matches.mime = ["text/x-diff" "text/x-patch"];
+      dependencies = [
+        pkgs.delta
+      ];
+    };
+  };
+}
diff --git a/modules/by-name/lf/lf/ctpv/prev/delta.sh b/modules/by-name/lf/lf/ctpv/prev/text/diff/delta.sh
index 6a4e9a4e..6a4e9a4e 100644
--- a/modules/by-name/lf/lf/ctpv/prev/delta.sh
+++ b/modules/by-name/lf/lf/ctpv/prev/text/diff/delta.sh
diff --git a/modules/by-name/lf/lf/ctpv/prev/glow.sh b/modules/by-name/lf/lf/ctpv/prev/text/glow.sh
index 301fe675..301fe675 100644
--- a/modules/by-name/lf/lf/ctpv/prev/glow.sh
+++ b/modules/by-name/lf/lf/ctpv/prev/text/glow.sh
diff --git a/modules/by-name/lf/lf/ctpv/prev/text/html/default.nix b/modules/by-name/lf/lf/ctpv/prev/text/html/default.nix
new file mode 100644
index 00000000..2a7a9a9f
--- /dev/null
+++ b/modules/by-name/lf/lf/ctpv/prev/text/html/default.nix
@@ -0,0 +1,13 @@
+{pkgs, ...}: {
+  # TODO: I might want to use lynx/w3m instead <2024-11-24>
+  soispha.programs.lf.ctpv.previewers = {
+    elinks = {
+      previewer = ./elinks.sh;
+      priority = 1;
+      matches.mime = ["text/html"];
+      dependencies = [
+        pkgs.elinks
+      ];
+    };
+  };
+}
diff --git a/modules/by-name/lf/lf/ctpv/prev/elinks.sh b/modules/by-name/lf/lf/ctpv/prev/text/html/elinks.sh
index ca0de22e..ca0de22e 100644
--- a/modules/by-name/lf/lf/ctpv/prev/elinks.sh
+++ b/modules/by-name/lf/lf/ctpv/prev/text/html/elinks.sh
diff --git a/modules/by-name/lf/lf/ctpv/prev/text/json/default.nix b/modules/by-name/lf/lf/ctpv/prev/text/json/default.nix
new file mode 100644
index 00000000..7e2ef090
--- /dev/null
+++ b/modules/by-name/lf/lf/ctpv/prev/text/json/default.nix
@@ -0,0 +1,12 @@
+{pkgs, ...}: {
+  soispha.programs.lf.ctpv.previewers = {
+    jq = {
+      previewer = ./jq.sh;
+      priority = 1;
+      matches.mime = ["application/json"];
+      dependencies = [
+        pkgs.jq
+      ];
+    };
+  };
+}
diff --git a/modules/by-name/lf/lf/ctpv/prev/jq.sh b/modules/by-name/lf/lf/ctpv/prev/text/json/jq.sh
index bf807d1d..bf807d1d 100644
--- a/modules/by-name/lf/lf/ctpv/prev/jq.sh
+++ b/modules/by-name/lf/lf/ctpv/prev/text/json/jq.sh
diff --git a/modules/by-name/lf/lf/ctpv/prev/video/default.nix b/modules/by-name/lf/lf/ctpv/prev/video/default.nix
new file mode 100644
index 00000000..47749d2c
--- /dev/null
+++ b/modules/by-name/lf/lf/ctpv/prev/video/default.nix
@@ -0,0 +1,11 @@
+{pkgs, ...}: {
+  soispha.programs.lf.ctpv.previewers = {
+    video = {
+      previewer = ./video.sh;
+      matches.mime = ["video/*"];
+      dependencies = [
+        pkgs.ffmpegthumbnailer
+      ];
+    };
+  };
+}
diff --git a/modules/by-name/lf/lf/ctpv/prev/video.sh b/modules/by-name/lf/lf/ctpv/prev/video/video.sh
index e42e3612..e42e3612 100644
--- a/modules/by-name/lf/lf/ctpv/prev/video.sh
+++ b/modules/by-name/lf/lf/ctpv/prev/video/video.sh
diff --git a/modules/by-name/lf/lf/module.nix b/modules/by-name/lf/lf/module.nix
index 840ba492..bff2feb0 100644
--- a/modules/by-name/lf/lf/module.nix
+++ b/modules/by-name/lf/lf/module.nix
@@ -12,6 +12,10 @@
 
   cfg = config.soispha.programs.lf;
 in {
+  imports = [
+    ./ctpv
+  ];
+
   options.soispha.programs.lf = {
     enable = lib.mkEnableOption "lf";
     keymaps = {
@@ -43,7 +47,6 @@ in {
       xdg.configFile = {
         "lf/icons".source = ./icons;
         "lf/colors".source = ./colors;
-        "ctpv/config".text = import ./ctpv {inherit pkgs sysLib lib;};
       };
 
       programs.lf = {