I have hundreds of folders containing video, audio, images.. which I need to create video thumbs foreach 10 seconds of video of every video file found (mp4/avi/mov/3gp) and placed in one location /thumbs/.
I have this, which I've been trying to figure out for days.
find . -exec ffmpeg -i {} -vf fps=1/8 {}.png \;
or
find /Users/media/Desktop/videoframes/input/ -regex ".*\.\(mp4\)" -print0 | while read -d $'\0' file; do ffmpeg -i $file -vf fps=1/8 ${file}%d.png done
I know little about the syntax but want to learn more but Im stumped.
Thanks!
Related
I'm trying to batch convert thousands of wav files into 96k m4a files on Mac OS Mojave using ffmpeg in the terminal.
I'm trying to use the following code:
for f in *.wav; do ffmpeg -i "$f" -c:a libfdk_aac -b:a 96k “${f%.wav}.m4a”; done
I'm being given the following error:
Unable to find a suitable output format for '“file.m4a”'
“file.m4a”: Invalid argument
Can anyone help?
Smartquotes are treated as part of the filename.
Use plain quotes instead:
for f in *.wav; do ffmpeg -i "$f" -c:a libfdk_aac -b:a 96k "${f%.wav}.m4a"; done
If you have lots of files to convert, you might want to do that in parallel:
find . -name '*. wav' -type f -print0 | parallel -0 ffmpeg -i {} -c:a libfdk_aac -b:a 96k {.}.m4a
Check this doc for how to work with parallel. If you don't have the tool, install it with brew install parallel.
Scott's answer is perfectly fine too. I like parallel as it also allows me to easily e.g. modify the name of the output file.
I have a code that using ffmpeg to conver *.avi to *.mp4 on all files in the folder. I want it to run on all folders and sub folders; and to save the result in this subfolder.
I tried already with find but no success
#!/bin/bash
for i in *.avi;
do
ffmpeg -i /location/"$i" -c:v libx264 -preset ultrafast \
-strict -2 -n /location/"$(basename "$i" .avi)".mp4
done
You can use find to find matching files in all sub-directories:
find <top-dir> -iname "*.avi" | while read filename; do ffmpeg -i "$filename" ... "${filename%avi}mp4"; done
Replace ... with your ffmpeg options.
You can also use GNU parallel utility to parallelize processing of found files over multiple CPUs:
find <top-dir> -iname "*.avi" | parallel -i -- ffmpeg -i "{}" ... "{.}.mp4"
I am faced with a challenge that requires multiple aspects of bash. I work in Linux (precisely Debian Stretch). Here is the situation (for all points/problem I write along the solution I considered for now, but I'm open to other ideas) :
I have videos of various types (and various upper-lower case), such as .mp4, .mov, .MOV, .MP4, .avi,... located in a directory (and spread across an almost un-structured tree of directories). To find all I tried to use the find command
For each video, I need to extract some metadata (i.e. the name of the file, duration of video, size of file and date of creation/last modification). The package mediainfo yields (among a lot of other things) the required fields.
The output of mediainfo is a long list of fields with format : <Tag>\t : <value>. I need to extract values for fields Complete name, Duration, File size and Encoded date.
So with all this information, I must filter the required fields value and put them in a CSV file. I considered using sed.
My goal is to achieve all these tasks either in a script or a small amount of separate commands.
The idea code (this code is hideously wrong, but you can get an idea) :
find . -type f -name "*.[mp4|MP4|mov|MOV|avi|AVI]" -exec mediainfo {} | sed '/Complete name|Duration|File size|Encoded date/p' > myfile.csv \;
Would you have any idea how to perform this task ? I feel terribly lost in combining find, exec and sed and outputting to a csv...
Thanks in advance for your help !
So I finally managed to write a script doing that. Probably not the best way to do, but here it is :
resFile="myresult.csv"
dstDir="./destination/"
srcDir="./source/"
#first copy all files at same level in dstDir (with preserve and update)
#this is somehow necessary, relative name for MOV files and mediainfo
#do not seem to work together.
find $srcDir -type f \( -name "*.mp4" -o -name "*.mov" -o -name "*.MOV" -o -name "*.avi" \) -exec cp -up {} $dstDir \;
#then for each file, output mediainfo of file and keep only interesting tags. add ### between each file.
find $dstDir -type f \( -name "*.mp4" -o -name "*.mov" -o -name "*.MOV" -o -name "*.avi"\
-exec sh -c " mediainfo --Output=XML {} | sed '1,15!d;/Duration\|Complete\|File_size\|Encoded_date/!d' >> $resFile && echo '########' >> $resFile" \;
#removes tags : <Duration>42s 15ms</Duration> -> 42s 15ms
sed -i 's/^<.*>\(.*\)<.*>/\1/I' $resFile
#Extract exact filename (and not relative)
sed -i 's/^\.\/.*\/\(.*\)\.[mp4|MOV|mov|avi|MP4]/\1/' $resFile
#Puts fields for a file on a unique line separated with commas
sed -i 'N;s/\n/,/;N;s/\n/,/;N;s/\n/,/;N;s/\n/,/' $resFile
#remove all trailing ###
sed -i 's/,#*$//' $resFile
I would still be interested if anyone has idea to improve the code.
I "minimized" a little bit, my actual code is a bit more modular and performs a few checks
Try this. Due to less time,I was not able to complete. You just have to send output to CSV.
for c in $(locate --basename .mp4 .mkv .wmv .flv .webm .mov .avi)
do
Complete_name=$(mediainfo --Output=XML $c | xml_grep 'Complete_name' --text_only| awk 'BEGIN{FS="/"}{print $NF}')
echo $Complete_name
Duration=$(mediainfo --Output=XML $c | xml_grep 'Duration' --text_only --nb_result 1)
echo $Duration
File_size=$(mediainfo --Output=XML $c | xml_grep 'File_size' --text_only)
echo $File_size
Encoded_date=$(mediainfo --Output=XML $c | xml_grep 'Encoded_date' --text_only -nb_result 1 | awk '{print $2}')
echo $Encoded_date
done
I wanted to just comment on an answer to a question very similar to this but I don't have enough rep. I'm looking for a way to change this line of code:
for i in *.mkv; do ffmpeg -i "$i" "${i%.*}.mp4"; done
So that it includes .avi files and so that it can search through nested folders. I want to be able to target /Videos and have the script automatically traverse through the folder tree into /Videos/2016/January, convert all of the video files contained within that folder, then do the same for /Videos/2016/February and so on.
Thanks in advance.
(credit for above line of code goes to LordNeckBeard, from this post.)
Using LordNeckBeard's reference to find, I came up with the following solution:
find ./ -iname '*.avi' -o -iname '*.mkv' -exec bash -c 'ffmpeg -i "{}" -c:v libx265 -preset medium -crf 28 -c:a aac "{}".mp4' \;
Tested and worked exactly how I expected, so it is currently running through the entire library.
If you want to give your converted files a different name to the original, see Parameter Expansion.
If you wish to destructively convert all files, be extremely careful with this command:
find ./ -iname '*.avi' -o -iname '*.mkv' -exec bash -c 'ffmpeg -i "{}" -c:v libx265 -preset medium -crf 28 -c:a aac "{}".mp4 && rm "{}"' \;
NOTE: The command above isn't bulletproof and was removing some files BEFORE the conversion process had finished, meaning I have now lost those files (thank God for backups). I tested with disposable files and have made sure I have a fully functional back up of my data before starting this procedure.
I am trying to convert a directory full of mp3's (with spaces in file names) to m4a.
To convert a single file (this works):
ffmpeg -i Traffic.mp3 -c:a libfaac -vn Traffic.m4a
The command that is failing (on OS X Mavericks):
find . -name \*.mp3 -print0 | xargs -0 ffmpeg -i {} -c:a libfaac -vn {}.m4a
find . -name '*.mp3' -type f -exec bash -c 'ffmpeg -i "$0" -c:a libfaac -vn "${0%.mp3}.m4a"' {} \;
Why do you use xargs? find -exec is enough:
find . -name \*.mp3 -exec ffmpeg -i {} -c:a libfaac -vn {}.m4a \;
The problem is that xargs is more similar to find -exec … + than find -exec … \;. It launches preferably just one instance of the command, replacing a single {} by sequence of space separated items read from input (more or less). If you want xargs to behave like find -exec … \;, you need to specify -I{} (xargs -0 -I{} ffmpeg …).
This converts Traffic.mp3 to Traffic.mp3.m4a. If you want to save the conversion result to Traffic.m4a, you can
rename the files after the conversion (not a very clean solution),
execute shell in the -exec action and remove the .mp3 before appending .m4a or
use xargs after sedding the .mp3 extension away from find result.
I vote for the last option as it executes less processes (and shell is quite a big one, though ffmpeg would be difinitely so large that the difference in performance is negligible).
find . -name \*.mp3 | sed 's/\.mp3$//' | xargs -I{} ffmpeg -i {}.mp3 -c:a libfaac -vn {}.m4a