bash script variable content in cmd line interpreted as filename by mplayer - bash

I have a script for playing movies through mplayer, for which I am attempting to include automatic conversion of 3D to 2D. Here is the relevant cmd line as it appears in the script
mplayer -fs "${g[$i]}" -ss $f "${d%.*}".* -hardframedrop -nocorrect-pts -identify &>> log.txt
This does not render to 2D even though "${g[$i]}" has the needed option
echo "${g[$i]}"
-vo gl:stereo=3
But if I modify the cmd line to show the option directly, the movie is shown in 2D
mplayer -fs -vo gl:stereo=3 -ss $f "${d%.*}".* -hardframedrop -nocorrect-pts -identify &>> log.txt
The problem seems to be that mplayer interprets the option as a filename when delivered via ${g[$i]}, thus from log.txt
Playing -vo gl:stereo=3 .
File not found: ' -vo gl:stereo=3 '
Failed to open -vo gl:stereo=3 .
How can I prevent this?

You are passing " -vo gl:stereo=3 " which is indeed invalid. You should be passing "-vo" followed by "gl:stereo=3". In other words, it should be passed as two separate arguments without the leading and trailing spaces.
The easiest workaround to make this happen is to skip the quotes around ${g[$i]}:
mplayer -fs ${g[$i]} -ss $f "${d%.*}".* -hardframedrop -nocorrect-pts -identify &>> log.txt
The more robust workaround would be to store the arguments separately in an array, e.g. opts=("-vo" "gl:stereo=3") and using "${opt[#]}", but Bash does not support multidimensional arrays so this may require some deeper changes to your script.

Related

ffmpeg in bash for loop: unexpected EOF while looking for matching `}'

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.

Shell script: unexpected token ´if"

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

How to run ffmpeg in a loop and test it for error?

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.

FFmpeg script skips files

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

Using mplayer to determine length of audio/video file

The following works very nicely to determine the length of various audio/video files:
mplayer -identify file.ogg 2>/dev/null | grep ID_LENGTH
However, I want to kill mplayer's output so I can determine the length of many files more efficiently. How do I do that?
The MPlayer source ships with a sample script called midentify, which looks like this:
#!/bin/sh
#
# This is a wrapper around the -identify functionality.
# It is supposed to escape the output properly, so it can be easily
# used in shellscripts by 'eval'ing the output of this script.
#
# Written by Tobias Diedrich <ranma+mplayer#tdiedrich.de>
# Licensed under GNU GPL.
if [ -z "$1" ]; then
echo "Usage: midentify.sh <file> [<file> ...]"
exit 1
fi
mplayer -vo null -ao null -frames 0 -identify "$#" 2>/dev/null |
sed -ne '/^ID_/ {
s/[]()|&;<>`'"'"'\\!$" []/\\&/g;p
}'
The -frames 0 makes mplayer exit immediately, and the -vo null -ao null prevent it from trying to open any video or audio devices. These options are all documented in man mplayer.
FFMPEG can give you the same information in a different format (and doesn't attempt playing the file):
ffmpeg -i <myfile>
There's another FF-way in addition to #codelogic's method, which doesn't exit with an error:
ffprobe <file>
and look for the duration entry.
Or grep for it directly in the error stream:
ffprobe <file> 2> >(grep Duration)
looks like there are a few other libs available, see time length of an mp3 file
Download your .mp3 file, play it with your Player (ex. Windows Media Player) and the player will show the total time at the end of play.

Resources