Ok so after days of searching, here I am. I am new to ffmpeg, applescript, and terminal.
I want to use ffmpeg to batch convert a group of selected files in any folder. I was successful in doing this by opening the terminal at the folder location and using this code:
for f in *.flv; do ffmpeg -i "$f" -acodec libmp3lame -b:a 256k "${f%.flv}.mp3" && rm "$f"; done
which finds all flv files, and converts it to 256 bit rate mp3, then deletes the original files.
Now I want it to be more automated, so I looked into creating a service. I tried running an apple script through automator, which I want it to open the terminal at the folder location the file then run the code to convert the files. Here's the code I attempted:
tell application "Finder" to set currentFolder to target of front Finder window as text
set theWin to currentFolder's POSIX path
tell application "Terminal"
if not (exists window 1) then reopen
activate
do script "cd " & quoted form of theWin & ";clear" in window 1
tell application "Terminal"
do script "for f in *.flv; do ffmpeg -i "$f" -acodec libmp3lame -b:a 256k "${f%.flv}.mp3" && rm "$f"; done"
end tell
end tell
The first part of code opens up terminal at the folder location just fine. But when I add the part with the ffmpeg code it crashes. The error is apparently with the "$", those are what light up as the error, the error message says "Expected end of line, but found unknown token". Looking for some assistance please. I need the "$" because those are what make the loop work for renaming the files and such.
You have to escape the quotes when you pass the string using \"
This line becomes:
do script "for f in *.flv; do ffmpeg -i \"$f\" -acodec libmp3lame -b:a 256k \"${f%.flv}.mp3\" && rm \"$f\"; done"
Related
I have 2 preset .json files(from the GUI version on windows) to convert mkv to mp4.
converts to h264 and adds subtitle 1
converts to h264
I'm only trying to get no.2 to work at this stage.
for i in `*.mkv`; do HandBrakeCLI --preset-import-file HPRESET.json -Z "MYPRESET" --output *.mp4; done
no output name
HandBrakeCLI -i $1 --preset-import-gui HPRESET.json -Z "MYPRESET" --output $1.mp4
errors on output name
for i in `*.mkv`; do HandBrakeCLI --title $i --preset "Very Fast 1080p30" --output *.mp4; done
errors on output name AND not valid preset.
$ for i in `seq 4`; do HandBrakeCLI --input /dev/dvd --title $i --preset Normal --output NameOfDisc_Title$i.mp4; done
copied this from another stackoverflow question, but outputs as 1.mp4 and then 2.mp4 etc.
You can extract the filename without extension with something like that:
noext=${i%.*}
Example:
╰─$ for i in *.mkv; do echo "$i"; noext=${i%.*}; echo "$noext"; done
asdf.mkv
asdf
test.mkv
test
Same loop, different notation:
for i in *.mkv
do
#put the commands you want to run after "do" and before "done"
echo "$i"
noext=${i%.*}
echo "$noext"
done
Note that the for command will search any file in the current directory ending with .mkv. For each file it has found, it will save the files name into the variable $i, then execute the commands after do, then save the next files name into the variable $i and execute the commands between do and done. It will repeat that cycle until every file which has been found is processed.
As I have no experience with handbrake presets, here a solution with ffmpeg:
for i in *.mkv
do
#put the commands you want to run after "do" and before "done"
noext=${i%.*}
ffmpeg -i "$i" -c:v libx264 -c:a copy -c:s copy "$noext.mp4"
done
I have two folders "Left channel" and "Right channel". Each folder contains mono files with same names. Example: "Left channel" contains "A.wav", "B.wav", "C.wav" and "Right channel" contains "A.wav", "B.wav", "C.wav". I need to make stereo files for each mono files.
So I have to combine
ffmpeg -i left.mp3 -i right.mp3 -filter_complex "[0:a][1:a]join=inputs=2:channel_layout=stereo[a]" -map "[a]" output.mp3
and
for file in /dir/* do ffmpeg -i ...; done
How can I go through all mono files and make bunch of stereo files from these mono files with ffmpeg in bash?
Would you please try the following:
#!/bin/bash
lch="Left channel"; rch="Right channel" # directory names of wav files
for f in "dir/$lch/"*.wav; do
fname=${f##*/} # filename such as "A.wav"
outfile="output_${fname%.*}.mp3" # output filename such as "output_A.mp3"
if [[ -f dir/$lch/$fname && dir/$rch/$fname ]]; then
echo ffmpeg -i "dir/$lch/$fname" -i "dir/$rch/$fname" -filter_complex "[0:a][1:a]join=inputs=2:channel_layout=stereo[a]" -map "[a]" "$outfile"
fi
done
It just outputs the command line as a dry run. If the output looks good, drop echo and run again.
Please note the output of echo removes the double quotes around the filenames. If you copy the output of echo and execute it on the command line, it will not work well.
Every one of my music folders are set up like Artist > Year Album >
Track 01.flac
Track 02.flac
Track 03.flac
folder.jpg, jpeg, png, etc
And what I need to do is if folder.* is available.
if [ -f folder.* ]; then
Run this command to set smaller size without replacing the original photo.
for small in folder.*
convert $small -resize 1000x1000 temp$small
Then run these commands on every file to automatically add the smaller sized cover to each audio file's tagging.
ffmpeg -i TRACK.flac -i SMALLFOLDER.* -map a -map 1:v -disposition:v attached_pic -metadata:s:v comment="Cover (Front)" -codec copy TRACKWITHART.flac
&& rm TRACK.flac
&& mv TRACKWITHART.flac TRACK.flac
&& rm temp$small
Last little bit there is me cleaning up. I'm having trouble piping commands into one another with this and not the most experienced with that sort of thing.
And also, if it's not available like above, will need to extract it from the first audio file by finding it.
else
find . -name "*.flac" -print -quit
And extracting it with this command.
ffmpeg -i TRACK.flac -vf scale=1000:1000 -an FOLDER.png
Then run the other commands above.
Now I don't know if anyone is familiar with FFmpeg but it's actually kind of nightmare because it's not necessarily for audio tagging but I don't know anything else to handle this kind of automated album art task in the terminal. If anyone can point me more in the right direction with a better CLI utility, that'd be awesome or just help with this bash scripting. You can see I'm fairly familiar with the terminal and getting some things done by searching the web but putting them altogether in a bash script is very difficult for me to understand, if anyone has some links for specifically this, that would be much appreciated.
You have the general right idea of how to do it.
The wooledge BashGuide is pretty much the best place to start when learning bash scripting. It's very accessible, and it directly addresses a lot of the pitfalls that beginners are susceptible to when writing scripts.
ALWAYS quote your variables when you are using them to store filenames/paths. You need to write your script as if every path/filename will have spaces, newlines, special characters, etc. Quoting your variables will go a long way towards preventing any chaos when your script runs.
Here is your code fixed up and thrown together into a working script:
#!/bin/bash
# check for album art file,
# if none, extract from first flac w/ ffmpeg
# exit script if ffmpeg fails
[[ -f folder.* ]] ||
{ tracks=(*.flac)
ffmpeg -i "${tracks[0]}" -vf scale=1000:1000 -an folder.png \
|| exit 1 ; }
# define an array of all folder.* files
albumart=(folder.*)
ffmpeg -i "${albumart[0]}" -vf scale=1000:1000 "tmp_${albumart[0]}" \
|| exit 1
# use the first element of the array,
# in case there are multiple folder.* files.
# exit if ffmpeg gives error code
for track in *.flac; do
ffmpeg -i "$track" -i "tmp_${albumart[0]}" -map a -map 1:v -disposition:v attached_pic -metadata:s:v comment="Cover (Front)" -codec copy "tmp_${track}" \
&& rm "$track" \
&& mv "tmp_${track}" "$track"
done
rm "tmp_${albumart[0]}"
I took the liberty of changing your convert line of image-resizing code so that it is instead handled by ffmpeg, since I am unfamiliar with "convert". If it is a script or binary you use, you will want to edit this line (keeping the new input & output variables intact).
This script does not need any arguments, and it will loop through and add the album art & metadata to all .flac files in your current directory. It is not designed to work recursively; you will need to cd into & run the script in each directory.
I'm trying to use ffmpeg to convert some .m4a audio files to .mp3, and have come across something that has me stumped. I'd like to create the .mp3 in the same location and with the same filename as the .m4a, and so I'm using a combination of find/exec and a bash script to do this, as follows:
find /Volumes/Untitled/ -name '[!.]*' -name '*.m4a' -exec ./m4atomp3.sh {} \;
where m4atomp3.sh looks like:
#!/usr/bin/env bash
[[ -f "$1" ]] || { echo "$1 not found" ; exit 1 ; }
P="$1"
echo "$P is the full filename"
filename=${P%.*}
echo "$filename is the stripped filename"
m4afilename=\"$filename.m4a\"
echo "$m4afilename is the input filename"
mp3filename=\"$filename.mp3\"
echo "$mp3filename is the output filename"
mycmd="/Users/nickstyles/Downloads/ffmpeg -i "$m4afilename" -codec:a libmp3lame -qscale:a 2 -nostdin "$mp3filename
echo $mycmd
$mycmd
Whenever I try this, it fails because ffmpeg doesn't find the file, seemingly because of the whitespace in the filename, e.g if the file was called /Volumes/Untitled/My M4As/My M4A.m4a I would see:
ffmpeg version N-99346-g003b5c800f-tessus https://evermeet.cx/ffmpeg/ Copyright (c) 2000-2020 the FFmpeg developers
built with Apple clang version 11.0.0 (clang-1100.0.33.17)
[configuration details]
"/Volumes/Untitled/My: No such file or directory
However, if I just paste what is returned by echo $mycmd into the command line, e.g:
/Users/nickstyles/Downloads/ffmpeg -i "/Volumes/Untitled/My M4As/My M4A.m4a" -codec:a libmp3lame -qscale:a 2 -nostdin "/Volumes/Untitled/My M4As/My M4A.mp3"
then it works absolutely fine. I'm sure I'm missing something very obvious, which hopefully someone can spot!
As Benjamin W. pointed out the problem was that the variable was still getting split by bash, due to WordSplitting, and the quotes I was adding to the content of the variable were not helping against this. The key was to ensure that the quotes were placed around the variable itself like:
m4afilename=$filename.m4a
echo "$m4afilename is the input filename"
mp3filename=$filename.mp3
echo "$mp3filename is the output filename"
/Users/nickstyles/Downloads/ffmpeg -i "$m4afilename" -codec:a libmp3lame -qscale:a 2 -nostdin "$mp3filename"
and now this works!
Try this : mycmd="/Users/nickstyles/Downloads/ffmpeg -i $m4afilename -codec:a libmp3lame -qscale:a 2 -nostdin $mp3filename"
In bash, you can put variable straight into double quotes.
I have a Mac computer. Usually all my batch programming is done on my PC. So I tried to create what I assumed would be a simple equivalent using a Mac shell. Obviously as you all know that was foolish of me to think that. After 2 days of scowering the web I found the closest thing I could to what I was looking for. But no, this doesn't work either.
All I'd like to do is throw a multimedia file onto the script, and have the terminal give me the ffmpeg info output. In my searching I did find this "$#" which as far as I can tell is the windows bat equivalent of %*. Meaning you can throw files on the script and the script refers to those files as variables which can be processed. So I believe what I want to do is possible.
Again the code at the bottom is just to look through the current directory of all .mov files and run ffmpeg. It doesn't work. But.. if no one can help me figure out the actual thing I'd like to do then I'd settle with something like below that does actually work.
#!/bin/bash
FFMPEG=/Applications/ffmpeg
FIND=/usr/bin/find
FILES=$(${FIND} . -type f -iname "*.mov")
if [ "$FILES" == "" ]
then
echo "There are no *.mov file in $(pwd) directory"
exit 1
fi
for f in *.mov
do
$FFMPEG -i "$f"
done
If someone can please help me figure this out I'd really appreciate it. Thank you in advance! Jules
I just found this solution from the "similar questions" sidebar, which is similar to the script above, so again, not completely what I wanted but.. didn't matter, didn't work for me. How to batch convert mp4 files to ogg with ffmpeg using a bash command or Ruby
.command files don't receive dropped files as input.
You might just open a Terminal window, type for f in, drop the files on the window, and type ; do ffmpeg -i "$f"; done.
Or save a script like this as an application in AppleScript Editor:
on open argv
set paths to ""
repeat with f in argv
set paths to paths & quoted form of POSIX path of f & " "
end repeat
tell application "Terminal"
do script "for f in " & paths & "; do ffprobe -i \"$f\"; done"
activate
end tell
end open
ffprobe -i is like ffmpeg -i but it doesn't show an error like At least one output file must be specified.
Edit: you could also use Platypus:
Set the script to something like for f; do ffprobe -i "$f"; done.
This might do it:
for FILE in "${#}"
do
/Applications/ffmpeg -i "$FILE"
done