X-Git-Url: https://zdv2.bktei.com/gitweb/BK-2020-03.git/blobdiff_plain/00fcff36572aa4cb92fe5363c52d6ed346a20120..51f3f8450b12a1f628a8c957ac30934540c5c826:/user/rand_media_pl.sh diff --git a/user/rand_media_pl.sh b/user/rand_media_pl.sh index ecf26da..abe7766 100755 --- a/user/rand_media_pl.sh +++ b/user/rand_media_pl.sh @@ -7,8 +7,8 @@ # - Prompts the user whether to use the cached data or regenerate it. # - Uses an EDL file to specify the starting offset for the first file. # Usage: rand_media_pl.sh [DIR] -# Version: 0.0.4 -# Dependencies: Bash 4, ffprobe, mpv, bc, shuf +# Version: 0.1.0 +# Dependencies: Bash 4, ffprobe, mpv, bc, shuf, GNU Parallel yell() { echo "$0: $*" >&2; } # Print script path and all args to stderr die() { yell "$*"; exit 111; } # Same as yell() but exits with code 111 @@ -16,70 +16,69 @@ must() { "$@" || die "cannot $*"; } # Runs args as command, reports args if comm # Configurable variables SEARCH_DIR="${1:-.}" # Default to current directory if no argument is provided -MAX_DEPTH=8 -EXTENSIONS=("*.flac" "*.mp3" "*.opus" "*.m4a" "*.m4b" "*.mp4" "*.mkv" "*.webm") -CACHE_FILE=".playlist_cache" +MAX_DEPTH=8; +EXTENSIONS=("*.flac" "*.mp3" "*.opus" "*.m4a" "*.m4b" "*.mp4" "*.mkv" "*.webm"); +CACHE_FILE=".playlist_cache"; -declare -gA file_durations -declare total_duration +declare -gA file_durations; +declare total_duration; # Function to prompt the user prompt_yes_no() { - yell "STATUS: User input required." - local prompt_message="$1" - local user_input + yell "STATUS: User input required."; + local prompt_message="$1"; + local user_input; while true; do - read -rp "$prompt_message [y/n]: " user_input + read -rp "$prompt_message [y/n]: " user_input; case "$user_input" in [Yy]*) return 0 ;; [Nn]*) return 1 ;; *) echo "Please answer yes or no." ;; - esac - done -} + esac; + done; +}; # Function to find files with specified extensions find_media_files() { - yell "STATUS: Finding media files." - local find_cmd=("find" "-L" "$SEARCH_DIR" "-maxdepth" "$MAX_DEPTH" "-type" "f" "(") + yell "STATUS: Finding media files."; + local find_cmd=("find" "-L" "$SEARCH_DIR" "-maxdepth" "$MAX_DEPTH" "-type" "f" "("); for ext in "${EXTENSIONS[@]}"; do - find_cmd+=("-iname" "$ext" "-o") - done - # Remove the last "-o" and add ")" - unset 'find_cmd[-1]' - find_cmd+=(")") - "${find_cmd[@]}" -} + find_cmd+=("-iname" "$ext" "-o"); + done; + # Remove the last "-o" and add ")"; + unset 'find_cmd[-1]'; + find_cmd+=(")"); + "${find_cmd[@]}"; +}; # Function to generate the playlist cache generate_playlist_cache() { yell "Generating playlist cache. This may take a while..." # Initialize or empty the cache file - : > "$CACHE_FILE" # Truncate or create the cache file + : > "$CACHE_FILE"; # Truncate or create the cache file - total_duration=0 + total_duration=0; while IFS= read -r file; do # Get the duration using ffprobe duration=$(ffprobe -v error -show_entries format=duration \ - -of default=noprint_wrappers=1:nokey=1 "$file") - declare -p file duration 1>&2 # Debugging statement + -of default=noprint_wrappers=1:nokey=1 "$file"); + declare -p file duration 1>&2; # Debugging statement # Validate the duration if [[ -n "$duration" && "$duration" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then # Append the file path and duration to the cache file - printf "%s|%s\n" "$file" "$duration" >> "$CACHE_FILE" - total_duration=$(echo "$total_duration + $duration" | bc) + printf "%s|%s\n" "$file" "$duration" >> "$CACHE_FILE"; + total_duration=$(echo "$total_duration + $duration" | bc); else - yell "WARNING: Invalid duration '$duration' for file '$file', skipping." 1>&2 - fi - done < <(find_media_files | sort) - - total_duration_s=$(printf "%.1f" "$total_duration") - total_duration_h=$(echo "scale=1; $total_duration / 3600" | bc -l) + yell "WARNING: Invalid duration '$duration' for file '$file', skipping." 1>&2; + fi; + done < <(find_media_files | parallel readlink -f '{}' | sort -u; ); - yell "Total duration of playlist: ${total_duration_s} seconds (${total_duration_h} hours)." -} + total_duration_s="$(printf "%.1f" "$total_duration"; )"; + total_duration_h="$(echo "scale=1; $total_duration / 3600" | bc -l; )"; + yell "Total duration of playlist: ${total_duration_s} seconds (${total_duration_h} hours)."; +}; # Function to read the playlist cache read_playlist_cache() { # Input: file_durations associative array @@ -97,56 +96,79 @@ read_playlist_cache() { else yell "WARNING: Invalid duration '$duration' for file '$file', skipping." 1>&2 fi - done < "$CACHE_FILE" -} + done < "$CACHE_FILE"; +}; +# Function to get byte count of file name +prepend_path_bc() { + # Desc: Prepends a file path with %[path bytecount]% + # Usage: prepend_filename_bc [FILE] + # Input: arg1 str file path + # Output: stdout str %[int path length]%path + # Example: 'foo.txt' yields '%7%foo.txt' + # Depends: GNU Coreutils 8.32 (for 'wc') + # BK-2020-03: yell(), die(), must() + # Ref/Attrib: See “Syntax of mpv EDL files” https://github.com/mpv-player/mpv/blob/master/DOCS/edl-mpv.rst#syntax-of-mpv-edl-files + fin="$1"; + + if [[ ! -f "$fin" ]]; then + yell "WARNING:File does not exist:${fin}"; + fi; + + bytecount="$(printf "%s" "$fin" | wc -c; )"; + re='[0-9]'; + if [[ ! "$bytecount" =~ $re ]]; then + die "FATAL:Not an int:${bytecount}; $(declare -p fin bytecount)"; + fi; + printf "%%%s%%%s" "$bytecount" "$fin"; +}; # Main script if [[ -f "$CACHE_FILE" ]]; then if prompt_yes_no "Playlist cache detected. Do you want to use it? (Faster)"; then - read_playlist_cache + read_playlist_cache; else - generate_playlist_cache - read_playlist_cache - fi + generate_playlist_cache; + read_playlist_cache; + fi; else - generate_playlist_cache - read_playlist_cache -fi + generate_playlist_cache; + read_playlist_cache; +fi; # Check if any files were found if [[ ${#file_durations[@]} -eq 0 ]]; then - declare -p file_durations 1>&2 - die "FATAL: No media files found." -fi + declare -p file_durations 1>&2; + die "FATAL: No media files found."; +fi; # Ensure total_duration is not empty if [[ -z "$total_duration" ]]; then - die "FATAL: total_duration is empty." -fi + die "FATAL: total_duration is empty."; +fi; # Convert total_duration to an integer -total_duration_int=$(printf "%.0f" "$total_duration") +total_duration_int=$(printf "%.0f" "$total_duration"); # Check if total_duration_int is a valid integer if ! [[ "$total_duration_int" =~ ^[0-9]+$ ]]; then - die "FATAL: total_duration_int is not a valid integer." -fi + die "FATAL: total_duration_int is not a valid integer."; +fi; # Generate a random integer between 0 and total_duration_int - 1 random_point=$(shuf -i 0-$((total_duration_int - 1)) -n1) -yell "DEBUG: total_duration=$total_duration, total_duration_int=$total_duration_int, random_point=$random_point" 1>&2 +yell "DEBUG: total_duration=$total_duration, total_duration_int=$total_duration_int, random_point=$random_point" 1>&2; # Get sorted list of files -mapfile -t sorted_files < <(printf '%s\n' "${!file_durations[@]}" | sort) +mapfile -t sorted_files < <(printf '%s\n' "${!file_durations[@]}" | sort; ); # Find the file and offset corresponding to the random starting point -accumulated_duration=0 -accumulated_duration_int=0 +accumulated_duration=0; +accumulated_duration_int=0; for idx in "${!sorted_files[@]}"; do - file="${sorted_files[$idx]}" - duration="${file_durations["$file"]}" - declare -p idx file duration accumulated_duration 1>&2 # Debugging statement + file="${sorted_files[$idx]}"; + duration="${file_durations["$file"]}"; + declare -p idx file duration accumulated_duration 1>&2; # Debugging statement # Validate the duration if [[ -n "$duration" && "$duration" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then @@ -160,37 +182,40 @@ for idx in "${!sorted_files[@]}"; do fi if (( random_point < new_accumulated_duration_int )); then - offset=$(echo "$random_point - $accumulated_duration_int" | bc) - yell "Starting playback from $offset seconds into file: $file" + offset=$(echo "$random_point - $accumulated_duration_int" | bc; ); + yell "Starting playback from $offset seconds into file: $file"; # Create an EDL file - edl_file=$(mktemp) - echo "# mpv EDL v0" > "$edl_file" - - # First line: file with offset - printf '%s,%s\n' "$file" "$offset" >> "$edl_file" + edl_file=$(mktemp); + yell "DEBUG:EDL file at:${edl_file}"; # debug + echo "# mpv EDL v0" > "$edl_file"; + + # Add first file to start playback at random offset position + file_bc="$(prepend_path_bc "$file")"; # See https://github.com/mpv-player/mpv/blob/master/DOCS/edl-mpv.rst#syntax-of-mpv-edl-files + printf '%s,%s\n' "$file_bc" "$offset" >> "$edl_file"; # Append the rest of the files declare -p file offset idx sorted_files edl_file 1>&2; # debug for (( i=idx+1; i<${#sorted_files[@]}; i++ )); do - next_file="${sorted_files[$i]}" + next_file="${sorted_files[$i]}"; + next_file_bc="$(prepend_path_bc "$next_file")"; yell "STATUS:Adding:$next_file"; - printf '%s\n' "$next_file" >> "$edl_file" - done + printf '%s\n' "$next_file_bc" >> "$edl_file"; + done; # Start playback using mpv - mpv "$edl_file" + mpv "$edl_file" || exit 1; # Securely delete the EDL file if [[ -n "$edl_file" && -f "$edl_file" ]]; then - rm "$edl_file" - exit 0 + rm "$edl_file"; + exit 0; else - die "FATAL: edl_file is either unset or not a valid file, skipping deletion." - fi - fi - accumulated_duration="$new_accumulated_duration" - accumulated_duration_int="$new_accumulated_duration_int" -done - -die "FATAL: Could not find file corresponding to random point." + die "FATAL: edl_file is either unset or not a valid file, skipping deletion."; + fi; + fi; + accumulated_duration="$new_accumulated_duration"; + accumulated_duration_int="$new_accumulated_duration_int"; +done; + +die "FATAL: Could not find file corresponding to random point.";