From 930d282cccae84a4abfb8c73c0ec72b27f84462a Mon Sep 17 00:00:00 2001 From: 0n1cOn3 <0n1cOn3@gmx.ch> Date: Mon, 7 Jul 2025 02:38:32 +0200 Subject: [PATCH] Tools added --- flac2m4a | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++++ typechecker | 56 +++++++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 flac2m4a create mode 100644 typechecker diff --git a/flac2m4a b/flac2m4a new file mode 100644 index 0000000..c2b5ddb --- /dev/null +++ b/flac2m4a @@ -0,0 +1,125 @@ +#!/usr/bin/env bash +# ----------------------------------------------------------------------------- +# Title: flac2m4a +# Purpose: Comprehensive flac to m4a auto-converter +# Version: 1.0.0 +# Developer: h@x +# ----------------------------------------------------------------------------- + +set -euo pipefail +trap 'echo "✗ ERROR on line $LINENO (exit code $?): ${BASH_COMMAND}" >&2' ERR +IFS=$'\n\t' + +# === Input & output directories === +SRC_DIR="${1:-.}" +DST_DIR="${2:-./m4a_output}" +[[ -d "$SRC_DIR" ]] || { + echo "ERROR: Source directory '$SRC_DIR' not found." >&2 + exit 1 +} +mkdir -p "$DST_DIR" + +# === FFmpeg capability check === +if ! ffmpeg -hide_banner -muxers 2>/dev/null | grep -qw mov; then + echo "ERROR: FFmpeg build lacks the MP4/mov muxer required for .m4a outputs." >&2 + exit 1 +fi + +# === Codec selection === +declare -a CODEC_OPTS +if ffmpeg -hide_banner -encoders 2>/dev/null | grep -qw libfdk_aac; then + CODEC_OPTS=( -c:a libfdk_aac -profile:a aac_he_v2 -cutoff 18000 -b:a 256k ) + echo "INFO: Using libfdk_aac HE-AAC v2 @256 kbps." >&2 +else + CODEC_OPTS=( -c:a aac -b:a 256k ) + echo "WARNING: Falling back to native AAC @256 kbps." >&2 +fi + +declare -a META_OPTS=( + -map_metadata 0 + -id3v2_version 3 + -write_id3v1 1 + -metadata:s:a encoder="ffmpeg/${CODEC_OPTS[1]}" +) +declare -a FFMPEG_OPTS=( -hide_banner -loglevel error -nostdin -n ) + +# === Discover .flac files === +mapfile -d '' files < <(find "$SRC_DIR" -type f -iname '*.flac' -print0) +TOTAL=${#files[@]} + +(( TOTAL > 0 )) || { + echo "Nothing to do: no .flac files found under '$SRC_DIR'." >&2 + exit 0 +} +echo "FOUND: $TOTAL .flac file(s) to process." >&2 + +# === Progress-bar initialization === +BAR_WIDTH=40 +COUNT=0 +LAST_PCT=-1 + +# pre-fill bar with dots, print initial line +empty_bar=$(printf '%*s' "$BAR_WIDTH" '' | tr ' ' '.') +printf "Progress: [%-${BAR_WIDTH}s] 0%% (%d/%d)\n" \ + "$empty_bar" 0 "$TOTAL" >&2 + +# === Conversion loop with ANSI overwrite === +##declare -a SKIPPED CONVERTED +##declare -A FAILED + +declare -a SKIPPED=() +declare -a CONVERTED=() +declare -A FAILED=() + +for SRC_FILE in "${files[@]}"; do + (( COUNT += 1 )) + REL_PATH="${SRC_FILE#"$SRC_DIR"/}" + DST_FILE="$DST_DIR/${REL_PATH%.flac}.m4a" + mkdir -p "$(dirname "$DST_FILE")" + + # calculate and render updated bar if % changed + PCT=$(( COUNT * 100 / TOTAL )) + if (( PCT != LAST_PCT )); then + LAST_PCT=$PCT + filled=$(( COUNT * BAR_WIDTH / TOTAL )) + fbar=$(printf '%*s' "$filled" '' | tr ' ' '#') + ebar=$(printf '%*s' "$(( BAR_WIDTH - filled ))" '' | tr ' ' '.') + # move cursor up one line, carriage-return, then overwrite + printf "\033[1A\rProgress: [%s%s] %3d%% (%d/%d)\n" \ + "$fbar" "$ebar" "$PCT" "$COUNT" "$TOTAL" >&2 + fi + + # skip or convert + if [[ -f "$DST_FILE" ]]; then + SKIPPED+=("$REL_PATH") + else + if ffmpeg "${FFMPEG_OPTS[@]}" -i "$SRC_FILE" \ + "${CODEC_OPTS[@]}" -vn "${META_OPTS[@]}" -f ipod "$DST_FILE"; then + CONVERTED+=("$REL_PATH") + else + FAILED["$REL_PATH"]=1 + fi + fi +done + +# === Summary === +echo >&2 +printf "========== SUMMARY ==========\n" >&2 + +printf "Skipped (already present): %d\n" "${#SKIPPED[@]}" >&2 +for f in "${SKIPPED[@]}"; do printf " • %s\n" "$f"; done >&2 + +echo >&2 +printf "Converted: %d\n" "${#CONVERTED[@]}" >&2 +for f in "${CONVERTED[@]}"; do printf " • %s\n" "$f"; done >&2 + +echo >&2 +printf "Failed: %d\n" "${#FAILED[@]}" >&2 +for f in "${!FAILED[@]}"; do printf " • %s\n" "$f"; done >&2 + +echo >&2 +printf "Output directory: '%s'\n" "$DST_DIR" >&2 +echo "=============================" >&2 + +# appropriate exit code +(( ${#FAILED[@]} )) && exit 2 || exit 0 diff --git a/typechecker b/typechecker new file mode 100644 index 0000000..402287d --- /dev/null +++ b/typechecker @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# ----------------------------------------------------------------------------- +# Title: audit-dir-content.sh +# Purpose: Comprehensive inventory of file types and subdirectory sizes +# Version: 1.0.0 +# Developer: h@x +# ----------------------------------------------------------------------------- + +set -euo pipefail +IFS=$'\n\t' + +WORKDIR="${PWD##*/}" +echo "=== typecheck - Check your folders for filetypes v1.1 ===" +echo +echo "=== Inventory for: ${WORKDIR} ===" +echo + +## 1) FILE-TYPE COUNTS +echo ">> File-type breakdown (MIME) with counts" +# Gather MIME types for all regular files at depth=1 +mapfile -t mime_types < <( + find . -maxdepth 1 -type f -exec file --mime-type --brief {} + 2>/dev/null +) + +if [ "${#mime_types[@]}" -eq 0 ]; then + echo " No regular files detected." +else + printf '%s\n' "${mime_types[@]}" \ + | sort \ + | uniq -c \ + | awk '{ printf " %-5s %s\n", $1, $2 }' + echo + printf " Total files: %d\n" "${#mime_types[@]}" +fi + +echo +## 2) SUBDIRECTORY SIZES +echo ">> Subdirectory inventory with sizes" +# Identify each directory (excluding the current “.”) at depth=1 +mapfile -t subdirs < <(find . -maxdepth 1 -mindepth 1 -type d | sort) + +if [ "${#subdirs[@]}" -eq 0 ]; then + echo " No subdirectories present." +else + # Compute human-readable size for each + for dir in "${subdirs[@]}"; do + # du -sh prints: “ ” + size=$(du -sh -- "${dir}" 2>/dev/null | awk '{print $1}') + name="${dir#./}" + printf " %-10s %s\n" "${size}" "${name}" + done + echo + printf " Total subdirectories: %d\n" "${#subdirs[@]}" +fi + +echo "=== End of report ==="