this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add dither video script

+158
+158
inkify-video.sh
··· 1 + #!/usr/bin/env -S nix shell nixpkgs#parallel nixpkgs#ffmpeg nixpkgs#imagemagick --command bash 2 + 3 + # Define temporary directory variables globally so trap can access them 4 + FRAMES_IN="" 5 + FRAMES_OUT="" 6 + 7 + # Function to clean up and exit 8 + cleanup_and_exit() { 9 + echo -e "\nInterrupted. Cleaning up temporary files..." 10 + # Only remove if directories were actually created 11 + [ -n "$FRAMES_IN" ] && [ -d "$FRAMES_IN" ] && rm -rf "$FRAMES_IN" 12 + [ -n "$FRAMES_OUT" ] && [ -d "$FRAMES_OUT" ] && rm -rf "$FRAMES_OUT" 13 + exit 1 14 + } 15 + 16 + # Set up trap for Ctrl+C (SIGINT) 17 + trap cleanup_and_exit INT 18 + 19 + # Check if input and output parameters are provided 20 + if [ $# -lt 2 ]; then 21 + echo "Usage: $0 input_video output_video [num_threads]" 22 + exit 1 23 + fi 24 + 25 + INPUT_VIDEO=$1 26 + OUTPUT_VIDEO=$2 27 + # Default to number of CPU cores if threads not specified 28 + NUM_THREADS=${3:-$(nproc)} 29 + 30 + # Determine and create temporary directories, preferring RAM disk 31 + TEMP_BASE="" 32 + if [ -d "/dev/shm" ] && [ -w "/dev/shm" ]; then 33 + TEMP_BASE="/dev/shm" 34 + echo "Using RAM disk (/dev/shm) for temporary files." 35 + elif [ -d "/tmp" ] && [ -w "/tmp" ] && mount | grep -q 'on /tmp type tmpfs'; then 36 + TEMP_BASE="/tmp" 37 + echo "Using RAM disk (/tmp) for temporary files." 38 + else 39 + TEMP_BASE="." 40 + echo "RAM disk not available or not writable, using current directory for temporary files." 41 + fi 42 + 43 + # Create unique temporary directory names using process ID 44 + # Add a random suffix for extra safety in case PID wraps or script runs concurrently 45 + RAND_SUFFIX=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 8) 46 + FRAMES_IN="${TEMP_BASE}/inkify_frames_in_${$}_${RAND_SUFFIX}" 47 + FRAMES_OUT="${TEMP_BASE}/inkify_frames_out_${$}_${RAND_SUFFIX}" 48 + 49 + # Check if magick command exists 50 + if ! command -v magick &>/dev/null; then 51 + echo "Error: ImageMagick (magick command) not found. Please install it." 52 + # Attempt cleanup before exiting 53 + [ -d "$FRAMES_IN" ] && rm -rf "$FRAMES_IN" 54 + [ -d "$FRAMES_OUT" ] && rm -rf "$FRAMES_OUT" 55 + exit 1 56 + fi 57 + 58 + # Check if eink-2color.png exists 59 + if [ ! -f "eink-2color.png" ]; then 60 + echo "Error: Colormap file 'eink-2color.png' not found in the current directory." 61 + # Attempt cleanup before exiting 62 + [ -d "$FRAMES_IN" ] && rm -rf "$FRAMES_IN" 63 + [ -d "$FRAMES_OUT" ] && rm -rf "$FRAMES_OUT" 64 + exit 1 65 + fi 66 + 67 + # Create temporary directories only after checks pass 68 + mkdir -p "$FRAMES_IN" 69 + if [ $? -ne 0 ]; then echo "Error: Could not create temporary directory $FRAMES_IN"; exit 1; fi 70 + mkdir -p "$FRAMES_OUT" 71 + if [ $? -ne 0 ]; then echo "Error: Could not create temporary directory $FRAMES_OUT"; rm -rf "$FRAMES_IN"; exit 1; fi 72 + 73 + 74 + # Extract frames from video and detect framerate 75 + FRAMERATE=$(ffprobe -v error -select_streams v -of default=noprint_wrappers=1:nokey=1 -show_entries stream=r_frame_rate "$INPUT_VIDEO" 2>/dev/null | bc -l | awk '{printf "%.2f", $0}') 76 + if [ -z "$FRAMERATE" ] || [ "$FRAMERATE" = "0.00" ]; then 77 + echo "Error: Could not detect framerate for $INPUT_VIDEO." 78 + cleanup_and_exit # Use cleanup function 79 + fi 80 + echo "Detected framerate: $FRAMERATE fps" 81 + 82 + # Extract frames from video (use -threads for faster extraction) 83 + echo "Extracting frames to $FRAMES_IN..." 84 + if ! ffmpeg -threads "$NUM_THREADS" -i "$INPUT_VIDEO" "$FRAMES_IN/frame_%04d.png" >/dev/null 2>&1; then 85 + echo "Error: ffmpeg failed to extract frames from $INPUT_VIDEO." 86 + cleanup_and_exit # Use cleanup function 87 + fi 88 + 89 + # Check if frames were extracted 90 + if ! ls "$FRAMES_IN"/frame_*.png > /dev/null 2>&1; then 91 + echo "Error: No frames were extracted to $FRAMES_IN. Check input video and permissions." 92 + cleanup_and_exit 93 + fi 94 + 95 + 96 + # Process frames in parallel using GNU Parallel 97 + if command -v parallel &>/dev/null; then 98 + echo "Processing frames in parallel with $NUM_THREADS threads..." 99 + # Need to export FRAMES_OUT for parallel to see it in subshells 100 + export FRAMES_OUT 101 + # Use parallel's built-in path manipulation {/} for basename 102 + if ! find "$FRAMES_IN" -name "frame_*.png" | parallel --progress -j "$NUM_THREADS" \ 103 + "magick {} -dither FloydSteinberg -define dither:diffusion-amount=100% -remap eink-2color.png \"$FRAMES_OUT/{/}\""; then 104 + echo "Error: Parallel processing with magick failed." 105 + cleanup_and_exit 106 + fi 107 + else 108 + echo "GNU Parallel not found. Processing frames sequentially..." 109 + # Process each frame with the dithering effect 110 + processed_count=0 111 + for frame in "$FRAMES_IN"/frame_*.png; do 112 + # Check if the file exists before processing 113 + if [ -f "$frame" ]; then 114 + # Construct output path correctly using basename 115 + output_frame="$FRAMES_OUT/$(basename "$frame")" 116 + if ! magick "$frame" -dither FloydSteinberg -define dither:diffusion-amount=100% -remap eink-2color.png "$output_frame"; then 117 + echo "Error: magick failed to process $frame." 118 + # Decide if you want to stop on first error or continue 119 + cleanup_and_exit # Stop on first error 120 + # continue # Or uncomment to continue processing other frames 121 + fi 122 + # echo "Processed: $frame" # Can be too verbose 123 + processed_count=$((processed_count + 1)) 124 + fi 125 + done 126 + echo "Processed $processed_count frames sequentially." 127 + if [ $processed_count -eq 0 ]; then 128 + echo "Error: No frames found to process sequentially in $FRAMES_IN" 129 + cleanup_and_exit 130 + fi 131 + fi 132 + 133 + # Check if output frames exist before attempting recombination 134 + if ! ls "$FRAMES_OUT"/frame_*.png > /dev/null 2>&1; then 135 + echo "Error: No processed frames found in $FRAMES_OUT. Image processing likely failed." 136 + cleanup_and_exit 137 + fi 138 + 139 + 140 + # Recombine frames into video with detected framerate (use -threads for faster encoding) 141 + echo "Recombining frames from $FRAMES_OUT..." 142 + # Allow ffmpeg to prompt for overwrite by removing output redirection 143 + if ! ffmpeg -framerate "$FRAMERATE" -threads "$NUM_THREADS" -i "$FRAMES_OUT/frame_%04d.png" -c:v libx264 -preset faster -pix_fmt yuv420p "$OUTPUT_VIDEO"; then 144 + echo "Error: ffmpeg failed to recombine frames into $OUTPUT_VIDEO (or user cancelled overwrite)." 145 + cleanup_and_exit # Use cleanup function 146 + fi 147 + 148 + # Use the cleanup function for consistency 149 + cleanup_and_exit() { 150 + echo -e "\nCleaning up temporary files..." 151 + # Only remove if directories were actually created 152 + [ -n "$FRAMES_IN" ] && [ -d "$FRAMES_IN" ] && rm -rf "$FRAMES_IN" 153 + [ -n "$FRAMES_OUT" ] && [ -d "$FRAMES_OUT" ] && rm -rf "$FRAMES_OUT" 154 + } 155 + # Call cleanup explicitly at the end (trap handles interruptions) 156 + cleanup_and_exit 157 + # Reset exit code to 0 for successful completion 158 + exit 0