]> zdv2.bktei.com Git - BK-2020-03.git/blobdiff - user/bk-copy-rand-music
feat(unitproc/bkt-get_media_bitrate):Add function to measure bitrate
[BK-2020-03.git] / user / bk-copy-rand-music
index 2303d50196a76e2b1a7c7a3ef6fa0e22bd81cc8d..f3ddb5576c6b58b144369f52c1db197606d399c1 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/env bash
 # Desc: Copies random audio files
 # Usage: bk-copy-rand-music [dir SOURCE] [dir DEST] [int DURATION] ([int BYTES])
-# Version: 0.3.0
+# Version: 0.5.0
 # Depends: BK-2020-03: bkshuf v0.1.0
 
 declare -Ag appRollCall # Associative array for storing app status
@@ -10,7 +10,7 @@ declare -Ag dirRollCall # Associative array for storing dir status
 declare -a music_codecs # Array for storing valid codec names (e.g. "aac" "mp3")
 
 # Adjustable parameters
-music_codecs=("vorbis" "aac" "mp3" "flac" "opus"); # whitelist of valid codec_names ffprobe might return
+music_codecs=("vorbis" "aac" "mp3" "flac" "opus" "eac3"); # whitelist of valid codec_names ffprobe might return
 ext_ignore=".ots\$|.mid\$|.json\$|.gz\$|.jpg\$|.png\$|.asc\$|.pdf\$|.txt\$|.vtt\$|\.SUM|.zip\$|.xz\$|.org\$|.txt\$"; # blacklist of file extensions for 'grep -Evi'
 max_filename_length="255"; # max output filename length
 min_file_duration="30"; # minimum duration per music file
@@ -19,6 +19,7 @@ min_file_size="100000"; # minimum size per music file (bytes)
 max_file_size="100000000"; # maximum size per music file (bytes)
 siz_dest="600000000"; # default destination size limit: 600 MB
 max_find_depth="10"; # max find depth
+limit_bitrate="320000"; # maximum bitrate (bps) for output audio files
 
 # Load env vars (bkshuf defaults for typical music albums)
 if [[ ! -v BKSHUF_PARAM_LINEC ]]; then export BKSHUF_PARAM_LINEC=1000000; fi;
