Problem with escaped quotes in variable (I think) [duplicate] - bash

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

Trying to change extension of filename on an ffmpeg script

(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.

appending a string constant selectively within a while loop in bash

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.

Launch a file script after the first one is done [duplicate]

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

What does the argument -ab in ffmpeg mean?

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

Batch avconv re-encode videos halfway through the list

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

Resources