2

I have little background in programming and need to create a batch to extract the audio of multiple video files. Execution is done through the context menu in Nautilus/Gnome Files, stored in Nautilus' scripts folder as a bash .sh. The following code works for 1 file, but when selecting multiple files it doesn't. Could someone please help me modify the code to make it work.

#!/bin/bash

FILENAME=$(echo $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS | sed -e 's/\r//g')
FILENAME2=$(echo "$FILENAME" | cut -f 1 -d '.') 

    ffmpeg -i "${FILENAME}" -vn -acodec pcm_s16le -ac 2 -ar 48000 "${FILENAME2}".wav

    # finished message box
    zenity --info --title "Procesing completed" --text "${FILENAME2}.wav at 48kHz has been generated." --width=700
3
  • You should use $FILENAME and $FILENAME2 as an array and iterate over each item of those arrays (both arrays should have the same length/number of items) Nov 30, 2022 at 4:14
  • Btw, FILENAME2=$(echo "$FILENAME" | cut -f 1 -d '.') is removing all what you have after the .. So if you have /path/to/file.mp4 /path/to/file2.mp4 using the cut you will get: /path/to/file Nov 30, 2022 at 4:18
  • Thank you @EdgarMagallon. Could you link me please an example of how to implement $FILENAME as an array and iterate the command (ffmpeg) over the array's item.
    – Domingo
    Nov 30, 2022 at 13:50

1 Answer 1

1

Use this script, cannot test it with ffmpeg but it should work.


#!/bin/bash

{

readarray FILENAME <<< "$(echo -e "$NAUTILUS_SCRIPT_SELECTED_FILE_PATHS" | sed -e 's/\r//g')"

echo -e "Logs: $(date)\n" > ~/Desktop/data.txt

for file in "${FILENAME[@]}"; do
    
    file=$(echo "$file" | tr -d $'\n')
    echo "Current file: $file" >> ~/Desktop/data.txt
    ffmpeg -i "$file" -vn -acodec pcm_s16le -ac 2 -ar 48000 "${file%.*}.wav"
    zenity --info --title "Procesing completed" --text "${file%.*}.wav at 48kHz has been generated." --width=700
done


} 2>~/Desktop/ffmpeg.logs

The code above will print a message with zenity each time a mp4 is processed. But if you want to display the message when all files are processed then you can use this script:

#!/bin/bash

{

readarray FILENAME <<< "$(echo -e "$NAUTILUS_SCRIPT_SELECTED_FILE_PATHS" | sed -e 's/\r//g')"

echo -e "Logs: $(date)\n" > ~/Desktop/data.txt

for file in "${FILENAME[@]}"; do
   
    file=$(echo "$file" | tr -d $'\n')
    echo "Current file: $file" >> ~/Desktop/data.txt
    ffmpeg -i "$file" -vn -acodec pcm_s16le -ac 2 -ar 48000 "${file%.*}.wav"
done

zenity --info --title "Procesing completed" --text "$( printf "%s.wav\n" "${FILENAME[@]%.*}") at 48kHz has been generated." --width=700

} 2>~/Desktop/ffmpeg.logs

I suggest you use this script. Because is able to detect what files failed and what were generated successfully:

#!/bin/bash

{

readarray FILENAME <<< "$(echo -e "$NAUTILUS_SCRIPT_SELECTED_FILE_PATHS" | sed -e 's/\r//g')"

echo -e "Logs: $(date)\n" > ~/Desktop/data.txt
okFiles=()
errFiles=()

for file in "${FILENAME[@]}"; do
    
    file=$(echo "$file" | tr -d $'\n')
    echo -e "\n===========================" >> ~/Desktop/data.txt
    echo "Current file: $file" >> ~/Desktop/data.txt
    ffmpeg -i "$file" -vn -acodec pcm_s16le -ac 2 -ar 48000 "${file%.*}.wav" && {
        okFiles+=("${file%.*}.wav")
        :
    } || {
        
        errFiles+=("${file%.*}.wav")
    }
done

if [[ ${#okFiles[@]} -gt 0 ]]; then
    zenity --info --title "Procesing completed" --text "$(printf '%s\n' ${okFiles[@]})\n at 48kHz have/has been generated." --width=700
fi

if [[ ${#errFiles[@]} -gt 0 ]]; then
    zenity --info --title "Error while processing some files" --text "Following files:\n$(printf "%s\n" "${errFiles[@]}")\ncould not be generated." --width=700
fi

} 2>~/Desktop/ffmpeg.logs

About:

{
  code
  code
} 2>~/Desktop/ffmpeg.logs

I used that to be able to detect what fails when you are processing every file. For example, if in some file ffmpeg throws an error you will be able to check the logs inside the path ~/Desktop/ffmpeg.logs

Btw, if you want every file processed to be located on specific path and not where you call the script you can do something like this (before readarray):

{
  cd ~/Audios/path/to/dir #the path you want can be placed here
  readarray ...
  code
} 2>~/Desktop/ffmpeg.logs

Finally, you can notice that FILENAME2 is no longer needed, because I use "${file%.*}.wav" instead (see bash parameter expnasion).

8
  • For some strange reason the files can't be located. Maybe some quotation marks are missing? ffmpeg.logs: "No such file or directory". Data.txt shows the same file path, which is correct and corresponds to an existing file (defined by Nautilus' selection).
    – Domingo
    Nov 30, 2022 at 20:23
  • @Domingo can you provide what path(s) are being sent to the script? I tested my script with paths containing spaces and it worked well. I don't think that the quotation marks are the problem but please provide your path(s) to be sure. Nov 30, 2022 at 20:38
  • I'm trying with an .mp4 on the desktop. data.txt shows: Current file: /home/chuma/Desktop/06.mp4. Last lines of the log are: ... libswresample 4. 7.100 / 4. 7.100 libpostproc 56. 6.100 / 56. 6.100 /home/chuma/Desktop/06.mp4 : No such file or directory
    – Domingo
    Nov 30, 2022 at 20:41
  • @Domingo thanks! I tested and I know what's going on! Nov 30, 2022 at 20:50
  • :) Tell me tell me!
    – Domingo
    Nov 30, 2022 at 21:10

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .