Last active
          June 5, 2025 03:06 
        
      - 
      
 - 
        
Save psiborg/c4da7a48d67b8ac6846ba0956aa78409 to your computer and use it in GitHub Desktop.  
Revisions
- 
        
psiborg revised this gist
Jun 5, 2025 . 1 changed file with 97 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,97 @@ #!/usr/bin/env bash # Usage: ./describe_diagrams.sh /path/to/folder # Exit on error, treat unset variables as an error set -euo pipefail # Ensure folder argument is provided if [ -z "$1" ]; then echo "Usage: $0 /path/to/folder" exit 1 fi FOLDER="$1" # Check if ImageMagick's 'convert' is installed if ! command -v convert &> /dev/null; then echo "Error: 'convert' (ImageMagick) is required but not installed." exit 1 fi # Check if ollama is available if ! command -v ollama &> /dev/null; then echo "Error: 'ollama' is required but not installed or not in PATH." exit 1 fi # --- Define and create output directories --- # These will be subdirectories within the main FOLDER TEXT_OUTPUT_SUBDIR="text" TEMP_IMAGE_SUBDIR="temp" TEXT_OUTPUT_DIR="${FOLDER}/${TEXT_OUTPUT_SUBDIR}" TEMP_IMAGE_DIR="${FOLDER}/${TEMP_IMAGE_SUBDIR}" echo "Ensuring text output directory exists: $TEXT_OUTPUT_DIR" mkdir -p "$TEXT_OUTPUT_DIR" echo "Ensuring temporary image directory exists: $TEMP_IMAGE_DIR" mkdir -p "$TEMP_IMAGE_DIR" # --- End of output directory creation --- echo # Add a newline for better readability # Loop through image files (case-insensitive) using process substitution and NUL delimiters while IFS= read -r -d $'\0' IMAGE; do BASENAME=$(basename "$IMAGE") NAME="${BASENAME%.*}" # DIRNAME=$(dirname "$IMAGE") TXTFILE="${TEXT_OUTPUT_DIR}/${NAME}.txt" EXT_NO_DOT="${IMAGE##*.}" EXT_LOWER="${EXT_NO_DOT,,}" # Bash 4+ for lowercase echo "Processing: $BASENAME" ANALYZE_IMAGE="$IMAGE" # Default to original image PATH_TO_FLATTENED_GIF="" # Path for the temporary flattened GIF, if created # Prepare image for analysis if [[ "$EXT_LOWER" == "gif" ]]; then # Flattened GIF will go into the TEMP_IMAGE_DIR PATH_TO_FLATTENED_GIF="${TEMP_IMAGE_DIR}/${NAME}_flattened.png" echo "Flattening animated GIF..." if convert "$IMAGE" -coalesce -layers merge +repage "$PATH_TO_FLATTENED_GIF"; then ANALYZE_IMAGE="$PATH_TO_FLATTENED_GIF" # ollama should analyze this flattened version else echo "Error: Failed to flatten GIF: $IMAGE. Skipping." rm -f "$PATH_TO_FLATTENED_GIF" # Attempt to clean up partial file on error continue fi fi # Prompt tailored for system design and technical diagrams PROMPT="You are a technical writer preparing a 1-paragraph summary for a software architecture diagram to be used in a product or solution catalog. Summarize the purpose of the system, highlight the major components (such as services, databases, APIs, etc.), and explain how they interact at a high level. Use clear and professional language suitable for software engineers, architects, and decision-makers. Do not include implementation details or code — focus on structure and flow." # Run ollama and write output to txt file echo "Running ollama analysis..." # Redirect ollama's stdin from /dev/null if ollama run llava "$PROMPT" "$ANALYZE_IMAGE" > "$TXTFILE" < /dev/null; then echo "Saved description." else echo "Error: ollama command failed for $ANALYZE_IMAGE. Check $TXTFILE for partial output or errors." # Optionally, you could 'continue' here if you don't want to attempt cleanup for failed ollama fi # Clean up temporary image if used #if [[ -n "$PATH_TO_FLATTENED_GIF" ]] && [[ -f "$PATH_TO_FLATTENED_GIF" ]]; then #echo "Removing temporary image: $PATH_TO_FLATTENED_GIF" #rm -f "$PATH_TO_FLATTENED_GIF" #fi echo done < <(find "$FOLDER" -type f \( -iname "*.jpg" -o -iname "*.png" -o -iname "*.gif" \) -print0) echo "Processing complete."  - 
        
