For loop not working ffmpeg - bash

my script is not giving me any output
for file in ./*
do
if [ "$file" = *.mov ];
then
ffmpeg -i $file -an -f framemd5
fi
done
whenever I run it it just give me back my prompt immediately.

Because your test:
if [ "$file" = *.mov ];
is false.
If, for exampe, the current directory has these files:
file1.mov
file2
file3.txt
file4.mov
Then the $file variable will be set as follows through each iteration:
./file1.mov
./file2
./file3.txt
./file4.mov
But the right-hand side of the test will remain to be "file1.mov file4.mov", so each test is:
if [ "./file1.mov" = file1.mov file4.mov];
if [ "./file2" = file1.mov file4.mov];
if [ "./file3.txt" = file1.mov file4.mov];
if [ "./file4.mov" = file1.mov file4.mov];
...neither of which is ever true.
If you want to loop through all the .mov files in the current directory, use this instead:
for file in ./*.mov; do
ffmpeg -i $file -an -f framemd5
done
By the way, you should always be ready for files with spaces and other annoying characters in the name, so this would be a bit more robust:
for file in ./*.mov; do
ffmpeg -i "$file" -an -f framemd5
done
As it will put quotes around the file name.

Assuming you are using bash, you need to use the [[ command for pattern matching.
for file in ./*
do
if [[ "$file" = *.mov ]];
then
ffmpeg -i "$file" -an -f framemd5
fi
done
However, it's simpler to just match the .mov files in the first pattern as shown by dovetalk.

Related

ffmpeg command exports flac with wrong 'length' metadata, works fine for mp3

I have some audio recorded in Audacity 3.2.3 that I have exported as an mp3 and a flac. Then I have this file split_by_silence.sh
Which has hardcoded input path values that take an input file, split it by detecting silence, and then finally run an ffmpeg command to split the files. If you save the below code into a file split.sh, you can call it with the command $ ./split_by_silence.sh "value1" "value2"
# ./split_by_silence.sh "full_lowq.flac" %03d_output.flac
#IN=$1
#OUT=$2
OUT="%03d_output.flac"
IN="/mnt/e/martinradio/rips/vinyl/WIP/Dogs On Fire (1983, Vinyl)/dog on fire.flac"
OUTPUT_LOCATION="/mnt/e/martinradio/rips/vinyl/WIP/Dogs On Fire (1983, Vinyl)/"
true ${SD_PARAMS:="-18dB"};
true ${MIN_FRAGMENT_DURATION:="20"};
export MIN_FRAGMENT_DURATION
if [ -z "$OUT" ]; then
echo "Usage: split_by_silence.sh full.mp3 output_template_%03d.mp3"
echo "Depends on FFmpeg, Bash, Awk, Perl 5. Not tested on Mac or Windows."
echo ""
echo "Environment variables (with their current values):"
echo " SD_PARAMS=$SD_PARAMS Parameters for FFmpeg's silencedetect filter: noise tolerance and minimal silence duration"
echo " MIN_FRAGMENT_DURATION=$MIN_FRAGMENT_DURATION Minimal fragment duration"
exit 1
fi
#
# get comma separated list of split points (use ffmpeg to determine points where audio is at SD_PARAMS [-18db] )
#
echo "_______________________"
echo "Determining split points..." >& 2
SPLITS=$(
ffmpeg -v warning -i "$IN" -af silencedetect="$SD_PARAMS",ametadata=mode=print:file=-:key=lavfi.silence_start -vn -sn -f s16le -y /dev/null \
| grep lavfi.silence_start= \
| cut -f 2-2 -d= \
| perl -ne '
our $prev;
INIT { $prev = 0.0; }
chomp;
if (($_ - $prev) >= $ENV{MIN_FRAGMENT_DURATION}) {
print "$_,";
$prev = $_;
}
' \
| sed 's!,$!!'
)
echo "SPLITS= $SPLITS"
#
# Add 5 seconds to each of the comma separated numbers
#
# Convert the comma-separated string into an array
arr=($(echo $SPLITS | tr ',' '\n'))
# Initialize a new array to store the results
new_arr=()
# Iterate through each element and add 5 seconds of padding
for i in "${arr[#]}"; do
result=$(echo "$i + 5" | bc -l)
new_arr+=("$result")
done
# Convert the array back into a comma-separated string
NEW_SPLITS=$(IFS=,; echo "${new_arr[*]}")
# Print the result
echo "NEW_SPLITS= $NEW_SPLITS"
SPLITS=$NEW_SPLITS
#
# Print how many tracks should be exported
#
res="${SPLITS//[^,]}"
CHARCOUNT="${#res}"
num=$((CHARCOUNT + 2))
echo "Exporting $num tracks"
echo "_______________________"
#
# Split audio into individual tracks
#
current_directory=$(pwd)
cd "$OUTPUT_LOCATION"
echo "Running ffmpeg command: "
ffmpeg -i "$IN" -c copy -map 0 -f segment -segment_times "$SPLITS" "$OUT"
#ffmpeg -i "full_lowq.flac" -c copy -map 0 -f segment -segment_times "302.825,552.017" "%03d_output.flac"
echo "Done."
cd $current_directory
echo "running flac command"
# check flac file intrgrity
If I call this code for my flac file:
OUT="%03d_output.flac"
IN="/mnt/e/martinradio/rips/vinyl/WIP/Dogs On Fire (1983, Vinyl)/dog on fire.flac"
The outputted files have an incorrect metadata for the length. They all report as having the same length, but if i import any of them into audacity, the file has a correct length.
but if i run this for my mp3 file, we can see the correct length metadata:
OUT="%03d_output.mp3"
IN="/mnt/e/martinradio/rips/vinyl/WIP/Dogs On Fire (1983, Vinyl)/dogs on fire.mp3"
So there is something with my ffmpeg command that causes it to export flac files with wrong 'length' metadata
ffmpeg -i "$IN" -c copy -map 0 -f segment -segment_times "$SPLITS" "$OUT"
I've tried with the flac example to change -c copy to -c:a flac, but that just gives every output flac file a length of 00:00:00
is it a problem with my ffmpeg command? Or my files? https://file.io/tIFsa1l70076
it works for mp3 files just fine, why does it have this issue with flac?

Bash script changing part of folder path on dynamic variable

So i have put together the following bash script and i want to modify the file path directory i grab
So my layout is like so
/home/videos_test/categoryname/1/vid.mp4
/home/videos_test/categoryname/2/randomvid.mp4
and i want to modify the file path it grabs to look like this
/home/videos_test/changed_folder/1/vid.mp4
/home/videos_test/changed_folder/2/randomvid.mp4
Full script
#!/bin/bash
echo "Type the category that you want, followed by [ENTER]:"
read category
export FILE_PATH="/home/videos_test"
export STAR="/*"
FILE_PATH=$FILE_PATH"/"$category;
echo $FILE_PATH;
minimumsize=1000
for dir in $FILE_PATH$STAR$STAR; do
[[ ! -f "${dir}" ]] && continue # if its NOT a file then skip
actualsize=$(du -k "$dir" | cut -f 1)
if [[ ! $actualsize -ge $minimumsize ]]; then #if file is less than 1mb delete it
echo $dir
echo size is under $minimumsize kilobytes
rm "$dir"
echo deleted
else #for files over 1mb in size convert with ffmpeg
full_file=$(basename $dir)
full_dir=$(dirname $dir)
echo $full_file
echo $full_dir
ffmpeg -i "$dir"
#ffmpeg output file directory needs to be mirrored but with a folder change
read -p "Press any key to resume ..."
fi
done
My folder output for ffmpeg i want to be
/home/videos_test/$category _new/1/vid.mp4
/home/videos_test/$category _new/2/randomvid.mp4
Assumptions:
user provides a base directory (read category) containing some video files
script needs to write converted files to new directory structure, replacing ${category} (in the directory name) with ${category}_new
question is: how to add _new suffix to directory name
One idea is to use parameter substitution to create the new target directory name, and then use mkdir -p to create the new directory:
$ category=videos
$ full_dir="/some/parent/dir/${category}/subdir1/subdir2"
$ echo "${full_dir}"
/some/parent/dir/videos/subdir1/subdir2
$ new_dir="${full_dir//${category}/${category}_new}"
$ echo "${new_dir}"
/some/parent/dir/videos_new/subdir1/subdir2
$ [ -d "${new_dir}" ]
$ echo $?
1 # ${new_dir} does not exist
$ mkdir -p "${new_dir}"
$ [ -d "${new_dir}" ]
$ echo $?
0 # ${new_dir} does exist
path_start=$(echo $dir | rev | cut -d'/' -f4- | rev)
path_end=$(echo $dir | cut -d'/' -f5-)
new=_new
new_path=$path_start/$category$new/$path_end
new_path_no_file=$(echo $new_path | rev | cut -d'/' -f2- | rev)
mkdir -p $new_path_no_file
ffmpeg -i "$dir" -c:v copy -c:a copy -x264opts opencl -movflags +faststart -analyzeduration 2147483647 -probesize 2147483647 -pix_fmt yuv420p "$new_path"

How does FFmpeg concat videos and output same file prefix

I have a lot of videos cut into three parts, and I want to concat them.
I now have the following bash to output videos in the folder to a file named mergedVideo.mp4.
for f in *.mp4 ; do echo file \'$f\' >> fileList.txt;done
ffmpeg -f concat -safe 0 -i fileList.txt -c copy mergedVideo.mp4
All my input files are like Something part1.mp4, Something part2.mp4, Something part3.mp4
What I want is to output a file named Something.mp4
It is possible?
How can I modify my bash to achieve that~?
Would you please try the following:
#!/bin/bash
pat="^([^[:space:]]+)[[:space:]]*part[[:digit:]]+\.mp4$"
for f in *.mp4; do
echo file \'$f\' >> fileList.txt
[[ $f =~ $pat ]] && mergedvideo="${BASH_REMATCH[1]}.mp4"
done
ffmpeg -f concat -safe 0 -i fileList.txt -c copy "$mergedvideo"
Explanation of the regex $pat:
^([^[:space:]]+) matches a non-space substring from the beginning of the
string $f and is assigned to the shell variable BASH_REMATCH[1].
As for the provided example filenames, BASH_REMATCH[1] will be assigned to Something.
[[:space:]]* matches a zero or more whitespace(s).
part[[:digit:]]+ matches a string "part" followed by digit(s).
\.mp4 matches the suffix.
[Alternative]
If you are familiar with sed as well, the following may be more readable and maintainable:
mergedvideo=$(sed -E 's/[[:space:]]*part[[:digit:]]+//' <<< "$f")
I extend tshiono's answer to be able to manipulate all the files in the same folders (just like Marinos An mentioned, the idea is to merge files with the same prefix)
Cases like a folder with the following files:
Monday Blue part1.mp4
Monday Blue part2.mp4
Tuesday.mp4
Wednesday part1.mp4
Wednesday part2.mp4
Wednesday part3.mp4
Expected Output:
Monday Blue.mp4
Tuesday.mp4
Wednesday.mp4
#!/bin/bash
pat="^(.+)[[:space:]]+part[[:digit:]]+\.mp4$"
for f in *.mp4; do
if [[ $f =~ $pat ]]; then
echo file \'$f\' >> "${BASH_REMATCH[1]}.txt"
fi
done
for f in *.txt; do
ffmpeg -f concat -safe 0 -i "${f%%.*}.txt" -c copy "${f%%.*}.mp4"
done

ffmpeg - How to convert massive amounts of files in parallel?

I need to convert about 1.5TiB or audio files which are in either flac or wav format. They need to be converted into mp3 files, keeping important meta data and the cover art etc. and the bitrate needs to be 320k.
This alone is easy:
ffmpeg -i "$flacFile" -ab 320k -map_metadata 0 -id3v2_version 3 -vsync 2 "$mp3File" < /dev/null
But the problem is making it faster. The command from above only uses 12.5% of the CPU. I'd much rather use like 80%. So I played around with the threads flag, but it doesn't make it faster or slower:
ffmpeg -i "$flacFile" -ab 320k -map_metadata 0 -id3v2_version 3 -vsync 2 -threads 4 "$mp3File" < /dev/null
But it only utilizes my CPU by 13%. I think it only uses one thread. My CPU has 8 physical cores btw (+ hyperthreading).
So my idea now is to somehow have multiple instances of ffmpeg running at the same time, but I have no clue how to do that properly.
This is my current script to take all flac/wav files from one directory (recursively) and convert them to mp3 files in a new directory with the exact same structure:
#!/bin/bash
SOURCE_DIR="/home/fedora/audiodata_flac"
TARGET_DIR="/home/fedora/audiodata_mp3"
echo "FLAC/WAV files will be read from '$SOURCE_DIR' and MP3 files will be written to '$TARGET_DIR'!"
read -p "Are you sure? (y/N)" -n 1 -r
echo # (optional) move to a new line
if [[ $REPLY =~ ^[Yy]$ ]] ; then # Continue if user enters "y"
# Find all flac/wav files in the given SOURCE_DIR and iterate over them:
find "${SOURCE_DIR}" -type f \( -iname "*.flac" -or -iname "*.wav" \) -print0 | while IFS= read -r -d '' flacFile; do
if [[ "$(basename "${flacFile}")" != ._* ]] ; then # Skip files starting with "._"
tmpVar="${flacFile%.*}.mp3"
mp3File="${tmpVar/$SOURCE_DIR/$TARGET_DIR}"
mp3FilePath=$(dirname "${mp3File}")
mkdir -p "${mp3FilePath}"
if [ ! -f "$mp3File" ]; then # If the mp3 file doesn't exist already
echo "Input: $flacFile"
echo "Output: $mp3File"
ffmpeg -i "$flacFile" -ab 320k -map_metadata 0 -id3v2_version 3 -vsync 2 "$mp3File" < /dev/null
fi
fi
done
fi
I mean I guess I could append an & to the ffmpeg command, but that would cause throusands of ffmpeg instances to run at the same time, which is too much.
Something like this:
#!/bin/bash
SOURCE_DIR="/home/fedora/audiodata_flac"
TARGET_DIR="/home/fedora/audiodata_mp3"
export SOURCE_DIR
export TARGET_DIR
doone() {
flacFile="$1"
if [[ "$(basename "${flacFile}")" != ._* ]] ; then # Skip files starting with "._"
tmpVar="${flacFile%.*}.mp3"
mp3File="${tmpVar/$SOURCE_DIR/$TARGET_DIR}"
mp3FilePath=$(dirname "${mp3File}")
mkdir -p "${mp3FilePath}"
if [ ! -f "$mp3File" ]; then # If the mp3 file doesn't exist already
echo "Input: $flacFile"
echo "Output: $mp3File"
ffmpeg -i "$flacFile" -ab 320k -map_metadata 0 -id3v2_version 3 -vsync 2 "$mp3File" < /dev/null
fi
fi
}
export -f doone
# Find all flac/wav files in the given SOURCE_DIR and iterate over them:
find "${SOURCE_DIR}" -type f \( -iname "*.flac" -or -iname "*.wav" \) -print0 |
parallel -0 doone

re-ordering the files in shell script

In my system file are in below format
image0.jpg
image1.jpg
image2.jpg
image3.jpg
.
.
.
.
.
image10.jpg
image11.jpg
i can order the file using this below script
for i in *.jpg; do
new=$(printf "xome%04d.jpg" "$a") #04 pad to length of 4
mv -- "$i" "$new"
let a=a+1
done
but what happen is it get the file in this order like image0.jpg,image10.jpg,image1.jpg,image11.jpg,image2.jpg..............
how to get sequence using shell script
Try this :
for i in {1..11}; do
printf -v newfile "xome%04d.jpg" "$i"
[ -f "image${i}" ] && mv "image${i}.jpg" "$newfile"
done
The -v option stores the printf result in variable newfile.
[ -f "image${i}" ] test if file exist before trying to rename.

Resources