@@ -293,6 +294,7 @@ get_media_length() {
     # Output: stdout: seconds (float)
     # Depends: ffprobe 4.1.8
     # Ref/Attrib: [1] How to get video duration in seconds? https://superuser.com/a/945604
+    
     local file_in
     file_in="$1";
     if [[ ! -f $file_in ]]; then
@@ -368,6 +370,35 @@ checkIsInArray() {
        return 1;
     fi;
 } # Check if string is element in array
+get_media_bitrate() {
+    # Use ffprobe to get audio/visual media container bitrate (bits per second integer)
+    # Usage: get_media_bitrate arg1
+    # Input:  arg1:    str  path to file
+    # Output: stdout:  int  bitrate (bits per second)
+    # Version: 0.0.1
+    # Depends: ffprobe 4.4.2
+    # Ref/Attrib: [1] How to get video duration in seconds? https://superuser.com/a/945604
+    #             [2] Determine video bitrate using ffmpeg https://superuser.com/questions/1106343/determine-video-bitrate-using-ffmpeg
+    local file_in
+    file_in="$1";
+    if [[ ! -f $file_in ]]; then
+       die "ERROR:Not a file:$file_in";
+    fi;
+    ffprobe -v error -show_entries format=bit_rate -of default=noprint_wrappers=1:nokey=1 "$file_in";
+
+    yell "DEBUG:Finished get_media_bitrate with $? on:$1";
+} # Get media container length in seconds via stdout
+transcode_copy() {
+    # Desc: Transcode high bitrate file into smaller opus file
+    # Note: Meant for downsizing large lossless FLAC
+    # Input: arg1                str  path to input audio file
+    #        arg2                str  output file path
+    #        var  limit_bitrate  int  max output transcode bitrate (bps)
+    # Output: file
+
+    ffmpeg -nostdin -i "$1" -c:a libopus -b:a "${limit_bitrate}" "${2}.opus";    
+}; # transcode to lower bitrate audio file
+
 main() {
     # Desc: Main program
     # Input: arg1: path to source tree
@@ -398,15 +429,19 @@ main() {
 
     # Check env vars
     if ! checkInt "$BKSHUF_PARAM_LINEC"; then
-        die "FATAL:Not an int:BKSHUF_PARAM_LINEC:$BKSHUF_PARAM_LINEC"; fi;
+        die "FATAL:Not an int:BKSHUF_PARAM_LINEC:${BKSHUF_PARAM_LINEC}"; fi;
     if ! checkInt "$BKSHUF_PARAM_GSIZE"; then
-        die "FATAL:Not an int:BKSHUF_PARAM_LINEC:$BKSHUF_PARAM_GSIZE"; fi;
+        die "FATAL:Not an int:BKSHUF_PARAM_LINEC:${BKSHUF_PARAM_GSIZE}"; fi;
+
+    # Check adjustable parameters
+    if ! checkInt "$limit_bitrate"; then
+        die "FATAL:Not an int:limit_bitrate:${limit_bitrate}"; fi;
     
     ## Check duration
     if checkInt "$arg3"; then
        dur_dest="$arg3";
     else
-       die "FATAL:Duration (seconds) not an int:$arg3"
+       die "FATAL:Duration (seconds) not an int:${arg3}"
     fi;
 
     ## Check size
@@ -414,7 +449,7 @@ main() {
         if checkInt "$arg4"; then
             siz_dest="$arg4";
         else
-            die "FATAL:Size (bytes) not an int:$arg4";
+            die "FATAL:Size (bytes) not an int:${arg4}";
         fi;
     fi;
     
@@ -463,7 +498,7 @@ main() {
         path_candfile="$line"; # path of candidate file
 
         ### Check size
-        siz_cand="$(du -b "$path_candfile" | awk '{ print $1 }')"; # size in bytes
+        siz_cand="$(du -Lb "$path_candfile" | awk '{ print $1 }')"; # size in bytes
        if ! checkInt "$siz_cand"; then continue; fi; # reject
         if [[ "$((siz + siz_cand))" -gt "$siz_dest" ]]; then continue; fi; # reject
        if [[ "$siz_cand" -lt "$min_file_size" ]]; then continue; fi; # reject
@@ -477,7 +512,7 @@ main() {
        if ! checkIsInArray "$file_format" "${music_codecs[@]}"; then continue; fi; # reject
 
         ### Check duration
-       dur_cand="$(get_media_length "$path_candfile")";
+       dur_cand="$(get_media_length "$path_candfile")"; # length in seconds (float)
        dur_cand="${dur_cand%%.*}"; # convert float to int
         if ! checkInt "$dur_cand"; then continue; fi; # reject
         if [[ "$((dur + dur_cand))" -gt "$dur_dest" ]]; then continue; fi; # reject
@@ -520,9 +555,11 @@ main() {
     # Copy files in list_copy to dir_dest;
     while read -r line; do
         #yell "DEBUG:line:$line"; # debug
-        fdur="$(printf "%s" "$line" | cut -d',' -f1)";
-        fsize="$(printf "%s" "$line" | cut -d',' -f2)";
+        fdur="$(printf "%s" "$line" | cut -d',' -f1)";  # duration in seconds (integer)
+        fsize="$(printf "%s" "$line" | cut -d',' -f2)"; # size in bytes
         fpath="$(printf "%s" "$line" | cut -d',' -f3-)";
+        fbitrate="$(get_media_bitrate "$fpath")"; # bitrate in bps
+        if ! checkInt "$fbitrate"; then die "FATAL:Invalid bitrate. $(declare -p fpath fbitrate)"; fi;
         ## Get basename of path
         file_basename="$(basename "$fpath")";
         ### Get basename without unprintable non-ASCII characters
@@ -540,8 +577,15 @@ main() {
        path_output="$dir_dest"/"$file_name";
        
        ## Copy
-       must cp "$fpath" "$path_output" && yell "NOTICE:Copied ($(printf "%""$dur_cand_w"d "$fdur") seconds): $fpath ";
-       #yell "DEBUG:Copied $file_basename to $dur_dest.";
+        yell "DEBUG:$(declare -p fdur fsize fbitrate fpath file_basename file_basename_compat fingerprint num file_name path_output)";
+        if [[ "$fbitrate" -lt "$limit_bitrate" ]]; then
+           must cp "$fpath" "$path_output" && yell "NOTICE:Copied ($(printf "%""$dur_cand_w"d "$fdur") seconds): $fpath ";
+            yell "DEBUG:Copied ${file_basename} to ${dir_dest}.";
+        elif [[ "$fbitrate" -ge "$limit_bitrate" ]]; then
+            must transcode_copy "$fpath" "$path_output";
+            yell "DEBUG:Wrote ${limit_bitrate}kbps transcoded ${file_basename} to ${dir_dest}.";
+        fi;
+               
 
        ## Append log
         fpath_can="$(readlink -f "$fpath")"; # resolve symlinks to canonical path