[Solved] DVD bookmarking using the shell and lua interface
Posted: 26 Jan 2015 10:19
This is an example of how to automate VLC using shell scripts on top of the lua
interface.
These scripts could also be modified to bookmark media files and bluray disks,
so I hope they are of more general interest than DVD playback.
Earlier versions of these scripts, and some general remarks, are posted here:
http://www.linuxquestions.org/questions ... 4175498541
http://www.linuxquestions.org/questions ... 4175495616
These scripts use a modified cli.lua with the following modifications (which
don't interfere with commandline use):
Usage: cd /path/to/vlc-src-dir; patch -b -p1 < /path/to/cli.lua.diff
Move the compiled bytecode to $HOME/.local/share/vlc/lua/intf/cli.luac, where it
will be found and used instead of the default.
The get_remaining function is not used by the scripts (but could be). The
set_time function is a replacement for seek thet will work with floating point
values. The get_time function is modified accordingly. If you lose the "ans_"
the scripts could make more use of eval, but I am unwilling to change something
that works as it is. Also the "ans_" is useful among lots of debug output.
The scripts (I recommend using them with vlc-2.2 or later, though I tried
earlier versions with vlc-2.0.8 in Ubuntu successfully):
The dvdplayer script (to open VLC and define an environment):
The bookmarking script (to be used while VLC, opened with the dvdplayer script,
is running):
The scripts are commented and have a --help option; it should be clear how to
use them.
DEVELOPERS: Is the lua interface maintained at all? It does not seem to have
changed in a while. Maybe a more scripting-friendly interface would be better
(and more interesting to developers and general users).
For instance, the output of atrack and strack should be arrays that could be
redirected to files (and returned to scripts) instead of pretty-printed and
hard-to-parse logfile entries. I could continue to modify the cli.lua script,
but sooner or later I'd probably run into incompatibilities.
interface.
These scripts could also be modified to bookmark media files and bluray disks,
so I hope they are of more general interest than DVD playback.
Earlier versions of these scripts, and some general remarks, are posted here:
http://www.linuxquestions.org/questions ... 4175498541
http://www.linuxquestions.org/questions ... 4175495616
These scripts use a modified cli.lua with the following modifications (which
don't interfere with commandline use):
Code: Select all
--- a/share/lua/intf/cli.lua.orig 2014-10-04 18:00:36.819182178 +0200
+++ b/share/lua/intf/cli.lua 2015-01-25 12:59:55.421282994 +0100
@@ -70,7 +70,7 @@
running = true
--[[ Setup default environement ]]
-env = { prompt = "> ";
+env = { prompt = "";
width = 70;
autocompletion = 1;
autoalias = 1;
@@ -427,26 +427,49 @@
end
function get_time(var)
+ -- modified for floating point values
return function(name,client)
local input = vlc.object.input()
if input then
- client:append(math.floor(vlc.var.get( input, var )))
+ local value = vlc.var.get( input, var )
+ -- to make it easier to bookmark the beginning of titles:
+ if var == "time" and value < 1 then
+ value = 0
+ end
+ client:append("ans_" .. tostring(name) .. "=" .. value .. '\n')
else
client:append("")
end
end
end
+function set_time(name,client,value)
+ -- unlike seek this will take float values
+ local input = vlc.object.input()
+ local check = (vlc.var.get( input, "length") - value )
+ if value and check > 0 then
+ vlc.var.set( input, "time", value )
+ end
+end
+
+function get_remaining(name,client)
+ local input = vlc.object.input()
+ local var = math.floor((vlc.var.get( input, "length") - (vlc.var.get(input, "time"))))
+ if input then
+ client:append("ans_get_remaining=" .. var .. '\n')
+ else
+ client:append("")
+ end
+end
+
function titlechap(name,client,value)
local input = vlc.object.input()
local var = string.gsub( name, "_.*$", "" )
if value then
vlc.var.set( input, var, value )
- else
- local item = vlc.var.get( input, var )
- -- Todo: add item name conversion
- client:append(item)
end
+ local item = vlc.var.get( input, var )
+ client:append("ans_" .. tostring(var) .. "=" .. item .. '\n')
end
function titlechap_offset(var,offset)
@@ -575,9 +598,11 @@
{ "info"; { func = input_info; help = "information about the current stream" } };
{ "stats"; { func = stats; help = "show statistical information" } };
{ "get_time"; { func = get_time("time"); help = "seconds elapsed since stream's beginning" } };
+ { "set_time"; { func = set_time; args = "X"; help = "seek to specified floating-point time position" } };
{ "is_playing"; { func = is_playing; help = "1 if a stream plays, 0 otherwise" } };
{ "get_title"; { func = get_title; help = "the title of the current stream" } };
{ "get_length"; { func = get_time("length"); help = "the length of the current stream" } };
+ { "get_remaining"; {func = get_remaining; help = "the remaining time in the current stream" } };
{ "" };
{ "volume"; { func = volume; args = "[X]"; help = "set/get audio volume" } };
{ "volup"; { func = ret_print(vlc.volume.up,"( audio volume: "," )"); args = "[X]"; help = "raise audio volume X steps" } };
Move the compiled bytecode to $HOME/.local/share/vlc/lua/intf/cli.luac, where it
will be found and used instead of the default.
The get_remaining function is not used by the scripts (but could be). The
set_time function is a replacement for seek thet will work with floating point
values. The get_time function is modified accordingly. If you lose the "ans_"
the scripts could make more use of eval, but I am unwilling to change something
that works as it is. Also the "ans_" is useful among lots of debug output.
The scripts (I recommend using them with vlc-2.2 or later, though I tried
earlier versions with vlc-2.0.8 in Ubuntu successfully):
The dvdplayer script (to open VLC and define an environment):
Code: Select all
#!/bin/bash
# Copyright (C) 2014-2015 nokangaroo nokangaroo@NOSPAM.aon.at
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# swiss-army sledgehammer for DVD playback and bookmarking using VLC
# needs modified cli.lua
# work in progress
# Not supposed to run as root:
if [[ $UID -eq 0 ]]; then
exit
fi
# Media folder:
MEDIADIR="${HOME}/vlc-DVDdata"
# Bookmark folder:
BMDIR="${MEDIADIR}/bookmarks"
if [[ ! -d $BMDIR ]]; then
mkdir -p $BMDIR
fi
PROG=`basename $0`
rm -f /tmp/${PROG}.${UID}.*
rm -f ${MEDIADIR}/{audiomap,data,flag,tmp,list,sed*}
# Logfile (needed for reading lua output into variables):
LOGFILE=`mktemp -q /tmp/${PROG}.${UID}.log.XXXXXXXXXX`
# Put dvdread output into a different file (only needed at startup):
LOGFILE0=`mktemp -q /tmp/${PROG}.${UID}.status.XXXXXXXXXX`
# Control file (fifo):
FIFO=`mktemp -u /tmp/${PROG}.${UID}.fifo.XXXXXXXXXX`
mkfifo -m 600 $FIFO
################################################################################
# It is theoretically possible to replace the logfile with a fifo, along those
# lines:
#
# mkfifo vlc.out
#
# while read line < vlc.out; do
# VAR=`echo $line | grep -o [[:digit:]]*$`
# #or just VAR=`echo $line` for multi-line output like atrack
# echo $VAR
# done
#
# vlc -I cli > vlc.out
#
# Whether this actually makes sense in the context of these scripts remains
# to be seen.
################################################################################
case $1 in
-h|--help)
cat << EOF
This is a dvd player wrapper script for VLC. It is intended for use with the
resuming script, which can create user-defined bookmarks for every DVD you play:
a default bookmark to start playback from (usually the beginning of the movie),
a resume bookmark, and named bookmarks.
This script creates a folder
$MEDIADIR
and a folder
$BMDIR
inside it, where your bookmarks are. They are identified by a function that
concatenates the IFO files in the VIDEO_TS folder and calculates the md5sum
of the resulting file, which should be reasonably unique. ISO images must
be mounted before this calculation can be performed, therefore this script
requires fusermount and fuseiso.
You can create a desktop icon or an "open with" right-click menu entry to
open this script; it will exit automatically when you close VLC.
Menus cannot be bookmarked, because there is no title associated with them.
Usage:
Calling this script without arguments will play a DVD in your drive.
External drives will be detected.
$PROG /path/to/image/or/DVD-folder
will play an iso image or DVD folder on your harddisk.
The filenames can have spaces in them.
$PROG -h or $PROG --help
will print this help text and exit.
No other arguments are accepted.
EOF
exit
;;
*)
# DVD device (this is a DVD player only!):
if [[ "$*" ]]; then
# Drag an iso file or DVD folder to the terminal (takes only one
# argument; the $* is for handling filenames with spaces):
DVD_DEVICE="$(echo "$*" | sed s/^\'\|\'$//g)"
else
# Will find external drive if present:
DVD_DEVICE="$(grep "udf" /etc/mtab | awk '{print $1}')"
# Helpful information for editing the data files manually; also needed for
# DVD disc_id:
DISK_LABEL="$(grep "udf" /etc/mtab | awk '{print $2}')"
fi
;;
esac
if [[ -z $DISK_LABEL ]]; then
DISK_LABEL="$DVD_DEVICE"
fi
# Functions:
get_data () {
local j
for j in "$@"; do
if [[ `grep "resume@$j" $DEFAULTFILE` ]]; then
eval "$j"=`grep -w -o "resume@$j=[0-9\.]\+" $DEFAULTFILE | cut -d \= -f 2`
else
eval "$j"=`grep -w -o "^$j=[0-9\.]\+" $DEFAULTFILE | cut -d \= -f 2`
fi
done
}
vlc_command () {
echo "$*" > $FIFO
}
alt_audio () {
# Cycle through audio and commentary track(s)
# This script was written by someone living in a non-English-speaking country
# who wants to watch DVDs in English. Some might find this function redundant
local lang
local current
local other
local audio
if [[ `grep '^lang' $DEFAULTFILE` ]]; then
lang=`grep '^lang' $DEFAULTFILE | cut -d \= -f 2`
else
lang='English'
fi
if [[ ! -f $AUDIOMAP ]]; then
vlc_command atrack
wait
current=`grep -B 8 'end of audio-es' $LOGFILE | tail -n 9 | grep '*' | grep -v 'Disable' | cut -d \ -f 2`
other=`grep -B 8 'end of audio-es' $LOGFILE | tail -n 9 | grep "$lang" | grep -v '*' | grep -v 'Disable' | cut -d \ -f 2`
echo "$other" | tr ' ' '\n' > $AUDIOMAP
echo "$current" >> $AUDIOMAP
fi
audio=`cat $AUDIOMAP | head -n 1`
if [[ $audio ]]; then
vlc_command atrack $audio
sed -i /"$audio"/d $AUDIOMAP
echo "$audio" >> $AUDIOMAP
fi
}
# The point of the following function is that we don't have to introduce
# audio_id and sub_id variables on the majority of DVDs that don't need them.
# This is the sledgehammer part. VLC's ordering and numbering of audio tracks
# cannot be relied on, and hardcoded audio track commands won't work if auto-
# detection of the correct language track fails.
check_default () {
local audio
local sub
local audio1
local sub1
local x
local y
get_data audio_id sub_id
if [[ $audio_id ]]; then
x=0
until [[ $x == 20 ]]; do
# Feel free to find a better solution. The point of the loop is to beat
# the crap out of sluggish DVD drives for not cooperating.
sleep 0.2
vlc_command atrack
audio=`grep "Track $audio_id" $LOGFILE | tail -n 1 | cut -d \ -f 2`
vlc_command atrack $audio
vlc_command atrack
audio1=`grep "Track $audio_id" $LOGFILE | tail -n 1 | cut -d \ -f 2`
if [[ $audio1 ]] && [[ $audio == $audio1 ]]; then
break
fi
x=`expr $x + 1`
done
fi
if [[ $sub_id ]]; then
y=0
if [[ $sub_id == 0 ]]; then
string='Disable'
else
string="Track $sub_id"
fi
until [[ $y == 20 ]]; do
sleep 0.2
vlc_command strack
sub=`grep "$string" $LOGFILE | tail -n 1 | cut -d \ -f 2`
vlc_command strack $sub
vlc_command strack
sub1=`grep "$string" $LOGFILE | tail -n 1 | cut -d \ -f 2`
if [[ $sub1 ]] && [[ $sub == $sub1 ]]; then
#if [[ $sub == $sub1 ]]; then #safer
break
fi
y=`expr $y + 1`
done
fi
}
# DVD disc_id:
# (almost) a replacement for disc_id of libdvdread-0.9.7
# iso images must be mounted first, so we need fusermount and fuseiso.
# Unlike disc_id it concatenates ALL ifo files, but is still fast enough.
TMPFILE=`mktemp -q /tmp/${PROG}.XXXXXXXX`
if [[ "$(echo `file "$DVD_DEVICE" | cut -d \: -f 2 | grep UDF`)" ]]; then
TMPDIR=`mktemp -d -q /tmp/${PROG}.mnt.XXXXXXXX`
fuseiso -p "$DVD_DEVICE" "$TMPDIR"
DISC="$TMPDIR"
else
DISC="$DISK_LABEL"
fi
cat "$DISC"/{VIDEO_TS,video_ts}/*{IFO,ifo} >> $TMPFILE 2>/dev/null
wait
if [[ "$(echo `file "$DVD_DEVICE" | cut -d \: -f 2 | grep UDF`)" ]]; then
fusermount -u "$TMPDIR"
fi
if [[ -s $TMPFILE ]]; then
DISC_ID=`md5sum "$TMPFILE" | cut -d \ -f 1`
rm "$TMPFILE"
else
rm "$TMPFILE"
exit 1
fi
wait
# The bookmark and defaults files (for disc_id of libdvdread-0.9.7 replace
# ${DISC_ID} with $(disc_id "$DVD_DEVICE"):
BOOKMARKFILE=${BMDIR}/${DISC_ID}.vlcbmk
touch -c $BOOKMARKFILE #don't create any empty files
DEFAULTFILE=${BMDIR}/${DISC_ID}.vlcdef
touch -c $DEFAULTFILE
if [[ -f $DEFAULTFILE ]] && [[ -z `grep "$DISK_LABEL" $DEFAULTFILE` ]]; then
#sed -i /device/d $DEFAULTFILE
echo 'device='"$DISK_LABEL" >> $DEFAULTFILE
fi
# Workaround for matedialog, which does not work reliably with piped input.
# Anyway, it seems more efficient to run sed over the bookmark file only when
# it changes, and not every time you open it:
LISTFILE=${MEDIADIR}/list
if [[ -f $BOOKMARKFILE ]]; then
sed -n '/^[0-9]\+\t.*$/ P' $BOOKMARKFILE > $LISTFILE
# These values will be dependent on the gtk theme used (and probably on the
# screen resolution) and have to be found out by experiment:
DISPLAY_WIDTH="$(echo "(`wc -L < $LISTFILE` * 7) + 48" | bc)"
DISPLAY_HEIGHT="$(echo "(`wc -l < $LISTFILE` * 23) + 88" | bc)"
fi
# Audio map for toggling audio tracks:
AUDIOMAP=${MEDIADIR}/audiomap
# These flagfiles can be used for per-bookmark commands (especially for book-
# marking alternate audio tracks with the resuming script's alt_audio function.
# As VLC's ordering of audio tracks can change when the DVD is started from a
# resume bookmark, hardcoded audio track commands won't work). A flag could be
# written into the default file, like the "debug" and "custom_config" flags, but
# that would make the global_commands lines more complicated and the script less
# reliable:
FLAG=${MEDIADIR}/flag
# This script:
DVDPLAYER="$0"
# Data for the bookmarking script to source. This will handle spaces in file
# names:
for data in LOGFILE FIFO DVD_DEVICE DISK_LABEL BOOKMARKFILE DEFAULTFILE LISTFILE AUDIOMAP FLAG DISPLAY_WIDTH DISPLAY_HEIGHT DVDPLAYER; do
eval echo $data=\$"$data" | sed 's/ /\\ /g' >> $MEDIADIR/data
done
# Get startup data from the defaults file:
get_data title get_time
wait
if [[ $title ]]; then
device="${DVD_DEVICE}#${title}"
else
device="$DVD_DEVICE"
fi
if [[ $get_time ]]; then
starttime="--start-time=$get_time"
else
starttime=""
fi
# Miscellaneous settings that I found to be useful (more could be added):
# Adjust cache values to avoid jerky playback at chapter marks:
if [[ `grep '^file-caching' $DEFAULTFILE` ]]; then
fcache="--file-caching=`grep '^file-caching' $DEFAULTFILE | cut -d \= -f 2`"
else
fcache=""
fi
if [[ `grep '^disc-caching' $DEFAULTFILE` ]]; then
dcache="--disc-caching=`grep '^disc-caching' $DEFAULTFILE | cut -d \= -f 2`"
else
dcache=""
fi
# Some DVDs contain incorrect aspect ratio information:
if [[ `grep '^aspect' $DEFAULTFILE` ]]; then
aspect="--aspect-ratio=`grep '^aspect' $DEFAULTFILE | cut -d \= -f 2`"
else
aspect=""
fi
# Custom video cropping:
if [[ `grep '^crop' $DEFAULTFILE` ]]; then
crop="--crop=`grep '^crop' $DEFAULTFILE | cut -d \= -f 2`"
else
crop=""
fi
# Correct monitor pixel aspect ratio (Buffy Season 2! Try mpar=0.96)
# WARNING: This will mess up subpicture rendering:
if [[ `grep '^mpar' $DEFAULTFILE` ]]; then
stretch="--monitor-par=`grep '^mpar' $DEFAULTFILE | cut -d \= -f 2`"
else
stretch=""
fi
# Custom video zoom:
if [[ `grep '^zoom' $DEFAULTFILE` ]]; then
zoom="--zoom=`grep '^zoom' $DEFAULTFILE | cut -d \= -f 2` --no-autoscale"
else
zoom=""
fi
# Deinterlace mode:
if [[ `grep '^deinterlace-mode' $DEFAULTFILE` ]]; then
deint="--deinterlace-mode=`grep '^deinterlace-mode' $DEFAULTFILE | cut -d \= -f 2`"
else
deint=""
fi
# Debugging:
if [[ `grep '^debug' $DEFAULTFILE` ]]; then
debug="--verbose=2"
else
debug="--quiet"
fi
# Custom commandline (could of course replace all of the above):
if [[ `grep '^custom@' $DEFAULTFILE` ]]; then
custom=`grep '^custom@' $DEFAULTFILE | cut -d \@ -f 2`
else
custom=""
fi
# VLC version to use:
if [[ `grep '^VLC=/usr.*vlc$\|^VLC=/opt.*vlc$\|^VLC=/home.*vlc$' $DEFAULTFILE` ]]; then
eval `grep '^VLC=/.*vlc$' $DEFAULTFILE`
else
#VLC=/home/BUILD/vlc/vlc
#VLC=/home/BUILD/vlc-2.2/vlc
#VLC=/home/BUILD/vlc-2.1.5/vlc
#VLC=/opt/vlc/bin/vlc
VLC=/usr/local/bin/vlc
#VLC=/usr/bin/vlc
fi
# Use different configfiles for VLC version >= 2.2.0 and < 2.2.0:
VERSION=`$VLC --version | cut -d \ -f 3 | sed 's/-.*//g' | sed 's/[^0-9]//g'`
if [[ $VERSION -lt 220 ]]; then
# Use custom configdir:
conf="--config=$HOME/.config/vlc-2.1/vlcrc" #Create and configure it
confdir="$HOME/.config/vlc-2.1"
custom_confdir="$MEDIADIR/vlcrc-2.1"
else
conf="--config=$HOME/.config/vlc/vlcrc"
confdir="$HOME/.config/vlc" #Or create a special one, like vlc-dvd
custom_confdir="$MEDIADIR/vlcrc"
fi
# Per-DVD configfile (I don't want to use this by default to avoid clutter):
if [[ `grep '^custom_config' $DEFAULTFILE` ]]; then
custom_conf="--config=$custom_confdir/${DISC_ID}.vlcrc"
if [[ ! -d $custom_confdir ]]; then
mkdir -p $custom_confdir
fi
if [[ ! -f $custom_confdir/${DISC_ID}.vlcrc ]]; then
cp $confdir/vlcrc $custom_confdir/${DISC_ID}.vlcrc
fi
echo "CONFIGFILE=$custom_confdir/${DISC_ID}.vlcrc" >> $MEDIADIR/data
touch -c $custom_confdir/${DISC_ID}.vlcrc
else
custom_conf=$conf
fi
# Avoid deafness when wearing earphones:
amixer set Master playback 40% #this will depend on config
# Hide panels, so they don't show up with the zenity window (these commands are
# for mate-panel; for other panels use something like this to find settings:
# gsettings list-recursively|grep panel
# for toplevel in top bottom; do gsettings list-recursively \
# org.mate.panel.toplevel:/org/mate/panel/toplevels/$toplevel/; done
# Replace org.mate.panel with whatever gsettings shows you)
dconf write /org/mate/panel/toplevels/top/auto-hide true
dconf write /org/mate/panel/toplevels/bottom/auto-hide true
# Starting VLC with fifo input and generating logfile (The --extraintf option
# enables use of right-click menus and controller. $title and $get_time can be
# empty, so these commands will work with empty bookmark and resume files. The
# logfile is needed for reading VLC's output into variables, unfortunately).
# The "exec" commands will open a new file descriptor for reading and writing
# (replacing the tail -f pipe) and close it after playback:
exec 3<> $FIFO
($VLC dvd://"$device" $starttime $fcache $dcache $aspect $crop $stretch $zoom $debug $deint $custom $custom_conf --one-instance --extraintf="luaintf{intf=cli}" <&3 1>$LOGFILE 2>$LOGFILE0; exec 3<&-; exec 3>&-; dconf write /org/mate/panel/toplevels/top/auto-hide false; dconf write /org/mate/panel/toplevels/bottom/auto-hide false; exit) &
#EVIL_CROCK_ALERT:
# The following will wait for VLC to start and then switch to the correct audio
# and subtitle tracks if autodetection fails. Polling for logfile entries is
# necessary because "wait `pidof vlc`" or synonyms will fail with a "not a child
# of this shell" error:
(if [[ `grep '^audio_id\|^sub_id' $DEFAULTFILE` ]] && [[ `grep "^title=$title\|^preserve=$title" $DEFAULTFILE` ]]; then
counter=0
until [[ $counter -eq 20 ]]; do
if [[ `grep 'libdvdread: Found\|libdvdread: Could' $LOGFILE0` ]]; then
if [[ -z "$*" ]]; then
sleep 2.5
else
sleep 0.3
fi
check_default
break
fi
sleep 0.3
counter=`expr $counter + 1`
done
fi) &
# Executing global commands in the bookmarkfile, such as switching the aspect
# ratio or disabling subtitles for certain titles:
(if [[ `grep '^global_commands@' $BOOKMARKFILE` ]]; then
counter1=0
until [[ $counter1 -eq 20 ]]; do
if [[ `grep 'libdvdread: Found\|libdvdread: Could' $LOGFILE0` ]]; then
if [[ -z "$*" ]]; then
sleep 1.5
else
sleep 0.3
fi
global_commands="$(grep '^global_commands@' $BOOKMARKFILE | cut -d \@ -f 2)"
wait
eval $global_commands
break
fi
sleep 0.3
counter1=`expr $counter1 + 1`
done
fi) &
The bookmarking script (to be used while VLC, opened with the dvdplayer script,
is running):
Code: Select all
#!/bin/bash
# Copyright (C) 2014-2015 nokangaroo nokangaroo@NOSPAM.aon.at
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# Bookmark script for use with the dvdplayer script
# needs modified cli.lua
# work in progress
# Not supposed to run as root:
if [[ $UID -eq 0 ]]; then
exit
fi
# Media folder:
MEDIADIR="${HOME}/vlc-DVDdata"
if [[ ! -d $MEDIADIR ]]; then
if [[ $1 == '-h' ]] || [[ $1 == '--help' ]]; then
continue
else
zenity --info --text 'You need to start VLC with the DVD player script first!' --timeout=3
exit
fi
fi
# The file to source for variables:
DATAFILE=${MEDIADIR}/data
# Functions:
vlc_command () {
echo "$*" > $FIFO
}
get_var () {
# Valid arguments: title get_time get_length get_remaining chapter
local i
for i in "$@"; do
echo "$i" > $FIFO
sleep 0.1
eval "$i"=`grep -w -o "ans_$i=[0-9\.]\+" $LOGFILE | tail -n 1 | cut -d \= -f 2`
wait
done
}
get_resume () {
local j
for j in "$@"; do
eval "$j"=`grep -w -o "^resume@$j=[0-9\.]\+" $DEFAULTFILE | cut -d \= -f 2`
wait
done
}
get_default () {
local k
for k in "$@"; do
eval "$k"=`grep -w -o "^$k=[0-9\.]\+" $DEFAULTFILE | cut -d \= -f 2`
wait
done
}
check_default () {
local audio
local sub
local audio1
local sub1
local x
local y
local string
sleep 0.3
if [[ $audio_id ]]; then
x=0
until [[ $x == 20 ]]; do
sleep 0.2
vlc_command atrack
audio=`grep "Track $audio_id" $LOGFILE | tail -n 1 | cut -d \ -f 2`
vlc_command atrack $audio
vlc_command atrack
audio1=`grep "Track $audio_id" $LOGFILE | tail -n 1 | cut -d \ -f 2`
if [[ $audio1 ]] && [[ $audio == $audio1 ]]; then
break
fi
x=`expr $x + 1`
done
fi
if [[ $sub_id ]]; then
y=0
if [[ $sub_id == 0 ]]; then
string='Disable'
else
string="Track $sub_id"
fi
until [[ $y == 20 ]]; do
sleep 0.2
vlc_command strack
sub=`grep "$string" $LOGFILE | tail -n 1 | cut -d \ -f 2`
vlc_command strack $sub
vlc_command strack
sub1=`grep "$string" $LOGFILE | tail -n 1 | cut -d \ -f 2`
if [[ $sub1 ]] && [[ $sub == $sub1 ]]; then
break
fi
y=`expr $y + 1`
done
fi
}
set_resume () {
local m
for m in "$@"; do
sed -i /"resume@$m"/d $DEFAULTFILE
eval echo "resume@$m"=\$"$m" >> $DEFAULTFILE
wait
done
}
set_default () {
local n
for n in "$@"; do
sed -i /"^$n"/d $DEFAULTFILE
eval echo "$n"=\$"$n" >> $DEFAULTFILE
sed -i /device/d $DEFAULTFILE
echo 'device='"$DISK_LABEL" >> $DEFAULTFILE
done
}
set_bookmark () {
echo -e "$(expr `grep -o ^[0-9]* $BOOKMARKFILE | sort -n | tail -n 1` + 1)"'\t'"$BMK" >> $BOOKMARKFILE
wait
local o
for o in "$@"; do
eval echo "$o"=\$"$o" >> $BOOKMARKFILE
done
}
run_global () {
if [[ `grep '^global_commands@' $BOOKMARKFILE` ]]; then
global_commands="$(grep '^global_commands@' $BOOKMARKFILE | cut -d \@ -f 2)"
eval $global_commands
fi
}
alt_audio () {
# Cycle through audio and commentary track(s)
local lang
local current
local other
local audio
if [[ `grep '^lang' $DEFAULTFILE` ]]; then
lang=`grep '^lang' $DEFAULTFILE | cut -d \= -f 2`
else
lang='English'
fi
if [[ ! -e $AUDIOMAP ]]; then
vlc_command atrack
wait
current=`grep -B 8 'end of audio-es' $LOGFILE | tail -n 9 | grep '*' | grep -v 'Disable' | cut -d \ -f 2`
other=`grep -B 8 'end of audio-es' $LOGFILE | tail -n 9 | grep "$lang" | grep -v '*' | grep -v 'Disable' | cut -d \ -f 2`
echo "$other" | tr ' ' '\n' > $AUDIOMAP
echo "$current" >> $AUDIOMAP
fi
audio=`cat $AUDIOMAP | head -n 1`
if [[ $audio ]]; then
vlc_command atrack $audio
sed -i /"$audio"/d $AUDIOMAP
echo "$audio" >> $AUDIOMAP
fi
}
quit_vlc () {
get_var title get_time
wait
set_resume title get_time
wait
vlc_command atrack -1
sleep 0.2
vlc_command quit
}
check_title () {
if [[ $title != $oldtitle ]]; then
rm -f $AUDIOMAP
vlc_command title $title
if [[ `grep '^audio_id\|^sub_id' $DEFAULTFILE` ]] && [[ `grep -w "^title=$title\|^preserve=$title" $DEFAULTFILE` ]]; then
get_default audio_id sub_id
check_default
fi
fi
}
sequential () {
# Make bookmark numbers sequential after manual reordering
# This is purely cosmetic; the bookmarks will work as long as the numbers are
# unique
local NUM
local LINES
local i
NUM=1
LINES=`sed -n '/^[0-9]\+\t/ =' $BOOKMARKFILE`
for i in $LINES; do
sed -i "$i s/^[0-9]*/$NUM/" $BOOKMARKFILE
NUM=`expr $NUM + 1`
done
}
sort_bookmarks () {
# Reorder vlc bookmarks by title and time
# TODO: Handle play_text entries correctly (not very important)
# The purpose of START_SORT is to keep items you don't want sorted (permanent
# entries like downloaded original episode titles, out-of-order titles, and
# entries for the play_text function with blank lines, see below) at the top
# of the list:
local START_SORT
local STARTLINE
local TMPFILE
local TITLES
local TIMES
local LINES
START_SORT=$SORT # TODO: SORT=0 to delete bookmarks
if [[ $START_SORT ]]; then
sed -i /start_sort/d $DEFAULTFILE
echo "start_sort=$START_SORT" >> $DEFAULTFILE
else
START_SORT=`grep '^start_sort=' $DEFAULTFILE | cut -d \= -f 2`
fi
if [[ ! $START_SORT ]]; then
zenity --info --text="No starting point selected.\nPlease enter the number of the\nfirst bookmark to be processed.\nUsage: `basename $0` -z <number>" --timeout=5
exit
# Or simply:
# START_SORT=1
fi
# Defining the part of the file to process:
STARTLINE=`sed -n "/^$START_SORT\t/ =" $BOOKMARKFILE`
TMPFILE=${MEDIADIR}/tmp
sed -n "$STARTLINE,$ p" $BOOKMARKFILE > $TMPFILE
sed -i "$STARTLINE,$ d" $BOOKMARKFILE
wait
TITLES=`sed -n '/^\<title=[0-9]*\>$/ s/^title=// p' $TMPFILE | sort -n | uniq`
for i in $TITLES; do
TIMES=`sed -n "/^\<title=$i\>$/ {
N
/get_time/ p}" $TMPFILE | sed '/title/d' | cut -d \= -f 2 | sort -n | uniq`
# This should handle duplicates (bookmarks with different names that point
# to the same place in the DVD):
LINES=`for j in $TIMES; do sed -n "/^\<get_time=$j\>$/ =" $TMPFILE; done`
for k in $LINES; do
if [[ `sed -n "$(expr $k - 1) p" $TMPFILE` =~ ^title=$i$ ]]; then
sed -n "$(expr $k - 2) p" $TMPFILE | sed "s/^[0-9]*/$START_SORT/" >> $BOOKMARKFILE
sed -n "$(expr $k - 1) p" $TMPFILE >> $BOOKMARKFILE
sed -n "$k p" $TMPFILE >> $BOOKMARKFILE
# commands@ lines will be processed if present:
sed -n "$(expr $k + 1) {/^commands@/ p}" $TMPFILE >> $BOOKMARKFILE
sed -n "$(expr $k + 2) {/^global_commands@/ p}" $TMPFILE >> $BOOKMARKFILE
START_SORT=`expr $START_SORT + 1`
fi
done
done
}
################################################################################
# The following functions can be used to play text pages in DVDs (Buffy,
# X-Files), and some slideshows, as long as they are on titles (Dead Like Me
# Season 2 Disk 4). The command in the bookmark file is:
# commands@play_text <title> <startchapter>
# startchapter is usually 2
# the "title" and "get_time" lines created by the bookmarking function should
# be deleted, they are not needed (but leave the blank lines in! The commands@
# line must be in the 3rd line after the bookmark)
# WARNING: the sort_bookmarks algorithm will not handle these entries!
# add dvdsimple://$DVD_DEVICE#$1:$2 etc. won't work properly; VLC's playlist
# interface is unsuited for DVD playback, and playlist items have arbitrary
# IDs that would be cumbersome to keep track of.
################################################################################
# You will probably want both functions:
play_text () {
quit_vlc
sleep 0.3
(mplayer -fs dvd://"$1" -dvd-device "$DVD_DEVICE" -chapter "$2" -speed 0.02 -nocache) &
echo "PLAYER=mplayer" >> $DATAFILE
}
play_text1 () {
quit_vlc
sleep 0.3
# Playback control is with the space bar - don't set the rate too low, just low
# enough that you have time to hit the space bar
(vlc dvdsimple://"${DVD_DEVICE}#$1:$2" --rate=0.8) &
echo "PLAYER=vlc" >> $DATAFILE
}
# End of functions block
# Get variables:
[[ -f $DATAFILE ]] && . $DATAFILE
# Restart after watching text files on a DVD:
if [[ $DVDPLAYER ]] && [[ $PLAYER ]]; then
kill `pgrep -U $USER $PLAYER`
sleep 0.3
if [[ $DVD_DEVICE =~ /dev ]]; then
DVD_DEVICE=""
fi
($DVDPLAYER $DVD_DEVICE) &
exit
fi
case $1 in
-e|--edit)
# Insert your favourite graphical editor here (and force it to open a single
# window):
if [[ `pidof vlc` ]]; then
[[ -f $BOOKMARKFILE ]] && DOCUMENT[0]=$BOOKMARKFILE
[[ -f $DEFAULTFILE ]] && DOCUMENT[1]=$DEFAULTFILE
[[ $CONFIGFILE ]] && DOCUMENT[2]=$CONFIGFILE
[[ ${DOCUMENT[@]} ]] && pluma ${DOCUMENT[@]}
else
# This gives a shortcut to open an editor at all times:
cd $HOME/Desktop; pluma --new-window
fi
;;
-i|--deinterlace-mode)
vlc_command vdeinterlace_mode "$2"
sed -i /^deinterlace-mode/d $DEFAULTFILE
echo 'deinterlace-mode='"$2" >> $DEFAULTFILE
;;
-m|--menu)
vlc_command title 0
;;
-n|--current-chapter)
get_var chapter
wait
vlc_command chapter $chapter
;;
-u|--unset-resume)
for x in title get_time audio_id sub_id ; do
sed -i /"resume@$x"/d $DEFAULTFILE
done
;;
-x|--alt-audio)
alt_audio
;;
-z|--sort-bookmarks)
SORT=$2
sort_bookmarks
;;
-y|--sequential)
sequential
;;
-h|--help)
cat << EOF
This is a bookmarking and resuming script for VLC. It is intended for use
with the dvdplayer script. Call it with Alt+F2 while VLC (started with the
dvdplayer script) is running or assign shortcuts to its various options.
Some of the options require arguments; they are indicated in angle brackets.
Calling this script without options will create a resume bookmark and close
VLC. The previous resume bookmark will be overwritten.
Options:
-b --create-default will create a default bookmark at the
current position to start the playback from.
Unlike the resume bookmark this will not be
overwritten unless you create a new default
bookmark.
-c --create will prompt for the name of a new bookmark
to be created at the current position.
-e --edit will open the bookmark and defaults files in a
graphical editor.
-i --deinterlace-mode <mode>
will set a custom deinterlace mode to override
the VLC config setting, and write it into the
defaults file.
-k --commentary will add code to the last created bookmark to
switch to the alternative audio track of the
same language. This will fail if there are more
than two audio tracks of the same language,
but this script has a function (see below) to
do the switching manually.
The commands@ and global_commands@ lines will
have to be edited manually if the DVD has a
complex structure.
-l --list will open a bookmark menu; choose one by number,
scrolling or double-clicking. The numbers will
not be consecutive if you deleted bookmarks
(it is of course possible to edit them to be
consecutive; see below).
-m --menu calls title 0, which on most DVDs will go to
the current DVD submenu without always jumping
back to the root menu (one of the most annoying
features of VLC; the root menu on most DVDs is
in front of all the nag screens).
-n --current-chapter will go to the beginning of the currently playing
DVD chapter.
-q --create-clip will add code to the last created bookmark to
turn it into an infinitely repeating video clip
that starts at the time specified in the bookmark
and ends (approximately; manual editing may be
required) at the time you call this option.
To exit the clip, do "killall `basename $0`".
-r --resume will start playback from the resume bookmark,
if present.
-s --start will start playback from the default bookmark,
if present.
-u --unset-resume will unset the resume bookmark.
-x --alt-audio will switch between audio tracks of the language
specified in the script (useful for switching to
the commentary track(s) and back, and sometimes needed
when the DVD was started from a resume bookmark and the
audio track order is messed up).
-y --sequential will restore consecutive bookmark numbers
in the bookmark file of the current DVD
after manual reordering of bookmarks.
-z --sort-bookmarks <number>
will sort all or part of the bookmarks in the
bookmark file of the current DVD according to
title and time. The number of the first bookmark
to be processed can be specified on the commandline
and will be written into the defaults file.
-A --set-atrack will set the current audio track on the
current title as default.
-B --custom-config will write a copy of ~/.config/vlc/vlcrc to
$MEDIADIR/vlcrc/<disc_id>.vlcrc for custom
configuration, which will be loaded on restart and
can be configured through the graphical interface
or with the -e option.
Requires restarting VLC.
-C --crop <crop ratio>
will force display cropping.
Requires restarting VLC.
-D --disc-caching <disc-caching value in ms>
will set a custom disc caching value
to override the VLC config setting.
Requires restarting VLC.
-E --custom-command <custom VLC commandline>
will write a custom command to the defaults file.
An existing custom@ line will be appended to.
Requires restarting VLC.
-F --file-caching <file-caching value in ms>
will set a custom file caching value
to override the VLC config setting.
Requires restarting VLC.
-M --monitor-par <monitor pixel aspect ratio>
will stretch or pinch the display if necessary.
Requires restarting VLC.
-R --aspect <aspect ratio>
will force the display aspect ratio.
Requires restarting VLC.
-S --set-strack will set the current subtitle track on the
current title as default.
-T --add-title will add the current title to the defaults file,
so audio and subtitle default settings will be
applied to it. Multiple titles can be added. This
has no effect on the default playback position.
-Z --reload will reload default settings from the defaults
file, if present.
The capitalized options are useful if default settings fail.
For many of these options, see also the VLC documentation.
EOF
exit
;;
-l|--list)
if [[ `pidof vlc` ]]; then
if [[ $BOOKMARKFILE -nt $LISTFILE ]]; then
#reload the file if it changed
sed -n '/^[0-9]\+\t.*$/ P' $BOOKMARKFILE > $LISTFILE
DISPLAY_WIDTH="$(echo "(`wc -L < $LISTFILE` * 7) + 48" | bc)"
DISPLAY_HEIGHT="$(echo "(`wc -l < $LISTFILE` * 23) + 88" | bc)"
sed -i /DISPLAY_.*/d $DATAFILE
echo "DISPLAY_WIDTH=$DISPLAY_WIDTH" >> $DATAFILE
echo "DISPLAY_HEIGHT=$DISPLAY_HEIGHT" >> $DATAFILE
fi
if [[ -s $LISTFILE ]]; then
get_var title
if [[ $PREV_SELECTION ]]; then
prev=" Last Selection: $PREV_SELECTION"
else
prev=""
fi
SELECTION="$(xargs -d '\n' -a "$LISTFILE" zenity --list --width="$DISPLAY_WIDTH" --height="$DISPLAY_HEIGHT" --title="Bookmarks" --text="Title: ${title}${prev}" --column="" --hide-header --timeout=30 | cut -f 1)"
else
zenity --info --text='No bookmarks yet.' --timeout=2
fi
if [[ $SELECTION ]]; then
sed -i /PREV_SELECTION/d $DATAFILE
echo "PREV_SELECTION=$SELECTION" >> $DATAFILE
#wait
oldtitle=$title
title=$(grep -w -A 1 "^$SELECTION" $BOOKMARKFILE | tail -n 1 | cut -d \= -f 2)
get_time=$(grep -w -A 2 "^$SELECTION" $BOOKMARKFILE | tail -n 1 | cut -d \= -f 2)
check_title
vlc_command set_time $get_time
run_global
if [[ `echo $(grep -w -A 3 "^$SELECTION" $BOOKMARKFILE | tail -n 1 | cut -d \@ -f 1)` == 'commands' ]]; then
commands="$(grep -w -A 3 "^$SELECTION" $BOOKMARKFILE | tail -n 1 | cut -d \@ -f 2)"
eval $commands
fi
fi
fi
;;
-A|--set-atrack)
vlc_command atrack
wait
audio_id=`grep 'Track.*\*' $LOGFILE | tail -n 1 | cut -d \ -f 5`
wait
set_default audio_id
;;
-B|--custom-config)
echo custom_config >> $DEFAULTFILE
quit_vlc
;;
-S|--set-strack)
vlc_command strack
wait
if [[ `grep 'Disable\ \*' $LOGFILE | tail -n 1` ]]; then
sub_id=0
else
sub_id=`grep 'Track.*\*' $LOGFILE | tail -n 1 | cut -d \ -f 5`
fi
wait
set_default sub_id
;;
-E|--custom-command)
shift
STRING=`grep '^custom@' $DEFAULTFILE`
if [[ -z "$STRING" ]]; then
echo 'custom@'"$*" >> $DEFAULTFILE
else
sed -i "s/$STRING/$STRING $*/" $DEFAULTFILE
fi
quit_vlc
;;
-M|--monitor-par)
sed -i /mpar/d $DEFAULTFILE
echo "mpar=$2" >> $DEFAULTFILE
wait
quit_vlc
;;
-T|--add-title)
get_var title
wait
echo "preserve=$title" >> $DEFAULTFILE
;;
-D|--disc-caching)
sed -i /disc-caching/d $DEFAULTFILE
echo "disc-caching=$2" >> $DEFAULTFILE
wait
quit_vlc
;;
-F|--file-caching)
sed -i /file-caching/d $DEFAULTFILE
echo "file-caching=$2" >> $DEFAULTFILE
wait
quit_vlc
;;
-C|--crop)
sed -i /crop/d $DEFAULTFILE
echo "crop=$2" >> $DEFAULTFILE
wait
quit_vlc
;;
-R|--aspect)
sed -i /aspect/d $DEFAULTFILE
echo "aspect=$2" >> $DEFAULTFILE
wait
quit_vlc
;;
-Z|--reload)
#if [[ `grep '^audio_id\|^sub_id' $DEFAULTFILE` ]] && [[ `grep -w "^title=$title\|^preserve=$title" $DEFAULTFILE` ]]; then
if [[ `grep '^audio_id\|^sub_id' $DEFAULTFILE` ]]; then
get_default audio_id sub_id
wait
check_default
fi
run_global
;;
-c|--create)
BMK=$(zenity --entry --title="New Bookmark" --text="A new bookmark will be created at the present position. Choose a name for it:")
if [[ $BMK ]]; then
get_var title get_time
wait
if [[ -z `grep '^title=' $DEFAULTFILE` ]] && [[ -z `grep '^title=' $BOOKMARKFILE` ]]; then
set_default title get_time
wait
fi
set_bookmark title get_time
wait
vlc_command play
fi
;;
-q|--create-clip)
starttime=`grep 'get_time' $BOOKMARKFILE | tail -n 1 | cut -d \= -f 2`
if [[ $starttime ]]; then
get_var get_time
wait
length=`echo $get_time - $starttime | bc`
echo 'commands@(while :; do sleep '"$length"'; vlc_command set_time '"$starttime"'; done) &' >> $BOOKMARKFILE
else
zenity --info --title="Error Creating Video Clip" --text="You need to create a bookmark first" --timeout=2
fi
vlc_command play
;;
-k|--commentary)
get_var title
wait
STRING1='if [[ -e $FLAG ]]; then sleep 0.4; alt_audio; rm -f $FLAG; fi; if [[ $title -ne '$title' ]]; then rm -f $AUDIOMAP; fi'
echo 'commands@if [[ ! -e $FLAG ]]; then sleep 0.4; alt_audio; touch $FLAG; fi' >> $BOOKMARKFILE
if [[ -z `grep 'global_commands@' $BOOKMARKFILE` ]]; then
echo 'global_commands@'$STRING1 >> $BOOKMARKFILE
fi
if [[ `grep '^global_commands@' $BOOKMARKFILE` ]] && [[ -z `grep 'global_commands@.*alt_audio' $BOOKMARKFILE` ]]; then
STRING="`grep '^global_commands@' $BOOKMARKFILE`"
sed -i "s/$STRING/${STRING}\; $STRING1/" $BOOKMARKFILE
fi
;;
-b|--create-default)
get_var title get_time
wait
set_default title get_time
wait
vlc_command play
;;
-s|--start)
if [[ `grep '^title\|^get_time' $DEFAULTFILE` ]]; then
get_var title
oldtitle=$title
get_default title get_time
check_title
vlc_command set_time $get_time
run_global
fi
;;
-r|--resume)
if [[ `grep "resume@" $DEFAULTFILE` ]]; then
get_var title
oldtitle=$title
get_resume title get_time
check_title
vlc_command set_time $get_time
run_global
fi
;;
*)
quit_vlc
;;
esac
The scripts are commented and have a --help option; it should be clear how to
use them.
DEVELOPERS: Is the lua interface maintained at all? It does not seem to have
changed in a while. Maybe a more scripting-friendly interface would be better
(and more interesting to developers and general users).
For instance, the output of atrack and strack should be arrays that could be
redirected to files (and returned to scripts) instead of pretty-printed and
hard-to-parse logfile entries. I could continue to modify the cli.lua script,
but sooner or later I'd probably run into incompatibilities.