Welcome to Vifm Q&A, where you can ask questions about using Vifm. Registration is optional, anonymous posts are moderated. E-mail and GitHub logins are enabled.
0 votes
in vifm by

I've been modifying the vifm-sixel-preview script to also display some info below the image preview, but I'm having trouble truncating CJK text, as i won't be able to properly count characters. It does seems like vifm itself is able to handle this, though. Any clues on how I can properly adapt my script? Thanks in advance! Love your work, I'm a huge vifm fan!

#!/bin/bash

# Ensure the cache directory exists
[ -d "$HOME/.cache/vifm" ] || mkdir -p "$HOME/.cache/vifm"

# Parameters
action="$1"
panel_width="$2"
panel_height="$3"
cell_width=7
cell_height=17
width=$((panel_width * cell_width))
height=$((panel_height * cell_height))
image_file="$4"
background=none

# Cache path
PCACHE="$HOME/.cache/vifm/thumbnail.$(stat --printf '%n\0%i\0%F\0%s\0%W\0%Y' -- "$(readlink -f "$PWD/$image_file")" | sha256sum | awk '{print $1}')"

# Function to calculate available pane width
calculate_pane_width() {
    echo "$panel_width"
}

# Function to check dependencies
check_dependencies() {
    local missing=()
    for dep in "$@"; do
        if ! command -v "$dep" &> /dev/null; then
            missing+=("$dep")
        fi
    done
    if [ ${#missing[@]} -ne 0 ]; then
        echo "Missing dependencies: ${missing[*]}"
        exit 1
    fi
}

# Cleanup function
cleanup() {
    printf '\33[s\33[5A\33[2K\33[u'
    clear
    exit 0
}

# Image function: Resizes image to fit within given dimensions without borders
image() {
    magick "$1" -background "$background" -resize "${2}x${3}" -gravity center -extent "${2}x${3}" sixel:-
}

# Function to get video information and format it appropriately
get_video_info() {
    local file="$1"
    local pane_width=$(calculate_pane_width)
    local max_info_width=$((pane_width * cell_width))

    local resolution=$(ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=s=x:p=0 "$file" 2>/dev/null)
    local duration_seconds=$(ffprobe -v error -select_streams v:0 -show_entries format=duration -of csv=p=0 "$file" 2>/dev/null | awk '{printf "%.0f", $1}')

    # Calculate hours, minutes, and seconds
    local hours=$((duration_seconds / 3600))
    local minutes=$(( (duration_seconds % 3600) / 60 ))
    local seconds=$((duration_seconds % 60))

    # Format duration with conditional hours
    if [ $hours -gt 0 ]; then
        local duration=$(printf "%02d:%02d:%02d" $hours $minutes $seconds)
    else
        local duration=$(printf "%02d:%02d" $minutes $seconds)
    fi

    local video_codec=$(ffprobe -v error -select_streams v:0 -show_entries stream=codec_name -of csv=p=0 "$file" 2>/dev/null)
    local audio_codec=$(ffprobe -v error -select_streams a:0 -show_entries stream=codec_name -of csv=p=0 "$file" 2>/dev/null)
    local fps=$(ffprobe -v error -select_streams v:0 -show_entries stream=r_frame_rate -of csv:p=0 "$file" 2>/dev/null | awk -F'/' '{if ($2==0) print $1; else print $1/$2}')

    # Format the information
    local info="$resolution | $duration | $video_codec | $audio_codec | ${fps%.*}fps"

    # Calculate the maximum number of characters that can fit in the pane width
    local max_chars=$((max_info_width / cell_width))

    # Truncate output if it exceeds max_chars
    if [ $(echo "$info" | wc -m) -gt $max_chars ]; then
        info=$(echo "$info" | cut -c 1-$((max_chars - 3)))"..."
    fi

    echo "$info"
}

# Function to get audio information and format it appropriately
get_audio_info() {
    local file="$1"
    local pane_width=$(calculate_pane_width)
    local max_info_width=$((pane_width * cell_width))

    # Extract embedded album art if available
    local album_art_cache="$HOME/.cache/vifm/album_art_$(basename "$file").jpg"
    if [ ! -f "$album_art_cache" ]; then
        ffmpeg -i "$file" -an -vcodec copy -f image2 "$album_art_cache" >/dev/null 2>&1
        if [ $? -ne 0 ]; then
            echo "Error extracting album art."
            exit 1
        fi
    fi

    # Display album art as preview
    image "$album_art_cache" "$width" "$height"

    # Format duration
    local duration_seconds=$(ffprobe -v error -select_streams a:0 -show_entries format=duration -of csv=p=0 "$file" 2>/dev/null | awk '{printf "%.0f", $1}')
    local hours=$((duration_seconds / 3600))
    local minutes=$(( (duration_seconds % 3600) / 60 ))
    local seconds=$((duration_seconds % 60))
    local duration=$(printf "%02d:%02d:%02d" $hours $minutes $seconds)

    # Get audio codec and bitrate
    local audio_codec=$(ffprobe -v error -select_streams a:0 -show_entries stream=codec_name -of csv=p=0 "$file" 2>/dev/null)
    local bitrate=$(ffprobe -v error -select_streams a:0 -show_entries format=bit_rate -of csv=p=0 "$file" 2>/dev/null | awk '{printf "%.0f kbps", $1 / 1000}')

    # Get artist, title, album, and year
    local artist=$(ffprobe -v error -show_entries format_tags=artist -of csv=p=0 "$file" 2>/dev/null)
    local title=$(ffprobe -v error -show_entries format_tags=title -of csv=p=0 "$file" 2>/dev/null)
    local album=$(ffprobe -v error -show_entries format_tags=album -of csv=p=0 "$file" 2>/dev/null)
    local year=$(ffprobe -v error -show_entries format_tags=date -of csv=p=0 "$file" 2>/dev/null)

    # Format the information
    local info="$duration | $audio_codec | $bitrate | $artist - $title ($album) - $year"

    # Calculate the maximum number of characters that can fit in the pane width
    local max_chars=$((max_info_width / cell_width))

    # Truncate output if it exceeds max_chars
    if [ $(echo "$info" | wc -m) -gt $max_chars ]; then
        info=$(echo "$info" | cut -c 1-$((max_chars - 3)))"..."
    fi

    echo "$info"
}

# Function to generate audio spectrum (currently disabled)
generate_audio_thumbnail() {
    # Uncomment and modify as needed if you want to generate an audio spectrum
    # local file="$1"
    # ffmpeg -i "$file" -filter_complex "[0:a]showspectrum=s=640x480:mode=combined:color=intensity:slide=scroll" -frames:v 1 "${PCACHE}.jpg"
    return  # Disable function, no spectrum generation
}

# Validate input
if [[ -z "$action" || -z "$panel_width" || -z "$panel_height" || -z "$image_file" ]]; then
    echo "Usage: $0 <action> <panel_width> <panel_height> <image_file>"
    exit 1
fi

# Check dependencies based on action
case "$action" in
  "video")
    check_dependencies ffmpegthumbnailer ffprobe
    ;;
  "epub")
    check_dependencies epub-thumbnailer
    ;;
  "pdf")
    check_dependencies pdftoppm
    ;;
  "audio")
    check_dependencies ffmpeg ffprobe
    # generate_audio_thumbnail "$image_file"  # Uncomment this line if you want to re-enable audio spectrum generation
    ;;
  "font")
    check_dependencies fontpreview
    ;;
  *)
    check_dependencies magick
    ;;