psiborg revised this gist
Jun 5, 2025 . 1 changed file with 349 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,349 @@ #!/usr/bin/env python3 import os from pathlib import Path from collections import defaultdict SUPPORTED_EXTENSIONS = { ".gif": "image", ".jpeg": "image", ".jpg": "image", ".md": "text", ".mp3": "audio", ".mp4": "video", ".ogg": "audio", ".pdf": "iframe", ".png": "image", ".svg": "image", ".txt": "text", ".wav": "audio", ".webm": "video", ".webp": "image" } HTML_TEMPLATE = """ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>File Viewer</title> <style> body {{ margin: 0; font-family: sans-serif; font-size: 8pt; display: flex; height: 100vh; }} nav {{ width: 25%; background: #f4f4f4; overflow-y: auto; border-right: 1px solid #ccc; padding: 10px; box-sizing: border-box; }} .content-area {{ width: 75%; display: flex; flex-direction: column; height: 100vh; overflow: hidden; /* Important for containing flex children */ }} main {{ flex-grow: 1; /* Takes up available space not used by resizer/preview */ min-height: 50px; /* Minimum height for main content */ padding: 10px; overflow: auto; box-sizing: border-box; }} #resizer {{ height: 8px; /* Height of the draggable resizer bar */ background: #ccc; cursor: ns-resize; flex-shrink: 0; /* Prevent resizer from shrinking */ }} #preview-pane {{ /* flex-basis will be set by JS. Initial value can be from here or JS. */ flex-basis: 30%; /* Default height as a percentage */ flex-shrink: 0; /* Prevent shrinking beyond its basis unless forced by JS */ min-height: 20px; /* Absolute minimum height, also used for "collapsed" state */ background: #e9e9e9; overflow-y: auto; padding: 10px; box-sizing: border-box; }} main iframe, main video, main audio {{ width: 100%; height: 100%; display: block; }} main img {{ max-width: 100%; max-height: 100%; display: block; object-fit: contain; }} .text-container {{ white-space: pre-wrap; font-family: monospace; }} #preview-pane .text-container {{ font-size: 0.9em; }} ul {{ list-style-type: disc; padding-left: 12px; }} li a {{ display: block; padding: 2px 0; text-decoration: none; color: #333; }} li a:hover {{ background: #ff0; }} </style> <script> // Configuration for resizable/collapsible pane const MIN_PREVIEW_PANE_HEIGHT_PX = 20; // Min height for preview pane (pixels), also collapsed height const MIN_MAIN_PANE_HEIGHT_PX = 50; // Min height for main content area (pixels) const RESIZER_HEIGHT_PX = 8; // Must match #resizer height in CSS (pixels) let lastUserSetPreviewFlexBasis = '30%'; // Stores the user's preferred size or default function showContent(type, src, textContent = "", descriptionContent = "") {{ const mainEl = document.getElementById("main"); const previewPaneEl = document.getElementById("preview-pane"); const resizerEl = document.getElementById("resizer"); // Display main content if (type === "iframe") {{ mainEl.innerHTML = `<iframe src="${{src}}" frameborder="0"></iframe>`; }} else if (type === "image") {{ mainEl.innerHTML = `<img src="${{src}}">`; }} else if (type === "audio") {{ mainEl.innerHTML = `<audio controls src="${{src}}"></audio>`; }} else if (type === "video") {{ mainEl.innerHTML = `<video controls src="${{src}}"></video>`; }} else if (type === "text") {{ mainEl.innerHTML = `<div class="text-container">${{textContent}}</div>`; }} else {{ mainEl.innerHTML = `<h2>Preview for ${{type}} not supported yet.</h2>`; }} // Display description in preview pane and manage its state if (descriptionContent && descriptionContent.trim() !== "") {{ previewPaneEl.innerHTML = `<div class="text-container">${{descriptionContent}}</div>`; previewPaneEl.style.flexBasis = lastUserSetPreviewFlexBasis; // Restore to user's size or default previewPaneEl.style.display = ''; // Ensure visible resizerEl.style.display = ''; // Show resizer previewPaneEl.style.overflowY = 'auto'; }} else {{ previewPaneEl.innerHTML = "<p>No description available.</p>"; previewPaneEl.style.flexBasis = `${{MIN_PREVIEW_PANE_HEIGHT_PX}}px`; // Collapse // previewPaneEl.style.display = 'none'; // Alternative: hide completely resizerEl.style.display = 'none'; // Hide resizer when collapsed previewPaneEl.style.overflowY = 'hidden'; // No scroll needed for "no description" }} }} document.addEventListener('DOMContentLoaded', () => {{ const resizer = document.getElementById('resizer'); const mainPane = document.getElementById('main'); const previewPane = document.getElementById('preview-pane'); const contentArea = document.querySelector('.content-area'); // Initialize lastUserSetPreviewFlexBasis from CSS or set a default const initialComputedFlexBasis = window.getComputedStyle(previewPane).flexBasis; if (initialComputedFlexBasis && initialComputedFlexBasis !== 'auto' && parseFloat(initialComputedFlexBasis) >= MIN_PREVIEW_PANE_HEIGHT_PX) {{ lastUserSetPreviewFlexBasis = initialComputedFlexBasis; }} else {{ lastUserSetPreviewFlexBasis = '30%'; // Fallback default if CSS is not suitable }} // Apply initial flexBasis based on whether there's a default description (usually not on first load) // This means the initial state will be collapsed if the welcome message has no "description" // Or, we can explicitly set it for the first load state: if (previewPane.innerHTML.includes("Description will appear here.")) {{ previewPane.style.flexBasis = `${{MIN_PREVIEW_PANE_HEIGHT_PX}}px`; resizer.style.display = 'none'; previewPane.style.overflowY = 'hidden'; }} else {{ previewPane.style.flexBasis = lastUserSetPreviewFlexBasis; }} let isResizing = false; let startY_mouse; resizer.addEventListener('mousedown', (e) => {{ e.preventDefault(); // Prevent text selection during drag isResizing = true; startY_mouse = e.clientY; // Store the starting height of the preview pane in pixels const currentPreviewPaneHeight = previewPane.offsetHeight; document.body.style.cursor = 'ns-resize'; mainPane.style.userSelect = 'none'; previewPane.style.userSelect = 'none'; document.addEventListener('mousemove', handleMouseMove); document.addEventListener('mouseup', handleMouseUp); function handleMouseMove(ev) {{ if (!isResizing) return; const deltaY = ev.clientY - startY_mouse; let newPreviewHeight = currentPreviewPaneHeight - deltaY; const contentAreaHeight = contentArea.offsetHeight; // Max preview height: content area height - min main pane height - resizer height const maxPreviewHeight = contentAreaHeight - MIN_MAIN_PANE_HEIGHT_PX - RESIZER_HEIGHT_PX; if (newPreviewHeight < MIN_PREVIEW_PANE_HEIGHT_PX) {{ newPreviewHeight = MIN_PREVIEW_PANE_HEIGHT_PX; }} if (newPreviewHeight > maxPreviewHeight) {{ newPreviewHeight = maxPreviewHeight; }} previewPane.style.flexBasis = `${{newPreviewHeight}}px`; }} function handleMouseUp() {{ if (!isResizing) return; isResizing = false; document.body.style.cursor = ''; mainPane.style.userSelect = ''; previewPane.style.userSelect = ''; // Update lastUserSetPreviewFlexBasis with the new size in pixels lastUserSetPreviewFlexBasis = previewPane.style.flexBasis; document.removeEventListener('mousemove', handleMouseMove); document.removeEventListener('mouseup', handleMouseUp); }} }}); }}); </script> </head> <body> <nav> {tree} </nav> <div class="content-area"> <main id="main"> <h2>Select a file to preview</h2> </main> <div id="resizer"></div> <div id="preview-pane"> <p>Description will appear here.</p> </div> </div> </body> </html> """ def build_tree(paths_with_desc_info): """ Builds a tree structure from a list of paths, each with its type, content, and description. paths_with_desc_info: list of tuples (path_str, file_type_str, main_content_str, description_content_str) """ tree = lambda: defaultdict(tree) root = tree() for path, file_type, main_content, description_content in paths_with_desc_info: parts = path.split('/') current = root for part in parts[:-1]: current = current[part] current[parts[-1]] = { '_type': file_type, '_content': main_content, # Content of the file itself (if text) '_description': description_content # Content of the associated .txt/.md } return root def render_tree(d, prefix=""): html = "<ul>" for key, value in sorted(d.items()): if isinstance(value, dict) and "_type" in value: # It's a file node file_type = value["_type"] main_content_for_js = value["_content"] description_content_for_js = value["_description"] escaped_main_content = main_content_for_js.replace("\\", "\\\\").replace("`", "\\`").replace("$", "\\$").replace("\r", "\\r").replace("\n", "\\n") escaped_description_content = description_content_for_js.replace("\\", "\\\\").replace("`", "\\`").replace("$", "\\$").replace("\r", "\\r").replace("\n", "\\n") file_path_for_js = prefix + key js_escaped_path = file_path_for_js.replace("\\", "\\\\").replace("'", "\\'") html += (f'<li><a href="javascript:void(0)" ' f'onclick="showContent(\'{file_type}\', \'{js_escaped_path}\', ' f'`{escaped_main_content}`, `{escaped_description_content}`)">{key}</a></li>') elif isinstance(value, dict): # It's a directory node html += f"<li>{key}{render_tree(value, prefix + key + '/')}</li>" html += "</ul>" return html def gather_files_nested(base_dir): file_info_list = [] for root, dirs, files in os.walk(base_dir): dirs.sort() files.sort() for file in files: ext = Path(file).suffix.lower() if ext in SUPPORTED_EXTENSIONS: abs_path = Path(root) / file rel_path = abs_path.relative_to(base_dir).as_posix() file_type = SUPPORTED_EXTENSIONS[ext] content = "" if file_type == "text": try: with open(abs_path, "r", encoding="utf-8", errors="ignore") as f: content = f.read() except Exception: content = f"Could not read file: {rel_path}" file_info_list.append((rel_path, file_type, content)) return file_info_list def create_static_index(directory="."): output_file = Path(directory) / "index.html" if output_file.exists(): response = input(f"{output_file} already exists. Overwrite? (y/N): ").strip().lower() if response != "y": print("Aborted.") return all_files_data = gather_files_nested(directory) text_file_contents_map = {} for rel_path, file_type, content in all_files_data: if file_type == "text": text_file_contents_map[rel_path] = content files_data_with_descriptions_attached = [] text_files_used_for_non_text_description = set() for rel_path_main_file, file_type_main_file, main_content_if_text in all_files_data: description_text_for_main_file = "" current_file_p_obj = Path(rel_path_main_file) potential_desc_txt_p_obj = current_file_p_obj.with_suffix('.txt') if potential_desc_txt_p_obj != current_file_p_obj: desc_txt_rel_path_str = potential_desc_txt_p_obj.as_posix() if desc_txt_rel_path_str in text_file_contents_map: description_text_for_main_file = text_file_contents_map[desc_txt_rel_path_str] if file_type_main_file != "text": text_files_used_for_non_text_description.add(desc_txt_rel_path_str) if not description_text_for_main_file: potential_desc_md_p_obj = current_file_p_obj.with_suffix('.md') if potential_desc_md_p_obj != current_file_p_obj: desc_md_rel_path_str = potential_desc_md_p_obj.as_posix() if desc_md_rel_path_str in text_file_contents_map: description_text_for_main_file = text_file_contents_map[desc_md_rel_path_str] if file_type_main_file != "text": text_files_used_for_non_text_description.add(desc_md_rel_path_str) files_data_with_descriptions_attached.append( (rel_path_main_file, file_type_main_file, main_content_if_text, description_text_for_main_file) ) processed_file_data_for_tree = [] for rel_path, file_type, main_content, description_content in files_data_with_descriptions_attached: if file_type == "text" and rel_path in text_files_used_for_non_text_description: pass else: processed_file_data_for_tree.append( (rel_path, file_type, main_content, description_content) ) tree_dict = build_tree(processed_file_data_for_tree) html_tree = render_tree(tree_dict) html_output = HTML_TEMPLATE.format(tree=html_tree) with open(output_file, "w", encoding="utf-8") as f: f.write(html_output) print(f"Created index.html in: {output_file.resolve()}") if __name__ == "__main__": create_static_index()  - 
        
