■ 🕑 1. PLAY NU MPV │ https://x0.at/30jh.png │ #!/bin/bash │ # ┏━┓╻ ┏━┓╻ ╻┏┓╻╻ ╻┏┳┓┏━┓╻ ╻ USAGE: playnumpv <days> │ # ┣━┛┃ ┣━┫┗┳┛┃┗┫┃ ┃┃┃┃┣━┛┃┏┛ (where days = number of days to │ # ╹ ┗━╸╹ ╹ ╹ ╹ ╹┗━┛╹ ╹╹ ┗┛ search back for newer media files) │ │ # Check if argument is provided │ if [ -z "$1" ] || [[ ! "$1" =~ ^[0-9]+$ ]]; then │ echo "Usage: $0 <days>" │ exit 1 │ fi │ │ # Create a temporary playlist file in a safe location │ playlist=$(mktemp) || { echo "Failed to create temporary file"; exit 1; } │ │ # Set trap to clean up temporary file on exit │ trap 'rm -f "$playlist"' EXIT │ │ # Get absolute path of the playlist to exclude it from search │ abs_playlist=$(realpath "$playlist") │ │ # Find all media files modified in the last $1 days and add them to the playlist │ # Using absolute paths to avoid issues with relative paths │ find "$PWD" -type f -regextype posix-extended -regex '.*\.(mp3|flac|opus|ogg|m4a|wav|nsf|mod|xm)' \ │ -mtime -"${1}" ! -path "$abs_playlist" -print0 | \ │ sort -z | \ │ tr '\0' '\n' > "$playlist" │ │ # Check if any files were found │ if [ ! -s "$playlist" ]; then │ echo "No files found newer than ${1} days."; exit 0 │ fi │ │ # Count the number of files in the playlist │ file_count=$(wc -l < "$playlist") │ echo "Found $file_count files to play." │ │ # Play playlist with mpv using unbuffer to maintain terminal connection │ # and process substitution to capture output without breaking stdin │ unbuffer -p mpv --shuffle --term-osd=auto --osd-level=2 --no-audio-display \ │ --playlist="$playlist" 2>&1 > >(grep -E "Playing") < /dev/tty │ │ └─■ 🕑 2. explanation console stdin, stdout, redirection #!/bin/bash # ┏━┓╻ ┏━┓╻ ╻┏┓╻╻ ╻┏┳┓┏━┓╻ ╻ USAGE: playnumpv <days> # ┣━┛┃ ┣━┫┗┳┛┃┗┫┃ ┃┃┃┃┣━┛┃┏┛ (where days = number of days to # ╹ ┗━╸╹ ╹ ╹ ╹ ╹┗━┛╹ ╹╹ ┗┛ search back for newer media files)
# Check if argument is provided if [ -z "$1" ] || [[ ! "$1" =~ ^[0-9]+$ ]]; then echo "Usage: $0 <days>" exit 1 fi
# Create a temporary playlist file in a safe location playlist=$(mktemp) || { echo "Failed to create temporary file"; exit 1; }
# Set trap to clean up temporary file on exit trap 'rm -f "$playlist"' EXIT
# Get absolute path of the playlist to exclude it from search abs_playlist=$(realpath "$playlist")
# Find all media files modified in the last $1 days and add them to the playlist # Using absolute paths to avoid issues with relative paths find "$PWD" -type f -regextype posix-extended -regex '.*\.(mp3|flac|opus|ogg|m4a|wav|nsf|mod|xm)' \ -mtime -"${1}" ! -path "$abs_playlist" -print0 | \ sort -z | \ tr '\0' '\n' > "$playlist"
# Check if any files were found if [ ! -s "$playlist" ]; then echo "No files found newer than ${1} days." exit 0 fi
# Count the number of files in the playlist file_count=$(wc -l < "$playlist") echo "Found $file_count files to play."
# Play the playlist with mpv, using unbuffer to maintain terminal connection # and process substitution to capture output without breaking stdin unbuffer -p mpv --shuffle --term-osd=auto --osd-level=2 --no-audio-display \ --playlist="$playlist" 2>&1 > >(grep -E "Playing") < /dev/tty
# Multi-line comment explaining the redirection technique: : <<'EOF' EXPLANATION OF THE REDIRECTION TECHNIQUE:
The command uses several advanced shell features to maintain keyboard control while filtering output:
1. `unbuffer -p`: - Creates a pseudo-terminal for mpv - Ensures mpv maintains interactive behavior including keyboard input handling
2. `2>&1`: - Redirects stderr to stdout - Combines both output streams
3. `> >(grep -E "Playing")`: - Uses process substitution (the `>(...)` syntax) - Creates a temporary file descriptor connected to grep - Sends mpv's output to grep without breaking terminal connection - Unlike a regular pipe, this doesn't disrupt keyboard input
4. `< /dev/tty`: - Explicitly redirects stdin to the terminal device - Ensures mpv gets keyboard input directly from the terminal - This is critical for maintaining keyboard control
WHY THIS WORKS WHEN REGULAR PIPES FAIL:
Regular pipes (mpv | grep) break keyboard input because: - The shell puts commands in separate process groups - mpv detects its output is going to a pipe and may change behavior - Terminal control gets disrupted
Our solution works because: - unbuffer maintains the terminal connection - Process substitution doesn't break the terminal connection like a pipe - Explicit terminal input redirection ensures keyboard control - The combination preserves mpv's interactive capabilities
DATA FLOW: Keyboard Input → /dev/tty → mpv (via unbuffer) → Process Substitution → grep → Terminal EOF