Renaming the output file name from ffmpeg when using find and xargs - bash

I am trying to rename the output files that are coming out of ffmpeg processes that I have spawned using find and xargs. I have a directory of files that I would like to convert to a certain format and all those files should ultimately be named .mov. So I am filtering out .mp4 & .mov files and then using xargs to pass it on to ffmpeg.
Suppose there are files such as abc.mp4, abcd.mp4, abcde.mp3, bcd.mov etc in the current working dir and then I am doing something like this to filter out just the mp4 and mov files and pass it on to ffmpeg for encoding:
find . -type f -name "*.mp4" -o -name "*.mov" | xargs -n 1 -p 5 -I '{}' ffmpeg -i '{}' ....... $PWD/{}"_h264.mov"
This will do what I want ffmpeg to do but it will name the files like
abc.mp4_h264.mov,abcd.mp4_h264.mov,bcd.mov_h264.mov
What I would ideally like to achieve is to name the output files as
abc_h264.mov,abcd_h264.mov,bcd_h264.mov
I am not getting any sparks in my brain on how to do that at the place where I specify the output files of ffmpeg. I know that I can call basename command using xargs like this
find . -type f -name "*.mp4" -o -name "*.mov" | xargs -n 1 basename | rev | cut -d . -f 2 | rev
and extract the filename without the extension but if i use that then how can I pass it on to ffmpeg as the input file because it would have lost the extension when it comes out of xargs and ffmpeg will throw an error.
Could anyone help me achieve this in one go where I can specify what 'basename' is doing but where I am specifying the output file name of ffmpeg please?
Thanks very much for any help that you can provide. Really appreciate the community.

Would you please try the following:
find . -type f -name "*.mp4" -o -name "*.mov" | xargs -I {} -n 1 -P 5 bash -c 'org="{}"; new="$PWD/${org%.*}_h264.mov"; ffmpeg -i "$org" {options} "$new"'
But it is not efficient for spawning bash command as a subprocess
multiple times. If you do not have a specific reason to use find
and xargs, it will be simpler to say:
for org in *.mp4 *.mov; do
new="$PWD/${org%.*}_h264.mov"
ffmpeg -i "$org" {options} "$new"
done

Related

FFprobe to recursively search files to output to csv