psiborg revised this gist
May 28, 2025 . 2 changed files with 3 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,4 @@ #!/usr/bin/env bash for file in *.mp3 *.flac; do if [ -f "$file" ]; then This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,3 +1,5 @@ #!/usr/bin/env bash for f in *.mkv; do ffmpeg -i "$f" -map 0:v:0 -map 0:a:0 -metadata title="${f%}" -vf "scale=1920:1080" -pix_fmt yuv420p -preset slow -crf 23 "${f%".mkv"}.mp4" done;  - 
        
psiborg created this gist
May 28, 2025 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,10 @@ #!/bin/sh for file in *.mp3 *.flac; do if [ -f "$file" ]; then echo "Metadata for: $file" #ffprobe -i "$file" -show_entries format_tags -v quiet -print_format json ffprobe -i "$file" -show_entries format_tags -v quiet -print_format flat echo "-------------------------------------------------------------------------------" fi done This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,5 @@ #!/usr/bin/env bash for f in *.flac; do ffmpeg -i "$f" -ab 320k -map_metadata 0 -id3v2_version 3 "${f%".flac"}.mp3" done This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,5 @@ #!/usr/bin/env bash for f in *.mkv; do ffmpeg -i "$f" -x265-params crf=23 "${f%".mkv"}.mp4" done; This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,3 @@ for f in *.mkv; do ffmpeg -i "$f" -map 0:v:0 -map 0:a:0 -metadata title="${f%}" -vf "scale=1920:1080" -pix_fmt yuv420p -preset slow -crf 23 "${f%".mkv"}.mp4" done; This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,5 @@ #!/usr/bin/env bash for f in *.mkv; do ffmpeg -i "$f" -map 0:v:0 -map 0:a:0 -metadata title="${f%}" -vf "scale=1280:720" -pix_fmt yuv420p -preset slow -crf 23 "${f%".mkv"}.mp4" done; This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,6 @@ #!/usr/bin/env bash for f in *.mkv; do ffmpeg -i "$f" "${f%".mkv"}.srt" ffmpeg -i "$f" -map 0:v:0 -map 0:a:0 -metadata title="${f%}" -codec copy "${f%".mkv"}.mp4" done; This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,5 @@ #!/usr/bin/env bash for f in *.mkv; do ffmpeg -i "$f" "${f%".mkv"}.srt" done; This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,6 @@ #!/usr/bin/env bash for f in *.mp4; do base="${f%.mp4}" ffmpeg -i "$f" -vf scale=1280:720 -c:v libx264 -crf 23 -preset medium -c:a copy "${base}_720p_h264.mp4" done This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,26 @@ #!/usr/bin/env bash ollama --version echo ollama list echo echo "Getting list of installed models..." echo models=$(ollama list | awk 'NR>1 {print $1}') if [ -z "$models" ]; then echo "No models found to update." echo exit 0 fi for model in $models; do echo "Pulling latest for $model..." echo ollama pull "$model" echo done ollama list This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,4 @@ #!/usr/bin/env bash yt-dlp -f 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best' --batch-file $1 #yt-dlp -f 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best' --batch-file $1 --cookies "/path/to/cookies.txt" This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,3 @@ #!/usr/bin/env bash yt-dlp --extract-audio --audio-format mp3 --audio-quality 0 -o "%(title)s.%(ext)s" $1 This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,4 @@ #!/usr/bin/env bash yt-dlp -f 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best' $1 #yt-dlp -f 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best' $1 --cookies "/path/to/cookies.txt"