appending a string constant selectively within a while loop in bash - 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.

Related

/usr/local/bin/ffmpeg: cannot execute binary file

Want to convert my m4a files into mp3 files using a script. It would save some time... I have over 100 GB of music files.
OS: OSX10.14 / Terminal vs Bash script
I can run ffmpeg -v 5 -y -i musicFile.m4a -acodec libmp3lame -ac 2 -b:a 320k musicFile.mp3 from the terminal. It converts the file and I can see and play the file from itunes.
When I run the same from a bash script it fails to convert.
ffmpeg -v 5 -y -i $ENTRY_FILE -acodec libmp3lame -ac 2 -b:a 320k $MP3NAME
My ipod nano just died and I got a new mp3 player. Now I need to convert my itunes files from AAC format to MP3.
ffmpeg is an established video and music file converter.
When I run it from the bash script I tried a few things.
I added ./ in front of the file, that failed because it was installed under /usr/local/bin and not under the same directory.
I also tried sh ffmpeg... and that gave me the cannot execute a binary file.
#!/usr/bin/env bash
# convert m4a file to mp3
set -e
file_convert() {
ENTRY_FILE=$(printf %q "${entry}")
FILE_NAME=$(printf %q "$(basename "${entry}")")
DIR=$(printf %q "$(dirname "${entry}")")
NAME="${FILE_NAME%.*}"
EXT="${FILE_NAME##*.}"
MP3NAME="${DIR}/${NAME}.mp3"
printf "%*s%s\n" $((indent+2)) '' "$ENTRY_FILE"
printf "%*s\tNew File :\t%s\n" $((indent+2)) '' "$MP3NAME"
if [ $EXT == "m4a" ]
then
printf "%*s\tConverting: \t%s\n" $((index+2)) '' "$ENTRY_FILE"
ffmpeg -v 5 -y -i $ENTRY_FILE -acodec libmp3lame -ac 2 -b:a 320k $MP3NAME
fi
}
walk() {
local indent="${2:-0}"
printf "\n%*s%s\n\n" "$indent" '' "$1"
# If the entry is a file convert it
for entry in "$1"/*; do [[ -f "$entry" ]] && file_convert; done
# If the entry is a directory recurse
for entry in "$1"/*; do [[ -d "$entry" ]] && walk "$entry" $((indent+2)); done
}
# If the path is empty use the current, otherwise convert relative to absolute; Exec walk()
[[ -z "${1}" ]] && ABS_PATH="${PWD}" || pushd "${1}" && ABS_PATH="${PWD}"
walk "${ABS_PATH}"
popd
echo
I expect >./aacToMp3.sh ./music to traverses the music directory and convert each m4a file to .mp3.
It is walking the file system and printing out correct files, with the spaces escaped. When it hits the ffmpeg line it halts. I put the set -e at the top of the file to force it to fail if the command fails. Without the set -e it happily walks all the music files and prints them to the stdout.
If you have lots of files to process and a decent multi-core CPU and fast disk, I would recommend GNU Parallel which you can install with homebrew:
brew install parallel
Then make a copy of a few files in a test directory and try:
parallel --dry-run ffmpeg -v 5 -y -i {} -acodec libmp3lame -ac 2 -b:a 320k {.}.mp3 ::: *.m4a
If that looks good, replace --dry-run with --progress.
If that looks good, you can (make a backup first) and do the whole lot:
find path/to/music -name "*.m4a" -print0 | parallel -0 --progress ffmpeg -v 5 -y -i {} -acodec libmp3lame -ac 2 -b:a 320k {.}.mp3
Thanks for all the input.
After a while I ended up writing all the ffmpeg lines to a script file. Glad I did. I was able to quickly scan the file and see some errors and fix them.
This is what I came up with.
Basically I am writing straight text to stdout and directing it to a file. Which I converted to a shell script to convert each file. One at a time. Ran the generated script overnight.
file_convert() {
ENTRY_FILE=$(printf %q "${entry}")
FILE_NAME=$(printf %q "$(basename "${entry}")")
DIR=$(printf %q "$(dirname "${entry}")")
NAME="${FILE_NAME%.*}"
EXT="${FILE_NAME##*.}"
MP3NAME="${DIR}/${NAME}.mp3"
if [ $EXT == "m4a" ]
then
printf 'echo "Converting %s ..."\n' "$FILE_NAME"
printf 'ffmpeg -v 5 -y -i %s -acodec libmp3lame -ac 2 -b:a 320k %s\n\n' "$ENTRY_FILE" "$MP3NAME"
fi
}
The output looked like.
#!/usr/bin/env bash
echo "Converting 03\ It\'s\ Not\ My\ Time.m4a ..."
ffmpeg -v 5 -y -i /Users/arthuranderson/Documents/work/projects/mp3Convert/music/3\ Doors\ Down/3\ Doors\ Down\ \(Bonus\ Track\ Version\)/03\ It\'s\ Not\ My\ Time.m4a -acodec libmp3lame -ac 2 -b:a 320k /Users/arthuranderson/Documents/work/projects/mp3Convert/music/3\ Doors\ Down/3\ Doors\ Down\ \(Bonus\ Track\ Version\)/03\ It\'s\ Not\ My\ Time.mp3
echo "Converting 01\ Down.m4a ..."
ffmpeg -v 5 -y -i /Users/arthuranderson/Documents/work/projects/mp3Convert/music/311/Greatest\ Hits\ \'93-\'03/01\ Down.m4a -acodec libmp3lame -ac 2 -b:a 320k /Users/arthuranderson/Documents/work/projects/mp3Convert/music/311/Greatest\ Hits\ \'93-\'03/01\ Down.mp3
Mark I will try parallel when I purchase some more music next time.
Thanks everyone!

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

ffmpeg: Transcoding multiple input files to multiple output files with single command

I want to convert three files with a single command. I used the following command:
-i "C:\fil1.mp4" -i "C:\file2.mp4" -i "C:\file3.mp4" -acodec libmp3lame -ab 32k -ar 22050 -ac 2 -b:v 128k -r 20 -s 176x144 -y file1.mp4 -acodec libmp3lame -ab 32k -ar 22050 -ac 2 -b:v 128k -r 20 -s 176x144 -y file2.mp4 -acodec libmp3lame -ab 32k -ar 22050 -ac 2 -b:v 128k -r 20 -s 176x144 -y file3.mp4
but it converts the first files with names fil1.mp4, fil2.mp4, fil3.mp4 but I want all files should be convert with its output file names.
Using -map helps with specifying which input goes with which output.
I used this code to convert multiple audio files:
ffmpeg -i infile1 -i infile2 -map 0 outfile1 -map 1 outfile2
also use -map_metadata to specify the metadata stream:
ffmpeg -i infile1 -i infile2 -map_metadata 0 -map 0 outfile1 -map_metadata 1 -map 1 outfile2
I wrote bash script for it.
You can modify as you want:
#!/bin/sh
BASE_DIR=$HOME/Videos/mov
OUTPUT_DIR=$BASE_DIR/avi
FILES=$(ls $BASE_DIR | grep .MOV)
for FILE in $FILES
do
FILENAME="${FILE:0:-4}"
ffmpeg -i $BASE_DIR/$FILE $OUTPUT_DIR/$FILENAME.avi
done
I have tried David Blum's script, there was a problem with spaces in filenames. Here's my modified script that uses basename to remove the file extension from filenames. It operates on the current directory, assuming a subdirectory called 'avi':
#!/bin/sh
OUTPUT_DIR=./avi
#IFS=$'\n' # split only on newlines
for FILE in *MOV
do
FILENAME=`basename "${FILE}" .MOV`
ffmpeg -i "$FILE" "$OUTPUT_DIR/$FILENAME.avi"
#echo $FILENAME
done
find ./ -name "*.wav" -exec ffmpeg -i {} -c:a libopus {}.opus \;
Maybe you could try using Different parallel outputs as in the FFMPEG documentation ?
When I have more files to work, I create a spreadsheet sheet and I do the command with "holes".
Every hole match to a file (filename).
I copy/paste in a file which become a script.
I know it is not perfect bet if it can help you.

shell script ffmpeg stops after 2 jobs

I have a pretty simple shell script and after doing the first two jobs, it just stops and sits there, doesnt do anything, it doesnt seem to matter what the third job is, if I switch the order etc, it will not finish it.
Any ideas would be great...
Here is my shell script
for f in "$#"
do
name=$(basename "$f")
dir=$(dirname "$f")
/opt/local/bin/ffmpeg -i "$f" -y -b 250k -deinterlace -vcodec vp8 -acodec libvorbis -nostdin "$dir/webm/${name%.*}.webm"
/opt/local/bin/ffmpeg -i "$f" -y -b 250k -strict experimental -deinterlace -vcodec h264 -acodec aac -nostdin "$dir/mp4/${name%.*}.mp4"
/opt/local/bin/ffmpeg -i "$f" -y -ss 00:00:15.000 -deinterlace -vcodec mjpeg -vframes 1 -an -f rawvideo -s 720x480 "$dir/img/${name%.*}.jpg"
done
Your final ffmpeg line needs -nostdin.
Running FFMPEG from Shell Script /bin/sh

How can I tell an if statement to skip a file but continue onto the next file if a certain condition is met?

for f in ~/Desktop/Uploads/*.flv; do
if /usr/local/bin/ffprobe ${f} 2>&1 | egrep 'Stream #0:0.+flv1'; then
<what do I put here for the batch file to skip the file and continue?>
else
if /usr/local/bin/ffprobe ${f} 2>&1 | egrep 'Stream #0:1.+speex'; then
/usr/local/bin/ffmpeg -i ${f} -vn -acodec libfaac -ab 128k -ar 48000 -async 1 ${f/%.flv/.m4a}
SPEEX_ADD="-i ${f/%.flv/.m4a}"
SPEEX_MAP="-map 1:0"
SPEEX_TRASH="rmtrash ${f/%.flv/.m4a}"
~/Desktop/Uploads/ffmpeg/ffmpeg -i ${f} ${SPEEX_ADD} -vcodec copy -acodec copy -map 0:0 ${SPEEX_MAP} ${f/%.flv/.mp4} && rmtrash ${f} && ${SPEEX_TRASH}
else
~/Desktop/Uploads/ffmpeg/ffmpeg -i ${f} -vcodec copy -an ${f/%.flv/.mp4} && rmtrash ${f}
fi
done
I'm using a batch file to convert video files but I need help. What can I put in that third line that will make the batch file skip over files with flv1 video but continue onto the next file? Thanks for your help.
continue. Check the bash man page for details.
You can use the continue keyword. Have a look at the Loop Control page in the Advanced Bash-Scripting Guide

Resources