I am trying to run a bash script but am encountering some errors.
I want to recursively make the command search through the folder and look for all mp4 files. Then i want it to run the ffprobe command and output it to a csv file in a different location with the filename of the mp4 it ran with .csv
So far i have this
#!/bin/bash
for file in /this/is/directory1/*.mp4; do
ffprobe -show_packets -of csv=print_section=0 $file >> /this/is/directory2/${file}.csv
done
Try this:
find . -type f -name "*.mp4" | xargs -I{} bash -c "ffprobe -show_packets -of csv=print_section=0 {} > /tmp/\$(basename {} | cut -d. -f1).csv"
OR without using xargs
find . -type f -name "*.mp4" -exec bash -c "ffprobe -show_packets -of csv=print_section=0 {} > /tmp/\$(basename {} | cut -d. -f1).csv" ';'
Change the path of directory output file in the command above.

Find files and HandBrakeCLI convert in a single line

Not sure if this is possible...
I'm trying to write a terminal command (linux) that would find all video files with a specific extension and then convert them using HandBrakeCLI
I have the first half of that down:
find . -type f -name "*.avi*" -exec
And I have a working HandBrakeCLI command:
HandBrakeCLI -i file.mkv -o file2.mkv -e x265 --vfr -q 20 --all-audio --all-subtitles
What I have been unable to figure out is how to insert the file name/path for the files found in the find into the file.mkv and then output the converted file with the same file name but in an mkv format.
Is it possible to do this in one line or do I need to break this out in a bash script?
As a one-liner, try something like:
find . -type f -name "*.avi" -print0 | perl -pe 's/\.avi\0/\0/g' | xargs -0 -I% HandBrakeCLI -i %.avi -o %.mkv -e x265 --vfr -q 20 --all-audio --all-subtitles
-print0 option in find prints the filename on the standard output, followed by a null character.
The following perl snippet removes the .avi extention to supply the basename to xargs.
-I% option in xargs replaces "%" with names read from standard input.

How do handle filename space and Character with FIND command

Good day all,
I am working on a bash script to merge multiple mp3 files to one. The code is working fine but cannot handle file name with space or Characters. Below is the code. Can you please tell me what I'm doing wrong. Thanks
for file in $(find . -type f -name "*.mp3" -print0 | xargs -0 ); do
ffmpeg -i "concat:intro.mp3|"$file"|outro.mp3" -acodec copy "${file%.mp3}-".mp3;
done
find has an -exec flag that allows you to call scripts with the search results.
e.g. creating a simple helper-script addxtros.sh:
#!/bin/sh
infile=$1
outfile=${infile%.mp3}-.mp3
ffmpeg -i "concat:intro.mp3|${infile}|outro.mp3" -acodec copy "${outfile}"
you can use it like:
find . -type f -name "*.mp3" -exec ./addxtros.sh {} ";"
read builtin with -d followed by empty argument to use NUL caracter as input record delimiter
while IFS= read -r -d '' file; do
ffmpeg -i "concat:intro.mp3|$file|outro.mp3" -acodec copy "${file%.mp3}-".mp3;
done < <(find . -type f -name "*.mp3" -print0)
You can use find together with bash -c command (that allows passing 2 arguments to ffmpeg):
find . -type f -name "*.mp3" -exec bash -c 'ffmpeg -i "concat:intro.mp3|$1|outro.mp3" -acodec copy "${1%.mp3}-.mp3"' _ {} \;

printing bad video files with xargs

I use
find . -name "*.mp4" -print0 | xargs -0 -I $ bash -c "ffmpeg -v error -xerror -i $ -f null - || echo $"
to go over a directory and print bad video files (as ffmpeg sees it)
if I run it in a directory with only mp4 files, it will print the name of file - but, if I run it on the parent folder (a folder with many folders, each has mp4 files) I only get $ being echoed
What am I doing wrong?
With GNU Parallel it looks like this:
find . -name "*.mp4" -print0 |
parallel -0 "ffmpeg -v error -xerror -i {} -f null - || echo {}"
It is tested with filename containing ', " and space.

Bash script to search and rename recursively

I have a bash script that converts *.mkv files to *.avi files. Here's what it looks like:
#!/bin/bash
for f in $(ls *mkv | sed ‘s/\(.*\)\..*/\1/’)
do
ffmpeg -i $f.mkv -sameq $f.avi
done
What I need this script to do however, is it needs to search recurssively in all folders for *.mkv files and then run the ffmpeg command and save the output to the same directory.
PLEASE can someone help me? :-)
find /some/path -name '*.mkv' | while read f
do
ffmpeg -i "$f" -sameq "${f:0:-4}.avi"
done
Try like this:
find <file_path> -name '*.mkv' -exec sh -c 'mv "$0" "${0%%.mkv}.avi"' {} \;
#!/bin/bash
find . -name "*.mkv" -exec ffmpeg -i {} -sameq `basename {} .mkv`.avi \;
Thanks to #Raul this is what worked for me and is the solution to what I wanted to do which is run recursively through directories and run the ffmpeg command on mkv files:
#!/bin/bash
find <file_path> -name '*.mkv' -exec sh -c 'ffmpeg -i "$0" -sameq "${0%%.mkv}.avi"' {} \;
exit;
Instead of ls *.mkv use find . -name "*.mkv".
This assumes no funny filenames (no spaces, newlines). Another possibility is using find in conjunction with xargs. The xargs manual makes for an instructive reading which will save your scripting life one day :-)

Resources