diff --git a/quiethost/metadata_from_fileneme.sh b/quiethost/metadata_from_fileneme.sh new file mode 100644 index 0000000..f52c9a8 --- /dev/null +++ b/quiethost/metadata_from_fileneme.sh @@ -0,0 +1,64 @@ +#!/bin/bash +set -Eeuo pipefail +escape_kid3() { + printf '%s' "$1" | sed "s/'/'\\\\''/g" +} +DRY_RUN=false + +if [[ "$1" == "--dry-run" ]]; then + DRY_RUN=true + shift +fi + +if [[ $# -ne 1 ]]; then + echo "Usage: $0 [--dry-run] file.txt" + exit 1 +fi + +list="$1" + +if [[ ! -f "$list" ]]; then + echo "ERROR: File list not found: $list" + exit 1 +fi + +while IFS= read -r file; do + + [[ -z "$file" ]] && continue + + if [[ ! -e "$file" ]]; then + echo "ERROR: Missing file: $file" + exit 1 + fi + + if [[ -d "$file" ]]; then + echo "WARNING: Is a directory, skipping: $file" + continue + fi + + bookdir="$(dirname "$file")" + booktitle="$(basename "$bookdir")" + author="$(basename "$(dirname "$bookdir")")" + + echo + echo "File: $file" + echo "Artist: $author" + echo "Album: $booktitle" + + if $DRY_RUN; then + echo "DRY RUN: no changes made" + continue + fi + +artist=$(escape_kid3 "$author") +album=$(escape_kid3 "$booktitle") + +kid3-cli "$file" \ + -c "tag 1" \ + -c "set artist '$artist'" \ + -c "set album '$album'" \ + -c "tag 2" \ + -c "set artist '$artist'" \ + -c "set album '$album'" \ + -c save +done < "$list" diff --git a/quiethost/metadata_read_by_from_file.sh b/quiethost/metadata_read_by_from_file.sh new file mode 100644 index 0000000..ae936a4 --- /dev/null +++ b/quiethost/metadata_read_by_from_file.sh @@ -0,0 +1,87 @@ +#!/bin/bash + +DRY_RUN=false +DELETE=false + +while [[ $# -gt 0 ]]; do + case "$1" in + --dry-run) + DRY_RUN=true + shift + ;; + --delete) + DELETE=true + shift + ;; + *) + break + ;; + esac +done + +if [[ $# -ne 1 ]]; then + echo "Usage: $0 [--dry-run] [--delete] file.txt" + exit 1 +fi + +list="$1" + +while IFS= read -r file; do + + [[ -z "$file" ]] && continue + + if [[ ! -e "$file" ]]; then + echo "ERROR: Missing file: $file" + exit 1 + fi + if [[ -d "$file" ]]; then + echo "WARNING: Is a directory, skipping: $file" + continue + fi + # Get comment from ID3v2 + comment=$(kid3-cli "$file" \ + -c "tag 2" \ + -c "get comment" 2>/dev/null) + + [[ -z "$comment" ]] && continue + + echo + echo "File: $file" + echo "Comment: $comment" + + composer="" + + # Match: read by Name; text + if [[ "$comment" =~ ^[Rr]ead[[:space:]]+[Bb]y[[:space:]]+([^;]+)\; ]]; then + composer="${BASH_REMATCH[1]}" + composer="$(echo "$composer" | sed 's/[[:space:]]*$//')" + + echo "Found narrator: $composer" + + if $DRY_RUN; then + echo "DRY RUN: set composer '$composer', delete comment" + else + kid3-cli "$file" \ + -c "tag 2" \ + -c "set composer '$composer'" \ + -c "set comment ''" \ + -c save + fi + + elif [[ ${#comment} -lt 40 ]]; then + + echo "Short comment (${#comment} chars): $comment" + + if $DELETE; then + if $DRY_RUN; then + echo "DRY RUN: delete comment" + else + kid3-cli "$file" \ + -c "tag 2" \ + -c "set comment ''" \ + -c save + fi + fi + fi + +done < "$list" diff --git a/quiethost/metadata_search_display.sh b/quiethost/metadata_search_display.sh new file mode 100644 index 0000000..8d1a65f --- /dev/null +++ b/quiethost/metadata_search_display.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash + +root="${1:-.}" +mode="${2:-}" +THRESHOLD=40 +find "$root" \( -iname '*.mp3' -o -iname '*.m4a' -o -iname '*.m4b' \) -print0 | +while IFS= read -r -d '' f; do + + comment=$(ffprobe -v error \ + -show_entries format_tags=comment \ + -of default=nw=1:nk=1 \ + -- "$f") + + description=$(ffprobe -v error \ + -show_entries format_tags=description \ + -of default=nw=1:nk=1 \ + -- "$f") + + # trim comment + comment="${comment#"${comment%%[![:space:]]*}"}" + comment="${comment%"${comment##*[![:space:]]}"}" + + # ---- DELETE MODE ---- + if [[ "$mode" == "del" && -n "$comment" && ${#comment} -lt $THRESHOLD ]]; then + tmp=$(mktemp -p "$(dirname -- "$f")" ".fftmp.XXXXXX.${f##*.}") || continue + + echo "Removing short comment: $f" + echo " comment: $comment" + + ffmpeg -nostdin -v error -y \ + -i "$f" \ + -map 0 \ + -map_metadata 0 \ + -c copy \ + -metadata comment= \ + "$tmp" && + mv -f -- "$tmp" "$f" || { + echo "FAILED: $f" >&2 + rm -f -- "$tmp" + } + + continue + fi + + # ---- SCAN MODE ---- + # only show short non-empty tags (<20 chars) + for tag in comment description; do + value="${!tag}" + + value="${value#"${value%%[![:space:]]*}"}" + value="${value%"${value##*[![:space:]]}"}" + + if [[ -n "$value" && ${#value} -lt $THRESHOLD ]]; then + printf "%s\n %s: %s\n\n" "$f" "$tag" "$value" + fi + done + +done diff --git a/quiethost/metadata_search_replace.sh b/quiethost/metadata_search_replace.sh new file mode 100644 index 0000000..918f6a4 --- /dev/null +++ b/quiethost/metadata_search_replace.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash + +find . \( -iname '*.mp3' -o -iname '*.m4a' -o -iname '*.m4b' \) -print0 | +while IFS= read -r -d '' f; do + + comment=$(ffprobe -v error \ + -show_entries format_tags=comment \ + -of default=nw=1:nk=1 \ + -- "$f") + + [[ $comment =~ ^[Rr]ead[[:space:]]+[Bb]y[[:space:]]+(.+) ]] || continue + + composer="${BASH_REMATCH[1]}" + echo "Updating: $f" + echo " Composer: $composer" + + dir=$(dirname -- "$f") + base=$(basename -- "$f") + ext="${base##*.}" + + case "${ext,,}" in + mp3) format=mp3 ;; + m4a|m4b) format=ipod ;; + *) continue ;; + esac + + tmp=$(mktemp -p "$dir" ".${base}.XXXXXX") || { + echo "Couldn't create temporary file in $dir" >&2 + continue + } + + if ffmpeg -nostdin -v error -y \ + -i "$f" \ + -map 0 \ + -map_metadata 0 \ + -c copy \ + -metadata composer="$composer" \ + -metadata comment= \ + -f "$format" \ + "$tmp" + then + mv -f -- "$tmp" "$f" + else + echo "FAILED: $f" >&2 + rm -f -- "$tmp" + fi +done