From 3b71cf33ee0c6a875edf8a40a769472e6a62de02 Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Fri, 23 Jan 2026 22:13:50 +0100 Subject: [PATCH] feat: fix stepmania and organize downloads scripts --- scripts/fixes/fix_stepmania.sh | 143 ++++++ scripts/utils/organize_downloads.sh | 707 ++++++++++++++++------------ 2 files changed, 548 insertions(+), 302 deletions(-) create mode 100755 scripts/fixes/fix_stepmania.sh diff --git a/scripts/fixes/fix_stepmania.sh b/scripts/fixes/fix_stepmania.sh new file mode 100755 index 0000000..2d6cb38 --- /dev/null +++ b/scripts/fixes/fix_stepmania.sh @@ -0,0 +1,143 @@ +#!/usr/bin/env bash + +# Fix StepMania AUR build failure due to missing vorbis libraries in linker +# +# Error addressed: +# /usr/bin/ld: /usr/local/lib/libavcodec.a(libvorbisenc.o): undefined reference to symbol 'vorbis_encode_setup_vbr' +# /usr/bin/ld: /usr/lib/libvorbisenc.so.2: error adding symbols: DSO missing from command line +# +# Cause: +# Static libavcodec.a depends on libvorbisenc but cmake doesn't add it to linker flags +# +# Solution: +# Add vorbis libraries to LDFLAGS before building +# +# Usage: +# ./fix_stepmania.sh + +set -euo pipefail + +SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" +# shellcheck source=../lib/common.sh +source "$SCRIPT_DIR/../lib/common.sh" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +log_info() { echo -e "${BLUE}[INFO]${NC} $*"; } +log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } +log_error() { echo -e "${RED}[ERROR]${NC} $*"; } +log_success() { echo -e "${GREEN}[OK]${NC} $*"; } + +check_dependencies() { + log_info "Checking dependencies..." + + local missing=() + + # Check for vorbis libraries + if ! pacman -Q libvorbis &>/dev/null; then + missing+=("libvorbis") + fi + + # Check for yay or paru + if ! has_cmd yay && ! has_cmd paru; then + log_error "Neither yay nor paru found. Please install an AUR helper." + exit 1 + fi + + if [[ ${#missing[@]} -gt 0 ]]; then + log_warn "Missing packages: ${missing[*]}" + log_info "Installing missing dependencies..." + sudo pacman -S --needed "${missing[@]}" + else + log_success "All dependencies present" + fi +} + +get_aur_helper() { + if has_cmd yay; then + echo "yay" + elif has_cmd paru; then + echo "paru" + fi +} + +build_stepmania() { + local aur_helper + aur_helper=$(get_aur_helper) + + log_info "Building StepMania with vorbis libraries in LDFLAGS..." + log_info "Using AUR helper: $aur_helper" + + # Export LDFLAGS with vorbis libraries to fix the linking issue + # The static libavcodec.a needs these shared libraries + export LDFLAGS="${LDFLAGS:-} -lvorbis -lvorbisenc -lvorbisfile -logg" + + log_info "LDFLAGS set to: $LDFLAGS" + + # Clean any previous failed build + if [[ -d "$HOME/.cache/$aur_helper/stepmania" ]]; then + log_info "Cleaning previous build cache..." + rm -rf "$HOME/.cache/$aur_helper/stepmania" + fi + + # Build with the modified LDFLAGS + # --noconfirm for non-interactive, --cleanafter to cleanup + "$aur_helper" -S --rebuild --noconfirm stepmania + + log_success "StepMania built successfully!" +} + +alternative_fix_info() { + cat <<'EOF' + +If the automated fix doesn't work, try these alternatives: + +1. Use system ffmpeg instead of static libavcodec: + - Edit the PKGBUILD to use shared ffmpeg libraries + - Remove any bundled/static ffmpeg references + +2. Manually edit CMakeLists.txt: + - Find target_link_libraries for StepMania executable + - Add: vorbis vorbisenc vorbisfile ogg + +3. Check if /usr/local/lib/libavcodec.a is from a custom ffmpeg build: + - If so, rebuild ffmpeg with --enable-shared or remove the static lib + - System ffmpeg in /usr/lib should be preferred + +4. Use the stepmania-git package instead which may have different build config + +EOF +} + +main() { + echo "======================================" + echo " StepMania Build Fix" + echo "======================================" + echo "" + + check_dependencies + echo "" + + log_info "This fix adds vorbis libraries to LDFLAGS to resolve:" + log_info " 'undefined reference to symbol vorbis_encode_setup_vbr'" + echo "" + + read -rp "Proceed with rebuild? [Y/n] " response + case "$response" in + [nN][oO] | [nN]) + log_info "Aborted." + alternative_fix_info + exit 0 + ;; + *) + build_stepmania + ;; + esac +} + +main "$@" diff --git a/scripts/utils/organize_downloads.sh b/scripts/utils/organize_downloads.sh index c3e2c92..8938ee3 100755 --- a/scripts/utils/organize_downloads.sh +++ b/scripts/utils/organize_downloads.sh @@ -10,10 +10,12 @@ set -euo pipefail # Defaults / flags DRY_RUN=false SAMPLE_LIMIT=20 +# Size threshold for "too big" files (in bytes) - default 100MB +SIZE_THRESHOLD=$((100 * 1024 * 1024)) # Simple usage helper usage() { - cat << EOF + cat <&2 - usage - exit 1 - ;; - esac + case "${1}" in + -n | --dry-run) + DRY_RUN=true + shift + ;; + --sample=*) + SAMPLE_LIMIT="${1#*=}" + shift + ;; + -h | --help) + usage + exit 0 + ;; + *) + echo "Unknown option: $1" >&2 + usage + exit 1 + ;; + esac done # Function to check if file has media extension is_media_file() { - local file="$1" - local extension="${file##*.}" - extension=$(echo "$extension" | tr '[:upper:]' '[:lower:]') + local file="$1" + local extension="${file##*.}" + extension=$(echo "$extension" | tr '[:upper:]' '[:lower:]') - # Check if it's an image - for ext in "${IMAGE_EXTENSIONS[@]}"; do - if [[ $extension == "$ext" ]]; then - return 0 - fi - done + # Check if it's an image + for ext in "${IMAGE_EXTENSIONS[@]}"; do + if [[ $extension == "$ext" ]]; then + return 0 + fi + done - # Check if it's a video - for ext in "${VIDEO_EXTENSIONS[@]}"; do - if [[ $extension == "$ext" ]]; then - return 0 - fi - done + # Check if it's a video + for ext in "${VIDEO_EXTENSIONS[@]}"; do + if [[ $extension == "$ext" ]]; then + return 0 + fi + done - return 1 + return 1 +} + +# Function to check if file is too big for archiving +is_too_big() { + local file="$1" + local size + size=$(stat -c%s "$file" 2>/dev/null || echo "0") + [[ $size -gt $SIZE_THRESHOLD ]] +} + +# Function to move oversized files to too_big directory +move_big_files() { + local files=("$@") + local moved_count=0 + + if [[ ${#files[@]} -eq 0 ]]; then + return 0 + fi + + # Create too_big directory if it doesn't exist + mkdir -p "$TOO_BIG_DIR" + + log "Moving ${#files[@]} oversized files to $TOO_BIG_DIR..." + + for file in "${files[@]}"; do + if [[ -f $file ]]; then + local basename + basename=$(basename "$file") + local dest="$TOO_BIG_DIR/$basename" + + # Handle filename collision + if [[ -f $dest ]]; then + local timestamp + timestamp=$(date '+%Y%m%d_%H%M%S') + local name="${basename%.*}" + local ext="${basename##*.}" + if [[ $name == "$ext" ]]; then + dest="$TOO_BIG_DIR/${name}_${timestamp}" + else + dest="$TOO_BIG_DIR/${name}_${timestamp}.${ext}" + fi + fi + + if mv "$file" "$dest" 2>/dev/null; then + log "Moved (too big): $(basename "$file") -> $dest" + moved_count=$((moved_count + 1)) + else + log "ERROR: Failed to move $(basename "$file")" + fi + fi + done + + log "Successfully moved $moved_count oversized files." } # Function to find media files in a directory (non-recursive for home, avoid common system dirs) find_media_files() { - local search_dir="$1" - local files=() - # Directories to exclude under Downloads - local -a EXCLUDES=( - ".git" ".hg" ".svn" ".cache" "node_modules" "dist" "build" "out" "target" "coverage" "__pycache__" "venv" ".venv" - # previous staging dirs created by this script - ".media_organize_" "media_organize_" - ) + local search_dir="$1" + local files=() + # Directories to exclude under Downloads + local -a EXCLUDES=( + ".git" ".hg" ".svn" ".cache" "node_modules" "dist" "build" "out" "target" "coverage" "__pycache__" "venv" ".venv" + # previous staging dirs created by this script + ".media_organize_" "media_organize_" + # too_big folder for oversized files + "too_big" + ) - if [[ $search_dir == "$HOME_DIR" ]]; then - # For home directory, only check files directly in ~ (not subdirectories) - # Exclude common system/config directories - while IFS= read -r -d '' file; do - local basename - basename=$(basename "$file") - # Skip hidden files and common system directories - if [[ ! $basename =~ ^\. ]] && [[ -f $file ]]; then - if is_media_file "$file"; then - files+=("$file") - fi - fi - done < <(find "$search_dir" -maxdepth 1 -type f -print0 2> /dev/null) - else - # For Downloads, search recursively, pruning excluded directories - # Build prune expression - local prune_expr=() - for ex in "${EXCLUDES[@]}"; do - prune_expr+=(-name "$ex*" -o) - done - # Remove trailing -o - unset 'prune_expr[${#prune_expr[@]}-1]' + if [[ $search_dir == "$HOME_DIR" ]]; then + # For home directory, only check files directly in ~ (not subdirectories) + # Exclude common system/config directories + while IFS= read -r -d '' file; do + local basename + basename=$(basename "$file") + # Skip hidden files and common system directories + if [[ ! $basename =~ ^\. ]] && [[ -f $file ]]; then + if is_media_file "$file"; then + files+=("$file") + fi + fi + done < <(find "$search_dir" -maxdepth 1 -type f -print0 2>/dev/null) + else + # For Downloads, search recursively, pruning excluded directories + # Build prune expression + local prune_expr=() + for ex in "${EXCLUDES[@]}"; do + prune_expr+=(-name "$ex*" -o) + done + # Remove trailing -o + unset 'prune_expr[${#prune_expr[@]}-1]' - while IFS= read -r -d '' file; do - if is_media_file "$file"; then - files+=("$file") - fi - done < <(find "$search_dir" \( -type d \( "${prune_expr[@]}" \) -prune \) -o -type f -print0 2> /dev/null) - fi + while IFS= read -r -d '' file; do + if is_media_file "$file"; then + files+=("$file") + fi + done < <(find "$search_dir" \( -type d \( "${prune_expr[@]}" \) -prune \) -o -type f -print0 2>/dev/null) + fi - printf '%s\n' "${files[@]}" + printf '%s\n' "${files[@]}" } # Function to create timestamped zip archive create_media_archive() { - local files=("$@") + local files=("$@") - if [[ ${#files[@]} -eq 0 ]]; then - log "No media files found to archive." - return 0 - fi + if [[ ${#files[@]} -eq 0 ]]; then + log "No media files found to archive." + return 0 + fi - # Create timestamp for archive name - local timestamp - timestamp=$(date '+%Y%m%d_%H%M%S') - local archive_name="media_archive_${timestamp}.zip" - local archive_path="$DOWNLOADS_DIR/$archive_name" + # Create timestamp for archive name + local timestamp + timestamp=$(date '+%Y%m%d_%H%M%S') + local archive_name="media_archive_${timestamp}.zip" + local archive_path="$DOWNLOADS_DIR/$archive_name" - # Create temporary directory (fallback to /tmp if needed) - if ! mkdir -p "$TEMP_DIR" 2> /dev/null; then - TEMP_DIR="/tmp/media_organize_$$" - mkdir -p "$TEMP_DIR" - fi + # Create temporary directory (fallback to /tmp if needed) + if ! mkdir -p "$TEMP_DIR" 2>/dev/null; then + TEMP_DIR="/tmp/media_organize_$$" + mkdir -p "$TEMP_DIR" + fi - # Ensure temp dir is cleaned up on function return; trap unsets itself after running - trap 'rm -rf "$TEMP_DIR" 2>/dev/null || true; trap - RETURN' RETURN + # Ensure temp dir is cleaned up on function return; trap unsets itself after running + trap 'rm -rf "$TEMP_DIR" 2>/dev/null || true; trap - RETURN' RETURN - log "Found ${#files[@]} media files to archive." - log "Creating archive: $archive_path" + log "Found ${#files[@]} media files to archive." + log "Creating archive: $archive_path" - # Copy files to temp directory maintaining relative structure - local successfully_copied=() - local copy_errors=0 + # Copy files to temp directory maintaining relative structure + local successfully_copied=() + local copy_errors=0 - for file in "${files[@]}"; do - if [[ -f $file ]]; then - local relative_path="" - if [[ $file == "$DOWNLOADS_DIR"* ]]; then - relative_path="downloads/${file#"$DOWNLOADS_DIR"/}" - else - relative_path="home/${file#"$HOME_DIR"/}" - fi + for file in "${files[@]}"; do + if [[ -f $file ]]; then + local relative_path="" + if [[ $file == "$DOWNLOADS_DIR"* ]]; then + relative_path="downloads/${file#"$DOWNLOADS_DIR"/}" + else + relative_path="home/${file#"$HOME_DIR"/}" + fi - local temp_file="$TEMP_DIR/$relative_path" - local temp_dir - temp_dir=$(dirname "$temp_file") + local temp_file="$TEMP_DIR/$relative_path" + local temp_dir + temp_dir=$(dirname "$temp_file") - mkdir -p "$temp_dir" - # Check readability first to provide a clearer error - if [[ ! -r $file ]]; then - log "WARNING: Cannot read $file (permission denied?)" - ((copy_errors++)) - continue - fi + mkdir -p "$temp_dir" + # Check readability first to provide a clearer error + if [[ ! -r $file ]]; then + log "WARNING: Cannot read $file (permission denied?)" + ((copy_errors++)) + continue + fi - # Attempt copy and capture any error for logging - local cp_err - if cp_err=$(cp -p "$file" "$temp_file" 2>&1); then - successfully_copied+=("$file") - else - # Surface the cp error so the user can see the reason - log "WARNING: Failed to copy $file -> $cp_err" - # Special hint for space issues - if echo "$cp_err" | grep -qi "No space left on device"; then - log "HINT: Not enough free space to stage files. Using $TEMP_DIR. Free up space or change TEMP_DIR." - fi - ((copy_errors++)) - fi - fi - done + # Attempt copy and capture any error for logging + local cp_err + if cp_err=$(cp -p "$file" "$temp_file" 2>&1); then + successfully_copied+=("$file") + else + # Surface the cp error so the user can see the reason + log "WARNING: Failed to copy $file -> $cp_err" + # Special hint for space issues + if echo "$cp_err" | grep -qi "No space left on device"; then + log "HINT: Not enough free space to stage files. Using $TEMP_DIR. Free up space or change TEMP_DIR." + fi + ((copy_errors++)) + fi + fi + done - if [[ ${#successfully_copied[@]} -eq 0 ]]; then - log "ERROR: No files were successfully copied to temp directory." - rm -rf "$TEMP_DIR" - return 1 - fi + if [[ ${#successfully_copied[@]} -eq 0 ]]; then + log "ERROR: No files were successfully copied to temp directory." + rm -rf "$TEMP_DIR" + return 1 + fi - if [[ $copy_errors -gt 0 ]]; then - log "WARNING: $copy_errors files failed to copy." - fi + if [[ $copy_errors -gt 0 ]]; then + log "WARNING: $copy_errors files failed to copy." + fi - # Create zip archive with maximum compression - log "Creating zip archive with ${#successfully_copied[@]} files..." - cd "$TEMP_DIR" - if zip -9 -r "$archive_path" . 2>&1; then - log "Successfully created archive with ${#successfully_copied[@]} files." + # Create zip archive with maximum compression + log "Creating zip archive with ${#successfully_copied[@]} files..." + cd "$TEMP_DIR" + if zip -9 -r "$archive_path" . 2>&1; then + log "Successfully created archive with ${#successfully_copied[@]} files." - # Verify the zip file was actually created and is not empty - if [[ ! -f $archive_path ]]; then - log "ERROR: Archive file was not created at $archive_path" - rm -rf "$TEMP_DIR" - return 1 - fi + # Verify the zip file was actually created and is not empty + if [[ ! -f $archive_path ]]; then + log "ERROR: Archive file was not created at $archive_path" + rm -rf "$TEMP_DIR" + return 1 + fi - local archive_size - archive_size=$(stat -c%s "$archive_path" 2> /dev/null || echo "0") - if [[ $archive_size -eq 0 ]]; then - log "ERROR: Archive file is empty" - rm -rf "$TEMP_DIR" - return 1 - fi + local archive_size + archive_size=$(stat -c%s "$archive_path" 2>/dev/null || echo "0") + if [[ $archive_size -eq 0 ]]; then + log "ERROR: Archive file is empty" + rm -rf "$TEMP_DIR" + return 1 + fi - # Remove original files only if zip was successful - local removed_count=0 - local remove_errors=0 + # Remove original files only if zip was successful + local removed_count=0 + local remove_errors=0 - log "Starting to remove ${#successfully_copied[@]} original files..." + log "Starting to remove ${#successfully_copied[@]} original files..." - # Temporarily disable strict error handling for file removal - set +e + # Temporarily disable strict error handling for file removal + set +e - for file in "${successfully_copied[@]}"; do - if [[ -f $file ]]; then - if rm "$file" 2> /dev/null; then - removed_count=$((removed_count + 1)) - log "Removed: $(basename "$file")" - else - remove_errors=$((remove_errors + 1)) - log "ERROR: Failed to remove $(basename "$file")" - fi - else - log "WARNING: File no longer exists: $(basename "$file")" - fi - done + for file in "${successfully_copied[@]}"; do + if [[ -f $file ]]; then + if rm "$file" 2>/dev/null; then + removed_count=$((removed_count + 1)) + log "Removed: $(basename "$file")" + else + remove_errors=$((remove_errors + 1)) + log "ERROR: Failed to remove $(basename "$file")" + fi + else + log "WARNING: File no longer exists: $(basename "$file")" + fi + done - # Re-enable strict error handling - set -e + # Re-enable strict error handling + set -e - log "Successfully removed $removed_count original files." - if [[ $remove_errors -gt 0 ]]; then - log "WARNING: Failed to remove $remove_errors files." - fi - log "Archive size: $(du -h "$archive_path" | cut -f1)" + log "Successfully removed $removed_count original files." + if [[ $remove_errors -gt 0 ]]; then + log "WARNING: Failed to remove $remove_errors files." + fi + log "Archive size: $(du -h "$archive_path" | cut -f1)" - # Cleanup temp directory (trap will also attempt, which is safe) - rm -rf "$TEMP_DIR" + # Cleanup temp directory (trap will also attempt, which is safe) + rm -rf "$TEMP_DIR" - # Return success only if we removed files or there were no files to remove - if [[ $removed_count -gt 0 ]] || [[ ${#successfully_copied[@]} -eq 0 ]]; then - return 0 - else - log "ERROR: Failed to remove any files after successful archive creation." - return 1 - fi - else - log "ERROR: Failed to create archive. Original files preserved." - log "Zip command failed." - rm -rf "$TEMP_DIR" - return 1 - fi + # Return success only if we removed files or there were no files to remove + if [[ $removed_count -gt 0 ]] || [[ ${#successfully_copied[@]} -eq 0 ]]; then + return 0 + else + log "ERROR: Failed to remove any files after successful archive creation." + return 1 + fi + else + log "ERROR: Failed to create archive. Original files preserved." + log "Zip command failed." + rm -rf "$TEMP_DIR" + return 1 + fi } # Main execution main() { - log "Starting media file organization..." + log "Starting media file organization..." - # Check if required directories exist - if [[ ! -d $DOWNLOADS_DIR ]]; then - log "ERROR: Downloads directory not found: $DOWNLOADS_DIR" - exit 1 - fi + # Check if required directories exist + if [[ ! -d $DOWNLOADS_DIR ]]; then + log "ERROR: Downloads directory not found: $DOWNLOADS_DIR" + exit 1 + fi - if [[ ! -d $HOME_DIR ]]; then - log "ERROR: Home directory not found: $HOME_DIR" - exit 1 - fi + if [[ ! -d $HOME_DIR ]]; then + log "ERROR: Home directory not found: $HOME_DIR" + exit 1 + fi - # Check if zip command is available - if ! command -v zip > /dev/null 2>&1; then - log "ERROR: zip command not found. Please install zip package." - exit 1 - fi + # Check if zip command is available + if ! command -v zip >/dev/null 2>&1; then + log "ERROR: zip command not found. Please install zip package." + exit 1 + fi - # Find all media files - log "Scanning for media files..." - local all_files=() + # Find all media files + log "Scanning for media files..." + local all_files=() - # Find files in Downloads directory - log "Scanning Downloads directory..." - while IFS= read -r file; do - [[ -n $file ]] && all_files+=("$file") - done < <(find_media_files "$DOWNLOADS_DIR") + # Find files in Downloads directory + log "Scanning Downloads directory..." + while IFS= read -r file; do + [[ -n $file ]] && all_files+=("$file") + done < <(find_media_files "$DOWNLOADS_DIR") - # Find files in home directory (only direct files, not subdirectories) - log "Scanning home directory (root level only)..." - while IFS= read -r file; do - [[ -n $file ]] && all_files+=("$file") - done < <(find_media_files "$HOME_DIR") + # Find files in home directory (only direct files, not subdirectories) + log "Scanning home directory (root level only)..." + while IFS= read -r file; do + [[ -n $file ]] && all_files+=("$file") + done < <(find_media_files "$HOME_DIR") - if $DRY_RUN; then - log "Dry-run mode: summarizing what would be archived." - if [[ ${#all_files[@]} -eq 0 ]]; then - log "No media files found to organize." - exit 0 - fi + if $DRY_RUN; then + log "Dry-run mode: summarizing what would be archived." + if [[ ${#all_files[@]} -eq 0 ]]; then + log "No media files found to organize." + exit 0 + fi - # Count by extension - declare -A ext_counts=() - # Count by top-level directory under Downloads - declare -A dir_counts=() - # Sample paths for suspect extensions - declare -A samples_ts=() - declare -A samples_svg=() - declare -A samples_ico=() + # Separate big files for dry-run reporting + local dry_regular_files=() + local dry_big_files=() + for f in "${all_files[@]}"; do + if is_too_big "$f"; then + dry_big_files+=("$f") + else + dry_regular_files+=("$f") + fi + done - for f in "${all_files[@]}"; do - # Extension - ext="${f##*.}" - ext="${ext,,}" - ((ext_counts["$ext"]++)) || true + # Count by extension + declare -A ext_counts=() + # Count by top-level directory under Downloads + declare -A dir_counts=() + # Sample paths for suspect extensions + declare -A samples_ts=() + declare -A samples_svg=() + declare -A samples_ico=() - # Top directory under Downloads - if [[ $f == "$DOWNLOADS_DIR"/* ]]; then - rel="${f#"$DOWNLOADS_DIR"/}" - topdir="${rel%%/*}" - [[ $topdir == "$rel" ]] && topdir="." - ((dir_counts["$topdir"]++)) || true - else - ((dir_counts["~"]++)) || true - fi + for f in "${all_files[@]}"; do + # Extension + ext="${f##*.}" + ext="${ext,,}" + ((ext_counts["$ext"]++)) || true - # Samples for suspect extensions - case "$ext" in - ts) - if [[ ${#samples_ts[@]} -lt $SAMPLE_LIMIT ]]; then samples_ts["$f"]=1; fi - ;; - svg) - if [[ ${#samples_svg[@]} -lt $SAMPLE_LIMIT ]]; then samples_svg["$f"]=1; fi - ;; - ico) - if [[ ${#samples_ico[@]} -lt $SAMPLE_LIMIT ]]; then samples_ico["$f"]=1; fi - ;; - esac - done + # Top directory under Downloads + if [[ $f == "$DOWNLOADS_DIR"/* ]]; then + rel="${f#"$DOWNLOADS_DIR"/}" + topdir="${rel%%/*}" + [[ $topdir == "$rel" ]] && topdir="." + ((dir_counts["$topdir"]++)) || true + else + ((dir_counts["~"]++)) || true + fi - echo "" - echo "Summary by extension (top 20):" - for k in "${!ext_counts[@]}"; do - printf "%8d %s\n" "${ext_counts[$k]}" "$k" - done | sort -nr | head -n 20 + # Samples for suspect extensions + case "$ext" in + ts) + if [[ ${#samples_ts[@]} -lt $SAMPLE_LIMIT ]]; then samples_ts["$f"]=1; fi + ;; + svg) + if [[ ${#samples_svg[@]} -lt $SAMPLE_LIMIT ]]; then samples_svg["$f"]=1; fi + ;; + ico) + if [[ ${#samples_ico[@]} -lt $SAMPLE_LIMIT ]]; then samples_ico["$f"]=1; fi + ;; + esac + done - echo "" - echo "Top contributing directories under Downloads (top 20):" - for k in "${!dir_counts[@]}"; do - printf "%8d %s\n" "${dir_counts[$k]}" "$k" - done | sort -nr | head -n 20 + echo "" + echo "Summary by extension (top 20):" + for k in "${!ext_counts[@]}"; do + printf "%8d %s\n" "${ext_counts[$k]}" "$k" + done | sort -nr | head -n 20 - echo "" - if [[ ${#samples_ts[@]} -gt 0 ]]; then - echo "Sample .ts files (TypeScript vs Transport Stream) up to $SAMPLE_LIMIT:" - for p in "${!samples_ts[@]}"; do echo " $p"; done | sort - echo "" - fi - if [[ ${#samples_svg[@]} -gt 0 ]]; then - echo "Sample .svg files up to $SAMPLE_LIMIT:" - for p in "${!samples_svg[@]}"; do echo " $p"; done | sort - echo "" - fi - if [[ ${#samples_ico[@]} -gt 0 ]]; then - echo "Sample .ico files up to $SAMPLE_LIMIT:" - for p in "${!samples_ico[@]}"; do echo " $p"; done | sort - echo "" - fi + echo "" + echo "Top contributing directories under Downloads (top 20):" + for k in "${!dir_counts[@]}"; do + printf "%8d %s\n" "${dir_counts[$k]}" "$k" + done | sort -nr | head -n 20 - echo "Total files that would be archived: ${#all_files[@]}" - echo "(Use: $(basename "$0") --dry-run --sample=50 to see more examples.)" - exit 0 - fi + echo "" + if [[ ${#samples_ts[@]} -gt 0 ]]; then + echo "Sample .ts files (TypeScript vs Transport Stream) up to $SAMPLE_LIMIT:" + for p in "${!samples_ts[@]}"; do echo " $p"; done | sort + echo "" + fi + if [[ ${#samples_svg[@]} -gt 0 ]]; then + echo "Sample .svg files up to $SAMPLE_LIMIT:" + for p in "${!samples_svg[@]}"; do echo " $p"; done | sort + echo "" + fi + if [[ ${#samples_ico[@]} -gt 0 ]]; then + echo "Sample .ico files up to $SAMPLE_LIMIT:" + for p in "${!samples_ico[@]}"; do echo " $p"; done | sort + echo "" + fi - # Create archive if files found - if [[ ${#all_files[@]} -gt 0 ]]; then - create_media_archive "${all_files[@]}" - log "Media organization completed successfully." - else - log "No media files found to organize." - fi + echo "Files to archive (regular size): ${#dry_regular_files[@]}" + echo "Files to move to too_big folder: ${#dry_big_files[@]}" + if [[ ${#dry_big_files[@]} -gt 0 ]]; then + echo "" + echo "Oversized files (> $((SIZE_THRESHOLD / 1024 / 1024))MB) that would be moved to too_big/:" + for f in "${dry_big_files[@]}"; do + local size + size=$(du -h "$f" 2>/dev/null | cut -f1) + echo " [$size] $f" + done | head -n "$SAMPLE_LIMIT" + if [[ ${#dry_big_files[@]} -gt $SAMPLE_LIMIT ]]; then + echo " ... and $((${#dry_big_files[@]} - SAMPLE_LIMIT)) more" + fi + fi + echo "" + echo "Total files that would be organized: ${#all_files[@]}" + echo "(Use: $(basename "$0") --dry-run --sample=50 to see more examples.)" + exit 0 + fi + + # Separate files into regular and too-big categories + local regular_files=() + local big_files=() + + for file in "${all_files[@]}"; do + if is_too_big "$file"; then + big_files+=("$file") + else + regular_files+=("$file") + fi + done + + log "Found ${#regular_files[@]} regular files and ${#big_files[@]} oversized files." + + # Move oversized files to too_big directory + if [[ ${#big_files[@]} -gt 0 ]]; then + move_big_files "${big_files[@]}" + fi + + # Create archive for regular-sized files + if [[ ${#regular_files[@]} -gt 0 ]]; then + create_media_archive "${regular_files[@]}" + log "Media organization completed successfully." + else + log "No regular-sized media files found to archive." + fi } # Run main function