So i've written a bash script to take newly written to video files and transcode them for playback and streaming in my server here is the script
while true; do
printf -v filename '%q' "$(inotifywait --format "%w""%f" -r -e close_write /var/$
echo $filename
if [[ $filename == *".mp4"* ]]; then
echo "1"
avconv -i "$filename" "`echo ${filename%.mp4}.webm`"
fi
if [[ $filename == *".mkv"* ]]; then
echo "2"
avconv -i "$filename" "`echo ${filename%.mkv}.mp4`"
avconv -i "$filename" "`echo ${filename%.mkv}.webm`"
fi
if [[ "$filename" == *".avi"* ]]
then echo "3"
avconv -i "$filename" "`echo ${filename%.avi}.mp4`" &
avconv -i "$filename" "`echo ${filename%.avi}.webm`"&
fi
done
The script works fairly well however if the $filename contains spaces avconv breaks and the script returns:
Watches established.
/var/www/media2net/tv/The\ Daily.mkv
2
avconv version 0.8.4-4:0.8.4-0ubuntu0.12.04.1, Copyright (c) 2000-2012 the Libav s
built on Nov 6 2012 16:51:33 with gcc 4.6.3
/var/www/media2net/tv/The\: No such file or directory
avconv version 0.8.4-4:0.8.4-0ubuntu0.12.04.1, Copyright (c) 2000-2012 the Libav
built on Nov 6 2012 16:51:33 with gcc 4.6.3
/var/www/media2net/tv/The\ Daily.mkv: No such file or directory
Setting up watches. Beware: since -r was given, this may take a while!
Watches established.
however this is not correct as I can view the said file inside it's respective directory, I also have tested avconv with the following in the shell and everything works properly and avconv transcodes video
avconv -i /var/www/media2net/The\ Daily.mkv /var/www/media2net/tv/The\ Daily.webm
Basically I am wondering what I am doing wrong in my script or if anyone else has experienced a similar problem using avconv or ffmpeg (as the 2 are fairly similar and avconv is ubuntu's fork of the project) any help would be greatly appreciated!
Thanks in advance
-brendan
There seems to be some inconsistencies in your usage of escape backslashes and quotes. In fact, you only need one of the two, and the easier to use is certainly the quotes.
So, to remove the backslashes in filenames would be the first good thing to do.
Additionally,
"$(inotifywait --format "%w""%f"...
should be corrected in :
"$(inotifywait --format '%w%f'...
Also, the lines which comprehend something like :
"`echo ${filename%.mkv}.mp4`"
are somewhat ambiguous, as in, fact, you are echoing - if there is a space in $filename - two chains of characters -. It should be corrected in :
"${filename/%.mkv/.mp4}"
Related
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 basic command (ffmpeg -i input_file out.srt) to turn .mkv video files to .srt subtitle files. The problem is that I have to manually run the command for every .mkv file. So I tried to implement a for loop in Bash, but I keep getting errors.
#!/bin/bash
# ffmpeg -i input_file out.srt
for i in *.mkv ; do
ffmpeg -i "$i" "$(basename "${i/.mkv)")".str
sleep 30
done
The two errors I get are:
./subcon.sh: line 6: unexpected EOF while looking for matching `}'
./subcon.sh: line 9: syntax error: unexpected end of file
I am not familiar with Bash to understand whats going on. Does anyone know where I can look stuff up or how to solve this particular problem?
Problems:
Mismatched brackets. Change { to (.
Incorrect subtitle extension. Change str to srt.
basename syntax is incorrect. Change "$(basename "${i/.mkv)")".str to "$(basename "$i" .mkv)".srt. Or use Bash parameter expansion instead of basename.
New script:
#!/bin/bash
# ffmpeg -i input_file out.srt
for i in *.mkv ; do
ffmpeg -i "$i" "$(basename "$i" .mkv)".srt
sleep 30
done
You can eliminate basename:
#!/bin/bash
# ffmpeg -i input_file out.srt
for i in *.mkv ; do
ffmpeg -i "$i" "${i%.*}.srt"
sleep 30
done
I recommend shellcheck.net to check your Bash scripts.
Disclaimer: I am just a guy who googled most of his knowledge when it was needed and there might be no cohesion at all. Sorry for this in advance.
I am writing a small script on my Ubuntu 18.04 server that should run a loop for encoding raw DVR files into easy to handle mp4. This "hard" part works fine already, I am just trying to get a clean script to let it run by the full hour. Unfortunately for whatever reason bash doesn't accept my if statement.
I tried dos2unix, semicolons, spacing, tabbing, intendation before and after the if statement but nothing worked. I scavenged almost all google results and stackoverflow-posts but nothing worked. I checked for hidden characters that could break the script. Still the error.
In the following script the XXX are placeholders for private stuff. Those lines already work.
#!/bin/bash
for file in $(find /var/www/vhosts/XXXXXX/* -name "*.h264");
if [ -z "$file" ]
then
echo "No new h264-files found. Exiting."; break
fi
do
echo "H264-files found. Calling ffmpeg for conversion to mp4."
ffmpeg -y -i "$file" -c:v libx264 -preset veryfast -crf 26 -an -sn "${file/h264/mp4}"
echo "Ffmpeg is finished."
#rm "$file" disabled until scripts works perfectly and is callable from plesk.
echo "All new files converted. Refreshing XXXXXcloud files."
sudo -u XXXX php /var/www/vhosts/XXXXX
echo "Script done."
done
I keep getting the following:
ffmpeg_transcode_h264tomp4.sh: line 3: syntax error near unexpected token `if'
ffmpeg_transcode_h264tomp4.sh: line 3: `if [ -z "$file" ]'
Using the ifne util and some advice from Charles Duffy, this should meet the spec:
find /var/www/vhosts/XXXXXX/* -name "*.h264" -print0 |
ifne -n echo "No new h264-files found. Exiting." |
while IFS= read -r -d '' file ; do
# ... more code goes here...
done
I have stuttering, seeking, and general playback issues when playing large mkv files through my Plex Media Server setup. I have been looking around for a way to automate scheduled tasks to move everything to mp4. The objective is:
Copy mkv files into mp4 preserving subtitles of every kind. Put the new file in the same subdir, and delete previous mkv version if conversion went successful.
When I tried to run ffmpeg on a loop, I run into the problem described here:
https://unix.stackexchange.com/questions/36310/strange-errors-when-using-ffmpeg-in-a-loop
This is my first adventure on shell scripting and I am pretty much stumbling around and trying to understand the syntax and philosophy of it. What I understand is that they use a file descriptor to redirect ffmpeg output to /dev/null.
The problem with that solution is that I would need to check ffmpeg output for errors to decide whether to delete the previous file or not. Furthermore, there is a common error when converting from picture based subtitles streams, which I circumvent by using a script I found (http://www.computernerdfromhell.com/blog/automatically-extract-subtitles-from-mkv/) to work after some modifications to my needs.
After much frustration I ended modifying the script so much that it does not serve to its purpose. It does not check for errors. Anyways, I will post it here. Mind you that this is my first shell script ever, and almost everything is confusing about it. The problem with this, is that I had to ditch my error checking and I am eliminating files that errored when converting. Losing the original without a valid copy.
#!/bin/bash
FOLDERS=( "/mnt/stg4usb/media0/test/matroska1" "/mnt/stg4usb/media0/test/season1" "/mnt/stg4usb/media0/test/secondtest")
FLAGS="-y -metadata title="" -c:v copy -map 0 -c:a libfdk_aac -ac 2 -movflags +faststart"
COUNTER=0
LOGFILE=batch-$(date +"%Y%m%d-%H%M%S").log
for FOLDER in "${FOLDERS[#]}"
do
echo "---===> STARTING folder: '$FOLDER'"
find $FOLDER -name "*.mkv" | while read line; do
OUTPUT=""
DATE=$(date +"%Y-%m-%d")
TIME=$(date +"%H:%M:%S")
COUNTER=$((COUNTER+1))
FILE=$(basename "$line")
DIR=$(dirname "${line}")
echo $'\n'$'\n'"[$COUNTER][$DATE][$TIME][FILE:'${line%.mkv}.mp4']"$'\n'
echo "#### Transcoding ####"'\n'
ffmpeg -i $line $FLAGS -sn "${line%.mkv}.mp4" < /dev/null
echo "#### Extracting subtitles ###"'\n''\n'
mkvmerge -i "$line" | grep 'subtitles' | while read subline
do
# Grep the number of the subtitle track
tracknumber=`echo $subline | egrep -o "[0-9]{1,2}" | head -1`
# Get base name for subtitle
subtitlename=${line%.*}
# Extract the track to a .tmp file
mkvextract tracks "$line" $tracknumber:"$subtitlename.$tracknumber.srt" < /dev/null
chmod g+rw "$subtitlename.$tracknumber"* < /dev/null
done
rm -frv "$line" < /dev/null
echo "Finished: $(date +"%Y%m%d-%H%M%S")"
done
echo '\n'"<===--- DONE with folder: '$FOLDER'"$'\n'$'\n' >> $LOGFILE
done
exit 0
So, basically, the idea is: run ffmpeg on a loop for all mkv under a directory and subdirectories (I was using find). Check it for all possible errors. If errors, try again without subtitles and extract the subtitles using mkvextract, else everything went ok, and delete the previous file.
I wrote a shell script to convert many video files and save them with something appended to the file name. The script works, but it seems to randomly skip files, and a lot of them.
When I re-run the script, it will convert files it skipped before. How can I get it to stop skipping files?
workingDir=/home/user/Videos
# get list of files to convert
find /video/folder -iname "*.mp4" > $workingDir/file_list
# convert files
cat $workingDir/file_list | while read LINE; do
# FFmpeg often cuts off the beginning of this line
echo "$(dirname "$LINE")/$(basename "$LINE")"
if /usr/bin/ffmpeg -n -loglevel panic -v quiet -stats -i "$LINE" \
-c:v libx264 -vf scale="trunc(oh*a/2)*2:320" \
-pix_fmt yuv420p -preset:v slow -profile:v main -tune:v animation -crf 23 \
"$(dirname "$LINE")/$(basename "$LINE" \.mp4)"_reencoded.mp4 2>/dev/null; then
echo "Success: $(dirname "$LINE")/$(basename "$LINE")" >> $workingDir/results
else
echo "Failed: $(dirname "$LINE")/$(basename "$LINE")" >> $workingDir/results
fi
done
One problem seems to be that FFmpeg interferes with the script. The FFmpeg output often cuts off the beginning of the next command, even if the output is not shown. This is demonstrated by the echo line before the if statement, which is often cut off. But even for lines that aren't cut off, most of them will be skipped for no apparent reason.
ffmpeg reads from stdin, thereby consuming input meant for while read. Just redirect stdin for ffmpeg by adding < /dev/null