Utensil's Zettelkasten-style forest of evergreen notes on math and tech. utensil.tngl.sh/forest/
0

Configure Feed

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

forest / convert_xml.sh
6.9 kB 212 lines
1#!/bin/bash 2# set -e 3 4OUT_DIR=output/forest 5# XSL_FILE=$OUT_DIR/default.xsl 6XSL_FILE=$OUT_DIR/uts-forest.xsl 7 8function convert_xml_to_html() { 9 local xml_file=$1 10 # Extract NOTE_ID from path: $OUT_DIR/NOTE_ID/index.xml 11 local note_id=$(basename $(dirname "$xml_file")) 12 local html_file="$OUT_DIR/$note_id/index.html" 13 14 # echo "[convert_xml_to_html] xml_file: $xml_file" 15 # echo "[convert_xml_to_html] note_id: $note_id" 16 # echo "[convert_xml_to_html] html_file: $html_file" 17 # echo "[convert_xml_to_html] xsltproc -v -o \"$html_file\" $XSL_FILE \"$xml_file\"" 18 19 # bunx xslt3 -s:"$xml_file" -xsl:output/uts-forest.xsl -o:"$html_file" 20 # -v 21 xsltproc --path $OUT_DIR -o "$html_file" $XSL_FILE "$xml_file" 22 # echo "[convert_xml_to_html] Finished xsltproc for $html_file" 23 # head -n 20 "$html_file" 24} 25# AGENT-NOTE: Updated for new forest path structure (input/output under $OUT_DIR/NOTE_ID/) 26 27function backup_html_files() { 28 mkdir -p $OUT_DIR/.bak 29 # Copy each $OUT_DIR/NOTE_ID/index.html to $OUT_DIR/.bak/NOTE_ID/index.html 30 for html_file in $OUT_DIR/*/index.html; do 31 note_id=$(basename $(dirname "$html_file")) 32 mkdir -p "$OUT_DIR/.bak/$note_id" 33 cp -f "$html_file" "$OUT_DIR/.bak/$note_id/index.html" 2>/dev/null || true 34 done 35} 36# AGENT-NOTE: Updated backup logic for forest structure 37 38function check_html_changes() { 39 local note_id=$1 40 local html_file="$OUT_DIR/$note_id/index.html" 41 local bak_file="$OUT_DIR/.bak/$note_id/index.html" 42 [ ! -f "$bak_file" ] || ! cmp -s "$html_file" "$bak_file" 43} 44# AGENT-NOTE: Updated for forest structure (compare $OUT_DIR/NOTE_ID/index.html) 45 46function sample_based_change_detected() { 47 local xml_files=("$@") 48 local total_files=${#xml_files[@]} 49 local sample_size=3 50 backup_html_files 51 local changes_detected=false 52 sample_pids=() 53 sample_note_ids=() 54 for ((i = 0; i < sample_size && i < total_files; i++)); do 55 local xml_file="${xml_files[i]}" 56 local note_id=$(basename $(dirname "$xml_file")) 57 convert_xml_to_html "$xml_file" & 58 sample_pids+=($!) 59 sample_note_ids+=("$note_id") 60 done 61 for pid in "${sample_pids[@]}"; do 62 wait "$pid" 63 done 64 cmp_pids=() 65 for note_id in "${sample_note_ids[@]}"; do 66 ( 67 if check_html_changes "$note_id"; then 68 touch "$OUT_DIR/.bak/$note_id/changed" 69 fi 70 ) & 71 cmp_pids+=($!) 72 done 73 for pid in "${cmp_pids[@]}"; do 74 wait "$pid" 75 done 76 for note_id in "${sample_note_ids[@]}"; do 77 if [ -f "$OUT_DIR/.bak/$note_id/changed" ]; then 78 changes_detected=true 79 break 80 fi 81 done 82 find $OUT_DIR/.bak -name changed -delete 83 $changes_detected && return 0 || return 1 84} 85# AGENT-NOTE: Updated for forest structure (sample and change detection) 86 87function update_progress_bar() { 88 local i=$1 89 local progress=$2 90 local progress_step=$3 91 local new_progress=$((i / progress_step)) 92 while [ $progress -lt $new_progress ] && [ $progress -lt 20 ]; do 93 echo -n "█" 94 ((progress++)) 95 done 96 return $progress 97} 98 99function parallel_convert_xml_files() { 100 local convert_all=$1 101 # echo "[parallel_convert_xml_files] convert_all: $convert_all" 102 local max_jobs=$2 103 shift 2 104 local xml_files=("$@") 105 local total_files=${#xml_files[@]} 106 local progress=0 107 local progress_step=$((total_files / 20)) # 5% intervals 108 [ $progress_step -eq 0 ] && progress_step=1 109 echo -n "Progress: " 110 local updated_file_list="$OUT_DIR/.updated_files" 111 : > "$updated_file_list" 112 pids=() 113 for ((i = 0; i < total_files; i++)); do 114 local xml_file="${xml_files[i]}" 115 # echo "[parallel_convert_xml_files] Processing: $xml_file" 116 ( 117 local note_id=$(basename $(dirname "$xml_file")) 118 local html_file="$OUT_DIR/$note_id/index.html" 119 local bak_file="$OUT_DIR/.bak/$note_id/index.html" 120 local do_convert=0 121 122 if [ "$convert_all" = true ] || [ -n "$XSL_CHANGED" ] || [ "$changes_detected" = true ]; then 123 do_convert=1 124 elif [ ! -f "$html_file" ]; then 125 do_convert=1 126 elif [ "$XSL_FILE" -nt "$html_file" ]; then 127 do_convert=1 128 fi 129 130 # echo "[parallel_convert_xml_files] note_id: $note_id do_convert: $do_convert" 131 132 if [ $do_convert -eq 1 ]; then 133 convert_xml_to_html "$xml_file" 134 echo "$note_id" >> "$updated_file_list" 135 exit 0 136 fi 137 exit 1 138 ) & 139 pids+=($!) 140 if [ "${#pids[@]}" -ge "$max_jobs" ]; then 141 wait "${pids[0]}" 142 pids=("${pids[@]:1}") 143 fi 144 145 update_progress_bar $i $progress $progress_step 146 progress=$? 147 done 148 for pid in "${pids[@]}"; do 149 wait "$pid" 150 done 151 echo # New line after progress bar 152} 153# AGENT-NOTE: Updated for forest structure (parallel conversion and progress) 154 155 156function detect_max_jobs() { 157 # Cross-platform CPU core detection 158 local num_cores=1 159 if [ -n "$CI" ]; then 160 num_cores=2 161 elif [ -f /proc/cpuinfo ]; then 162 num_cores=$(grep -c ^processor /proc/cpuinfo) 163 elif [ "$(uname)" = "Darwin" ]; then 164 num_cores=$(sysctl -n hw.ncpu 2>/dev/null || echo 1) 165 else 166 # Default to 1 core if we can't detect 167 num_cores=1 168 fi 169 local max_jobs=$((num_cores > 2 ? num_cores - 2 : 2)) 170 echo "$max_jobs" 171} 172 173function convert_xml_files() { 174 local convert_all=$1 175 local start_time=$(date +%s) 176 177 # Get all XML files in $OUT_DIR/*/index.xml 178 local xml_files=($OUT_DIR/*/index.xml) 179 local total_files=${#xml_files[@]} 180 # echo "[convert_xml_files] Found $total_files XML files:" 181 # for xml_file in "${xml_files[@]}"; do 182 # echo "[convert_xml_files] $xml_file" 183 # done 184 local max_jobs=$(detect_max_jobs) 185 echo "Max jobs: $max_jobs" 186 187 if [ "$convert_all" = true ]; then 188 if [ -n "$XSL_CHANGED" ]; then 189 if ! sample_based_change_detected "${xml_files[@]}"; then 190 echo "⏩ XSL changes don't affect HTML output, skipping conversion" 191 return 0 192 fi 193 fi 194 echo "Converting all ${total_files} XML files..." 195 fi 196 197 parallel_convert_xml_files "$convert_all" "$max_jobs" "${xml_files[@]}" 198 199 local updated_file_list="$OUT_DIR/.updated_files" 200 local updated_count=0 201 if [ -f "$updated_file_list" ]; then 202 updated_count=$(wc -l < "$updated_file_list") 203 rm -f "$updated_file_list" 204 fi 205 206 local end_time=$(date +%s) 207 local duration=$((end_time - start_time)) 208 echo "📝 Updated $updated_count HTML file(s) in ${duration}s" 209 210} 211# AGENT-NOTE: Updated for forest structure (xml listing and updated files) 212