linuxbashffmpegdirnamebasename

Input path errors while running FFMPEG and keeping subdirectory structure


My goal is to reduce the size of all .ogg files found under the parent directory "Formatted", while keeping the sub-directories and files structure the same.

Example

.../Formatted/"test1, test2, test3 and so on"/"files.ogg"

to

.../Contributors/"test1, test2, test3 and so on"/"reduced_files.ogg"

However. While the script runs ffmpeg it seems that the some input file paths gives "Error opening input: No such file or directory". eg. It either cuts off the 'h' of home or adds a '.' to the end of the file extension.

"ome/user1/Downloads/Encoding/Formatted/test1/"file""

or

Parse error, at least 3 arguments were expected, only 1 given in string "oding/Formatted/test1/"file"

Resulting in

.../Contributors/"test1, test2, test3 and so on"/ with only a single file or missing .ogg files.

I'm new to coding, any help is appreciated . I've tried echoing the variables to make sure they are correct but early on in the process echo "3" "$dirName" shows the absolute path being inrorrect.

#!/bin/bash

sourceDir=$HOME/Downloads/Encoding/Formatted
destDir=$HOME/Backup/LEARN/Contributors

find ~/Downloads/Encoding/Formatted/ -type f -name "*.ogg" | while IFS= read -r d; do
#    find -name "*.ogg" 
    echo "1" "$d"
baseName=$(basename "$d")
    echo "2" "$baseName"
dirName=$(dirname "$d")
    echo "3" "$dirName"
dirName_0=$(basename "$dirName")
    echo "4" "$dirName_0"
    echo "5" "$destDir"
outputDir="$destDir/$dirName_0"
    echo "6" "$outputDir"
outputPath="$outputDir/$baseName"
   
#    echo "1" "$d"
#    echo "2" "$baseName"
    echo "3" "$dirName"
#    echo "4" "$dirName_0"
#    echo "5" "$destDirecho"
#    echo "6" "$outputDir"
#    echo "7" "$outputPath"
#    echo "8" "$destDir/$dirName_0/$baseName"
#    echo "9" "$sourceDir"

    mkdir -p "$outputDir"

    ffmpeg -i "$d" -b:a 24k -acodec libopus -map_metadata 0 -map_metadata 0:s:0 "$outputPath"
 
done

For the first file found, *echo* returns:

1 /home/user/Downloads/Encoding/Formatted/test/file1.ogg
2 file1.ogg
3 /home/user/Downloads/Encoding/Formatted/test
4 test
5 /home/user/Backup/LEARN/Contributors
6 /home/user/Backup/LEARN/Contributors/test
7 /home/user/Backup/LEARN/Contributors/test/file1.ogg
8 /home/user/Backup/LEARN/Contributors/test/file1.ogg
9 /home/user/Downloads/Encoding/Formatted

Which is correct, but then for the next file found:

1 ome/user/Downloads/Encoding/Formatted/test/file2.ogg
2 file2.ogg
3 ome/user/Downloads/Encoding/Formatted/test
4 test
5 /home/user/Backup/LEARN/Contributors
6 /home/user/Backup/LEARN/Contributors/test
7 /home/user/Backup/LEARN/Contributors/test/file2.ogg
8 /home/user/Backup/LEARN/Contributors/test/file2.ogg
9 /home/user/Downloads/Encoding/Formatted

line 1 and 3 both miss a h from home? It does this for files in multiple folders.

Solution

  • FFmpeg has an interaction mode enabled by default. With that, you can type for e.g. ? to get help in the console. And it reads that from the input, which in your case is the result of the find command. So, it reads the first char of your path as input and removes it.

    To prevent this, use the -nostdin flag, which disables interaction.

    And now to preserve your subdirectory structure, first cd into the source directory then run the find command. This way, you will get the paths relative to the source directory, which you can concat with your destination directory. Your current solution only works for files located within a single subdir of the source directory.

    Here is the code:

    #!/bin/bash
    
    sourceDir=$HOME/Downloads/Encoding/Formatted
    destDir=$HOME/Backup/LEARN/Contributors
    
    cd "$sourceDir"
    
    find . -type f -name "*.ogg" | while IFS= read -r relPath; do
        inputFile="$sourceDir/$relPath"
        outputFile="$destDir/$rel_path"
    
        mkdir -p "$(dirname "$outputFile")"
    
        ffmpeg -nostdin -i "$inputFile" -b:a 24k -acodec libopus -map_metadata 0 -map_metadata 0:s:0 "$outputFile"
    done