This question already has an answer here:
Pass dynamically generated parameters to command inside script
(1 answer)
Closed 11 months ago.
I am trying to write a parameter driven routine to extract parts of audio files using ffmpeg.
Because the routine is parameter driven I end up with a number of options in variables (a technique I have used successfully before in simpler examples) and for some reason this time it isn't working. having stared at it and tried various experiments for hours I give up and hope the helpful experts can sort me out
This is a simplified version with the variables set directly
...
#!/bin/bash
a="a b c.mp3"
b="out-$a"
trackstring="-metadata track=\"07/93\""
echo "trackstring=$trackstring"
titlestring="-metadata title=\"$a\""
echo "titlestring=$titlestring"
startpoint="-ss 0"
echo "startpoint=$startpoint"
endpoint="-to 300"
echo "endpoint=$endpoint"
coverstring="-c:v copy"
echo "coverstring=$coverstring"
audiostring="-c:a libmp3lame -ab 32k -ac 1"
echo "audiostring=$audiostring"
echo "ffmpeg $startpoint $endpoint -i \"$a\" -hide_banner -loglevel warning $coverstring $audiostring $titlestring $trackstring \"$b\""
ffmpeg $startpoint $endpoint -i "$a" -hide_banner -loglevel warning $coverstring $audiostring $titlestring $trackstring "$b"
...
The resulting output from my script looks like this:
trackstring=-metadata track="07/93"
titlestring=-metadata title="a b c.mp3"
startpoint=-ss 0
endpoint=-to 300
coverstring=-c:v copy
audiostring=-c:a libmp3lame -ab 32k -ac 1
ffmpeg -ss 0 -to 300 -i "a b c.mp3" -hide_banner -loglevel warning -c:v copy -c:a libmp3lame -ab 32k -ac 1 -metadata title="a b c.mp3" -metadata track="07/93" "out-a b c.mp3"
Which gives me exactly what I am expecting and I think all valid BUT....
Then ffmpeg gives me:
[mp3 # 0x55ae679e4640] Estimating duration from bitrate, this may be inaccurate
[NULL # 0x55ae679ea0c0] Unable to find a suitable output format for 'b'
b: Invalid argument
A bad one!
But interestingly putting the whole command into a string and the using an explicit sub-shell works exactly as expected: So starting from the last of the assignments in the original post
...
audiostring="-c:a libmp3lame -ab 32k -ac 1"
echo "audiostring=$audiostring"
cmd="ffmpeg $startpoint $endpoint -i \"$1\" -hide_banner -loglevel warning $coverstring $audiostring $titlestring $trackstring \"$2\""
echo "$cmd"
bash -c "$cmd"
Frankly, although I now have "working code" I think I am more confused than before.
The output is unchanged (except no error from ffmpeg) and the file(s) are generated exactly as expected
Related
(first time posting a question here)
So I'm looking to write a ffmmpeg script to automate encoding my files to VP9.
The problem I'm having is when I try to strip the extension and add a new one.
For example
Demo.mp4
Should change to
Demo.webm
I'm running this on a Ubuntu-16.04 (Server Non-GI Version)
I've tried a few different ways to accomplish this (using google and other posts on StackOverflow) but I can't seem to make it work
This is the error I keep getting..
line 31: Demo.mp4+.vp9: syntax error: invalid arithmetic operator (error token is ".mp4+.vp9")
I've also commented (in the code below) where the syntax error is pointing to..
#!/bin/bash
# Welcome Message
clear
printf "====================================\n"
printf "FFMPEG Encoder\n"
printf "(Using HDR-4k Profile)\n"
printf "====================================\n\n"
printf " Loading Files in Current Directory...\n\n"
sleep 3s
# Variables
i=1
ext=".webm"
vadd=4000000
vsub=2000000
# Iterate through files in current directory
for j in *.{mp4,mkv};
do
echo "$i.$j"
file[i]=$j
i=$(( i + 1 ))
done
# Select File & Bitrate
printf "Enter file number\n"
read fselect
printf "${file[$fselect]}: Selected for encoding\n\n"
printf "Enter Average Bitrate (Eg: 8000000)\n\n"
read bselect
# ***THIS IS WHERE THE PROBLEM IS***
# Prepare output file, strip trailing extension (eg .mkv) and add .webm
ftemp1="${file[$fselect]}"
ftemp2="${ftemp1::-4}"
fout="$(($ftemp2+$ext))"
printf "Output file will be: $fout"
printf "Preparing to encode..."
sleep 5s
# Encode with User-Defined Parameters
ffmpeg -y -report -i ${file[$fselect]} -b:v $bselect -speed 4 -pass 1 \
-pix_fmt yuv420p10le \
-color_primaries 9 -color_trc 16 -colorspace 9 -color_range 1 \
-maxrate "$(($bselect+$vadd))" -minrate "$(($bselect-$vsub))" \
-profile:v 2 -vcodec libvpx-vp9 -f webm /dev/null && \
ffmpeg -y -report -i ${file[$fselect]} -b:v $bselect -pass 2 \
-pix_fmt yuv420p10le \
-color_primaries 9 -color_trc 16 -colorspace 9 -color_range 1 \
-maxrate "$(($bselect+$vadd))" -minrate "$(($bselect-$vsub))" \
-profile:v 2 -vcodec libvpx-vp9 \
$fout
I'm certain there is a much cleaner way to do this - but I'm not expecting help with that :P
My suspicion is that I'm trying to add two different types of variables? But I thought I defined them as strings..I could be wrong
Please Help... lol
You are trying to do arithmetic calculus ($((...))). But you just need to concatenate two strings:
fout="$ftemp2$ext"
BTW, you can simplify this transformation in three lines with a single line:
fout="${file[$fselect]/%.mp4/$ext}"
This works as a regular expression, where an .mp4 string found at the end (the % symbol) is repalced by the contents of $ext.
I have a script called automateutube that I edit in VIM and execute in the terminal with sh ./automateutube.sh This script pulls youtube links from a file called songs.txt and downloads the video from youtube then extracts the audio.
The songs.txt file looks like this
https://www.youtube.com/watch?v=IxQOlZ3pqtI
https://www.youtube.com/watch?v=IxQOlZ3pqtI
https://www.youtube.com/watch?v=IxQOlZ3pqtI
https://www.youtube.com/watch?v=IxQOlZ3pqtI
It is just a bunch of links, one per line.
The script looks like this
#!/bin/bash
while read p; do
x=/tmp/.youtube-dl-$(date +%y.%m.%d_%H.%M.%S)-$RANDOM.flv
youtube-dl --audio-quality 160k --output=$x --format=18 "$p"
ffmpeg -i $x -acodec libmp3lame -ac 2 -ab 128k -vn -y "$p"
rm $x
done <songs.txt
Now the first part executes. It downloads the video and starts to unpack it.
It is the second part that fails. ffmpeg -i $x -acodec libmp3lame -ac 2 -ab 128k -vn -y "$p"
This is because "$p" is supposed to be in format "filename.mp3" However as it is p takes the value of a youtube link, without ".mp3" appended.
This works for the first line
youtube-dl --audio-quality 160k --output=$x --format=18 "$p"
because "$p" is supposed to be in the form of a link there.
Now I have tried adding three lines in
a="$.mp3"
b="$p"
c=$b$a
and making ffmpeg -i $x -acodec libmp3lame -ac 2 -ab 128k -vn -y "$p"
into ffmpeg -i $x -acodec libmp3lame -ac 2 -ab 128k -vn -y "$c"
but I am still getting an error. Any ideas?
parse error, at least 3 arguments were expected, only 1 given in string 'om/watch?v=sOAHOxbMOJY'
So after some experimentation using advice from the comments, I came to this, which works.
#!/bin/sh
while read -r p; do
x=/tmp/.youtube-dl-$(date +%y.%m.%d_%H.%M.%S)-$RANDOM.flv
youtube-dl --audio-quality 160k --output="$x" --format=18 "$p"
SUBSTRING=$(echo "$p"| cut -c33-50)
ffmpeg -i "$x" -acodec libmp3lame -ac 2 -ab 128k -vn -y "$SUBSTRING.mp3" </dev/null
rm "$x"
done <songs.txt
What this fixes is keeping /'s out of the file name, and eliminating parser error.
This question already has answers here:
bash script order of execution
(2 answers)
Closed 7 years ago.
I will be on 3 days vacation, so I would like to do a task in one file, after it's done, launch the other one using bash script, the way I would like to do is:
List files location in one file, i.e (toDo.txt)
Once the first file is done it goes to the other one.
Example:
doDo.txt contents:
/home/me/www/some_dir/file1F42.sh
/home/me/www/another_dir/fileD2cD.sh
/home/me/www/third_dir/fileG0IU.sh
/home/me/www/last_dir/fileVFpO.sh
file1F42.sh:
some commands here
Once is done, it should jump to line 2, which is: fileD2cD.sh
I do NOT want to use cron, because I do not know when the files will finish treatment, and at the same time I do NOT want to launch all of them at once.
This is a real example that I just finish to do:
ffmpeg -i Original/$domainName"_"$fileName"_"$f-Original.mp4 -strict experimental -vf "drawtext=fontfile='/usr/share/fonts/truetype/freefont/FreeSansBold.ttf':text='www.alfirdaous.com':x="$SizeX":y="$SizeY":fontsize="$textSize":fontcolor=$textColor" -vcodec libx264 -preset medium -crf 24 -acodec copy Done/"$domainName"_"$fileName"_$f-Done.mp4
mp4Box=$(MP4Box -add Done/"$domainName"_"$fileName"_$f-Done.mp4 "$domainName"_"$fileName"_$f.mp4)
echo $mp4Box >> ffmpeg_exec.log;
# Delete Done files
rm Done/"$domainName"_"$fileName"_$f-Done.mp4
# Get master thumbnail
ffmpeg -itsoffset -150 -i "$domainName"_"$fileName"_$f.mp4 -vcodec mjpeg -vframes 1 -an -f rawvideo -s 640x480 "$silsilaName"_$f.png
n=0
for offset in 140 160 180 200 220 240 260 280 300 320
do
printf -v outfile "$silsilaName"_"$f"_"%03d.png" "$((++n))"
ffmpeg -itsoffset -$offset -i $domainName"_"$fileName"_"$f.mp4 -vcodec mjpeg -vframes 1 -an -f rawvideo -s 640x480 "$outfile"
done
ffmpeg -i "$domainName"_"$fileName"_$f.mp4 -vn -ar 44100 -ac 2 -ab 128 -f mp3 $f.mp3
done
Last command line is:
ffmpeg -i "$domainName""$fileName"$f.mp4 -vn -ar 44100 -ac 2 -ab 128 -f mp3 $f.mp3
How do I know that it is DONE, finish, so I can go to my file list "doDo.txt" and start running the next file?
#! /bin/sh
while read file; do
echo "Executing $file"
sh "$file"
done < /dev/stdin
Where usage is <script.sh> < file_list
From someone I received a batch file that can do ffmpeg conversion using 2 command arguments. Let's say the file is called convert.sh and only contains this line:
ffmpeg -ss 0 -t 36000 -i "$1" -ar 8000 -ab 16K -ac 1 -y "$2"
I call it as follows:
sh convert.sh inputfile.wav outputfile.wav
In the ffmpeg documentation, I can find what all arguments mean, except for -ab. I guess 16K is its value. But I have no idea what it does. Anyone?
-ab argument means audio bitrate
-ar- set the audio sampling frequency
-ac- Set the number of audio channels
-ab- Set the audio bitrate
for more info visit this link
As I have a low-end computer running Linux I often need to re-encode HD videos, lowering the quality to be able to watch them in my machine. A typical case is when I download several episodes of a series, I can't convert them all at once, and I need to start re-encoding halfway through the series.
I typically use this line to convert a single episode to a lower quality:
avconv -i anime_episode_01.mkv -map 0 -c copy -c:v libx264 -crf 31 anime_01.mkv
If I were to batch-convert them at once I would use something like:
for i in *.mkv;do avconv -i "$i" -map 0 -c copy -c:v libx264 -crf 31 "encoded/$i";done
Where encoded is a subdirectory.
But what if I need to start re-encoding at, say, episode 5?
I have no idea.
There are probably lots of ways to do this, here are a couple of options.
Option 1: Use seq
Use seq to generate a sequence, loop over this and encode.
A sequence from 5 to 15:
seq 5 15
If you need to format the numbers, e.g. to get a 0 prefix for one digit numbers, you can
use the -f switch, which takes a printf style formatting argument of a float/double.
seq -f %02.0f 5 15
This can be used in a loop, e.g. something like this:
for i in $(seq -f %02.0f 5 15); do
filename="anime_episode${i}.mkv"
echo "Encoding episode $i: $filename"
avconv -i "$filename" -map 0 -c copy -c:v libx264 -crf 31 "encoded/$filename"
done
Option 2: Check whether encoded file exists
Do pretty much the same as you do in your current loop, but only perform encoding if
the encoded file does not already exist.
for i in *.mkv; do
if [ ! -f encoded/$i ]; then
echo "Encoding file: $i"
avconv -i "$i" -map 0 -c copy -c:v libx264 -crf 31 "encoded/$i"
else
echo "Skipped file: $i"
fi
done