aboutsummaryrefslogtreecommitdiffstats
path: root/modules/scripts/file-preview
diff options
context:
space:
mode:
Diffstat (limited to 'modules/scripts/file-preview')
-rw-r--r--modules/scripts/file-preview/assertions.nix19
-rw-r--r--modules/scripts/file-preview/default.nix36
-rw-r--r--modules/scripts/file-preview/script.sh142
3 files changed, 197 insertions, 0 deletions
diff --git a/modules/scripts/file-preview/assertions.nix b/modules/scripts/file-preview/assertions.nix
new file mode 100644
index 0000000..41fd4d6
--- /dev/null
+++ b/modules/scripts/file-preview/assertions.nix
@@ -0,0 +1,19 @@
+{ config, ... }:
+[
+ {
+ assertion = !config.scripts.file-preview.enable || config.terminal.kitty.enable;
+ message = "scripts.file-preview is enabled but requires `terminal.kitty.enable`.";
+ }
+ {
+ assertion = !config.scripts.file-preview.enable || config.media.ffmpeg.enable;
+ message = "scripts.file-preview is enabled but requires `media.ffmpeg.enable`.";
+ }
+ {
+ assertion = !config.scripts.file-preview.enable || config.file-management.ouch.enable;
+ message = "scripts.file-preview is enabled but requires `file-management.ouch.enable`.";
+ }
+ {
+ assertion = !config.scripts.file-preview.enable || config.terminal.bat.enable;
+ message = "scripts.file-preview is enabled but requires `terminal.bat.enable`.";
+ }
+]
diff --git a/modules/scripts/file-preview/default.nix b/modules/scripts/file-preview/default.nix
new file mode 100644
index 0000000..eec5fb2
--- /dev/null
+++ b/modules/scripts/file-preview/default.nix
@@ -0,0 +1,36 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+let
+ inherit ((import ../lib.nix { inherit lib pkgs; })) mkScriptModule;
+ base = mkScriptModule {
+ name = "file-preview";
+ path = ./script.sh;
+ description = "Timeout protection (prevents hanging)";
+ deps = [
+ config.terminal.kitty.package
+ config.terminal.bat.package
+ config.media.ffmpeg.package
+ config.file-management.ouch.package
+ pkgs.coreutils
+ pkgs.file
+ pkgs.findutils
+ pkgs.fontconfig
+ pkgs.librsvg
+ pkgs.antiword
+ pkgs.pandoc
+ pkgs.poppler-utils
+ pkgs.util-linux
+ ];
+ inherit config;
+ };
+in
+{
+ options = base.options;
+ config = base.moduleConfig // {
+ assertions = base.moduleConfig.assertions ++ (import ./assertions.nix { inherit config lib; });
+ };
+}
diff --git a/modules/scripts/file-preview/script.sh b/modules/scripts/file-preview/script.sh
new file mode 100644
index 0000000..748b1ea
--- /dev/null
+++ b/modules/scripts/file-preview/script.sh
@@ -0,0 +1,142 @@
+TIMEOUT=5
+if [ -z "$LF_PREVIEW_RUNNING" ]; then
+ export LF_PREVIEW_RUNNING=1
+ exec timeout "$TIMEOUT" "$0" "$@"
+fi
+
+# Parameters from lf
+FILE_PATH="$1"
+WIDTH="$2"
+HEIGHT="$3"
+X="$4"
+Y="$5"
+
+# Temp dir
+TMP_DIR="/tmp/file-preview-$(id -u)"
+mkdir -p "$TMP_DIR"
+
+# Unique temp file per actual file using inode
+INODE=$(stat -c '%i' "$FILE_PATH" 2>/dev/null || echo "0")
+
+# Render image via kitty
+render_image() {
+ local file="$1"
+ local tries=0
+ while [ ! -s "$file" ]; do
+ sleep 0.2
+ tries=$((tries + 1))
+ done
+ [ ! -s "$file" ] && exit 0
+ kitten icat --stdin no --transfer-mode file --place "${WIDTH}x${HEIGHT}@${X}x${Y}" "$file" </dev/null >/dev/tty
+ exit 1
+}
+
+# Ensure file exists
+[ ! -r "$FILE_PATH" ] && exit 0
+
+MIMETYPE=$(file --dereference --brief --mime-type "$FILE_PATH")
+case "$MIMETYPE" in
+ # --- IMAGES ---
+ image/svg+xml)
+ TMP_IMG="$TMP_DIR/svg-${INODE}.png"
+ if [ ! -f "$TMP_IMG" ]; then
+ rsvg-convert "$FILE_PATH" -o "$TMP_IMG" >/dev/null 2>&1
+ [ -s "$TMP_IMG" ] && mv "$TMP_IMG" "$TMP_IMG"
+ fi
+ render_image "$TMP_IMG"
+ ;;
+
+ image/*)
+ render_image "$FILE_PATH"
+ ;;
+
+ # --- VIDEOS ---
+ video/*)
+ TMP_IMG="$TMP_DIR/vid-${INODE}.png"
+ if [ ! -f "$TMP_IMG" ]; then
+ if command -v ffmpegthumbnailer >/dev/null; then
+ ffmpegthumbnailer -i "$FILE_PATH" -o "$TMP_IMG" -s 0 -q 5 >/dev/null 2>&1
+ else
+ ffmpeg -y -i "$FILE_PATH" -ss 00:00:01 -vframes 1 "$TMP_IMG" >/dev/null 2>&1
+ fi
+ fi
+ render_image "$TMP_IMG"
+ ;;
+
+ # --- AUDIO ---
+ audio/*)
+ TMP_IMG="$TMP_DIR/aud-${INODE}.png"
+ if [ ! -f "$TMP_IMG" ]; then
+ ffmpeg -y -i "$FILE_PATH" \
+ -filter_complex "showwavespic=s=${WIDTH}x${HEIGHT}:colors=white" \
+ -frames:v 1 "$TMP_IMG" >/dev/null 2>&1
+ fi
+ ffprobe -hide_banner -v quiet -show_format -show_streams "$FILE_PATH"
+ render_image "$TMP_IMG"
+ exit 0
+ ;;
+
+ # --- PDF ---
+ application/pdf)
+ TMP_IMG="$TMP_DIR/pdf-${INODE}.png"
+ if [ ! -f "$TMP_IMG" ]; then
+ pdftoppm -f 1 -l 1 -png -singlefile \
+ -scale-to $((WIDTH * 8)) \
+ -r 72 \
+ "$FILE_PATH" "${TMP_IMG%.png}" >/dev/null 2>&1
+ fi
+ render_image "$TMP_IMG"
+ ;;
+
+ # --- EPUB / MOBI ---
+ application/epub+zip|application/x-mobipocket-ebook)
+ TMP_IMG="$TMP_DIR/epub-${INODE}.png"
+ if [ ! -f "$TMP_IMG" ]; then
+ EPUB_DIR="$TMP_DIR/epub-${INODE}"
+ ouch decompress --yes --dir "$EPUB_DIR" "$FILE_PATH" >/dev/null 2>&1
+ COVER=$(find "$EPUB_DIR" -type f \( -iname 'cover*.jpg' -o -iname 'cover*.jpeg' -o -iname 'cover*.png' \) -print -quit)
+ [ -n "$COVER" ] && cp "$COVER" "$TMP_IMG"
+ fi
+ [ -s "$TMP_IMG" ] && render_image "$TMP_IMG"
+ ;;
+
+ # --- OFFICE DOCUMENTS ---
+ # Extract text in the terminal instead of pulling in a graphical office suite.
+ application/vnd.openxmlformats-officedocument.wordprocessingml.document|application/vnd.oasis.opendocument.text)
+ pandoc --to plain "$FILE_PATH" 2>/dev/null
+ exit 0
+ ;;
+
+ application/msword)
+ antiword "$FILE_PATH" 2>/dev/null
+ exit 0
+ ;;
+
+ # --- ARCHIVES ---
+ application/zip|application/x-7z-compressed|application/x-rar|application/x-tar|application/x-gzip)
+ ouch list -t "$FILE_PATH" 2>/dev/null
+ exit 0
+ ;;
+
+ # --- TEXT / CODE ---
+ text/*|application/json|application/javascript|application/xml)
+ bat --color=always --style=numbers --line-range :200 "$FILE_PATH" 2>/dev/null
+ exit 0
+ ;;
+
+ # --- FONTS ---
+ font/*|application/vnd.ms-fontobject|application/x-font-ttf)
+ fc-scan "$FILE_PATH"
+ exit 0
+ ;;
+
+ # --- FALLBACK ---
+ *)
+ file --dereference --brief "$FILE_PATH"
+ echo "------------------------------------------------"
+ hexdump -C "$FILE_PATH" | head -n 100
+ exit 0
+ ;;
+esac
+
+exit 0