esac

# Perform action based on input
case "$action" in
  "clear")
    cleanup
    ;;
  *)
    # Display preview
    case "$action" in
        "draw")
            image "$image_file" "$width" "$height"
            ;;
        "video")
            # Generate thumbnail if not cached
            if [ ! -f "${PCACHE}.jpg" ]; then
                if ! ffmpegthumbnailer -i "$image_file" -o "${PCACHE}.jpg" -s 0 -q 5; then
                    echo "Error generating video thumbnail for $image_file"
                    exit 1
                fi
            fi
            # Display thumbnail
            image "${PCACHE}.jpg" "$width" "$height"
            # Display video information
            get_video_info "$image_file"
            ;;
        "epub")
            [ ! -f "${PCACHE}.jpg" ] && epub-thumbnailer "$image_file" "$PCACHE" 1024
            image "${PCACHE}.jpg" "$width" "$height"
            ;;
        "pdf")
            [ ! -f "${PCACHE}.jpg" ] && pdftoppm -jpeg -f 1 -singlefile "$image_file" "$PCACHE"
            image "${PCACHE}.jpg" "$width" "$height"
            ;;
        "audio")
            # Extract album art if not cached
            get_audio_info "$image_file"
            ;;
        "font")
            [ ! -f "${PCACHE}.jpg" ] && fontpreview -i "$image_file" -o "${PCACHE}.jpg"
            image "${PCACHE}.jpg" "$width" "$height"
            ;;
        *)
            echo "Unknown action: $action"
            exit 1
            ;;
    esac
    ;;
esac

1 Answer

0 votes
by

Vifm uses wcwidth() for that. Try piping the text to wc -L which seems to return the width.

by

Thanks for the reply Xaizek! wc -L does returns the correct number of cels. I was using wc -m before

...