summaryrefslogtreecommitdiff
path: root/ar/.local/bin/rmev
diff options
context:
space:
mode:
Diffstat (limited to 'ar/.local/bin/rmev')
-rwxr-xr-xar/.local/bin/rmev178
1 files changed, 178 insertions, 0 deletions
diff --git a/ar/.local/bin/rmev b/ar/.local/bin/rmev
new file mode 100755
index 0000000..954b06d
--- /dev/null
+++ b/ar/.local/bin/rmev
@@ -0,0 +1,178 @@
+#!/usr/bin/env bash
+# rmev - clean up videos that mpv integrity-check flagged as corrupt.
+#
+# The corrupt list comes from integrity_cache.tsv (current status). A file is
+# only eligible for deletion when its on-disk mtime/size exactly match what the
+# cache recorded. (If the file changed after the scan it is skipped as
+# "changed" and a re-scan is suggested.)
+#
+# Default behaviour:
+# * Show the target list and ask for y/N confirmation before deleting.
+# * Use trash-put (trash) if available, otherwise fall back to rm.
+# * After deletion, remove the entries from integrity_cache.tsv and corrupted.log.
+
+set -euo pipefail
+
+CONFIG_DIR="${MPV_CONFIG_DIR:-$HOME/.config/mpv}"
+CACHE="$CONFIG_DIR/integrity_cache.tsv"
+LOG="$CONFIG_DIR/corrupted.log"
+
+DRY=0
+FORCE=0
+MODE=auto # MODE: auto|trash|rm
+
+usage() {
+ cat <<'EOF'
+Usage: rmev [options]
+
+Delete video files that mpv integrity-check flagged as corrupt.
+
+Options:
+ -n, --dry-run Show the targets without deleting anything
+ -f, --force Delete without the confirmation prompt
+ -t, --trash Move to trash (requires trash-put)
+ -r, --rm Permanently delete (rm) instead of trashing
+ -h, --help Show this help
+
+Default: trash if trash-put exists, otherwise rm. Confirms the list first.
+Deleted files are also removed from integrity_cache.tsv and corrupted.log.
+EOF
+}
+
+while [ $# -gt 0 ]; do
+ case "$1" in
+ -n | --dry-run) DRY=1 ;;
+ -f | --force) FORCE=1 ;;
+ -t | --trash) MODE=trash ;;
+ -r | --rm) MODE=rm ;;
+ -h | --help)
+ usage
+ exit 0
+ ;;
+ *)
+ printf 'Unknown option: %s\n\n' "$1" >&2
+ usage >&2
+ exit 2
+ ;;
+ esac
+ shift
+done
+
+if [ ! -f "$CACHE" ]; then
+ printf 'Cache file not found: %s\n' "$CACHE" >&2
+ exit 1
+fi
+
+# Decide deletion method
+if [ "$MODE" = auto ]; then
+ if command -v trash-put >/dev/null 2>&1; then MODE=trash; else MODE=rm; fi
+fi
+if [ "$MODE" = trash ] && ! command -v trash-put >/dev/null 2>&1; then
+ printf 'trash-put not found. Use --rm to delete permanently, or install trash-cli.\n' >&2
+ exit 1
+fi
+
+# Extract entries whose last cached status is corrupt (mtime, size, path).
+# The cache is append-only, so a path may appear on several lines; last wins.
+mapfile -t entries < <(
+ awk -F'\t' '
+ NF>=5 {
+ p=$5; for (i=6; i<=NF; i++) p = p "\t" $i
+ mt[p]=$1; sz[p]=$2; st[p]=$3
+ }
+ END { for (p in st) if (st[p]=="corrupt") printf "%s\t%s\t%s\n", mt[p], sz[p], p }
+ ' "$CACHE"
+)
+
+if [ "${#entries[@]}" -eq 0 ]; then
+ printf 'No files flagged as corrupt.\n'
+ exit 0
+fi
+
+# Select real targets: must exist and match the cached mtime/size.
+targets=()
+skipped=()
+for line in "${entries[@]}"; do
+ mt=${line%%$'\t'*}
+ rest=${line#*$'\t'}
+ sz=${rest%%$'\t'*}
+ path=${rest#*$'\t'}
+
+ if [ ! -e "$path" ]; then
+ skipped+=("missing | $path")
+ continue
+ fi
+ cur_mt=$(stat -c %Y -- "$path" 2>/dev/null || echo -1)
+ cur_sz=$(stat -c %s -- "$path" 2>/dev/null || echo -1)
+ if [ "$cur_mt" != "$mt" ] || [ "$cur_sz" != "$sz" ]; then
+ skipped+=("changed | $path (re-scan needed)")
+ continue
+ fi
+ targets+=("$path")
+done
+
+printf 'Corrupt files (%d):\n' "${#targets[@]}"
+for p in "${targets[@]}"; do printf ' %s\n' "$p"; done
+
+if [ "${#skipped[@]}" -gt 0 ]; then
+ printf '\nSkipped (%d):\n' "${#skipped[@]}"
+ for s in "${skipped[@]}"; do printf ' %s\n' "$s"; done
+fi
+
+if [ "${#targets[@]}" -eq 0 ]; then
+ exit 0
+fi
+
+if [ "$DRY" -eq 1 ]; then
+ printf '\n(dry-run) Nothing deleted.\n'
+ exit 0
+fi
+
+method_label=$([ "$MODE" = trash ] && echo 'move to trash' || echo 'permanently delete')
+if [ "$FORCE" -ne 1 ]; then
+ printf '\n%s %d file(s)? [y/N] ' "$method_label" "${#targets[@]}"
+ read -r ans
+ case "$ans" in
+ y | Y | yes | YES) ;;
+ *)
+ printf 'Cancelled.\n'
+ exit 0
+ ;;
+ esac
+fi
+
+deleted=()
+for p in "${targets[@]}"; do
+ if [ "$MODE" = trash ]; then
+ if trash-put -- "$p"; then deleted+=("$p"); else printf 'Failed: %s\n' "$p" >&2; fi
+ else
+ if rm -f -- "$p"; then deleted+=("$p"); else printf 'Failed: %s\n' "$p" >&2; fi
+ fi
+done
+
+past_label=$([ "$MODE" = trash ] && echo 'moved to trash' || echo 'deleted')
+printf '%d file(s) %s.\n' "${#deleted[@]}" "$past_label"
+
+# Remove deleted entries from the cache and log
+if [ "${#deleted[@]}" -gt 0 ]; then
+ tmpset=$(mktemp)
+ printf '%s\n' "${deleted[@]}" >"$tmpset"
+
+ prune() { # $1: file, $2: field number where the path starts
+ local f="$1" start="$2" tmp
+ [ -f "$f" ] || return 0
+ tmp=$(mktemp)
+ awk -F'\t' -v start="$start" '
+ NR==FNR { del[$0]=1; next }
+ {
+ p=$start; for (i=start+1; i<=NF; i++) p = p "\t" $i
+ if (!(p in del)) print
+ }
+ ' "$tmpset" "$f" >"$tmp"
+ mv "$tmp" "$f"
+ }
+
+ prune "$CACHE" 5 # integrity_cache.tsv: mtime size status errors PATH
+ prune "$LOG" 2 # corrupted.log: timestamp PATH
+ rm -f "$tmpset"
+fi