Index: gs2-extensions/video-and-audio/trunk/CASCADE-MAKE.bat
===================================================================
--- gs2-extensions/video-and-audio/trunk/CASCADE-MAKE.bat	(revision 23035)
+++ 	(revision )
@@ -1,79 +1,0 @@
-@echo off
-pushd "%CD%"
-CD /D "%~dp0"
-
-if "x%GSDLHOME%" == "x" (
-  REM This extension needs to have the main GSDL setup.bat sourced
-
-  echo Greenstone environment variable GSDLHOME not set.
-  echo Have you sourced the main installation\'s setup file, e.g. setup.bat?
-  goto ErrorExitNeg1 
-)
-
-if "x%$APACHE_HTTPD_HOME%" == "x" (
-  set APACHE_HTTPD_HOME=%GSDLHOME%\apache-httpd\windows
-)
-
-if not exist "%GSDLHOME%\apache-httpd" (
-  REM ... and it also needs to have been configured with --enable-apache-httpd
-  REM This is because the entension needs to add mod_flvx to the web server
-
-  echo Warning: Could not find the directory "%GSDLHOME%\apache-httpd"
-  echo          Has the main Greenstone been configure for this? 
-  echo          e.g. nmake /f win32.mak APACHE_HTTPD=1 [... other options]
-  echo.
-  echo => Only the build-time code for the extension will be compiled
-  set APACHE_HTTPD_HOME=
-)
-
-
-REM call cascade-make/bin/script/test-setup.bat
-call cascade-make/lib/cascade-lib.bat
-
-:: Guaranteed that GSDLOS has now been set
-
-
-echo "Test for directory in Windows might not be correct syntax!!"
-if not exist "%GEXTVIDEO_INSTALLED%" (
-  echo Creating %GEXTVIDEO_INSTALLED%
-  mkdir "%GEXTVIDEO_INSTALLED%"
-)
-
-set dirlist=build-srcpack
-
-if "x%APACHE_HTTPD_HOME%" != "x" (
-  set dirlist=%dirlist% runtime-srcpack
-)
-
-if exist "devel-srcpack" (
-  set dirlist=%dirlist% devel-srcpack
-)
-
-for /D %%d IN (%dirlist%) do (
-  echo Running %%d\CASCADE-MAKE.bat %*
-
-  (cd %%d && call CASCADE-MAKE.bat %* && cd ..)
-  
-  if not ERRORLEVEL 0 (
-    echo Error encountered running %%d\CASCADE-MAKE.bat
-    goto ErrorExitPos1
-  )
-)
-
-
-if  "%install%" == "1" (
-  type NOTES.txt
-)
-goto End
-
-:ErrorExitNeg1
-set ERRORLEVEL=-1
-goto End
-
-:ErrorExitPos1
-set ERRORLEVEL=1
-goto End
-
-:End
-
-popd
Index: gs2-extensions/video-and-audio/trunk/CASCADE-MAKE.sh
===================================================================
--- gs2-extensions/video-and-audio/trunk/CASCADE-MAKE.sh	(revision 23035)
+++ 	(revision )
@@ -1,69 +1,0 @@
-#!/bin/bash
-
-if [ "x$GSDLHOME" = "x" ] ; then
-  # This extension needs to have the main GSDL setup.bash sourced
-
-  echo "Greenstone environment variable GSDLHOME not set."
-  echo "Have you sourced the main installation's setup file, e.g. setup.bash?"
-  exit -1
-fi
-
-if [ -z $APACHE_HTTPD_HOME ] ; then
-  export APACHE_HTTPD_HOME=$GSDLHOME/apache-httpd/$GSDLOS
-fi
-
-if [ ! -d "$APACHE_HTTPD_HOME" ] ; then
-  # ... and it also needs to have been configured with --enable-apache-httpd
-  # This is because the entension needs to add mod_flvx to the web server
-
-  if [ -z $GSDL3SRCHOME  ] ; then
-    # Greenstone 2
-    echo "Warning: Did not find the directory \"$APACHE_HTTPD_HOME\""
-    echo "         Has the main Greenstone been configured for this? "
-    echo "         e.g. ./configure --enable-apache-httpd [... other options]"
-    echo ""
-    echo "=> Only the build-time code for the extension will be compiled"
-  else 
-    # Greenstone 3
-    echo "  Compiling build-time video extension for Greenstone3 use"
-  fi
-  export APACHE_HTTPD_HOME=
-fi
-
-
-source cascade-make/bin/script/test-setup.bash
-source cascade-make/lib/cascade-lib.bash
-
-# Guaranteed that GSDLOS has now been set
-
-if [ ! -e $GEXTVIDEO_INSTALLED ] ; then
-  echo "Creating $GEXTVIDEO_INSTALLED"
-  mkdir $GEXTVIDEO_INSTALLED
-fi
-
-dirlist="build-srcpack"
-
-if [ ! -z $APACHE_HTTPD_HOME ] ; then
-  dirlist="$dirlist runtime-srcpack"
-fi
-
-if [ -e devel-srcpack ] ; then
-  dirlist="$dirlist devel-srcpack"
-fi
-
-for d in $dirlist ; do
-  echo "Running $d/CASCADE-MAKE.sh $*"
-
-  (cd $d ; ./CASCADE-MAKE.sh $*)
-
-  if [ $? != 0 ] ; then
-    echo "Error encountered running $d/CASCADE-MAKE.sh"
-    exit 1
-  fi
-done
-
-
-
-if [ $install = "1" ] ; then
-  cat NOTES.txt
-fi
Index: gs2-extensions/video-and-audio/trunk/NOTES.txt
===================================================================
--- gs2-extensions/video-and-audio/trunk/NOTES.txt	(revision 23035)
+++ 	(revision )
@@ -1,12 +1,0 @@
-
-Now add:
-
-  AddHandler flv-stream .flv 
-
-to the appropriate section of 
-
-  $GSDLHOME/apache-httpd/$GSDLOS/conf/httpd.conf
-
-
-
-
Index: gs2-extensions/video-and-audio/trunk/START-VIDEO-SERVER.sh.OBSOLETE
===================================================================
--- gs2-extensions/video-and-audio/trunk/START-VIDEO-SERVER.sh.OBSOLETE	(revision 23035)
+++ 	(revision )
@@ -1,11 +1,0 @@
-#!/bin/bash
-
-if [ "x$GEXTVIDEO" = "x" ] ; then
-  echo "Video extension environment variable GEXTVIDEO not set."
-  echo "Have you sourced the extension's setup file, e.g. setup.bash?"
-  exit
-fi
-
-echo "Starting Lighttpd: $GEXT_VIDEO_SERVER$GEXT_VIDEO_PREFIX"
-
-$GEXTVIDEO_INSTALLED/server/sbin/lighttpd -f $GEXTVIDEO_INSTALLED/server/conf/lighttpd.conf
Index: gs2-extensions/video-and-audio/trunk/setup.bash
===================================================================
--- gs2-extensions/video-and-audio/trunk/setup.bash	(revision 23035)
+++ 	(revision )
@@ -1,45 +1,0 @@
-
-extdesc="the Video and Audio Extension"
-
-full_setup=`pwd`/${BASH_SOURCE}
-fulldir=${full_setup%/*}
-fulldir=${fulldir%/.}
-
-source cascade-make/lib/cascade-lib.bash
-
-if [ "x$GEXTVIDEO" = "x" ] ; then
-  export GEXTVIDEO=$fulldir
-  export GEXTVIDEO_INSTALLED=$GEXTVIDEO/$GSDLOS
-
-  export PATH=$GEXTVIDEO_INSTALLED/bin:$PATH
-
-  if [ "x$LD_LIBRARY_PATH" = "x" ] ; then
-    export LD_LIBRARY_PATH=$GEXTVIDEO_INSTALLED/lib
-  else
-    export LD_LIBRARY_PATH=$GEXTVIDEO_INSTALLED/lib:$LD_LIBRARY_PATH
-  fi
-  if [ "x$DYLD_LIBRARY_PATH" = "x" ] ; then
-    export DYLD_LIBRARY_PATH=$GEXTVIDEO_INSTALLED/lib
-  else
-    export DYLD_LIBRARY_PATH=$GEXTVIDEO_INSTALLED/lib:$DYLD_LIBRARY_PATH
-  fi
-
-  if [ -e "devel-srcpack" ] ; then
-    export LD_LIBRARY_PATH=$GEXTVIDEO_INSTALLED/devel/lib:$LD_LIBRARY_PATH
-    export DYLD_LIBRARY_PATH=$GEXTVIDEO_INSTALLED/devel/lib:$DYLD_LIBRARY_PATH
-  fi
-
-  
-  extdir=${GEXTVIDEO##*/}
-
-  if [ "x$GSDLEXTS" = "x" ] ; then
-    export GSDLEXTS=$extdir
-  else 
-    export GSDLEXTS=$GSDLEXTS:$extdir
-  fi
-
-
-  echo "+Your environment is now setup for $extdesc"
-else
-  echo "+Your environment is already setup for $extdesc"
-fi
Index: gs2-extensions/video-and-audio/trunk/setup.bat
===================================================================
--- gs2-extensions/video-and-audio/trunk/setup.bat	(revision 23035)
+++ 	(revision )
@@ -1,57 +1,0 @@
-@echo off
-pushd "%CD%"
-CD /D "%~dp0"
-set GSDLLANG=en
-set extdesc="the Video and Audio Extension"
-
-if "%OS%" == "Windows_NT" goto WinNT
-if "%OS%" == "" goto Win95
-if "%GSDLLANG%" == "en" echo Setup failed - your PATH has not been set
-if "%GSDLLANG%" == "es" echo No se pudo realizar la configuraci¢n - no se ha establecido la RUTA.
-if "%GSDLLANG%" == "fr" echo Echc de l'installation - votre variable PATH n'a pas t ajuste
-if "%GSDLLANG%" == "ru" echo áâ ­®¢ª  ­¥ ã€ « áì -  ­¥ ¡ë« ãáâ ­®¢«¥­
-goto End
-
-:WinNT
-set GEXTVIDEO=%CD%
-set GEXTVIDEO_INSTALLED=%GEXTVIDEO%\windows
-
-set PATH=%GEXTVIDEO_INSTALLED%\bin;%PATH%
-set GS_CP_SET=yes
-goto Success
-
-:Win95
-if "%1" == "SetEnv" goto Win95Env
-REM We'll invoke a second copy of the command processor to make
-REM sure there's enough environment space
-COMMAND /E:2048 /K %0 SetEnv
-goto End
-
-:Win95Env
-set GEXTVIDEO=%CD%
-set PATH="%GEXTVIDEO_INSTALLED%\bin";"%PATH%"
-set GS_CP_SET=yes
-goto Success
-
-:Success
-
-set fulldir=%~dp0
-
-:: strip off everything up to (and including) ext dir
-set extdir=%fulldir:*ext\=%
-
-:: remove trailing slash
-set extdir=%extdir:\=%
-
-
-if "x%GSDLEXTS%" == "x" (
-  set GSDLEXTS=%extdir%
-) else (
-  set GSDLEXTS=%GSDLEXTS%:%extdir%
-)
-
-echo +Your environment is now setup for %extdesc%
-
-:End
-
-popd
Index: gs2-extensions/video-and-audio/trunk/src/CASCADE-MAKE.bat
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/CASCADE-MAKE.bat	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/CASCADE-MAKE.bat	(revision 23036)
@@ -0,0 +1,79 @@
+@echo off
+pushd "%CD%"
+CD /D "%~dp0"
+
+if "x%GSDLHOME%" == "x" (
+  REM This extension needs to have the main GSDL setup.bat sourced
+
+  echo Greenstone environment variable GSDLHOME not set.
+  echo Have you sourced the main installation\'s setup file, e.g. setup.bat?
+  goto ErrorExitNeg1 
+)
+
+if "x%$APACHE_HTTPD_HOME%" == "x" (
+  set APACHE_HTTPD_HOME=%GSDLHOME%\apache-httpd\windows
+)
+
+if not exist "%GSDLHOME%\apache-httpd" (
+  REM ... and it also needs to have been configured with --enable-apache-httpd
+  REM This is because the entension needs to add mod_flvx to the web server
+
+  echo Warning: Could not find the directory "%GSDLHOME%\apache-httpd"
+  echo          Has the main Greenstone been configure for this? 
+  echo          e.g. nmake /f win32.mak APACHE_HTTPD=1 [... other options]
+  echo.
+  echo => Only the build-time code for the extension will be compiled
+  set APACHE_HTTPD_HOME=
+)
+
+
+REM call cascade-make/bin/script/test-setup.bat
+call cascade-make/lib/cascade-lib.bat
+
+:: Guaranteed that GSDLOS has now been set
+
+
+echo "Test for directory in Windows might not be correct syntax!!"
+if not exist "%GEXTVIDEO_INSTALLED%" (
+  echo Creating %GEXTVIDEO_INSTALLED%
+  mkdir "%GEXTVIDEO_INSTALLED%"
+)
+
+set dirlist=build-srcpack
+
+if "x%APACHE_HTTPD_HOME%" != "x" (
+  set dirlist=%dirlist% runtime-srcpack
+)
+
+if exist "devel-srcpack" (
+  set dirlist=%dirlist% devel-srcpack
+)
+
+for /D %%d IN (%dirlist%) do (
+  echo Running %%d\CASCADE-MAKE.bat %*
+
+  (cd %%d && call CASCADE-MAKE.bat %* && cd ..)
+  
+  if not ERRORLEVEL 0 (
+    echo Error encountered running %%d\CASCADE-MAKE.bat
+    goto ErrorExitPos1
+  )
+)
+
+
+if  "%install%" == "1" (
+  type NOTES.txt
+)
+goto End
+
+:ErrorExitNeg1
+set ERRORLEVEL=-1
+goto End
+
+:ErrorExitPos1
+set ERRORLEVEL=1
+goto End
+
+:End
+
+popd
Index: gs2-extensions/video-and-audio/trunk/src/CASCADE-MAKE.sh
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/CASCADE-MAKE.sh	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/CASCADE-MAKE.sh	(revision 23036)
@@ -0,0 +1,69 @@
+#!/bin/bash
+
+if [ "x$GSDLHOME" = "x" ] ; then
+  # This extension needs to have the main GSDL setup.bash sourced
+
+  echo "Greenstone environment variable GSDLHOME not set."
+  echo "Have you sourced the main installation's setup file, e.g. setup.bash?"
+  exit -1
+fi
+
+if [ -z $APACHE_HTTPD_HOME ] ; then
+  export APACHE_HTTPD_HOME=$GSDLHOME/apache-httpd/$GSDLOS
+fi
+
+if [ ! -d "$APACHE_HTTPD_HOME" ] ; then
+  # ... and it also needs to have been configured with --enable-apache-httpd
+  # This is because the entension needs to add mod_flvx to the web server
+
+  if [ -z $GSDL3SRCHOME  ] ; then
+    # Greenstone 2
+    echo "Warning: Did not find the directory \"$APACHE_HTTPD_HOME\""
+    echo "         Has the main Greenstone been configured for this? "
+    echo "         e.g. ./configure --enable-apache-httpd [... other options]"
+    echo ""
+    echo "=> Only the build-time code for the extension will be compiled"
+  else 
+    # Greenstone 3
+    echo "  Compiling build-time video extension for Greenstone3 use"
+  fi
+  export APACHE_HTTPD_HOME=
+fi
+
+
+source cascade-make/bin/script/test-setup.bash
+source cascade-make/lib/cascade-lib.bash
+
+# Guaranteed that GSDLOS has now been set
+
+if [ ! -e $GEXTVIDEO_INSTALLED ] ; then
+  echo "Creating $GEXTVIDEO_INSTALLED"
+  mkdir $GEXTVIDEO_INSTALLED
+fi
+
+dirlist="build-srcpack"
+
+if [ ! -z $APACHE_HTTPD_HOME ] ; then
+  dirlist="$dirlist runtime-srcpack"
+fi
+
+if [ -e devel-srcpack ] ; then
+  dirlist="$dirlist devel-srcpack"
+fi
+
+for d in $dirlist ; do
+  echo "Running $d/CASCADE-MAKE.sh $*"
+
+  (cd $d ; ./CASCADE-MAKE.sh $*)
+
+  if [ $? != 0 ] ; then
+    echo "Error encountered running $d/CASCADE-MAKE.sh"
+    exit 1
+  fi
+done
+
+
+
+if [ $install = "1" ] ; then
+  cat NOTES.txt
+fi
Index: gs2-extensions/video-and-audio/trunk/src/NOTES.txt
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/NOTES.txt	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/NOTES.txt	(revision 23036)
@@ -0,0 +1,12 @@
+
+Now add:
+
+  AddHandler flv-stream .flv 
+
+to the appropriate section of 
+
+  $GSDLHOME/apache-httpd/$GSDLOS/conf/httpd.conf
+
+
+
+
Index: gs2-extensions/video-and-audio/trunk/src/START-VIDEO-SERVER.sh.OBSOLETE
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/START-VIDEO-SERVER.sh.OBSOLETE	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/START-VIDEO-SERVER.sh.OBSOLETE	(revision 23036)
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+if [ "x$GEXTVIDEO" = "x" ] ; then
+  echo "Video extension environment variable GEXTVIDEO not set."
+  echo "Have you sourced the extension's setup file, e.g. setup.bash?"
+  exit
+fi
+
+echo "Starting Lighttpd: $GEXT_VIDEO_SERVER$GEXT_VIDEO_PREFIX"
+
+$GEXTVIDEO_INSTALLED/server/sbin/lighttpd -f $GEXTVIDEO_INSTALLED/server/conf/lighttpd.conf
Index: gs2-extensions/video-and-audio/trunk/src/cascade-make.old/bin/script/test-setup.bash
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/cascade-make.old/bin/script/test-setup.bash	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/cascade-make.old/bin/script/test-setup.bash	(revision 23036)
@@ -0,0 +1,8 @@
+
+
+if [ "x$GEXTVIDEO" = "x" ] ; then
+  echo "Video extension environment variable GEXTVIDEO not set."
+  echo "Have you sourced the extension's setup file, e.g. setup.bash?"
+  exit -1
+fi
+
Index: gs2-extensions/video-and-audio/trunk/src/cascade-make.old/lib/cascade-lib.bash
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/cascade-make.old/lib/cascade-lib.bash	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/cascade-make.old/lib/cascade-lib.bash	(revision 23036)
@@ -0,0 +1,193 @@
+
+if test -z $GSDLOS ; then
+  GSDLOS=`uname -s | tr '[A-Z]' '[a-z]'`
+  # check for running bash under cygwin
+  if test "`echo $GSDLOS | sed 's/cygwin//'`" != "$GSDLOS" ;
+  then
+    GSDLOS=windows
+  fi
+  export GSDLOS
+fi
+
+
+run_untar()
+{
+  local package=$1
+  local version=$2
+  local ext=$3
+
+  tar_args="";
+
+  if [ $ext = ".tar.gz" ] ; then
+    tar_args="xvzf"
+  elif [ $ext = ".tgz" ] ; then
+    tar_args="xvzf"
+  elif [ $ext = ".tar.bz2" ] ; then
+    tar_args="xvjf"
+  elif [ $ext = ".tar.bz" ] ; then
+    tar_args="xvjf"
+  elif [ $ext = ".tar" ] ; then
+    tar_args="xvf"
+  else 
+    echo "Warning: Unrecognized extension: $ext"
+    echo "Assuming tarred, gzipped file"
+    tar_args="xvjf"
+  fi
+
+
+  echo tar $tar_args $package$version$ext 
+  tar $tar_args $package$version$ext
+
+  if [ $? != 0 ] ; then
+    echo "      Error encountered running *untar* stage of $progname"
+    exit 1
+  fi
+}
+
+opt_run_tarclean()
+{
+  local type=$1
+  local package=$2
+  local version=$3
+
+  if [ $type = "1" ] ; then
+
+    local dir=$package$version
+    if [ -d $dir ] ; then
+      echo /bin/rm -rf $dir
+      /bin/rm -rf $dir
+
+      if [ $? != 0 ] ; then
+        echo "      Error encountered running *tarclean* stage of $progname"
+        exit 1
+      fi
+    else 
+      echo "Unable to find directory $dir"
+    fi
+  fi
+}
+
+opt_run_untar()
+{
+  local force_untar=$1
+  local auto_untar=$2
+  local package=$3
+  local version=$4
+
+  local ext="";
+
+  if [ ! -z "$5" ] ; then
+    ext=$5
+  else 
+    ext=".tar.gz"
+  fi
+
+  if [ $force_untar = "1" ] ; then
+    run_untar $package $version $ext
+  elif [ $auto_config = "1" ] ; then
+    if [ -d $package$version ] ; then
+      echo "Found untarred version of $package$version$ext => no need to untar"
+    else
+      run_untar $package $version $ext
+    fi
+  fi
+}
+
+
+opt_run_configure()
+{
+  local force_config=$1
+  local auto_config=$2
+  local package=$3
+  local version=$4
+  local prefix=$5
+  local opt_args="";
+
+  if [ ! -z "$6" ] ; then
+    opt_args=$6
+  fi
+
+  if [ $force_config = "1" ] ; then
+    echo "[pushd $package$version]"
+    ( cd $package$version ; \
+      echo ./configure --prefix="$prefix" $opt_args ; \
+      ./configure --prefix="$prefix" $opt_args )
+    if [ $? != 0 ] ; then
+        echo "      Error encountered running *configure* stage of $progname"
+        exit 1
+    fi
+    echo "[popd]"
+  else
+    if [ $auto_config = "1" ] ; then
+      echo "Found top-level for ${progname%.*} => no need to run ./configure"
+    fi
+  fi
+}
+
+
+opt_run_make()
+{
+  local type=$1
+  local package=$2
+  local version=$3
+  local opt_target=""
+
+  if [ ! -z "$4" ] ; then
+    opt_target=$4
+  fi
+
+  if [ $type = "1" ] ; then
+    ( cd $package$version ; \
+      make $opt_target)
+
+    if [ $? != 0 ] ; then
+        echo "      Error encountered running *make $target* stage of $progname"
+        exit 1
+    fi
+  fi
+}
+
+
+
+force_untar=0
+auto_untar=0
+
+force_config=0
+auto_config=0
+
+compile=0
+install=0
+clean=0
+distclean=0
+tarclean=0
+
+if [ $# -gt 0 ] ; then
+  for cmd in $* ; do
+    echo $cmd
+    if   [ $cmd = "untar" ] ;     then force_untar=1
+    elif [ $cmd = "configure" ] ; then force_config=1
+    elif [ $cmd = "compile" ]   ; then compile=1
+    elif [ $cmd = "install" ]   ; then install=1  
+    elif [ $cmd = "clean" ]     ; then clean=1  
+    elif [ $cmd = "distclean" ] ; then distclean=1
+    elif [ $cmd = "tarclean" ] ;  then tarclean=1
+    fi
+  done
+else
+  # defaults
+  auto_untar=1
+  auto_config=1
+  compile=1
+  install=1
+  clean=0
+  distclean=0
+  tarclean=0
+fi
+
+if [ $auto_config = "1" ] ; then
+  if [ ! -e $package$version/Makefile ] &&  [ ! -e $package$version/config.h ] ; then
+    force_config=1
+  fi
+
+fi
+
Index: gs2-extensions/video-and-audio/trunk/src/cascade-make.old/lib/cascade-lib.bat
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/cascade-make.old/lib/cascade-lib.bat	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/cascade-make.old/lib/cascade-lib.bat	(revision 23036)
@@ -0,0 +1,38 @@
+@echo off
+
+if "x%GSDLOS%" == "x" (
+  set GSDLOS=windows
+)
+
+set force_untar=0
+set auto_untar=0
+
+set compile=0
+set install=0
+set clean=0
+set distclean=0
+set tarclean=0
+set installclean=0
+
+if "%*" NEQ "" (
+  for /D %%c IN (%*) do (
+    echo %%c
+    if "%%c" == "untar"        set force_untar=1
+    if "%%c" == "compile"      set compile=1
+    if "%%c" == "install"      set install=1  
+    if "%%c" == "clean"        set clean=1  
+    if "%%c" == "distclean"    set distclean=1
+    if "%%c" == "tarclean"     set tarclean=1
+    if "%%c" == "installclean" set installclean=1
+  )
+) else (
+  :: defaults
+  set auto_untar=1
+  set compile=1
+  set install=1
+  set clean=0
+  set distclean=0
+  set tarclean=0
+  set installclean=0
+)
+
Index: gs2-extensions/video-and-audio/trunk/src/macros/document.dm
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/macros/document.dm	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/macros/document.dm	(revision 23036)
@@ -0,0 +1,128 @@
+package document
+
+_baseurl_{_httpassocdir_/_1_}
+
+
+_videoplayer_{
+
+<div class="player" id="FlowPlayerDiv" style="width: 352px; height: 290px">
+  <object type="application/x-shockwave-flash"
+    data="_httpprefix_/ext/video/web/flash/FlowPlayerLP.swf"
+    width="352" height="290" id="FlowPlayer">
+      <param name="allowScriptAccess" value="always" />
+      <param name="movie" value="_httpprefix_/ext/video/web/flash/FlowPlayerLP.swf" />
+      <param name="quality" value="high" />
+      <param name="scaleMode" value="showAll" />
+      <param name="allowfullscreen" value="true" />
+      <param name="allowNetworking" value="all" />
+      <param name="flashvars" value="config=\{
+         autoPlay: _If_(_3_,_3_,false),
+         autoBuffering: true,
+         loop: false,
+         initialScale: 'orig',
+         showLoopButton: false,
+         showPlayListButtons: false,
+         allowFullScreen: true,
+         useNativeFullScreen: true,
+         progressBarColor2: '0x308030',
+         progressBarColor1: '0x60d060',
+         videoFile: '_baseurl_(_1_)/_2_',
+         streamingServer: 'apache'
+       \}" />
+  </object>
+</div>
+}
+
+_audioplayer_{
+
+<div class="player" id="FlowPlayerDiv">
+  <object type="application/x-shockwave-flash"
+    data="_httpprefix_/ext/video/web/flash/FlowPlayerLP.swf"
+    width="352" height="22" id="FlowPlayer">
+      <param name="allowScriptAccess" value="always" />
+      <param name="movie" value="_httpprefix_/ext/video/web/flash/FlowPlayerLP.swf" />
+      <param name="quality" value="high" />
+      <param name="scaleMode" value="showAll" />
+      <param name="allowfullscreen" value="false" />
+      <param name="allowNetworking" value="all" />
+      <param name="flashvars" value="config=\{
+         autoPlay: _If_(_3_,_3_,false),
+         autoBuffering: true,
+         loop: false,
+         initialScale: 'orig',
+         showLoopButton: false,
+         showPlayListButtons: false,
+         showFullScreenButton: false,
+         showMenu: false,
+         allowFullScreen: false,
+         progressBarColor2: '0x308030',
+         progressBarColor1: '0x60d060',
+         videoFile: '_baseurl_(_1_)/_2_',
+         streamingServer: 'apache'
+       \}" />
+  </object>
+</div>
+}
+
+
+_pagescriptfileextra_ {
+
+<script type="text/javascript" src="_httpvideoscript_/document.js"></script>
+
+}
+
+
+# Don't bother going through the "document could be large, continue?"
+# => go directly there
+
+_imageexpandtext_ {_docbutton_(_httpcurrentdocument_&amp;gt=2,_textEXPANDTEXT_,_texticonexpandtext_)}
+
+_optnavigationbar_ {
+
+_If_("_cgiargonlytext_" eq "1" ,, 
+
+_:optnavigationbar_
+)
+}
+
+
+_blankheader_ {}
+
+# header overridden for text pages
+_textheader_ {
+_cgihead_
+_htmlhead_
+_blankheader_
+_startspacer_
+
+_If_("_cgiargonlytext_" eq "1" ,, 
+
+<!-- document:textheader -->
+<div id="banner">
+<div class="pageinfo"><p class="bannerlinks">_globallinks_</p></div>
+<br>
+</div> <!-- banner -->
+)
+}
+
+_textheader_ [v=1] {_cgihead_
+_htmlhead_
+_globallinks_
+}
+
+
+
+_footer_ {
+
+_If_("_cgiargonlytext_" eq "1" ,, 
+
+</div> <!-- document:footer -->
+
+<div class="navarrowsbottom">
+_navarrowsbottom_
+</div>
+}
+
+_endspacer__htmlfooter_
+}
+
Index: gs2-extensions/video-and-audio/trunk/src/macros/extra.dm
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/macros/extra.dm	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/macros/extra.dm	(revision 23036)
@@ -0,0 +1,44 @@
+package Global
+
+_httpvideoweb_    {_httpprefix_/ext/video/web}
+_httpvideoimages_ {_httpvideoweb_/images}
+_httpvideoscript_ {_httpvideoweb_/script}
+_httpvideostyle_  {_httpvideoweb_/style}
+_httpvideoflash_  {_httpvideoweb_/flash}
+_httpvideojava_   {_httpvideoweb_/java}
+
+
+_httpcurrentdocumentwithvplayer_ {_httpcurrentdocument_&showvplayer=1}
+_httpcurrentdocumentnovplayer_   {_httpcurrentdocument_&showvplayer=}
+
+_notranscript_ {No transcript available}
+
+package document
+
+_pagescriptextra_ {
+
+  function extraFlowplayerShow() \{
+      var dandp = document.getElementById("DandPPlayer");
+      dandp.style.display = "none";
+      var pandh = document.getElementById("PandHPlayer");
+      pandh.style.display = "block";
+      flowplayershow();
+  \}
+
+  function extraFlowplayerHide() \{
+      var pandh = document.getElementById("PandHPlayer");
+      pandh.style.display = "none";
+      var dandp = document.getElementById("DandPPlayer");
+      dandp.style.display = "block";
+      flowplayerhide();
+  \}
+}
+
+
+_displayandhideflowplayer_ {
+
+  <a id="DandPPlayer" style="display: block;"
+     href="javascript:extraFlowplayerShow()">Display and Play _1_</a>
+  <a id="PandHPlayer" style="display: none;"
+     href="javascript:extraFlowplayerHide()">Pause and Hide _1_</a>
+}
Index: gs2-extensions/video-and-audio/trunk/src/perllib/plugins/AudioConverter.pm
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/perllib/plugins/AudioConverter.pm	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/perllib/plugins/AudioConverter.pm	(revision 23036)
@@ -0,0 +1,200 @@
+###########################################################################
+#
+# AudioConverter - helper plugin that does audio conversion
+#
+# A component of the Greenstone digital library software
+# from the New Zealand Digital Library Project at the 
+# University of Waikato, New Zealand.
+#
+# Copyright (C) 2008 New Zealand Digital Library Project
+#
+# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+###########################################################################
+package AudioConverter;
+
+use MultimediaConverter;
+
+
+use strict;
+no strict 'refs'; # allow filehandles to be variables and viceversa
+
+use gsprintf 'gsprintf';
+
+BEGIN {
+    @AudioConverter::ISA = ('MultimediaConverter');
+}
+
+
+my $arguments = [
+		 ];
+
+my $options = { 'name' => "ImageConverter",
+		'desc' => "{ImageConverter.desc}",
+		'abstract' => "yes",
+		'inherits' => "yes",
+		'args' => $arguments };
+
+sub new {
+    my ($class) = shift (@_);
+    my ($pluginlist,$inputargs,$hashArgOptLists) = @_;
+    push(@$pluginlist, $class);
+
+    push(@{$hashArgOptLists->{"ArgList"}},@{$arguments});
+    push(@{$hashArgOptLists->{"OptList"}},$options);
+
+    my $self = new MultimediaConverter($pluginlist, $inputargs, $hashArgOptLists, 1);
+
+
+    return bless $self, $class;
+
+}
+
+# Discover the characteristics of a audio file.
+
+#  Equivalent step to that in ImagePlugin that uses ImageMagicks's
+#  'indentify' utility
+
+#  Here we use 'ffmpeg' for audio but for consistency keep the Perl
+#  method name the same as before
+
+
+sub identify { 
+    my ($audio, $outhandle, $verbosity) = @_;
+
+    # Use the ffmpeg command to get the file specs
+    my $command = "ffmpeg -i \"$audio\"";
+
+    print $outhandle "  $command\n" if ($verbosity > 2);
+    my $result = '';
+    $result = `$command 2>&1`;
+    print $outhandle "  $result\n" if ($verbosity > 4);
+
+    # Read the type, width, and height etc.
+    my $vtype =  'unknown';
+    my $vcodec = 'unknown';
+    my $width =  'unknown';
+    my $height = 'unknown';
+    my $fps    = 'unknown';
+
+    my $atype =  'unknown';
+    my $afreq =  'unknown';
+    my $achan =  'unknown';
+    my $arate =  'unknown';
+
+    my $audio_safe = quotemeta $audio;
+
+    # strip off everything up to filename
+    $result =~ s/^.*\'$audio_safe\'://s;
+
+    if ($result =~ m/Audio: (\w+), (\d+) Hz, (\w+)(?:, (\d+.*))?/m) {
+	$atype = $1;
+	$afreq = $2;
+	$achan = $3;
+	$arate = $4 if (defined $4);
+    }
+
+    # Read the duration
+    my $duration = "unknown";
+    if ($result =~ m/Duration: (\d+:\d+:\d+\.\d+)/m) {
+	$duration = $1;
+    }
+    print $outhandle "  file: $audio:\t $atype, $duration\n" 
+	if ($verbosity > 2);
+
+    if ($verbosity >3) {
+	print $outhandle "\t freq = $afreq Hz, $achan, $arate\n";
+    }
+
+    # Return the specs
+    return ($duration, -s $audio, $atype,$afreq,$achan,$arate);
+}
+
+
+
+sub audio_converto_cmd
+{
+    my $self = shift (@_);
+    my ($originalfilename,$converttotype) = @_;
+
+    my $output_dir = $self->{'cached_dir'};
+    my $iaudio_root = $self->{'cached_file_root'};
+
+    
+    my $file = "$iaudio_root.$converttotype";
+    my $filename = &util::filename_cat($output_dir,$file);
+    
+    my $exp_duration = $self->{'exp_duration'};
+    my $t_opt = (defined $exp_duration) ? "-t $exp_duration" : "";
+    
+    my $main_opts = "-y $t_opt";
+    
+
+    my $originalfilename_gsdlenv = $self->gsdlhome_independent($originalfilename);
+    my $filename_gsdlenv = $self->gsdlhome_independent($filename);
+    
+    
+    my $convertto_cmd = "ffmpeg $main_opts -i \"$originalfilename_gsdlenv\"";
+    $convertto_cmd .= " -y \"$filename_gsdlenv\"";
+    
+    return $convertto_cmd;
+}
+
+
+
+sub audio_stream_cmd
+{
+    my $self = shift (@_);
+    my ($iaudio_filename,
+	$streaming_bitrate,$streaming_size, 
+	$opt_streaming_achan, $opt_streaming_arate) = @_;
+
+    my $streaming_achan 
+	= (defined $opt_streaming_achan) ? $opt_streaming_achan : 2;
+
+    my $streaming_arate 
+	= (defined $opt_streaming_arate) ? $opt_streaming_arate : 22050;
+
+    my $output_dir = $self->{'cached_dir'};
+    my $iaudio_root = $self->{'cached_file_root'};
+
+    my $mp3_file = "${iaudio_root}_stream.mp3";
+    my $mp3_filename = &util::filename_cat($output_dir,$mp3_file);
+
+    my $exp_duration = $self->{'exp_duration'};
+    my $t_opt = (defined $exp_duration) ? "-t $exp_duration" : "";
+
+    my $main_opts = "-y $t_opt";
+
+    my $bitrate_opt = "-b $streaming_bitrate";
+    my $stream_opts .= " -ac $streaming_achan -ar $streaming_arate";
+
+
+    my $all_opts = "$main_opts $stream_opts"; 
+
+    my $ffmpeg_cmd;
+
+    my $iaudio_filename_gsdlenv = $self->gsdlhome_independent($iaudio_filename);
+    my $mp3_filename_gsdlenv = $self->gsdlhome_independent($mp3_filename);
+
+    
+    $ffmpeg_cmd = "ffmpeg -i \"$iaudio_filename_gsdlenv\" $all_opts -y \"$mp3_filename_gsdlenv\"";
+
+    return ($ffmpeg_cmd,$mp3_filename,$mp3_file);
+}
+
+
+
+1;
Index: gs2-extensions/video-and-audio/trunk/src/perllib/plugins/AudioPlugin.pm
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/perllib/plugins/AudioPlugin.pm	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/perllib/plugins/AudioPlugin.pm	(revision 23036)
@@ -0,0 +1,365 @@
+######################################################################
+#
+# AudioPlugin.pm -- plugin for processing video largely based on ImagePlug
+# A component of the Greenstone digital library software
+# from the New Zealand Digital Library Project at the 
+# University of Waikato, New Zealand.
+#
+# Copyright (C) 1999 New Zealand Digital Library Project
+#
+# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+###########################################################################
+
+package AudioPlugin;
+
+use strict;
+no strict 'refs'; # allow filehandles to be variables and viceversa
+no strict 'subs';
+
+use gsprintf;
+
+use MultimediaPlugin;
+use AudioConverter;
+
+sub BEGIN {
+    @AudioPlugin::ISA = ('MultimediaPlugin', 'AudioConverter');
+}
+
+
+my $arguments =
+    [ { 'name' => "process_exp",
+	'desc' => "{BasePlugin.process_exp}",
+	'type' => "regexp",
+	'deft' => &get_default_process_exp(),
+	'reqd' => "no" },
+      { 'name' => "converttotype",
+	'desc' => "{AudioPlugin.converttotype}",
+	'type' => "string",
+	'deft' => "",
+	'reqd' => "no" },
+      { 'name' => "converttobitrate",
+	'desc' => "{AudioPlugin.converttobitrate}",
+	'type' => "string",
+	'deft' => "128k",
+	'reqd' => "no" },
+      { 'name' => "streamingbitrate",
+	'desc' => "{AudioPlugin.streamingbitrate}",
+	'type' => "string",
+	'deft' => "200k",
+	'reqd' => "no" } ];
+
+my $options = { 'name'     => "AudioPlugin",
+		'desc'     => "{AudioPlugin.desc}",
+		'abstract' => "no",
+		'inherits' => "yes",
+		'args'     => $arguments };
+
+
+sub new {
+    my ($class) = shift (@_);
+    my ($pluginlist,$inputargs,$hashArgOptLists) = @_;
+    push(@$pluginlist, $class);
+
+    push(@{$hashArgOptLists->{"ArgList"}},@{$arguments});
+    push(@{$hashArgOptLists->{"OptList"}},$options);
+
+    new AudioConverter($pluginlist, $inputargs, $hashArgOptLists);
+    my $self = new MultimediaPlugin($pluginlist, $inputargs, $hashArgOptLists);
+
+
+    return bless $self, $class;
+}
+
+
+sub begin {
+    my $self = shift (@_);
+    my ($pluginfo, $base_dir, $processor, $maxdocs) = @_;
+
+    $self->SUPER::begin(@_);
+    $self->AudioConverter::begin(@_); 
+}
+
+
+sub init {
+    my $self = shift (@_);
+    my ($verbosity, $outhandle, $failhandle) = @_;
+
+    $self->SUPER::init(@_);
+    $self->AudioConverter::init(@_); 
+}
+
+
+sub get_default_process_exp {
+    my $self = shift (@_);
+
+    return q^(?i)\.(mp3|au|aiff?|aifc|wav|ogg|flac|shn)$^; 
+}
+
+
+
+# Create the keyframes, thumbnail and screenview images, and discover
+# the Video's size, width, and height using the ffmpeg utility.
+
+sub run_convert {
+    my $self = shift (@_);
+    my $base_dir = shift (@_);
+    my $filename = shift (@_);   # filename with full path
+    my $file = shift (@_);       # filename without path
+    my $doc_obj = shift (@_);
+
+    my $section = $doc_obj->get_top_section();
+    
+    my $verbosity = $self->{'verbosity'};
+    my $outhandle = $self->{'outhandle'};
+
+    # check the filename is okay
+    return 0 if ($file eq "" || $filename eq "");
+
+    my $minimumsize = $self->{'minimumsize'};
+    if (defined $minimumsize && (-s $filename < $minimumsize)) {
+        print $outhandle "AudioPlugin: \"$filename\" too small, skipping\n"
+	    if ($verbosity > 1);
+    }
+
+    my ($aduration,$asize,$atype,$afreq,$achan,$arate) 
+	= &AudioConverter::identify($filename, $outhandle, $verbosity);
+
+    if ($aduration eq "N/A") {
+	print $outhandle "Unable to determine duration of $file\n";
+	$aduration = undef;
+    }
+
+    my ($dur_hour,$dur_min,$dur_sec) 
+	= ($aduration =~ m/(\d+):(\d+):(\d+\.\d+)/);
+
+    my $total_dur_secs = undef;
+
+    if (defined $dur_hour && defined $dur_min && defined *dur_sec) {
+	$total_dur_secs = $dur_hour*3600 + $dur_min*60 + $dur_sec;
+    }
+
+
+    # Convert the audio to a new type (if required).
+    my $converttotype = $self->{'converttotype'};
+    my $converttosize = $self->{'converttosize'};
+
+    # shorten duration prcessed for experimentation purposes
+    my $exp_duration = undef;
+    
+    my $excerpt_duration = $self->{'excerpt_duration'};
+
+    if ((defined $excerpt_duration) && ($excerpt_duration ne "")) {
+	$exp_duration = $excerpt_duration;
+	my ($hh,$mm,$ss,$ms) = ($exp_duration =~ m/^(\d\d):(\d\d):(\d\d)\.?(\d\d)?/);
+	my $excerpt_dur_in_secs = $hh * 3600 + $mm * 60 + $ss;
+
+	if (defined $total_dur_secs) {
+	    if ($excerpt_dur_in_secs > $total_dur_secs) {
+		# clip is already shorter than requested video excerpt duration
+		# set exp_duration back to undefined
+		$exp_duration = undef;
+	    }
+	}
+	else {
+	    $exp_duration = undef;
+	}
+    }
+
+
+    if (defined $exp_duration)
+    {
+	print $outhandle "Only encoding first $exp_duration of video.\n";
+	$self->{'exp_duration'} = $exp_duration;
+    }
+
+    my $ascii_only_filenames = $self->{'use_ascii_only_filenames'};
+
+    $self->init_cache_for_file($filename);
+
+
+    my $originalfilename = undef;
+    my $type = "unknown";
+
+    my $output_dir = $self->{'cached_dir'};
+    my $iaudio_root = $self->{'cached_file_root'};
+
+    my $convertto_regenerated = 0;
+    if (($converttotype ne "" && $filename !~ m/$converttotype$/i)) {
+
+	$originalfilename = $filename;
+
+	my $convertto_command = $self->audio_convertto_cmd($filename,$converttotype);
+
+	my $convertto_result;
+	my $convertto_error;
+	
+	my $convertto_options = { @{$self->{'ffmpeg_monitor'}},
+				  'message_prefix' => "Convert to",
+				  'message' => "Converting audio to $converttotype" }; 
+
+	($convertto_regenerated,$convertto_result,$convertto_error)
+	    = $self->run_cached_general_cmd($convertto_command,$filename,$convertto_options);
+						    
+	$type = $converttotype;
+    }
+    
+
+    # Add the audio metadata
+
+    my $file_unicode = pack("U0C*", map { ord($_) } split(//,$file)); # force explicitly to unicode
+
+    $file_unicode =~ s/\x{2018}|\x{2019}|\x{201C}|\x{201D}//g; # remove smart quotes as cause problem in URL for video server
+    $file_unicode =~ s/\x{2013}/\-/g; # change en-dash to '-' as again causes problems for video server
+
+##    print STDERR "**** file metadata = ", &gsprintf::debug_unicode_string($file_unicode), "\n";
+
+    
+    # split filename on char if specified
+    if ($file_unicode =~ m/\-/) {
+
+	my @file_split = split(/\s*\-\s*/,$file_unicode);
+	my $creator = shift @file_split;
+	my $title = shift @file_split;
+
+	$title =~ s/\..*?$//;
+	$title =~ s/^(.)/\u$1/;
+
+	$doc_obj->add_utf8_metadata($section,"Title",$title);
+
+	my @creator_split = split(/\s+and\s+/,$creator);
+	foreach my $c (@creator_split) {
+	    $doc_obj->add_utf8_metadata($section,"Creator",$c);
+	}
+    }
+
+    $file = $file_unicode;
+    my $filemeta = $self->filename_to_utf8_metadata($file);
+    my $filemeta_url_safe = $self->url_safe($filemeta);
+
+    $doc_obj->add_utf8_metadata ($section, "Audio", $filemeta_url_safe);
+
+    # Also want to set filename as 'Source' metadata to be
+    # consistent with other plugins
+    $doc_obj->add_utf8_metadata ($section, "Source", $filemeta_url_safe);
+
+
+    if ($atype ne " ") {
+	$type = $atype;
+    }
+    
+    $doc_obj->add_metadata ($section, "FileFormat", $type);
+    $doc_obj->add_metadata ($section, "FileSize",   $asize);
+
+    $doc_obj->add_metadata ($section, "AudioType",     $atype);
+    $doc_obj->add_metadata ($section, "AudioDuration", $aduration);
+
+    $doc_obj->add_metadata ($section, "AudioFreq",     $afreq);
+    $doc_obj->add_metadata ($section, "AudioChannels", $achan);
+    $doc_obj->add_metadata ($section, "AudioRate",     $arate);
+
+    $doc_obj->add_utf8_metadata ($section, "srclink", 
+			    "<a href=\"_httpprefix_/collect/[collection]/index/assoc/[assocfilepath]/[Audio]\">");
+    $doc_obj->add_utf8_metadata ($section, "/srclink", "</a>");
+    $doc_obj->add_metadata ($section, "srcicon", "[AudioType]");
+
+    # Add the image as an associated file
+    $doc_obj->associate_file($filename,$file,"audio/$type",$section);
+
+
+
+
+    my $streamable_regenerated = 0;
+    my $optionally_run_general_cmd 
+	= ($convertto_regenerated) ? "regenerate_general_cmd" : "run_cached_general_cmd";
+
+    if ($self->{'enable_streaming'}) {
+
+	# Even if the original file or 'converttotype' is set to MP3
+	# we'll still go ahead and generate this MP3 as it might very
+	# well have different settings (such as a lower sample rate)
+
+	my $streaming_bitrate = $self->{'streamingbitrate'};
+	my $streaming_size    = $self->{'streamingsize'};
+	
+	
+	my ($stream_cmd,$mp3_filename,$mp3_file) 
+	    = $self->audio_stream_cmd($originalfilename || $filename,
+				      $streaming_bitrate, $streaming_size);
+	
+	
+	my $streamable_options = { @{$self->{'ffmpeg_monitor'}},
+				   'message_prefix' => "Stream",
+				   'message' => "Generating streamable audio: $mp3_file" }; 
+	
+	my $streamable_result;
+	my $streamable_had_error;
+	($streamable_regenerated,$streamable_result,$streamable_had_error)
+	    = $self->$optionally_run_general_cmd($stream_cmd,$mp3_filename,$streamable_options);
+	
+	if (!$streamable_had_error) {
+	    
+	    my $streamable_url = $mp3_file;
+	    my $streamable_url_safe = $self->url_safe($streamable_url);
+	    
+	    $doc_obj->add_utf8_metadata ($section, "streamableaudio", $streamable_url_safe);
+	    $doc_obj->associate_file($mp3_filename,$mp3_file,"audio/mpeg",
+				     $section);
+	}
+	
+	# The following aren't currently used
+	
+	$self->{'mp3_file'} = $mp3_file;
+	$self->{'mp3_filename'} = $mp3_filename;
+    }
+
+
+    return $type;
+}
+
+
+
+
+sub read_into_doc_obj {
+    my $self = shift (@_);  
+    my ($pluginfo, $base_dir, $file, $block_hash, $metadata, $processor, $maxdocs, $total_count, $gli) = @_;
+
+    $self->{'media_type'} = "audio";
+
+    my ($rv,$doc_obj) = $self->SUPER::read_into_doc_obj(@_);
+
+    if ($rv != 1) {
+	return ($rv,$doc_obj);
+    }
+    
+    $self->{'media_type'} = undef;
+
+    return ($rv,$doc_obj);
+}
+
+
+
+1;
+
+
+
+
+
+
+
+
+
+
+
Index: gs2-extensions/video-and-audio/trunk/src/perllib/plugins/MultimediaConverter.pm
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/perllib/plugins/MultimediaConverter.pm	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/perllib/plugins/MultimediaConverter.pm	(revision 23036)
@@ -0,0 +1,308 @@
+###########################################################################
+#
+# MultimediaConverter - helper plugin that does audio and video conversion using ffmpeg
+#
+# A component of the Greenstone digital library software
+# from the New Zealand Digital Library Project at the 
+# University of Waikato, New Zealand.
+#
+# Copyright (C) 2008 New Zealand Digital Library Project
+#
+# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+###########################################################################
+package MultimediaConverter;
+
+use BaseMediaConverter;
+
+
+use strict;
+no strict 'refs'; # allow filehandles to be variables and viceversa
+
+use gsprintf 'gsprintf';
+
+BEGIN {
+    @MultimediaConverter::ISA = ('BaseMediaConverter');
+}
+
+my $arguments = [
+      { 'name' => "converttotype",
+	'desc' => "{ImageConverter.converttotype}",
+	'type' => "string",
+	'deft' => "",
+	'reqd' => "no" },
+      { 'name' => "minimumsize",
+	'desc' => "{ImageConverter.minimumsize}",
+	'type' => "int",
+	'deft' => "100",
+	'range' => "1,",
+	'reqd' => "no" },
+		 ];
+
+my $options = { 'name' => "MultimediaConverter",
+		'desc' => "{MultimediaConverter.desc}",
+		'abstract' => "yes",
+		'inherits' => "yes",
+		'args' => $arguments };
+
+sub new {
+    my ($class) = shift (@_);
+    my ($pluginlist,$inputargs,$hashArgOptLists) = @_;
+    push(@$pluginlist, $class);
+
+    push(@{$hashArgOptLists->{"ArgList"}},@{$arguments});
+    push(@{$hashArgOptLists->{"OptList"}},$options);
+
+    my $self = new BaseMediaConverter($pluginlist, $inputargs, $hashArgOptLists, 1);
+
+
+    return bless $self, $class;
+
+}
+
+
+
+# needs to be called after BasePlugin init, so that outhandle is set up.
+sub init {
+    my $self = shift(@_);
+
+    my $outhandle = $self->{'outhandle'};
+
+    $self->{'tmp_file_paths'} = ();
+
+    # Check that ffmpeg is installed and available on the path 
+    my $multimedia_conversion_available = 1;
+    my $no_multimedia_conversion_reason = "";
+
+    # None of this works very well on Windows 95/98...
+    if ($ENV{'GSDLOS'} eq "windows" && !Win32::IsWinNT()) {
+	$multimedia_conversion_available = 0;
+	$no_multimedia_conversion_reason = "win95notsupported";
+    } else {
+
+	my $result = `ffmpeg -h 2>&1`;
+
+	if (!defined $result || $result !~ m/^FFmpeg version/m) {
+	    $self->{'ffmpeg_installed'} = 0;
+	    print $outhandle $result;
+	    $multimedia_conversion_available = 0;
+	    $no_multimedia_conversion_reason = "ffmpegnotinstalled";
+	}
+	else {
+	    $self->{'ffmpeg_installed'} = 1;
+	}
+    }
+
+    $self->{'multimedia_conversion_available'} = $multimedia_conversion_available;
+    $self->{'no_multimedia_conversion_reason'} = $no_multimedia_conversion_reason;
+
+    if ($self->{'multimedia_conversion_available'} == 0) {
+	&gsprintf($outhandle, "MultimediaConverter: {MultimediaConverter.noconversionavailable} ({MultimediaConverter.".$self->{'no_multimedia_conversion_reason'}."})\n");
+    }
+       
+}
+	
+
+
+
+sub identify { 
+    my ($filename, $outhandle, $verbosity) = @_;
+
+    die "MultimediaConvert::identify() needs to be defined by inheriting plugin";
+}
+
+
+
+sub init_cache_for_file {
+    my $self = shift(@_);
+
+    my ($media_filename) = @_;
+
+    $self->SUPER::init_cache_for_file($media_filename);
+
+
+    # This should probably be replaced with Anu's work that replaced
+    # non-ASCII chars with URL encodings
+
+    my $ascii_only_filenames = $self->{'use_ascii_only_filenams'};
+
+    if (defined $ascii_only_filenames && ($ascii_only_filenames)) {
+	my $file_root = $self->{'cached_file_root'};
+	$self->{'cached_file_root'} = ascii_only_filename($file_root);
+    }
+				  
+    my @ffmpeg_monitor   = ( 'monitor_init'   , "MultimediaConverter::unbuffered_monitor_init",
+			     'monitor_line'   , "MultimediaConverter::ffmpeg_monitor_line",
+			     'monitor_deinit' , "MultimediaConverter::unbuffered_monitor_deinit" );
+					
+					
+    $self->{'ffmpeg_monitor'} = \@ffmpeg_monitor;
+
+  my @handbrake_monitor   = ( 'monitor_init'   , "MultimediaConverter::unbuffered_monitor_init",
+			     'monitor_line'   , "MultimediaConverter::handbrake_monitor_line",
+			     'monitor_deinit' , "MultimediaConverter::unbuffered_monitor_deinit" );
+					
+					
+    $self->{'handbrake_monitor'} = \@handbrake_monitor;
+
+}
+
+
+
+
+sub ascii_only_filename
+{
+    my ($file) = @_;
+
+    my $file_unicode = pack("U0C*", map { ord($_) } split(//,$file)); # force explicitly to unicode
+
+    my @ascii_only_chars 
+	= map { $_ >= 128 # if non-ascii
+		    ? "" 
+		    :  chr($_) } unpack("U*", $file_unicode); # unpack Unicode characters
+
+    my $ascii_file = join("",@ascii_only_chars);
+
+    if ($ascii_file eq "") {
+	print STDERR "Warning: filename includes no ASCII characters\n";
+	print STDERR "         Keeping as original filename\n";
+	$ascii_file = $file;
+    }
+
+    return $ascii_file;
+}
+
+
+sub remove_difficult_chars
+{
+    my $self = shift @_;
+
+    my ($file) = @_;
+
+    # remove problematic characters from filename that make using it in a URL difficult
+
+    my $file_unicode = pack("U0C*", map { ord($_) } split(//,$file)); # force explicitly to unicode
+
+    my $url = $file_unicode;
+    $url =~ s/\x{2018}|\x{2019}|\x{201C}|\x{201D}//g; # remove smart quotes as cause problem in URL for streaming web server
+    $url =~ s/\x{2013}/\-/g; # change en-dash to '-' as again causes problems for streaming web server
+    
+    return $url;
+}
+
+
+sub url_safe
+{
+    my $self = shift @_;
+
+    my ($file) = @_;
+    
+    my @url_utf8_chars 
+	= map { $_ >= 128 # if non-ascii
+		    ? "%" . sprintf("%02X", $_) 
+		    :  chr($_) } unpack("U*", $file); # unpack Unicode characters
+
+    my $url = join("",@url_utf8_chars);
+
+
+    return $url;
+}
+
+
+
+sub gsdlhome_independent
+{
+    my $self = shift @_;
+    my ($filename) = @_;
+
+    my $gsdlhome = $ENV{'GSDLHOME'};
+    $gsdlhome = &util::filename_to_regex($gsdlhome);
+
+    my $filename_gsdlenv = $filename;
+
+    if ($ENV{'GSDLOS'} =~ /^windows$/i) {
+	$filename_gsdlenv =~ s@^$gsdlhome@%GSDLHOME%@;
+    }
+    else {
+	$filename_gsdlenv =~ s@^$gsdlhome@\$GSDLHOME@;
+    }
+
+    return $filename_gsdlenv;
+}
+
+
+sub unbuffered_monitor_init
+{
+    my $saved_record_sep = $/;
+    $/ = "\r";
+
+    my $saved_buffer_len = $|;
+    $| = 1;
+
+    my $saved_rec = { 'saved_record_sep' => $saved_record_sep,
+		      'saved_buffer_len' => $saved_buffer_len };
+
+    return $saved_rec;
+}
+
+
+
+sub unbuffered_monitor_deinit
+{
+    my ($saved_rec) = @_;
+
+    my $saved_record_sep = $saved_rec->{'saved_record_sep'};
+    my $saved_buffer_len = $saved_rec->{'saved_buffer_len'};
+
+    $/ = $saved_record_sep;
+    $| = $saved_buffer_len;
+}
+
+
+sub ffmpeg_monitor_line
+{
+    my ($line) = @_;
+    
+    my $had_error = 0;
+    my $generate_dot = 0;
+	
+    if ($line =~ m/^frame=/) 
+    {
+	$generate_dot = 1;
+    }
+
+    return ($had_error,$generate_dot);
+}
+
+
+sub handbrake_monitor_line
+{
+    my ($line) = @_;
+    
+    my $had_error = 0;
+    my $generate_dot = 0;
+	
+    if ($line =~ m/^Encoding:/) 
+    {
+	    print STDERR $line;
+    }
+	else {
+		$generate_dot = 1;
+	}
+
+    return ($had_error,$generate_dot);
+}
+
+1;
Index: gs2-extensions/video-and-audio/trunk/src/perllib/plugins/MultimediaPlugin.pm
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/perllib/plugins/MultimediaPlugin.pm	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/perllib/plugins/MultimediaPlugin.pm	(revision 23036)
@@ -0,0 +1,290 @@
+######################################################################
+#
+# MultimediaPlugin.pm -- internal plugin to support processing audio and video
+# A component of the Greenstone digital library software
+# from the New Zealand Digital Library Project at the 
+# University of Waikato, New Zealand.
+#
+# Copyright (C) 1999 New Zealand Digital Library Project
+#
+# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+###########################################################################
+
+# -- Largely modeled on how ImagePlugin works 
+
+package MultimediaPlugin;
+
+use strict;
+no strict 'refs'; # allow filehandles to be variables and viceversa
+no strict 'subs';
+
+use gsprintf;
+
+use BasePlugin;
+
+sub BEGIN {
+    @MultimediaPlugin::ISA = ('BasePlugin');
+
+    if (!defined $ENV{'GEXTVIDEO'}) {
+	print STDERR "Warning: Greenstone Video extension not detected.\n";
+    }
+
+}
+
+
+
+ # do later the description
+my $arguments =
+    [ { 'name' => "use_ascii_only_filenames",
+	'desc' => "{VideoPlug.use_ascii_only_filenames}",
+	'type' => "flag",
+	'reqd' => "no" },
+      { 'name' => "excerpt_duration",
+	'desc' => "{MultimediaPlug.excerpt_duration}",
+	'type' => "string",
+	'deft' => "",
+	'reqd' => "no" },
+      { 'name' => "converttotype",
+	'desc' => "{MultimediaPlug.converttotype}",
+	'type' => "string",
+	'deft' => "",
+	'reqd' => "no" },
+	  { 'name' => "enable_streaming",
+	'desc' => "{MultimediaPlug.enable_streaming}",
+	'type' => "enum",
+	'list' => [{'name' => "disabled", 'desc' => "Do not create any video file optimised for streaming over the Internet."},
+		       {'name' => "flv", 'desc' => "Uses the FLV format for streaming media. Better to target old computers."},
+		       {'name' => "mp4", 'desc' => "Uses the MP4 container with H264 and AAC codecs. Better quality at very low bitrates but more ressources intensive."}],
+	'deft' => "disabled",
+	'reqd' => "no" }];
+
+my $options = { 'name'     => "MultimediaPlugin",
+		'desc'     => "{MultimediaPlug.desc}",
+		'abstract' => "yes",
+		'inherits' => "yes",
+		'args'     => $arguments };
+
+
+sub new {
+    my ($class) = shift (@_);
+    my ($pluginlist,$inputargs,$hashArgOptLists) = @_;
+    push(@$pluginlist, $class);
+
+    push(@{$hashArgOptLists->{"ArgList"}},@{$arguments});
+    push(@{$hashArgOptLists->{"OptList"}},$options);
+
+    my $self = new BasePlugin($pluginlist, $inputargs, $hashArgOptLists);
+
+    # Check that ffmpeg is installed and available on the path (except for Windows 95/98)
+    # This test is "inherited" from ImagePlugin where ImageMagick support
+    # for building cannot be used on these two versions of Windows as they
+    # do not have enough flexablity in the use of stdout and stderr to
+    # support how our code works.  It seems reasonable to assume the
+    # same is true for MultimediaPlugin work using ffmpeg.
+
+    if (($ENV{'GSDLOS'} ne "windows" || Win32::IsWinNT())) {
+
+	my $result = `ffmpeg -h 2>&1`;
+
+	if (!defined $result || $result !~ m/^FFmpeg version/m) {
+	    $self->{'ffmpeg_installed'} = 0;
+	    if (defined $result) {
+		print STDERR $result;
+	    }
+	    else {
+		print STDERR "Unable to find ffmpeg\n";
+	    }
+	}
+	else {
+	    $self->{'ffmpeg_installed'} = 1;
+	}
+
+    }
+
+    return bless $self, $class;
+}
+
+
+
+sub begin {
+    my $self = shift (@_);
+    my ($pluginfo, $base_dir, $processor, $maxdocs) = @_;
+
+    $self->SUPER::begin(@_);
+
+    $self->{'base_dir'} = $base_dir;
+}
+
+
+sub init {
+    my $self = shift (@_);
+    my ($verbosity, $outhandle, $failhandle) = @_;
+
+    $self->SUPER::init(@_);
+}
+
+# Want to hash on doc.xml rather than original file which in the case of 
+# audio and video can be HUGE
+
+sub get_oid_hash_type {
+
+    my $self = shift (@_);
+
+    return "hash_on_ga_xml";
+}
+
+
+
+# Create the keyframes, thumbnail and screenview images, and discover
+# the Video's size, width, and height using the ffmpeg utility.
+
+sub run_convert {
+    my $self = shift (@_);
+
+    die "$self The inheriting class must implement run_convert method";
+}
+
+
+sub read_into_doc_obj {
+    my $self = shift (@_);  
+    my ($pluginfo, $base_dir, $file, $block_hash, $metadata, $processor, $maxdocs, $total_count, $gli) = @_;
+
+    my $outhandle = $self->{'outhandle'};
+
+    # should we move this to read? What about secondary plugins?
+    print STDERR "<Processing n='$file' p='$self->{'plugin_type'}'>\n" if ($gli);
+    print $outhandle "$self->{'plugin_type'} processing $file\n"
+	    if $self->{'verbosity'} > 1;
+
+    my ($filename_full_path, $filename_no_path) = &util::get_full_filenames($base_dir, $file);
+
+    # create a new document
+    my $doc_obj = new doc ($filename_full_path, "indexed_doc", $self->{'file_rename_method'});
+
+
+    my $top_section = $doc_obj->get_top_section();
+
+
+    $doc_obj->add_utf8_metadata($top_section, "Plugin", "$self->{'plugin_type'}");
+    $doc_obj->add_utf8_metadata($top_section, "FileSize", (-s $filename_full_path));
+ 
+    # sets the UTF8 filename (Source) for display and sets the url ref to URL encoded version
+    # of the UTF8 filename (SourceFile) for generated files
+    $self->set_Source_metadata($doc_obj, $filename_no_path);
+
+
+    # plugin specific stuff - what args do we need here??
+    unless (defined ($self->process($pluginfo, $base_dir, $file, $metadata, $doc_obj, $gli))) {
+	print STDERR "<ProcessingError n='$file'>\n" if ($gli);
+	return (-1,undef);
+    }
+    
+    # include any metadata passed in from previous plugins 
+    # note that this metadata is associated with the top level section
+    my $section = $doc_obj->get_top_section();
+    # can we merge these two methods??
+    $self->add_associated_files($doc_obj, $filename_full_path);
+    $self->extra_metadata ($doc_obj, $section, $metadata);
+    $self->auto_extract_metadata($doc_obj);
+
+    # if we haven't found any Title so far, assign one
+    # this was shifted to here from inside read()
+    $self->title_fallback($doc_obj,$section,$filename_no_path);
+    
+    $self->add_OID($doc_obj);
+
+    $self->post_process_doc_obj($pluginfo, $base_dir, $file, $metadata, $doc_obj, $gli);
+
+    return (1,$doc_obj);
+}
+
+sub add_dummy_text {
+    my $self = shift(@_);
+    my ($doc_obj, $section) = @_;
+
+    # add NoText metadata so we can hide this dummy text in format statements
+    $doc_obj->add_metadata($section, "NoText", "1");
+    $doc_obj->add_text($section, &gsprintf::lookup_string("{BasePlugin.dummy_text}",1));
+    
+}
+
+
+
+
+# do plugin specific processing of doc_obj
+sub process {
+    my $self = shift (@_);
+    # options??
+    my ($pluginfo, $base_dir, $file, $metadata, $doc_obj, $gli) = @_;
+
+    my $outhandle = $self->{'outhandle'};
+
+    my ($filename_full_path, $filename_no_path) = &util::get_full_filenames($base_dir, $file);
+
+
+    if ($self->{'ffmpeg_installed'}) {
+	my $utf8_filename_no_path = $self->filepath_to_utf8($filename_no_path);
+	my $url_encoded_filename = &util::rename_file($utf8_filename_no_path, $self->{'file_rename_method'});
+
+
+	#run convert to get the thumbnail and extract size and type info
+	my $result = $self->run_convert($base_dir, $filename_full_path, 
+					$url_encoded_filename, $doc_obj);
+	
+	if (!defined $result) {
+	    if ($gli) {
+		print STDERR "<ProcessingError n='$file'>\n";
+	    }
+	    print $outhandle "MultimediaPlugin: couldn't process \"$filename_full_path\"\n";
+	    return (-1,undef); # error during processing
+	}
+    } 
+    else {
+	if ($gli) {
+	    &gsprintf(STDERR, "<Warning p='MultimediaPlugin' r='{MultimediaConverter.noconversionavailable}: {MultimediaConverter.".$self->{'no_multimedia_conversion_reason'}."}'>");
+	}
+	# all we do is add the original video file as an associated file, and set up srclink etc
+	my $assoc_file = $doc_obj->get_assocfile_from_sourcefile();
+	my $section = $doc_obj->get_top_section();
+
+	$doc_obj->associate_file($filename_full_path, $assoc_file, "", $section);
+
+	$doc_obj->add_metadata ($section, "srclink", "<a href=\"_httpprefix_/collect/[collection]/index/assoc/[assocfilepath]/[SourceFile]\">");
+	$doc_obj->add_metadata ($section, "/srclink", "</a>");
+	$doc_obj->add_metadata ($section, "srcicon", "<img src=\"_httpprefix_/collect/[collection]/index/assoc/[assocfilepath]/[SourceFile]\" width=\"100\">");
+
+    }
+    #we have no text - adds dummy text and NoText metadata
+    $self->add_dummy_text($doc_obj, $doc_obj->get_top_section());
+
+    return 1;
+
+}
+
+
+
+1;
+
+
+
+
+
+
+
+
+
+
+
Index: gs2-extensions/video-and-audio/trunk/src/perllib/plugins/TimedHTMLPlugin.pm
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/perllib/plugins/TimedHTMLPlugin.pm	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/perllib/plugins/TimedHTMLPlugin.pm	(revision 23036)
@@ -0,0 +1,927 @@
+###########################################################################
+#
+# TimedHTMLPlug.pm -- 
+#
+# A component of the Greenstone digital library software
+# from the New Zealand Digital Library Project at the 
+# University of Waikato, New Zealand.
+#
+# Copyright (C) 1999 New Zealand Digital Library Project
+#
+# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+###########################################################################
+
+
+package TimedHTMLPlugin;
+
+use HTMLPlugin;
+use VideoConverter;
+
+sub BEGIN {
+    @TimedHTMLPlugin::ISA = ('HTMLPlugin', 'VideoConverter');
+}
+
+
+use strict; # every perl program should have this!
+no strict 'refs'; # make an exception so we can use variables as filehandles
+
+my $arguments =
+    [ 
+      { 'name' => "video_excerpt_duration",
+	'desc' => "{VideoPlug.video_excerpt_duration}",
+	'type' => "string",
+	'reqd' => "no" }
+      ];
+
+
+
+
+my $options = { 'name'     => "TimedHTMLPlugin",
+		'desc'     => "{TimedHTMLPlugin.desc}",
+		'abstract' => "no",
+		'inherits' => "yes",
+	        'args'     => $arguments };
+
+
+sub new {
+    my ($class) = shift (@_);
+    my ($pluginlist,$inputargs,$hashArgOptLists) = @_;
+    push(@$pluginlist, $class);
+    
+    if(defined $arguments){ push(@{$hashArgOptLists->{"ArgList"}},@{$arguments});}
+    if(defined $options) { push(@{$hashArgOptLists->{"OptList"}},$options)};
+   
+    new VideoConverter($pluginlist, $inputargs, $hashArgOptLists);
+    
+    my $self = (defined $hashArgOptLists) 
+	? new HTMLPlugin($pluginlist,$inputargs,$hashArgOptLists)
+	: new HTMLPlugin($pluginlist,$inputargs);
+ 
+    my $collect_dir = $ENV{'GSDLCOLLECTDIR'};
+
+    my $collect_bin = &util::filename_cat($collect_dir,"bin");
+    my $collect_script = &util::filename_cat($collect_bin,"script");
+    
+    $ENV{'PATH'} .= ":" if (defined $ENV{'PATH'});
+    $ENV{'PATH'} .= "$collect_bin:$collect_script";
+
+    return bless $self, $class;
+}
+
+sub begin {
+    my $self = shift (@_);
+    my ($pluginfo, $base_dir, $processor, $maxdocs) = @_;
+
+    $self->SUPER::begin(@_);
+    $self->VideoConverter::begin(@_); 
+}
+
+
+sub init {
+    my $self = shift (@_);
+    my ($verbosity, $outhandle, $failhandle) = @_;
+
+    $self->SUPER::init(@_);
+    $self->VideoConverter::init(@_);
+}
+
+
+sub html_to_text
+{
+    my ($self, $text) = @_;
+
+    $text =~ s/<\/?[^>]*>//g;
+
+    $text =~ s/&nbsp;/ /g;
+
+    $text =~ s/\s+/ /gs;
+    $text =~ s/^\s+//;
+    $text =~ s/\s+$//;
+
+    return $text;
+}
+
+
+sub extract_metadata_table
+{
+    my ($self, $textref) = @_;
+
+    my $metatable_store = {};
+
+    my $metatable_re = "<table([^>]*)>(.*?<p[^>]+class=\"?Metadata\"?[^>]*>.*?)<\\/table>";
+
+    if ($$textref =~ m/$metatable_re/gsi) {
+	my $metatable_attr = $1;
+	my $metatable_body = $2;
+
+	my @tr_list = ($metatable_body =~ m/(<tr[^>]*>.*?<\/tr>)/gsi);
+
+	my @filtered_tr_list = ();
+
+	foreach my $tr (@tr_list) {
+
+	    my $in_filtered_tr = 1;
+
+	    my @td_list = ($tr =~ m/<td[^>]*>(.*?)<\/td>/gsi);
+	    my $td_list_num = scalar(@td_list);
+	    
+	    if ($td_list_num == 2) {
+		my $meta_name  = $self->html_to_text($td_list[0]);
+		my $meta_value = $self->html_to_text($td_list[1]);
+		
+		$meta_name =~ s/\s*//g;
+		
+
+		if ($meta_value =~ m/^(\s*|\?+)$/) {
+		    $in_filtered_tr = 0;
+		}
+		else {
+		    my $meta_name_label = $meta_name;
+		    $meta_name_label =~ s/\s+//g;
+
+		    $metatable_store->{$meta_name_label} = $meta_value;
+
+		    if ($meta_name_label =~ m/^GS\./) {
+			# even though we want to store this as metadata, we
+			# don't want this going into table presented at top of document
+			$in_filtered_tr = 0; 
+		    }
+
+		}
+	    }
+	    else {
+		print STDERR "Warning: Metadata table does not consist of two cells (metadata name : metadata value)\n";
+		print STDERR "         Skipping\n";
+	    }
+
+
+	    if ($in_filtered_tr) {
+		push(@filtered_tr_list,$tr);
+	    }
+	}
+
+	my $filtered_tr_block = join("\n",@filtered_tr_list);
+	my $filtered_table = "<table$metatable_attr>$filtered_tr_block</table>";
+
+
+	### my $metatable_re = "<table[^>]*>.*?<p class=\"?Metadata\"?>.*?<\\/table>";
+
+###	print STDERR "*** filtered table = $filtered_table\n";
+
+	$$textref =~ s/$metatable_re/$filtered_table/si;
+	
+    }
+
+    return $metatable_store;
+
+}
+
+
+
+
+
+my $only_num = 0;
+my $only_limit = 100;
+
+sub hms_to_secs
+{
+    my $self = shift @_;
+    my ($hh,$mm,$ss) = @_;
+
+    return $hh*3600 + $mm*60 + $ss;
+}
+
+
+
+sub timed_event
+{
+    my $self = shift (@_);
+
+    my ($front, $tape, $hh, $mm, $ss, $back) = @_;
+
+    my $time_encoded = "$hh:$mm:$ss";
+    my $time_encoded_file = "$hh$mm$ss";
+
+    print STDERR "***** time endocde = $time_encoded\n";
+
+    my $time_pos = $self->{'time_pos'};
+    my $time_num = scalar(@{$self->{'time_seq'}});
+
+####    my $videoconvert = $self->{'videoconvert'};
+
+    # excerpt from tim_pos -> time_pos + 2;
+    my $adv_time_pos = $time_pos+2;
+    $adv_time_pos = $time_num-1 if ($adv_time_pos>=$time_num);
+
+    my $adv_time_elem = $self->{'time_seq'}->[$adv_time_pos];
+
+#    print STDERR "*** ", substr($adv_time_elem,0,800), "\n";
+
+    my ($atape,$ah,$am,$as) = ($adv_time_elem =~ m/(T\d+:) ?(\d\d):(\d\d):(\d\d)(?:\:\d\d)?/);
+
+    my $excerpt_len = undef;
+    my $time_in_secs     = $self->hms_to_secs($hh,$mm,$ss);
+
+    if (!defined $tape) {
+	if (defined $self->{'ptape'}) {
+	    $tape = $self->{'ptape'};
+	}
+	else {
+	    print STDERR "Warning: First time offset does not define tape side\n";
+	    print STDERR "         Defaulting to 1\n";
+	    $tape = "T1:";
+	}
+    }
+
+    if (!defined $atape) {
+	$atape = "T1";
+    }
+
+#    print STDERR "**** atape = $atape\n";
+#    print STDERR "*** tape = $tape\n";
+
+    if ($atape eq $tape) {
+	my $adv_time_in_secs = $self->hms_to_secs($ah,$am,$as);
+
+	$excerpt_len = $adv_time_in_secs - $time_in_secs;
+    }
+
+    my $collect_dir = $ENV{'GSDLCOLLECTDIR'};
+
+    my $media_type = $self->{'media_type'};
+    my $media_dir  = $self->{'media_dir'};
+    my $media_root = $self->{'media_root'};
+
+    my $media_base_dir = $self->{'media_base_dir'};
+
+    my $mp3_html;    
+    my $avi_html;
+
+    my $tape_num = "1";
+
+    if (defined $media_type) {
+
+	my $src_file;
+	my $src_filename;
+
+	if ($media_type eq "video") {
+	    $src_file = "$media_root\_01_$tape_num.VOB";
+	    $src_filename 
+		= &util::filename_cat($media_base_dir, $src_file);
+	}
+	elsif ($media_type =~ m/^audio$/) {
+	    $src_file = "$media_root\_Tape$tape_num.mp3";
+	    $src_filename 
+		= &util::filename_cat($media_base_dir, $src_file);
+	}
+	else {
+	    print STDERR "Warning: Unrecognised media type: $media_type\n";
+	}
+
+
+	# Embed link to allow video to stream from marked time event
+	if ($media_type eq "video") {
+
+	    # video excerpt
+		    
+	    $avi_html .= "<a class=\"mm\" href=\"javascript:videoplayerseek($hh,$mm,$ss)\"><img border=1 src=\"_httpvideoimages_/ivideo.gif\"></a>";
+
+	    # save 'time-stamp' as metadata???	       
+
+	}
+	
+	# Do similar for audio.
+	#   Obviously, pure audio needs to be handled here, but also
+	#   video (and link in to its audio track)
+
+	if ($media_type =~ m/^(video|audio)$/) {
+
+		
+		my ($voa_root) = ($src_file =~ m/^(.*)\..*?$/);
+	
+		my $mp3_url = "javascript:audioplayerseek($hh,$mm,$ss)";
+		$mp3_html = "<a class=\"mm\" href=\"$mp3_url\"><img border=1 src=\"_httpvideoimages_/iaudio.gif\"></a>";
+
+#		my $baseexcerpt_url = "_httpcollection_/index/assoc/{If}{[assocfilepath],[assocfilepath],[parent(Top):assocfilepath]}";
+		
+	    }
+    }
+    else {
+	print STDERR "Warning: no media type defined.\n";
+    }
+
+
+    $only_num++;
+
+    $self->{'time_pos'}++;
+
+    my $table = "<table width=\"100%\" cellspacing=\"1\" cellpadding=\"0\"><tr valign=\"middle\">";
+    $table .= "<td>$avi_html</td>" if (defined $avi_html);
+    $table .= "<td>$mp3_html</td>";
+    $table .= "<td>$time_encoded</td>";
+    $table .= "</tr></table>";
+
+    # my $line = "$front$avi_html&nbsp;$mp3_html&nbsp;$time_encoded$back";
+
+    my $line = "$front$table$back";
+
+    # Remember tape prefix for next call
+    $self->{'ptape'} = $tape;
+
+    return $line;
+}
+
+
+sub hyperlink_timing_info
+{
+    my ($self, $textref) = @_;
+
+    my $media_type = $self->{'media_type'};
+    my $media_dir  = $self->{'media_dir'};
+    my $media_root = $self->{'media_root'};
+
+
+
+    ##my $front_re = "<p class=Timing>(?:<span\\s+.*?>)?";
+    my $front_re = "<p[^>]+class=\"?Timing(?:-.*?)?\"?[^>]*>(?:<span\\s+.*?>)?";
+    my $tape_re  = "T\\d+:";
+    my $tt_re = "\\d\\d";
+    my $back_re = ".*?(?:<\\/span>\\s*)?(?:<\\/p>)";
+
+    my $time_match_re = "$front_re$tt_re:$tt_re:$tt_re$back_re(?:\:$tt_re)?";
+    # my $time_sub_re   = "($front_re)($tape_re)? ?($tt_re):($tt_re):($tt_re)(?:\:$tt_re)?($back_re)";
+
+    my $tape_match_re   = "$front_re($tape_re)? ?$tt_re:$tt_re:$tt_re(?:\:$tt_re)?$back_re";
+    my $tape_sub_re   = "($front_re)($tt_re):($tt_re):($tt_re)(?:\:$tt_re)?($back_re)";
+
+
+
+    my @time_seq = ($$textref =~ m/$time_match_re/sgi);
+
+    print STDERR "***** got ", scalar(@time_seq), " matches\n";
+
+    # artifically add tape information into all time indexes
+    my $extrapolate_tape = "";
+    my $num_seq = scalar(@time_seq);
+    for (my $i=0; $i<$num_seq; $i++) {
+	my ($curr_tape) = ($time_seq[$i] =~ m/$tape_match_re/sgi);
+	if (!defined $curr_tape) {
+	    $time_seq[$i] =~ s/$tape_sub_re/$1$extrapolate_tape$2:$3:$4$5/sgi;
+	}
+	else {
+	    if ($curr_tape ne $extrapolate_tape) {
+		$extrapolate_tape = $curr_tape;
+	    }
+	}
+    }
+
+
+    $self->{'time_seq'} = \@time_seq;
+    $self->{'time_pos'} = 0;
+
+    # my $front_re = "<p[^>]+class=\"?Timing(?:-.*?)?\"?[^>]*>(?:<span\\s+.*?>)?";
+
+    $$textref =~ s/($front_re)(T\d+:)? ?(\d\d):(\d\d):(\d\d)(?:\:\d\d)?.*?((?:<\/span>)?\s*<\/p>)/$self->timed_event($1,$2,$3,$4,$5,$6)/sgie;
+
+    ##$$textref =~ s/(<p class=Timing><span\s+.*?>)(T\d+:)? ?(\d\d):(\d\d):(\d\d)(?:\:\d\d)?.*?(<\/span>)/$self->timed_event($1,$2,$3,$4,$5,$6)/sgie;
+
+
+    # knock out any div tags as they interfere with document broken up into sections
+    $$textref =~ s/<\/?div.*?>//gi;
+
+    # Embedded word style infomation is lost by HTMLPlug => make Question
+    # style use HTML italics
+
+    $$textref =~ s/<p class=\"Question\">(.*?)<\/p>/<p><i>$1<\/i><\/p>/gsi;
+
+}
+
+
+
+sub store_block_files
+{
+    my $self =shift (@_);
+    my ($filename_full_path, $block_hash) = @_;
+
+    # Would be better if this were tied to particular HTML docs that
+    # this plugin has processed.
+
+    # For now block all files native to DVD format
+
+    if (($filename_full_path =~ m/\.(IFO|BUP)$/)        # block info and backup-info data
+	|| ($filename_full_path =~ m/VIDEO_TS\..*?$/)   # block top-level files
+	|| ($filename_full_path =~ m/VTS_\d\d_\d\.VOB$/) # 
+	) {
+	
+	$block_hash->{'file_blocks'}->{$filename_full_path} = 1;
+    }
+}
+
+sub read_block {
+    my $self = shift (@_);  
+  
+    my ($pluginfo, $base_dir, $file, $metadata, $processor, $maxdocs, $total_count, $gli) = @_;
+
+    print STDERR "***** TimedHTMLPlugin read_block\n";
+
+    my $filename = $file;
+    $filename = &util::filename_cat ($base_dir, $file) if $base_dir =~ /\w/;
+
+    if (($filename =~ m/\.(IFO|BUP)$/)        # block info and backup-info data
+	|| ($filename =~ m/VIDEO_TS\..*?$/)   # block top-level files
+	|| ($filename =~ m/VTS_\d\d_\d\.VOB$/) # 
+	) {
+	$self->{'num_blocked'} ++;
+	return (0,undef); # blocked
+    }
+
+
+#####	|| ($filename =~ m/VTS_\d\d_0\.VOB$/) # block menu item for this track
+
+
+    if (defined $self->{'file_blocks'}->{$filename} && $self->{'file_blocks'}->{$filename} == 1){
+	$self->{'num_blocked'} ++;
+	return (0,undef); # blocked
+    }
+
+
+    if (defined $self->{'re_file_block'}) {
+	my $re_file_blocks = $self->{'re_file_block'};
+	foreach my $re_file_block (@$re_file_blocks) {
+###	    print STDERR "**** re file block: $filename =~ m $re_file_block\n";
+	    if ($filename =~ m/$re_file_block/) {
+###		print STDERR "*** blocking $filename\n";
+
+		$self->{'num_blocked'} ++;
+		return (0,undef); # blocked
+	    }
+	}
+    }
+
+
+    return $self->SUPER::read_block(@_);
+
+}
+
+
+sub read_file {
+    my $self = shift @_;
+    my ($filename, $encoding, $language, $textref) = @_;
+
+    $only_num = 0;
+
+    $self->SUPER::read_file(@_);
+
+    my $metatable_store = $self->extract_metadata_table($textref);
+    $self->{'metatable_store'} = $metatable_store;
+
+    my ($root, $dirname, $suffix)
+        = &File::Basename::fileparse($filename, "\\.[^\\.]+\$");
+    
+    # media defaults
+    my $media_type = undef;
+    my $media_dir  = $root;
+    ## my $media_root = "VTS_%02d_%02d";
+    my $media_root = "VTS";
+
+    if (defined $metatable_store->{'GS.Media'}) {
+	$media_type = $metatable_store->{'GS.Media'};
+	$media_type = lc($media_type);
+    }
+    if (defined $metatable_store->{'GS.MediaDirectory'}) {
+	$media_dir = $metatable_store->{'GS.MediaDirectory'};
+    }
+    if (defined $metatable_store->{'GS.MediaRoot'}) {
+	$media_root = $metatable_store->{'GS.MediaRoot'};
+    }
+
+    $self->{'media_type'} = $media_type;
+    $self->{'media_dir'}  = $media_dir;
+    $self->{'media_root'} = $media_root;
+
+    my $media_base_dir = &util::filename_cat($dirname,$media_dir);
+    $self->{'media_base_dir'} = $media_base_dir;
+
+    $self->{'vob_duration_info'} = &VideoConverter::vob_durations($media_base_dir,"1",$self->{'outhandle'});
+
+##    $self->hyperlink_timing_info($textref);
+
+    # Convert entities to their UTF8 equivalents
+    $$textref =~ s/&(lt|gt|amp|quot|nbsp);/&z$1;/go;
+    $$textref =~ s/&([^;]+);/&ghtml::getcharequiv($1,1)/gseo;
+    $$textref =~ s/&z(lt|gt|amp|quot|nbsp);/&$1;/go;
+}
+
+
+sub streamable_video
+{
+    my $self = shift (@_);
+
+    my ($base_dir,$filename,$doc_obj,$section) = @_;
+
+    my $outhandle = $self->{'outhandle'};
+    my $verbosity = $self->{'verbosity'};
+
+    my $media_base_dir = $self->{'media_base_dir'};
+
+    #---
+    # Determine size of input video
+
+    my $collect_dir = $ENV{'GSDLCOLLECTDIR'};
+
+    my $media_type = $self->{'media_type'};
+    my $media_dir  = $self->{'media_dir'};
+    my $media_root = $self->{'media_root'};
+
+    my $tape_num = "1";
+
+    my $src_file;
+    my $src_filename;
+
+    if ($media_type eq "video") {
+	# $src_file = "$media_root\_01_$tape_num.VOB";
+	$src_file = "$media_root\_01.VOB";
+	$src_filename 
+	    = &util::filename_cat($media_base_dir, $src_file);
+    }
+    elsif ($media_type =~ m/^audio$/) {
+	# $src_file = "$media_root\_Tape$tape_num.mp3";
+	$src_file = "$media_root.mp3";
+	$src_filename 
+	    = &util::filename_cat($media_base_dir, $src_file);
+    }
+    else {
+	print STDERR "Warning: Unrecognised media type: $media_type\n";
+    }
+
+    if (-e $src_filename) {
+	$self->{'file_blocks'}->{$src_filename} = 1;
+
+#	my $re_file_block = $src_filename;
+#	$re_file_block =~ s/_01_$tape_num.VOB$/_\\d+_\\d+.VOB\$/;
+		
+#	push(@{$self->{'re_file_block'}},$re_file_block);
+    }
+
+    my ($video_type, $video_width, $video_height, $video_duration, $video_size,
+	$vcodec,$vfps,$atype,$afreq,$achan,$arate) 
+	= &VideoConverter::identify($src_filename, $outhandle, $verbosity);
+
+    if ($vfps eq "unknown") {
+	print $outhandle "Unknown framerate, defaulting to 25 frames per second.\n";
+	$vfps = 25;
+    }
+
+###    print STDERR "*** video duration = $video_duration\n";
+    my ($dur_hour,$dur_min,$dur_sec) 
+	= ($video_duration =~ m/(\d+):(\d+):(\d+\.\d+)/);
+    my $total_dur_secs = $dur_hour*3600 + $dur_min*60 + $dur_sec;
+
+    $self->{'video-fps'} = $vfps;
+    $self->{'num-total-frames'} = $total_dur_secs * $vfps;
+
+
+    # shorten duration prcessed for experimentation purposes
+    my $exp_duration = undef;
+
+    my $video_excerpt_duration = $self->{'video_excerpt_duration'};
+
+    print STDERR "**** video/audio duration (secs) = $total_dur_secs\n";
+
+
+    if ((defined $video_excerpt_duration) && ($video_excerpt_duration ne "")) {
+	$exp_duration = $video_excerpt_duration;
+	my ($hh,$mm,$ss,$ms) = ($exp_duration =~ m/^(\d\d):(\d\d):(\d\d)\.?(\d\d)?/);
+	my $excerpt_dur_in_secs = $hh * 3600 + $mm * 60 + $ss;
+
+	if ($excerpt_dur_in_secs < $total_dur_secs) {
+	    $self->{'num-total-frames'} = $excerpt_dur_in_secs * $vfps; # override calculation for full length duration
+	}
+	else {
+	    # clip is already shorter than requested video excerpt duration
+	    # set exp_duration back to undefined
+	    $exp_duration = undef;
+	}
+    }
+
+    print $outhandle "TimedHTMLPlug: Preparing video files associated with $filename for HTMLPlug\n";
+
+    if (defined $exp_duration)
+    {
+	print $outhandle "Only encoding first $exp_duration of video.\n";
+	$self->{'exp_duration'} = $exp_duration;
+    }
+
+    
+    $self->init_cache_for_file($src_filename);
+
+    # TimedHTML streaming isn't as complicated as VideoPlugin or AudioPlugin
+    # => src file is never converted beforehand to another type
+
+    my $convertto_regenerated = 0;
+    my $originalfilename = $src_filename;
+
+    my $streamable_regenerated = 0;
+
+    if ($media_type eq "audio") {
+	$streamable_regenerated = 
+	    $self->enable_audio_streaming($doc_obj,
+					  $originalfilename,$src_filename,
+					  $convertto_regenerated);
+    }
+    else {
+	# generate streamable video and audio
+	$streamable_regenerated = 
+	    $self->enable_full_streaming($doc_obj,
+					 $originalfilename,$src_filename,
+					 $convertto_regenerated,
+					 $video_width,$video_height);
+    }
+
+    $self->{'streamable_regenerated'} = $streamable_regenerated;
+
+    my $base_url = "_httpstreamserverprefix_/collect/[collection]/index/assoc/{If}{[assocfilepath],[assocfilepath],[parent(Top):assocfilepath]}/";
+
+    $doc_obj->add_metadata ($section, "baseurl",$base_url);
+
+    return;
+
+#*********************
+    #---
+    # Generate the Flash FLV format for streaming purposes
+
+    my $streaming_bitrate = "256k"; # used to be 192k
+    my $streaming_size    = "360"; # used to be 352 (CIF?)
+    my $streaming_achan   = "1";
+    my $streaming_arate   = "44100";
+    my $streaming_quality = "high";
+
+    #---
+    # Convert video to flash
+    #---
+    my ($stream_cmd,$oflash_filename,$oflash_file) 
+	= $self->stream_cmd($src_filename,
+			    $video_width,$video_height,
+			    $streaming_quality, $streaming_bitrate, $streaming_size,
+			    $streaming_achan, $streaming_arate);
+
+    my $streamable_options = { @{$self->{'ffmpeg_monitor'}},
+			       'message_prefix' => "Stream",
+			       'message' => "Generating streamable video: $oflash_file" }; 
+
+    my ($streamable_regenerated2,$streamable_result,$streamable_had_error)
+	= $self->run_cached_general_cmd($stream_cmd,$oflash_filename,$streamable_options);
+
+    $self->{'streamable_regenerated2'} = $streamable_regenerated2;
+
+    if (!$streamable_had_error) {
+	#---
+	# Make video seekable
+	#---
+	my ($streamseekable_cmd,$ostreamseekable_filename) = $self->streamseekable_cmd($oflash_filename);
+	
+	my $streamseekable_options = { @{$self->{'flvtool2_monitor'}},
+				       'message_prefix' => "Stream Seekable",
+				       'message' => "Reprocessing video stream to be seekable by timeline: $oflash_file" }; 
+
+	if ($streamable_regenerated2) {
+	    $self->run_general_cmd($streamseekable_cmd,$streamseekable_options);
+	}
+
+	my $streamable_url = $oflash_file;
+##	$streamable_url =~ s/ /%20/g;
+
+	$doc_obj->add_metadata ($section, "streamablevideo", $streamable_url);
+	$doc_obj->associate_file($oflash_filename,$oflash_file,"video/flash",
+				 $section);
+    }
+
+    #
+    # FlowPlayer.swf       height+22 pixels
+    # FlowPlayerBlack.swf  height+16 pixels
+    # FlowPlayerThermo.swf height+16 pixels
+    # FlowPlayerWhite.swf  height+26 pixels
+    if ($media_type eq "audio") {
+	$doc_obj->add_metadata ($section, "audioflashwidth",    $video_width);
+	$doc_obj->add_metadata ($section, "audioflashheight",   22 + 100);
+    }
+    else {
+	$doc_obj->add_metadata ($section, "flashwidth",    $video_width);
+	$doc_obj->add_metadata ($section, "flashheight",   $video_height + 22 + 100);
+    }
+
+    
+    my $base_url2 = "_httpstreamserverprefix_/collect/[collection]/index/assoc/{If}{[assocfilepath],[assocfilepath],[parent(Top):assocfilepath]}/";
+
+    $doc_obj->add_metadata ($section, "baseurl",$base_url2);
+    
+    $self->{'oflash_file'} = $oflash_file;
+    $self->{'oflash_filename'} = $oflash_filename;
+
+}
+
+
+
+sub add_cuepoints
+{
+    my ($self) = shift @_;
+    my ($doc_obj) = @_;
+
+    my $section = $doc_obj->get_top_section();
+    my $chapters = $doc_obj->get_children($section);
+
+
+    # open file
+    my $output_dir = $self->{'cached_dir'};
+    my $cue_filename = &util::filename_cat($output_dir,"on_cue.xml");
+
+    open(CUEOUT,">$cue_filename")
+	|| die "Unable to open $cue_filename: $!\n";
+    print CUEOUT "<tags>\n";
+
+
+    my $cc = 0;
+
+    foreach my $chap (@$chapters)
+    {
+	$cc++;
+
+	my $chap_text = $doc_obj->get_text($chap);
+
+	my @cuepoints 
+	    = ($chap_text =~ m/<p class=Timing><span\s+.*?>(?:T\d+:)? ?(\d\d:\d\d:\d\d)(?:\:\d\d)?/gsi);
+
+	my $count = 0;
+
+	foreach my $cuepoint (@cuepoints)
+	{	    
+	    my ($hh,$mm,$ss) = ($cuepoint =~ m/(\d\d):(\d\d):(\d\d)/);
+
+	    my $cuept_insecs = $self->hms_to_secs($hh,$mm,$ss);
+
+	    if ($cuept_insecs < 2) {
+		$cuept_insecs += 2;
+	    }
+	    my $cuept_inmsecs = 1000 * $cuept_insecs;
+	    
+	    $doc_obj->add_metadata($section,"cuepoint",$cuept_inmsecs);
+	    my $chap_title = $doc_obj->get_metadata_element($chap,"Title");
+
+	    # Navigation point (used in seeking)
+#	    print CUEOUT "  <metatag event=\"onCuePoint\" overwrite=\"true\">\n";
+#	    print CUEOUT "    <name>$chap_title</name>\n";
+#	    print CUEOUT "    <timestamp>$cuept_inmsecs</timestamp>\n";
+#	    print CUEOUT "    <parameters>\n";
+#	    print CUEOUT "      <thumb>dummy</thumb>\n";
+#	    print CUEOUT "      <sectionnum>$chap</sectionnum>\n";
+#	    print CUEOUT "      <subsectionnum>$count</subsectionnum>\n";
+#	    print CUEOUT "      <cuept>$cuept_inmsecs</cuept>\n";
+#	    print CUEOUT "    </parameters>\n";
+#	    print CUEOUT "    <type>navigation</type>\n";
+#	    print CUEOUT "  </metatag>\n";
+
+
+#	    print CUEOUT "  <metatag event=\"onCuePoint\" overwrite=\"true\">\n";
+#	    print CUEOUT "    <name>$chap_title</name>\n";
+#	    print CUEOUT "    <timestamp>$cuept_inmsecs</timestamp>\n";
+#	    print CUEOUT "    <parameters>\n";
+#	    print CUEOUT "      <thumb>foo.jpg</thumb>\n";
+#	    print CUEOUT "    </parameters>\n";
+#	    print CUEOUT "    <type>navigation</type>\n";
+#	    print CUEOUT "  </metatag>\n";
+
+	    if ($count==0)
+	    {
+		# Event (used to hook in to javascipt callback)
+		# Only do for the first in each chapter
+
+		print CUEOUT "  <metatag event=\"onCuePoint\" overwrite=\"true\">\n";
+##		print CUEOUT "    <name>Test $cc</name>\n";
+		print CUEOUT "    <name>$chap_title</name>\n";
+		print CUEOUT "    <timestamp>$cuept_inmsecs</timestamp>\n";
+		print CUEOUT "    <parameters>\n";
+		print CUEOUT "      <cuept>$cuept_inmsecs</cuept>\n";
+		print CUEOUT "      <sectionnum>$chap</sectionnum>\n";
+		print CUEOUT "    </parameters>\n";
+		print CUEOUT "    <type>event</type>\n";
+##		print CUEOUT "    <type>actionscript</type>\n";
+		print CUEOUT "  </metatag>\n";
+	    }
+
+	    $count++;
+	}
+    }
+
+
+    print CUEOUT "</tags>\n";
+    close(CUEOUT);
+
+####    my $videoconvert = $self->{'videoconvert'};
+    my $oflash_filename = $self->{'oflash_filename'};
+    my $oflash_file = $self->{'oflash_file'};
+
+    my ($streamcuepts_cmd,$ostreamcuepts_filename) = $self->streamcuepts_cmd($oflash_filename);
+
+    my $verbosity = $self->{'verbosity'};
+    my $outhandle = $self->{'outhandle'};
+
+    my $streamcuepts_options = { @{$self->{'flvtool2_monitor'}},
+				 'message_prefix' => "Stream Cue Points",
+				 'message' => "Reprocessing video stream to add cuepoints on timeline: $oflash_file" }; 
+
+    my $streamable_regenerated2 = $self->{'streamable_regenerated'};
+
+    if ($streamable_regenerated2) {
+	$self->run_general_cmd($streamcuepts_cmd,$streamcuepts_options);
+    }
+
+    
+
+}
+
+
+
+sub process {
+    my $self = shift (@_);
+    my ($textref, $pluginfo, $base_dir, $file, $metadata, $doc_obj, $gli) = @_;
+
+    my $filename = $file;
+    $filename = &util::filename_cat ($base_dir, $file) if $base_dir =~ /\w/;
+
+    my $top_section = $doc_obj->get_top_section();
+
+    $self->streamable_video($base_dir,$filename,$doc_obj,$top_section);
+
+    $self->hyperlink_timing_info($textref);
+
+    foreach my $mp3_excerpt (@{$self->{'mp3_excerpts'}}) {
+	my $omp3_file = $mp3_excerpt->{'omp3_file'};
+	my $omp3_filename = $mp3_excerpt->{'omp3_filename'};
+
+	$doc_obj->associate_file($omp3_filename,$omp3_file,"audio/mp3",
+				 $top_section);
+    }
+    $self->{'mp3_excerpts'} = undef;
+
+    my $metatable_store = $self->{'metatable_store'};
+    foreach my $metaname (keys %$metatable_store) {
+	my $metadata = $metatable_store->{$metaname};
+	$doc_obj->add_metadata($top_section,$metaname,$metadata);
+    }
+    $self->{'metatable_store'} = undef;
+
+    my $ret_val = $self->SUPER::process(@_);
+
+    $self->add_cuepoints($doc_obj);
+
+
+    return $ret_val;
+
+}
+
+
+
+sub replace_images {
+    my $self = shift (@_);
+    my ($front, $link, $back, $base_dir, 
+	$file, $doc_obj, $section) = @_;
+
+    if ($link =~ m/^"?_.*?_"?\//) {
+	# variety of greenstone macro, so let through unchanged
+	return $front . $link . $back ;
+    }
+
+    return $self->SUPER::replace_images(@_);
+}
+
+
+
+
+
+sub replace_href_links {
+    my $self = shift (@_);
+    my ($front, $link, $back, $base_dir, $file, $doc_obj, $section) = @_;
+
+
+    if ($link =~ m/^"?_.*?_"?/) {
+	# variety of greenstone macro, so let through unchanged
+	return $front . $link . $back ;
+    }
+
+    return $self->SUPER::replace_href_links(@_);
+}
+
+1;
Index: gs2-extensions/video-and-audio/trunk/src/perllib/plugins/VideoConverter.pm
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/perllib/plugins/VideoConverter.pm	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/perllib/plugins/VideoConverter.pm	(revision 23036)
@@ -0,0 +1,1314 @@
+###########################################################################
+#
+# VideoConverter - helper plugin that does video (and audio) conversion using ffmpeg
+#
+# A component of the Greenstone digital library software
+# from the New Zealand Digital Library Project at the 
+# University of Waikato, New Zealand.
+#
+# Copyright (C) 2008 New Zealand Digital Library Project
+#
+# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+###########################################################################
+package VideoConverter;
+
+use MultimediaConverter;
+
+
+use strict;
+no strict 'refs'; # allow filehandles to be variables and viceversa
+
+use gsprintf 'gsprintf';
+
+BEGIN {
+    @VideoConverter::ISA = ('MultimediaConverter');
+}
+
+
+my @thumb_arguments = (       
+      { 'name' => "create_thumbnail",
+	'desc' => "{ImageConverter.create_thumbnail}",
+	'type' => "enum",
+	'list' => [{'name' => "true", 'desc' => "{common.true}"},
+		   {'name' => "false", 'desc' => "{common.false}"}],
+	'deft' => "true",
+	'reqd' => "no" },
+	      { 'name' => "keep_original_video",
+	'desc' => "Copy the original video file in the collection as an associated file.",
+	'type' => "enum",
+	'list' => [{'name' => "true", 'desc' => "Include the original video file in the collection"},
+		   {'name' => "false", 'desc' => "Do not copy the original video file in the collection"}],
+	'deft' => "false",
+	'reqd' => "no" },
+      { 'name' => "thumbnailsize",
+	'desc' => "{ImageConverter.thumbnailsize}",
+	'type' => "int",
+	'deft' => "100",
+	'range' => "1,",
+	'reqd' => "no" },
+      { 'name' => "thumbnailtype",
+	'desc' => "{ImageConverter.thumbnailtype}",
+	'type' => "string",
+	'deft' => "jpg",
+	'reqd' => "no" },
+      { 'name' => "noscaleup",
+	'desc' => "{ImageConverter.noscaleup}",
+	'type' => "flag",
+	'reqd' => "no" } );
+
+
+my @screenview_arguments = (
+      { 'name' => "create_screenview",
+	'desc' => "{ImageConverter.create_screenview}",
+	'type' => "enum",
+	'list' => [{'name' => "true", 'desc' => "{common.true}"},
+		   {'name' => "false", 'desc' => "{common.false}"}],
+	'deft' => "true",
+	'reqd' => "no" },
+      { 'name' => "screenviewsize",
+	'desc' => "{ImageConverter.screenviewsize}",
+	'type' => "int",
+	'deft' => "720",
+	'range' => "1,",
+	'reqd' => "no" },
+      { 'name' => "screenviewtype",
+	'desc' => "{ImageConverter.screenviewtype}",
+	'type' => "string",
+	'deft' => "jpg",
+	'reqd' => "no" } );
+
+my $arguments = [
+      @thumb_arguments,
+      @screenview_arguments,
+
+      { 'name' => "converttotype",
+	'desc' => "{ImageConverter.converttotype}",
+	'type' => "string",
+	'deft' => "",
+	'reqd' => "no" },
+
+
+      { 'name' => "converttosize",
+	'desc' => "{VideoPlugin.converttosize}",
+	'type' => "int",
+	'deft' => "",
+##	'deft' => "352",
+	'reqd' => "no" },
+      { 'name' => "converttobitrate",
+	'desc' => "{VideoPlugin.converttobitrate}",
+	'type' => "string",
+	'deft' => "200k",
+	'reqd' => "no" },
+      { 'name' => "extract_keyframes",
+	'desc' => "{VideoPlugin.extractkeyframes}",
+	'type' => "flag",
+	'deft' => "0",
+	'reqd' => "no" },
+      { 'name' => "keep_keyframes",
+	'desc' => "{VideoPlugin.keep_keyframes}",
+	'type' => "string",
+	'deft' => "all",
+	'reqd' => "no" },
+      { 'name' => "streamingsize",
+	'desc' => "{VideoPlugin.streamingsize}",
+	'type' => "int",
+	'deft' => "352",
+	'reqd' => "no" },
+      { 'name' => "streamingbitrate",
+	'desc' => "{VideoPlugin.streamingbitrate}",
+	'type' => "string",
+	'deft' => "200k",
+	'reqd' => "no" },
+	{ 'name' => "streamingHQsize",
+	'desc' => "{VideoPlugin.streamingsize}",
+	'type' => "int",
+	'deft' => "720",
+	'reqd' => "no" },
+	{ 'name' => "streamingHQVideoBitrate",
+	'desc' => "{VideoPlugin.streamingbitrate}",
+	'type' => "int",
+	'deft' => "496",
+	'reqd' => "no" },
+	{ 'name' => "streamingHQAudioBitrate",
+	'desc' => "{VideoPlugin.streamingbitrate}",
+	'type' => "int",
+	'deft' => "80",
+	'reqd' => "no" },
+	{ 'name' => "videoDeinterlacingFilter",
+	'desc' => "Activate a deinterlacing filter to increase the quality of TV footage",
+	'type' => "enum",
+	'list' => [{'name' => "true", 'desc' => "{common.true}"},
+		   {'name' => "false", 'desc' => "{common.false}"}],
+	'deft' => "false",
+	'reqd' => "no" },
+	{ 'name' => "getMaximumMetadata",
+	'desc' => "Extract the maximum technical information of each video",
+	'type' => "enum",
+	'list' => [{'name' => "true", 'desc' => "{common.true}"},
+		   {'name' => "false", 'desc' => "{common.false}"}],
+	'deft' => "false",
+	'reqd' => "no" },
+
+      { 'name' => "minimumsize",
+	'desc' => "{ImageConverter.minimumsize}",
+	'type' => "int",
+	'deft' => "100",
+	'range' => "1,",
+	'reqd' => "no" },
+
+		 ];
+
+my $options = { 'name' => "VideoConverter",
+		'desc' => "{VideoConverter.desc}",
+		'abstract' => "yes",
+		'inherits' => "yes",
+		'args' => $arguments };
+
+sub new {
+    my ($class) = shift (@_);
+    my ($pluginlist,$inputargs,$hashArgOptLists) = @_;
+    push(@$pluginlist, $class);
+
+    push(@{$hashArgOptLists->{"ArgList"}},@{$arguments});
+    push(@{$hashArgOptLists->{"OptList"}},$options);
+
+    my $self = new MultimediaConverter($pluginlist, $inputargs, $hashArgOptLists, 1);
+
+
+    return bless $self, $class;
+
+}
+
+
+
+
+# Discover the characteristics of a video file.
+
+#  Equivalent step to that in ImagePlugin that uses ImageMagicks's
+#  'indentify' utility
+
+#  Here we use 'MediaInfo' for video but for consistency keep the Perl
+#  method name the same as before
+
+sub mediaInfo_Inform_Cmd {
+	my ($video, $parameter, $outhandle, $verbosity) = @_;
+	
+	# Use MediaInfo CLI to get the file spec according to the requested parameter
+    my $command = "mediainfo --Inform=\"$parameter\" \"$video\"";
+
+    print $outhandle "  $command\n" if ($verbosity > 2);
+    my $result = '';
+    $result = `$command 2>&1`;
+	chomp $result;
+    print $outhandle "  $result\n" if ($verbosity > 4);
+	
+	# Return the asked spec
+    return ($result);
+}
+
+# Here we use mediaInfo to get all the possible metadata in XML form that could then be parsed
+#the result will vary depending on the file type and how it was encoded
+sub mediaInfo_XML_Cmd {
+    my ($video, $outhandle, $verbosity) = @_;
+	
+    # Use MediaInfo CLI to get the file spec according to the requested parameter
+    my $command = "mediainfo --Output=XML \"$video\"";
+    
+    print $outhandle "  $command\n" if ($verbosity > 2);
+    my $result = '';
+    $result = `$command 2>&1`;
+    print $outhandle "  $result\n" if ($verbosity > 4);
+    
+	# Return the asked spec
+    return ($result);
+}
+
+#Here we parse the video specs contained in the XML text in order to create a metadata entry for each field
+sub mediaInfo_parse_XML {
+    my ($xmlTxt, $outhandle, $verbosity) = @_;
+    
+    my @parts = split(/<track\s+type=\"(.*?)\">/is,$xmlTxt);
+    
+    shift @parts; # Skip preamble
+    my $metadata_table = {};
+    
+    while (@parts) {
+	my $type = shift(@parts);
+	my $vals = shift(@parts);
+	my @fieldlist=();
+	while ($vals =~ s/<(.*?)>(.*?)<\/\1>//) {
+	    my $metaname = $1;
+	    my $metaval  = $2;
+	    my $fullmetaname = "mi.$type\^$metaname";
+	    $metadata_table->{$fullmetaname}= $metaval;
+	    push(@fieldlist,$fullmetaname);
+	}
+	
+	$metadata_table->{"mi.${type}Fields"}= join(",",@fieldlist);
+    }
+    
+    return $metadata_table;
+
+	while (@parts) {
+		my $type = shift(@parts);
+		my $vals = shift(@parts);
+		my @fieldlist=();
+		while ($vals =~ s/<(.*?)>(.*?)<\/\1>//) {
+		  my $metaname = $1;
+		  my $metaval  = $2;
+		  my $fullmetaname = "mi.$type\^$metaname";
+		  if($metaname ne "Complete_name") #ignore this field as it is pointing to the cache directory
+			  {
+			  $metadata_table->{$fullmetaname}= $metaval;
+			  push(@fieldlist,$fullmetaname);
+			  }
+		}
+		$metadata_table->{"mi.${type}Fields"}= join(",",@fieldlist);
+	}
+	return $metadata_table;
+
+}
+
+sub identifyMI { 
+    my ($video, $outhandle, $verbosity) = @_;
+
+    # Use the ffmpeg command to get the file specs
+    my $command = "ffmpeg -i \"$video\"";
+
+    print $outhandle "  $command\n" if ($verbosity > 2);
+    my $result = '';
+    $result = `$command 2>&1`;
+    print $outhandle "  $result\n" if ($verbosity > 4);
+
+    # Read the type, width, and height etc.
+	# This could be done in a more efficient way by only calling the application mediainfo once and
+	# giving all the parameters then parsing the results, however on most operating system
+	# once the application is called once it is then cached for subsequent calls.
+    my $vtype =  mediaInfo_Inform_Cmd($video,"General;%Format%",$outhandle,0);
+	my $duration = mediaInfo_Inform_Cmd($video,"General;%Duration%",$outhandle,0);
+	my $durationDisplay = mediaInfo_Inform_Cmd($video,"General;%Duration/String1%",$outhandle,0);
+	my $filesize = mediaInfo_Inform_Cmd($video,"General;%FileSize/String%",$outhandle,0);
+    my $vcodec = mediaInfo_Inform_Cmd($video,"Video;%Format%",$outhandle,0);
+	my $vrate = mediaInfo_Inform_Cmd($video,"Video;%BitRate%",$outhandle,0);
+    my $width =  mediaInfo_Inform_Cmd($video,"Video;%Width%",$outhandle,0);
+    my $height = mediaInfo_Inform_Cmd($video,"Video;%Height%",$outhandle,0);
+    my $fps    = mediaInfo_Inform_Cmd($video,"Video;%FrameRate%",$outhandle,0);
+    my $atype =  mediaInfo_Inform_Cmd($video,"Audio;%Format%",$outhandle,0);
+    my $afreq =  mediaInfo_Inform_Cmd($video,"Audio;%SamplingRate/String%",$outhandle,0);
+    my $achan =  mediaInfo_Inform_Cmd($video,"Audio;%Channel(s)%",$outhandle,0);
+    my $arate =  mediaInfo_Inform_Cmd($video,"Audio;%BitRate%",$outhandle,0);
+
+	my $xmlTxt =  mediaInfo_XML_Cmd ($video,$outhandle,0);
+	my $metadata_table = mediaInfo_parse_XML($xmlTxt,$outhandle,0);
+	
+    # Return the specs
+    return ($vtype,$width,$height,$duration,$durationDisplay,$filesize,
+	    $vcodec,$vrate,$fps,$atype,$afreq,$achan,$arate,$metadata_table);
+}
+
+
+sub identify { 
+    my ($video, $outhandle, $verbosity) = @_;
+
+    # Use the ffmpeg command to get the file specs
+    my $command = "ffmpeg -i \"$video\"";
+
+    print $outhandle "  $command\n" if ($verbosity > 2);
+    my $result = '';
+    $result = `$command 2>&1`;
+    print $outhandle "  $result\n" if ($verbosity > 4);
+
+    # Read the type, width, and height etc.
+    my $vtype =  'unknown';
+    my $vcodec = 'unknown';
+    my $width =  'unknown';
+    my $height = 'unknown';
+    my $fps    = 'unknown';
+
+    my $atype =  'unknown';
+    my $afreq =  'unknown';
+    my $achan =  'unknown';
+    my $arate =  'unknown';
+
+    my $video_safe = quotemeta $video;
+
+    # strip off everything up to filename
+    $result =~ s/^.*\'$video_safe\'://s;
+
+##    if ($result =~ m/Video: (.*?) fps/m) {
+    if ($result =~ m/^\s+Stream.*?Video: (.*?)$/m) {
+	my $video_info = $1;
+	$video_info =~ s/\s*\[.*?\]//g;
+
+	my @video_fields = split(/,\s*/,$video_info);
+	
+	$vtype = shift @video_fields;
+
+	if ($video_fields[0] !~ m/(\d+)x(\d+)/) {
+	    $vcodec = shift @video_fields;
+	}
+	my $video_dim = shift @video_fields;
+
+	($width,$height) = ($video_dim =~ m/(\d+)x(\d+)/);
+	
+	if ($video_fields[0] =~ m/(\d+)\s+tbr$/) {
+	    $fps = $1;
+	}
+
+#	if ($video_info =~ m/([^,]+),(?: ([^,]+),)? (\d+)x(\d+),.*?(\d+\.\d+)/)
+#	{
+#	    $vtype = $1;
+#	    $vcodec = $2 if defined $2;
+#	    $width = $3;
+#	    $height = $4;
+#	    $fps = $5;
+#	}
+    }
+
+    if ($result =~ m/Audio: (\w+), (\d+) Hz, (\w+)(?:, (\d+.*))?/m) {
+	$atype = $1;
+	$afreq = $2;
+	$achan = $3;
+	$arate = $4 if (defined $4);
+    }
+
+    # Read the duration
+    my $duration = "unknown";
+    if ($result =~ m/Duration: (\d+:\d+:\d+\.\d+)/m) {
+	$duration = $1;
+    }
+    print $outhandle "  file: $video:\t $vtype, $width, $height, $duration\n" 
+	if ($verbosity > 2);
+
+    if ($verbosity >3) {
+	print $outhandle "\t video codec=$vcodec, fps = $fps\n";
+	print $outhandle "\t audio codec=$atype, freq = $afreq Hz, $achan, $arate\n";
+    }
+
+    # Return the specs
+    return ($vtype, $width, $height, $duration, -s $video,
+	    $vcodec,$fps,$atype,$afreq,$achan,$arate);
+}
+
+
+sub vob_durations
+{
+    my ($media_base_dir,$title_num,$outhandle) = @_;
+
+    my $filter_re = sprintf("^VTS_%02d_[1-9]\\.VOB\$",$title_num);
+
+    my $duration_info = {};
+
+    if (opendir(VTSIN,$media_base_dir)) {
+	my @vts_title_vobs = grep { $_ =~ m/$filter_re/ } readdir(VTSIN);
+	closedir(VTSIN);
+
+	foreach my $v (@vts_title_vobs) {
+	    my $full_v = &util::filename_cat($media_base_dir,$v);
+
+	    my ($vtype, $width, $height, $duration, $durationDisplay, $vsize,
+		$vcodec,$vrate,$fps,$atype,$afreq,$achan,$arate) = identify($full_v,$outhandle,0);
+
+	    my ($vob_num) = ($v =~ m/^VTS_\d\d_(\d)\.VOB$/);
+
+	    $duration_info->{$vob_num} = $duration;
+	    print STDERR "**** $title_num: $title_num, storing {$vob_num} => $duration\n";
+
+	}
+
+    }
+    else {
+	print $outhandle "Warning: unable to read files in directory $media_base_dir\n";
+    }
+    
+    return $duration_info;
+
+}
+
+
+
+sub init_cache_for_file {
+    my $self = shift(@_);
+
+    my ($video_filename) = @_;
+
+    $self->SUPER::init_cache_for_file($video_filename);
+
+					
+    my @flvtool2_monitor = ( 'monitor_init'   ,"convertutil::monitor_init_unbuffered",
+			     'monitor_line'   , "VideoConverter::flvtool2_monitor_line",
+			     'monitor_deinit' , "convertutil::monitor_deinit_unbuffered" );
+					
+    $self->{'flvtool2_monitor'} = \@flvtool2_monitor;
+
+
+}
+
+
+
+sub optional_frame_scale
+{
+    my $self = shift (@_);
+    my ($orig_size,$video_width,$video_height) = @_;
+
+    my $s_opt = "";
+
+    if ($video_width > $video_height) {
+	if ($video_width > $orig_size) {
+	    my $scale_factor = $orig_size/$video_width;
+	    my $scaled_width = int($video_width * $scale_factor);
+	    my $scaled_height = int($video_height * $scale_factor);
+
+	    # round to be ensure multiple of 2 (needed by some codecs)
+	    $scaled_width  = int($scaled_width/2)*2;
+	    $scaled_height = int($scaled_height/2)*2;
+
+	    $s_opt = "-s ${scaled_width}x${scaled_height}";
+	}
+	# else, video is smaller than requested size, don't scale up
+    }
+    else {
+	if ($video_height > $orig_size) {
+	    my $scale_factor = $orig_size/$video_height;
+	    my $scaled_width = int($video_width * $scale_factor);
+	    my $scaled_height = int($video_height * $scale_factor);
+
+	    # round to be ensure multiple of 2 (needed by some codecs)
+	    $scaled_width  = int($scaled_width/2)*2;
+	    $scaled_height = int($scaled_height/2)*2;
+
+	    $s_opt = "-s ${scaled_width}x${scaled_height}";
+	}
+	# else, video is smaller than requested size, don't scale up
+	
+    }
+    
+    return $s_opt;
+}
+
+
+sub keyframe_cmd
+{
+    my $self = shift (@_);
+    my ($ivideo_filename) = @_;
+
+    my $video_ext_dir = &util::filename_cat($ENV{'GSDLHOME'},"ext","video");
+
+    my $output_dir = $self->{'cached_dir'};
+    my $ivideo_root = $self->{'cached_file_root'};
+
+    my $oshot_filename = &util::filename_cat($output_dir,"shots.xml");
+
+    my $exp_duration = $self->{'exp_duration'};
+    my $t_opt = (defined $exp_duration) ? "-t $exp_duration" : "";
+
+    my $main_opts = "-y $t_opt";
+
+    my $hive = &util::filename_cat($video_ext_dir,"lib","vhook","hive.so");
+
+    my $oflash_filename = &util::filename_cat($output_dir,"$ivideo_root\_keyframe.flv");
+
+    my $vhook_opts = "$hive  -o $oshot_filename -k $output_dir $ivideo_filename";
+
+    my $ivideo_filename_gsdlenv = $self->gsdlhome_independent($ivideo_filename);
+    my $oflash_filename_gsdlenv = $self->gsdlhome_independent($oflash_filename);
+
+    my $ffmpeg_cmd = "ffkeyframe  $main_opts -vhook \"$vhook_opts\"   -i \"$ivideo_filename_gsdlenv\" -an -y \"$oflash_filename_gsdlenv\"";
+
+
+    return ($ffmpeg_cmd,$oflash_filename);
+}
+
+sub get_ovideo_file
+{
+	my $self = shift (@_);
+	my ($stream_type) = @_;
+	
+    my $ivideo_root = $self->{'cached_file_root'};
+	my $ofile;
+	
+	if ($stream_type eq "flv")
+	{
+		$ofile = "${ivideo_root}_vstream.flv";
+	}
+	else
+	{
+		$ofile = "${ivideo_root}_vHQstream.mp4";
+	}
+	
+	return $ofile;
+}
+
+sub get_ovideo_filename
+{
+	my $self = shift (@_);
+	my ($stream_type) = @_;
+	
+	my $output_dir = $self->{'cached_dir'};
+
+	my $ofile = $self->get_ovideo_file($stream_type);
+		
+	my $ofilename = &util::filename_cat($output_dir,$ofile);
+	
+	return $ofilename;
+}
+
+# Generate the command to use with Handbrake to recompress a video using h264/aac compatible with Flash Player 10
+sub stream_mp4_video_cmd
+{
+    my $self = shift (@_);
+    my ($ivideo_filename,$streaming_HQ_size, $streaming_HQ_VideoBitrate,
+	 $streaming_HQ_AudioBitrate) = @_;
+
+    my $output_dir = $self->{'cached_dir'};
+    my $ivideo_root = $self->{'cached_file_root'};
+
+    my $omp4_file = "${ivideo_root}_vHQstream.mp4";
+    my $omp4_filename = &util::filename_cat($output_dir,$omp4_file);
+
+
+    #my $exp_duration = $self->{'exp_duration'};
+    #my $t_opt = (defined $exp_duration) ? "-t $exp_duration" : "";
+
+	my $deinterlace = $self->{'videoDeinterlacingFilter'};
+	my $video_processing_parameters;	
+	# Use specific resizing algorythms depending if we need to downsize the video resolution or not
+	if(!$streaming_HQ_size || $streaming_HQ_size eq "fullsize") { 
+		$video_processing_parameters = "--strict-anamorphic";
+	} else {
+		$video_processing_parameters = "-w $streaming_HQ_size --loose-anamorphic";
+	}
+	# Use the deinterlace video filter if enabled in the plugin
+	if ($deinterlace eq "true")
+	{
+		$video_processing_parameters .= " --decomb";
+	}
+
+    my $ivideo_filename_gsdlenv = $self->gsdlhome_independent($ivideo_filename);
+    my $omp4_filename_gsdlenv = $self->gsdlhome_independent($omp4_filename);
+	
+	# Generate the pre and post video parameters for Handbrake v0.9.4
+	my $pre_opts = "-t 1 -c 1";
+	my $post_opts = "-f mp4 -O $video_processing_parameters -e x264 -b $streaming_HQ_VideoBitrate -a 1 -E faac -6 dpl2 -R Auto -B $streaming_HQ_AudioBitrate -D 0.0 -x ref=2:bframes=2:subq=6:mixed-refs=0:weightb=0:8x8dct=0:trellis=0";
+
+	my $handbrake_cmd = "HandbrakeCLI.exe -i \"$ivideo_filename_gsdlenv\" $pre_opts -o \"$omp4_filename_gsdlenv\" $post_opts";
+
+    #print STDERR "****\nHandbrake command: $handbrake_cmd\n****\n";
+	
+    return ($handbrake_cmd,$omp4_filename,$omp4_file);
+}
+
+
+
+sub stream_flv_video_cmd
+{
+    my $self = shift (@_);
+    my ($ivideo_filename,$video_width,$video_height,
+	$streaming_quality,
+	$streaming_bitrate,$streaming_size, 
+	$opt_streaming_achan, $opt_streaming_arate) = @_;
+
+    my $streaming_achan 
+	= (defined $opt_streaming_achan) ? $opt_streaming_achan : 2;
+
+    my $streaming_arate 
+	= (defined $opt_streaming_arate) ? $opt_streaming_arate : 22050;
+
+    my $output_dir = $self->{'cached_dir'};
+    my $ivideo_root = $self->{'cached_file_root'};
+
+    my $oflash_file = "${ivideo_root}_vstream.flv";
+    my $oflash_filename = &util::filename_cat($output_dir,$oflash_file);
+
+    my $s_opt = "";
+    my $media_type = $self->{'media_type'};
+    if ($media_type ne "audio") {
+	$s_opt = $self->optional_frame_scale($streaming_size,$video_width,$video_height);
+    }
+
+
+    my $exp_duration = $self->{'exp_duration'};
+    my $t_opt = (defined $exp_duration) ? "-t $exp_duration" : "";
+
+    my $main_opts = "-y $t_opt";
+
+    my $bitrate_opt = "-b $streaming_bitrate";
+    ### my $stream_opts = "-r 25 $s_opt";
+    my $stream_opts .= " $s_opt -ac $streaming_achan -ar $streaming_arate";
+
+    # -flags +ilme+ildct' and maybe '-flags +alt' for interlaced material, and try '-top 0/1'
+
+    my $all_opts = "$main_opts $stream_opts"; 
+
+    my $ffmpeg_cmd;
+
+    my $ivideo_filename_gsdlenv = $self->gsdlhome_independent($ivideo_filename);
+    my $oflash_filename_gsdlenv = $self->gsdlhome_independent($oflash_filename);
+
+    if ($streaming_quality eq "high") {
+
+	my $pass_log_file = &util::filename_cat($output_dir,"$ivideo_root-logpass.txt");
+	if (-e $pass_log_file) {
+	    &util::rm($pass_log_file);
+	}
+
+	my $pass_log_file_gsdlenv = $self->gsdlhome_independent($pass_log_file);
+
+	$all_opts .= " -passlogfile \"$pass_log_file_gsdlenv\"";
+
+	my $ffmpeg_cmd_pass1 = "ffmpeg -pass 1 -i \"$ivideo_filename_gsdlenv\" $all_opts -y \"$oflash_filename_gsdlenv\"";
+
+	my $ffmpeg_cmd_pass2 = "ffmpeg -pass 2 -i \"$ivideo_filename_gsdlenv\" $all_opts $bitrate_opt -y \"$oflash_filename_gsdlenv\"";
+	$ffmpeg_cmd = "( $ffmpeg_cmd_pass1 && $ffmpeg_cmd_pass2 )";
+    }
+    else {
+	# single pass
+
+	$ffmpeg_cmd = "ffmpeg -i \"$ivideo_filename_gsdlenv\" $all_opts -y \"$oflash_filename_gsdlenv\"";
+    }
+
+    return ($ffmpeg_cmd,$oflash_filename,$oflash_file);
+}
+
+
+
+
+sub stream_flv_audio_cmd
+{
+    # AudioConverter also has a routine for doing this
+    # => merge into one!
+    # **************
+
+    my $self = shift (@_);
+    my ($ivideo_filename, $opt_streaming_achan, $opt_streaming_arate) = @_;
+
+    # Convert either audio or video to streamable audio format
+
+    my $streaming_achan 
+	= (defined $opt_streaming_achan) ? $opt_streaming_achan : 2;
+
+    my $streaming_arate 
+	= (defined $opt_streaming_arate) ? $opt_streaming_arate : 22050;
+
+    my $output_dir = $self->{'cached_dir'};
+    my $ivideo_root = $self->{'cached_file_root'};
+
+    my $ofla_file = "${ivideo_root}_astream.flv";
+    my $ofla_filename = &util::filename_cat($output_dir,$ofla_file);
+
+    my $exp_duration = $self->{'exp_duration'};
+    my $t_opt = (defined $exp_duration) ? "-t $exp_duration" : "";
+
+    my $main_opts = "-vn -y $t_opt";
+
+    my $stream_opts .= " -ac $streaming_achan -ar $streaming_arate";
+
+    my $all_opts = "$main_opts $stream_opts"; 
+
+    my $ffmpeg_cmd;
+
+    my $ivideo_filename_gsdlenv = $self->gsdlhome_independent($ivideo_filename);
+    my $ofla_filename_gsdlenv = $self->gsdlhome_independent($ofla_filename);
+    $ffmpeg_cmd = "ffmpeg -i \"$ivideo_filename_gsdlenv\" $all_opts -y \"$ofla_filename_gsdlenv\"";
+
+    return ($ffmpeg_cmd,$ofla_filename,$ofla_file);
+}
+
+
+
+
+sub audio_excerpt_cmd
+{
+    my $self = shift (@_);
+    my ($ivoa_filename,$hh,$mm,$ss,$opt_excerpt_len) = @_;
+
+    # ivoa = input video or audio
+
+    my $time_encoded = "$hh:$mm:$ss";
+    my $time_encoded_file = "$hh$mm$ss";
+    
+    
+    my $output_dir = $self->{'cached_dir'};
+    my $ivoa_root = $self->{'cached_file_root'};
+
+    my $omp3_file = "${ivoa_root}_$time_encoded_file.mp3";
+    my $omp3_filename = &util::filename_cat($output_dir,$omp3_file);
+
+    my $all_opts = "-y -acodec mp3 -ss $time_encoded ";
+
+    if (defined $opt_excerpt_len) {
+	$all_opts .= "-t $opt_excerpt_len ";
+    }
+
+
+    my $ivoa_filename_gsdlenv = $self->gsdlhome_independent($ivoa_filename);
+    my $omp3_filename_gsdlenv = $self->gsdlhome_independent($omp3_filename);
+
+
+    my $ffmpeg_cmd = "ffmpeg -i \"$ivoa_filename_gsdlenv\" $all_opts  \"$omp3_filename_gsdlenv\"";
+
+    return ($ffmpeg_cmd,$omp3_filename,$omp3_file);
+}
+
+
+
+sub streamseekable_cmd
+{
+    my $self = shift (@_);
+    my ($oflash_filename) = @_;
+
+    my $output_dir = $self->{'cached_dir'};
+    my $ivideo_root = $self->{'cached_file_root'};
+
+    ## my $cue_filename = &util::filename_cat($output_dir,"on_cue.xml");
+
+    my $flvtool_cmd = "flvtool2 -vUP \"$oflash_filename\"";
+
+    return ($flvtool_cmd,$oflash_filename);
+}
+
+
+sub streamkeyframes_cmd
+{
+    my $self = shift (@_);
+    my ($oflash_filename,$doc_obj,$section) = @_;
+
+    my $assocfilepath 
+	= $doc_obj->get_metadata_element($section,"assocfilepath");
+
+    my $output_dir = $self->{'cached_dir'};
+
+    my $cue_filename = &util::filename_cat($output_dir,"on_cue.xml");
+ 
+    my $flvtool_cmd = "flvtool2 -vAUtP \"$cue_filename\"  \"$oflash_filename\"";
+
+    return ($flvtool_cmd,$oflash_filename);
+}
+
+
+sub streamkeyframes_cmd_old
+{
+    my $self = shift (@_);
+    my ($oflash_filename,$doc_obj,$section) = @_;
+
+    my $assocfilepath 
+	= $doc_obj->get_metadata_element($section,"assocfilepath");
+
+    my $output_dir = $self->{'cached_dir'};
+
+    my $cue_filename = &util::filename_cat($output_dir,"on_cue.xml");
+
+    my $video_server = $ENV{'GEXT_VIDEO_SERVER'}; 
+    my $video_prefix = $ENV{'GEXT_VIDEO_PREFIX'};
+
+    my $collect = $ENV{'GSDLCOLLECTION'};
+ 
+    print STDERR "**** Warning: need to remove dependency of GEXT_VIDEO_SERVER and _PREFIX\n";
+
+    my $flvtool_cmd = "flvtool2 -vAUtP \"$cue_filename\" -thumbLocation:$video_server$video_prefix/collect/$collect/index/assoc/$assocfilepath \"$oflash_filename\"";
+
+
+    return ($flvtool_cmd,$oflash_filename);
+}
+
+
+sub streamcuepts_cmd
+{
+    my $self = shift (@_);
+    my ($oflash_filename) = @_;
+
+    my $output_dir = $self->{'cached_dir'};
+
+    my $cue_filename = &util::filename_cat($output_dir,"on_cue.xml");
+
+    ##my $video_server = $ENV{'GEXT_VIDEO_SERVER'}; 
+    ##my $video_prefix = $ENV{'GEXT_VIDEO_PREFIX'};
+
+    ## my $collect = $ENV{'GSDLCOLLECTION'};
+
+    ## my $thumbloc = "$video_server$video_prefix/collect/$collect";
+
+
+#    my $flvtool_cmd = "flvtool2 -vUAtP \"$cue_filename\" -thumbLocation:$thumbloc \"$oflash_filename\"";
+
+#    my $flvtool_cmd = "flvtool2 -vUAt \"$cue_filename\" \"$oflash_filename\"";
+
+
+
+#    my $flvtool_cmd = "flvtool2 -vUAt \"$cue_filename\" \"$oflash_filename\"";
+
+
+##    my $flvtool_cmd = "flvtool2 -vAt \"$cue_filename\" -UP \"$oflash_filename\" \"$output_dir/updated.flv\"";
+
+##    my $flvtool_cmd = "flvtool2 -vAtU \"$cue_filename\" \"$oflash_filename\" \"$output_dir/updated.flv\"";
+
+    my $flvtool_cmd = "flvtool2 -vAtUP \"$cue_filename\" \"$oflash_filename\"";
+
+    return ($flvtool_cmd,$oflash_filename);
+}
+
+
+sub keyframe_thumbnail_cmd
+{
+    my $self = shift (@_);
+    my ($ivideo_filename,$thumbnailfile,$thumbnailwidth,$thumbnailheight) = @_;
+
+    my $output_dir = $self->{'cached_dir'};
+    my $ivideo_root = $self->{'cached_file_root'};
+
+    my $key_filename_prefix = &util::filename_cat($output_dir,$ivideo_root);
+
+
+    # Try for 4th keyframe, but fall back to 1st if doesn't exist
+    my $key_filename = "${key_filename_prefix}_0003.jpg";
+    $key_filename = "${key_filename_prefix}_0000.jpg" if (!-e $key_filename);
+
+    my $key_filename_gsdlenv = $self->gsdlhome_independent($key_filename);
+    my $thumbnailfile_gsdlenv = $self->gsdlhome_independent($thumbnailfile);
+
+    my $command;
+
+    if (-e $key_filename) {
+	$command = "convert -interlace plane -verbose -geometry $thumbnailwidth" 
+	    . "x$thumbnailheight \"$key_filename_gsdlenv\" \"$thumbnailfile_gsdlenv\"";
+    }
+    else {
+	# extract_keyframe has either not been switched on, or else had
+	# a problem when running
+	# => extract a from 
+	# my $frame_rate = 1.0 / 60.0;
+	
+	my $ivideo_filename_gsdlenv = $self->gsdlhome_independent($ivideo_filename);
+
+	$command = "ffmpeg -i \"$ivideo_filename_gsdlenv\"  -ss 3.5 -vframes 1 -f image2 -s ${thumbnailwidth}x${thumbnailheight} -y \"$thumbnailfile_gsdlenv\"";
+
+	# fmpeg -i input.dv -r 1 -f image2 -s 120x96 images%05d.png
+    }
+
+    return ($command,$thumbnailfile);
+}
+
+
+sub keyframe_montage_cmd
+{
+    my $self = shift (@_);
+    my ($ivideo_filename,$montagefile) = @_;
+
+    my $output_dir = $self->{'cached_dir'};
+    my $ivideo_root = $self->{'cached_file_root'};
+
+    my $key_filename_prefix = &util::filename_cat($output_dir,$ivideo_root);
+
+    my $options = "-tile 10  -geometry 75x62+2+2";
+
+    my $command = "montage $options ${key_filename_prefix}_*.jpg \"$montagefile\"";
+
+    return ($command,$montagefile);
+}
+
+
+
+sub parse_shot_xml
+{
+    my ($self) = shift(@_);
+
+    my $outhandle = $self->{'outhandle'};
+    my $output_dir = $self->{'cached_dir'};
+
+    my $shots_filename = &util::filename_cat($output_dir,"shots.xml");
+
+    eval {
+	$self->{'parser'}->parsefile($shots_filename);
+    };
+    
+    if ($@) {
+	print $outhandle "VideoConverter: skipping $shots_filename as not conformant to Hive shot syntax\n" if ($self->{'verbosity'} > 1);
+	print $outhandle "\n Perl Error:\n $@\n" if ($self->{'verbosity'}>2);
+	return 0;
+    }
+
+}
+
+sub associate_keyframes_old
+{
+    my ($self) = shift(@_);
+
+    my ($doc_obj,$section) = @_;
+
+    my $output_dir = $self->{'cached_dir'};
+
+    my $count = 1;
+    foreach my $kframe_file (@{$self->{'keyframe_fnames'}}) {
+
+	my $kframe_filename = &util::filename_cat($output_dir,$kframe_file);
+	$doc_obj->associate_file($kframe_filename,"keyframe$count.jpg","image/jpeg",
+				 $section);
+	$count++;
+    }
+
+    $doc_obj->add_utf8_metadata($section,"NumKeyframes",scalar(@{$self->{'keyframe_fnames'}}));
+
+
+    # *****
+    # $doc_obj->add_metadata ($section, "thumblist", $self->{'flowplayer_thumblist'});
+
+}
+
+sub associate_keyframes
+{
+    my ($self) = shift(@_);
+
+    my ($doc_obj,$topsection,$timeline) = @_;
+
+    my $output_dir = $self->{'cached_dir'};
+    
+    my $count = 1;
+
+    foreach my $t (sort { $timeline->{$a}->{'keyframeindex'} <=> $timeline->{$b}->{'keyframeindex'} } keys %$timeline)
+    {
+	my $kframe_file = $timeline->{$t}->{'thumb'};
+	my $timestamp = $timeline->{$t}->{'timestamp'};
+
+	# create next sub-section to video "document"
+	my $endchild = $doc_obj->insert_section($doc_obj->get_end_child($topsection));
+	$doc_obj->add_utf8_metadata($endchild,"Title","Timestamp $timestamp");
+	$doc_obj->add_utf8_metadata($endchild,"FrameNum",$t);
+
+	my $kframe_filename = &util::filename_cat($output_dir,$kframe_file);
+	$doc_obj->associate_file($kframe_filename,"keyframe$count.jpg",
+				 "image/jpeg",
+				 $endchild);
+
+	$doc_obj->add_utf8_metadata($endchild,"assockeyframe","keyframe$count.jpg");
+
+	$doc_obj->add_utf8_metadata($topsection,"KeyframeTimestamp",$timestamp);
+	$doc_obj->add_utf8_metadata($topsection,"KeyframeFrameNum",$t);
+
+
+
+	$count++;
+    }
+
+    #### $doc_obj->add_utf8_metadata($topsection,"NumKeyframes",scalar(@{$self->{'keyframe_fnames'}}));
+
+    $doc_obj->add_utf8_metadata($topsection,"NumKeyframes",scalar(keys %$timeline));
+
+
+    # *****
+    # $doc_obj->add_metadata ($topsection, "thumblist", $self->{'flowplayer_thumblist'});
+}
+
+
+
+sub enable_audio_streaming
+{
+    my $self = shift (@_);
+    my ($doc_obj,$originalfilename,$filename,$convertto_regenerated) = @_;
+
+    my $section = $doc_obj->get_top_section();
+
+    my $output_dir   = $self->{'cached_dir'};
+    my $ivideo_root  = $self->{'cached_file_root'};
+    
+    # Generate FLV audio-only format for streaming purposes
+
+    my $optionally_run_general_cmd = "run_uncached_general_cmd";
+    if ($self->{'enable_cache'}) {
+	$optionally_run_general_cmd
+	    = ($convertto_regenerated) ? "regenerate_general_cmd" : "run_cached_general_cmd";
+    }
+
+    
+    my $ifilename = $originalfilename || $filename;
+
+    my ($stream_cmd,$ofla_filename,$ofla_file) 
+	= $self->stream_flv_audio_cmd($ifilename);
+    
+    
+    my $streamable_options = { @{$self->{'ffmpeg_monitor'}},
+			       'message_prefix' => "Stream",
+			       'message' => "Generating streamable audio: $ofla_file" }; 
+    
+
+    my ($streamable_regenerated,$streamable_result,$streamable_had_error)
+	= $self->$optionally_run_general_cmd($stream_cmd,
+					     $ifilename,$ofla_filename,
+					     $streamable_options);
+
+
+    if (!$streamable_had_error) {
+	my ($streamseekable_cmd,$ostreamseekable_filename) = $self->streamseekable_cmd($ofla_filename);
+	
+	my $streamseekable_options = { @{$self->{'flvtool2_monitor'}},
+				       'message_prefix' => "Stream Seekable",
+				       'message' => "Reprocessing audio stream to be seekable by timeline: $ofla_file" }; 
+	
+	if ($streamable_regenerated) {
+	    $self->run_general_cmd($streamseekable_cmd,$streamseekable_options);
+	}
+    
+	my $streamable_url = $ofla_file;
+	my $streamable_url_safe = $self->url_safe($streamable_url);
+
+	$doc_obj->add_utf8_metadata ($section, "streamableaudio", $streamable_url_safe);
+	$doc_obj->associate_file($ofla_filename,$ofla_file,"audio/flash",
+			     $section);
+    }
+
+    $doc_obj->add_metadata ($section, "audioflashwidth",    400);
+    $doc_obj->add_metadata ($section, "audioflashheight",   22 + 100);
+
+    $self->{'ofla_file'} = $ofla_file;
+    $self->{'ofla_filename'} = $ofla_filename;
+
+    return $streamable_regenerated;
+}
+
+
+
+
+
+sub enable_video_streaming
+{
+    my $self = shift (@_);
+    my ($doc_obj,$originalfilename,$filename,$convertto_regenerated,
+	$video_width,$video_height) = @_;
+
+    my $section = $doc_obj->get_top_section();
+
+    my $output_dir   = $self->{'cached_dir'};
+    my $ivideo_root  = $self->{'cached_file_root'};
+    
+    # Generate the Flash FLV format for streaming purposes
+
+    my $optionally_run_general_cmd = "run_uncached_general_cmd";
+    if ($self->{'enable_cache'}) {
+	$optionally_run_general_cmd
+	    = ($convertto_regenerated) ? "regenerate_general_cmd" : "run_cached_general_cmd";
+    }
+
+
+    my $streaming_bitrate = $self->{'streamingbitrate'};
+    my $streaming_size    = $self->{'streamingsize'};
+    
+    my $streaming_quality = "high";
+    
+    my $ifilename = $originalfilename || $filename;
+
+    my ($stream_cmd,$oflash_filename,$oflash_file) 
+	= $self->stream_flv_video_cmd($ifilename,
+				     $video_width,$video_height,
+				     $streaming_quality,
+				     $streaming_bitrate, $streaming_size);
+    
+    
+    my $streamable_options = { @{$self->{'ffmpeg_monitor'}},
+			       'message_prefix' => "Stream",
+			       'message' => "Generating streamable video: $oflash_file" }; 
+    
+
+    my ($streamable_regenerated,$streamable_result,$streamable_had_error)
+	= $self->$optionally_run_general_cmd($stream_cmd,
+					     $ifilename,$oflash_filename,
+					     $streamable_options);
+    
+    if (!$streamable_had_error) {
+	my ($streamseekable_cmd,$ostreamseekable_filename) = $self->streamseekable_cmd($oflash_filename);
+	
+	my $streamseekable_options = { @{$self->{'flvtool2_monitor'}},
+				       'message_prefix' => "Stream Seekable",
+				       'message' => "Reprocessing video stream to be seekable by timeline: $oflash_file" }; 
+	
+	if ($streamable_regenerated) {
+	    $self->run_general_cmd($streamseekable_cmd,$streamseekable_options);
+	}
+	
+	my $streamable_url = $oflash_file;
+	my $streamable_url_safe = $self->url_safe($streamable_url);
+	
+	$doc_obj->add_utf8_metadata ($section, "streamablevideo", $streamable_url_safe);
+	$doc_obj->associate_file($oflash_filename,$oflash_file,"video/flash",
+				 $section);
+    }
+
+
+    #
+    # FlowPlayer.swf       height+22 pixels
+    # FlowPlayerBlack.swf  height+16 pixels
+    # FlowPlayerThermo.swf height+16 pixels
+    # FlowPlayerWhite.swf  height+26 pixels
+
+    my $flashwidth = $video_width;
+    my $flashheight = $video_height + 22;
+
+    if ($self->{'extract_keyframes'}) {
+	$flashheight += 100;
+    }
+
+    $doc_obj->add_metadata ($section, "flashwidth",    $flashwidth);
+    $doc_obj->add_metadata ($section, "flashheight",   $flashheight);
+     
+    $self->{'oflash_file'} = $oflash_file;
+    $self->{'oflash_filename'} = $oflash_filename;
+
+    return $streamable_regenerated;
+}
+
+sub enable_h264_streaming
+{
+    my $self = shift (@_);
+    my ($doc_obj,$originalfilename,$filename,$convertto_regenerated,
+	$video_width,$video_height) = @_;
+    
+
+    my $section = $doc_obj->get_top_section();
+
+    my $output_dir   = $self->{'cached_dir'};
+    my $ivideo_root  = $self->{'cached_file_root'};
+    
+    # Generate the H264 video file format for streaming purposes using the cache feature if used
+
+    my $optionally_run_general_cmd = "run_uncached_general_cmd";
+    if ($self->{'enable_cache'}) {
+	$optionally_run_general_cmd
+	    = ($convertto_regenerated) ? "regenerate_general_cmd" : "run_cached_general_cmd";
+    }
+
+	my $streaming_HQ_size    = $self->{'streamingHQsize'};
+	my $streaming_HQ_VideoBitrate    = $self->{'streamingHQVideoBitrate'};
+	my $streaming_HQ_AudioBitrate    = $self->{'streamingHQAudioBitrate'};
+
+    my $ifilename = $originalfilename || $filename;
+    my ($stream_cmd,$oflash_filename,$oflash_file) 
+	= $self->stream_mp4_video_cmd($ifilename,
+				     $streaming_HQ_size,
+				     $streaming_HQ_VideoBitrate,
+				     $streaming_HQ_AudioBitrate);
+    
+    
+    my $streamable_options = { @{$self->{'handbrake_monitor'}},
+			       'message_prefix' => "MP4 Stream",
+			       'message' => "Generating streamable MP4 video: $oflash_file" }; 
+    
+
+    my ($streamable_regenerated,$streamable_result,$streamable_had_error)
+	= $self->$optionally_run_general_cmd($stream_cmd,
+					     $ifilename,$oflash_filename,
+					     $streamable_options);
+    
+    if (!$streamable_had_error) {
+
+	
+	my $streamable_url = $oflash_file;
+	my $streamable_url_safe = $self->url_safe($streamable_url);
+	
+	my $outhandle = $self->{'outhandle'};
+
+	my ($vtype, $width, $height, $duration, $durationDisplay, $vsize,
+		$vcodec,$vrate,$fps,$atype,$afreq,$achan,$arate,$metadata_table) = identifyMI($oflash_filename,$outhandle,0);
+	#Add the most common metadata extracted by MediaInfo from the streamable video file
+	$doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoFormat", $vtype);
+	$doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoFileSize", $vsize);
+	$doc_obj->add_utf8_metadata ($section, "tv.HQStreamingWidth", $width);
+	$doc_obj->add_utf8_metadata ($section, "tv.HQStreamingHeight", $height);
+	$doc_obj->add_utf8_metadata ($section, "tv.HQStreamingDuration", $durationDisplay);
+	$doc_obj->add_utf8_metadata ($section, "tv.HQStreamingFPS", $fps);
+	$doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoCodec", $vcodec);
+	$doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoBitrate", $vrate);
+	$doc_obj->add_utf8_metadata ($section, "tv.HQStreamingAudioCodec", $atype);
+	$doc_obj->add_utf8_metadata ($section, "tv.HQStreamingAudioBitrate", $arate);
+	$doc_obj->add_utf8_metadata ($section, "tv.HQStreamingFileBitrate", $vrate + $arate);
+	
+	my $get_max_metadata = $self->{'getMaximumMetadata'};
+	if ($get_max_metadata eq "true"){
+		foreach my $metaname(keys %$metadata_table)
+		{
+			my $metaval = $metadata_table->{$metaname};
+			$doc_obj->add_utf8_metadata ($section, $metaname, $metaval);
+		}
+	}
+	
+	#add the metadata for the resulting file that should be open inside the flash video player
+	$doc_obj->add_utf8_metadata ($section, "streamablevideo", $streamable_url_safe);
+	$doc_obj->associate_file($oflash_filename,$oflash_file,"video/mp4",
+				 $section);
+    }
+
+
+
+
+    my $flashwidth = $video_width;
+    my $flashheight = $video_height + 22;
+
+    if ($self->{'extract_keyframes'}) {
+	$flashheight += 100;
+    }
+
+    $doc_obj->add_metadata ($section, "flashwidth",    $flashwidth);
+    $doc_obj->add_metadata ($section, "flashheight",   $flashheight);
+     
+    $self->{'oflash_file'} = $oflash_file;
+    $self->{'oflash_filename'} = $oflash_filename;
+
+    return $streamable_regenerated;
+	
+	
+}
+
+sub enable_full_streaming
+{
+    my $self = shift (@_);
+    my ($doc_obj,$originalfilename,$filename,$convertto_regenerated,
+	$video_width,$video_height) = @_;
+
+    my $video_streamable_regenerated
+	= $self->enable_video_streaming($doc_obj,$originalfilename,$filename,
+					$convertto_regenerated,
+					$video_width,$video_height);
+
+    my $audio_streamable_regenerated
+	= $self->enable_audio_streaming($doc_obj,$originalfilename,$filename,
+					$convertto_regenerated);
+
+    return ($video_streamable_regenerated || $audio_streamable_regenerated);
+}
+
+
+
+sub flvtool2_monitor_line
+{
+    my ($line) = @_;
+
+    my $had_error = 0;
+    my $generate_dot = 1;
+
+    if ($line =~ m/\s+\- /) {
+	# ignore tabulated output printed at end of command
+	$generate_dot = 0;
+    }
+    
+    if ($line =~ m/^Error:/i) {
+	$had_error = 1;
+    }
+
+    return ($had_error,$generate_dot);
+}
+
+
+
+
+
+1;
Index: gs2-extensions/video-and-audio/trunk/src/perllib/plugins/VideoPlugin.pm
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/perllib/plugins/VideoPlugin.pm	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/perllib/plugins/VideoPlugin.pm	(revision 23036)
@@ -0,0 +1,992 @@
+######################################################################
+#
+# VideoPlugin.pm -- plugin for processing video files 
+# A component of the Greenstone digital library software
+# from the New Zealand Digital Library Project at the 
+# University of Waikato, New Zealand.
+#
+# Copyright (C) 1999 New Zealand Digital Library Project
+#
+# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+###########################################################################
+
+# -- Largely modeled on how ImagePlugin works 
+# -- Can convert to audio as well as video
+
+package VideoPlugin;
+
+use strict;
+no strict 'refs'; # allow filehandles to be variables and viceversa
+no strict 'subs';
+
+use XMLParser;
+use gsprintf;
+
+use MultimediaPlugin;
+use VideoConverter;
+use ReadXMLFile;
+
+sub BEGIN {
+    @VideoPlugin::ISA = ('MultimediaPlugin', 'VideoConverter', 'ReadXMLFile');
+}
+
+
+my $arguments =
+    [ { 'name' => "process_exp",
+	'desc' => "{BasePlugin.process_exp}",
+	'type' => "regexp",
+	'deft' => &get_default_process_exp(),
+	'reqd' => "no" } ];
+
+
+my $options = { 'name'     => "VideoPlugin",
+		'desc'     => "{VideoPlugin.desc}",
+		'abstract' => "no",
+		'inherits' => "yes",
+		'args'     => $arguments };
+
+sub new {
+    my ($class) = shift (@_);
+    my ($pluginlist,$inputargs,$hashArgOptLists) = @_;
+    push(@$pluginlist, $class);
+
+    push(@{$hashArgOptLists->{"ArgList"}},@{$arguments});
+    push(@{$hashArgOptLists->{"OptList"}},$options);
+
+    new VideoConverter($pluginlist, $inputargs, $hashArgOptLists);
+    my $self = new MultimediaPlugin($pluginlist, $inputargs, $hashArgOptLists);
+
+    if ($self->{'info_only'}) {
+	# don't worry about creating the XML parser as all we want is the 
+	# list of plugin options
+	return bless $self, $class;
+    }
+
+
+    # create XML::Parser object for parsing keyframe files (produced by hive)
+    my $parser = new XML::Parser('Style' => 'Stream',
+                                 'Pkg' => 'VideoPlugin',
+                                 'PluginObj' => $self,
+				 'Namespaces' => 1, # strip out namespaces
+				 'Handlers' => {'Char' => \&Char,
+						'XMLDecl' => \&XMLDecl,
+						'Entity'  => \&Entity,
+						'Doctype' => \&Doctype,
+						'Default' => \&Default
+                                 });
+
+    $self->{'parser'} = $parser;
+
+
+    return bless $self, $class;
+}
+
+
+
+sub begin {
+    my $self = shift (@_);
+    my ($pluginfo, $base_dir, $processor, $maxdocs) = @_;
+
+    $self->SUPER::begin(@_);
+    $self->VideoConverter::begin(@_); 
+}
+
+
+sub init {
+    my $self = shift (@_);
+    my ($verbosity, $outhandle, $failhandle) = @_;
+
+    $self->SUPER::init(@_);
+    $self->VideoConverter::init(@_);
+}
+
+
+sub get_default_process_exp {
+    my $self = shift (@_);
+
+    return q^(?i)\.(mpe?g|flv|mov|qt|wmv|vob|avi|mp4|m4v|ts)$^; 
+}
+
+
+sub extract_keyframes
+{
+    my $self = shift (@_);
+    my ($doc_obj,$originalfilename,$filename) = @_;
+
+    my $section = $doc_obj->get_top_section();
+
+    my $output_dir   = $self->{'cached_dir'};
+    my $ivideo_root  = $self->{'cached_file_root'};
+
+    # Generate the keyframes with ffmpeg and hive
+    my $ifilename = $originalfilename || $filename;
+    my ($keyframe_cmd,$okeyframe_filename) = $self->keyframe_cmd($ifilename);
+    
+    my $keyframe_options = { @{$self->{'ffmpeg_monitor'}},
+			     'message_prefix' => "Keyframe",
+			     'message' => "Extracting keyframes" }; 
+    
+    $self->run_cached_general_cmd($keyframe_cmd,
+				  $ifilename,$okeyframe_filename,
+				  $keyframe_options);
+    $self->parse_shot_xml();
+
+    my $keep_keyframe_timeline = $self->{'keep_keyframe_timeline'};
+    $self->associate_keyframes($doc_obj,$section,$keep_keyframe_timeline);
+
+}
+
+
+
+sub extract_thumbnail
+{
+    my $self = shift (@_);
+    my ($doc_obj,$filename,$convertto_regenerated,$thumbnailtype,
+	$thumbnail_width, $thumbnail_height) = @_;
+
+    my $section = $doc_obj->get_top_section();
+
+    my $output_dir   = $self->{'cached_dir'};
+    my $ivideo_root  = $self->{'cached_file_root'};
+
+    my $verbosity = $self->{'verbosity'};
+    my $outhandle = $self->{'outhandle'};
+
+
+    # Generate the thumbnail with convert, a la ImagePlugin
+
+    my $thumbnailfile = &util::filename_cat($output_dir,"$ivideo_root-thumbnail.$thumbnailtype");
+    
+    my $optionally_run_general_cmd = "run_uncached_general_cmd";
+    if ($self->{'enable_cache'}) {	
+	$optionally_run_general_cmd 
+	= ($convertto_regenerated) ? "regenerate_general_cmd" : "run_cached_general_cmd";
+    }
+    
+###    print STDERR "**** creating thumbnail: $thumbnail_width x $thumbnail_height\n";
+    my $ofilename = $self->get_ovideo_filename($self->{'enable_streaming'});
+    my ($thumb_cmd ,$othumb_filename)
+	= $self->keyframe_thumbnail_cmd($ofilename,$thumbnailfile,$thumbnail_width,$thumbnail_height);
+    
+    my $thumb_options = { 'verbosity' => $verbosity,
+			  'outhandle' => $outhandle,
+			  'message_prefix' => "Thumbnail",
+			  'message' => "Generating thumbnail" }; 
+    
+    my ($thumb_regenerated,$thumb_result,$thumb_had_error)
+	= $self->$optionally_run_general_cmd($thumb_cmd,$ofilename,$thumbnailfile,$thumb_options);
+    
+    # Add the thumbnail as an associated file ...
+    if (-e "$thumbnailfile") { 
+	$doc_obj->associate_file("$thumbnailfile", "thumbnail.$thumbnailtype", 
+				 "image/$thumbnailtype",$section);
+	$doc_obj->add_metadata ($section, "ThumbType", $thumbnailtype);
+	$doc_obj->add_metadata ($section, "Thumb", "thumbnail.$thumbnailtype");
+	
+	$doc_obj->add_utf8_metadata ($section, "thumbicon", "<img src=\"_httpprefix_/collect/[collection]/index/assoc/[assocfilepath]/[Thumb]\" width=[ThumbWidth] height=[ThumbHeight]>");
+###	    $doc_obj->add_utf8_metadata ($section, "thumbicon", "<img src=\"_httpprefix_/collect/[collection]/index/assoc/[assocfilepath]/[Thumb]\">");
+    }
+    
+    # Extract Thumnail metadata from convert output
+    if ($thumb_result =~ m/[0-9]+x[0-9]+=>([0-9]+)x([0-9]+)/) {
+	$doc_obj->add_metadata ($section, "ThumbWidth", $1);
+	$doc_obj->add_metadata ($section, "ThumbHeight", $2);
+    }
+    else {
+	# Two reasons for getting to here:
+	#   1.thumbnail was generated by ffmpeg, not imagemagick convert
+	#   2.thumbnail was cached, so imagemagick convert was not run
+	# Either way, the solution is the same:
+	# => run "identify $thumbnailfile" and parse result	
+	$thumb_result = `identify \"$thumbnailfile\" 2>&1`;
+	
+	if ($thumb_result =~ m/([0-9]+)x([0-9]+)/) {
+	    $doc_obj->add_metadata ($section, "ThumbWidth", $1);
+	    $doc_obj->add_metadata ($section, "ThumbHeight", $2);
+	}
+    }
+}
+
+
+sub extract_keyframes_montage
+{
+    my $self = shift (@_);
+    my ($doc_obj,$filename,$thumbnailtype) = @_;
+
+    my $section = $doc_obj->get_top_section();
+
+    my $output_dir   = $self->{'cached_dir'};
+    my $ivideo_root  = $self->{'cached_file_root'};
+
+    
+    # Generate the mosaic with 'montage'
+    my $montagefile = &util::filename_cat($output_dir,"$ivideo_root\_montage.$thumbnailtype");
+    
+    my ($montage_cmd,$omontage_filename)
+	= $self->keyframe_montage_cmd($filename,$montagefile);
+    
+    my $montage_options = { 'message_prefix' => "Montage",
+			    'message' => "Generating montage" }; 
+    
+    my ($montage_result,$montage_had_error)
+	= $self->run_general_cmd($montage_cmd,$montage_options);
+    
+    # Add the montage as an associated file ...
+    if (-e "$montagefile") { 
+	$doc_obj->associate_file("$montagefile", "montage.$thumbnailtype", 
+				 "image/$thumbnailtype",$section);
+	$doc_obj->add_metadata ($section, "MontageType", $thumbnailtype);
+	$doc_obj->add_metadata ($section, "Montage", "montage.$thumbnailtype");
+	
+	$doc_obj->add_utf8_metadata ($section, "montageicon", "<img src=\"_httpprefix_/collect/[collection]/index/assoc/[assocfilepath]/[Montage]\" >");
+    }
+}
+
+
+
+sub extract_screenview
+{
+    my $self = shift (@_);
+    my ($doc_obj,$filename,$convertto_regenerated, $screenviewtype, $screenview_width,$screenview_height) = @_;
+
+    my $section = $doc_obj->get_top_section();
+
+    my $output_dir   = $self->{'cached_dir'};
+    my $ivideo_root  = $self->{'cached_file_root'};
+    
+    my $optionally_run_general_cmd = "run_uncached_general_cmd";
+    if ($self->{'enable_cache'}) {	
+	$optionally_run_general_cmd 
+	= ($convertto_regenerated) ? "regenerate_general_cmd" : "run_cached_general_cmd";
+    }
+    # make the screenview image
+
+    my $screenviewfilename = &util::filename_cat($output_dir,"$ivideo_root-screenview.$screenviewtype");
+
+    
+    my ($screenview_cmd,$oscreenview_filename)
+	= $self->keyframe_thumbnail_cmd($filename,$screenviewfilename,$screenview_width,$screenview_height);
+
+    my $screenview_options = { 'message_prefix' => "Screenview",
+			       'message' => "Generating screenview image" }; 
+
+				   
+	my ($screenview_regenerated,$screenview_result,$screenview_had_error)
+	= $self->$optionally_run_general_cmd($screenview_cmd,
+					     $filename,$screenviewfilename,
+					     $screenview_options);
+    
+
+    # get screenview dimensions, size and type
+    if ($screenview_result =~ m/[0-9]+x[0-9]+=>([0-9]+)x([0-9]+)/) {
+	$doc_obj->add_metadata ($section, "ScreenWidth", $1);
+	$doc_obj->add_metadata ($section, "ScreenHeight", $2);
+    }
+    else {
+	$doc_obj->add_metadata ($section, "ScreenWidth", $screenview_width);
+	$doc_obj->add_metadata ($section, "ScreenHeight", $screenview_height);
+    }
+    
+    #add the screenview as an associated file ...
+    if (-e "$screenviewfilename") { 
+	$doc_obj->associate_file("$screenviewfilename", "screenview.$screenviewtype",
+				 "image/$screenviewtype",$section);
+	$doc_obj->add_metadata ($section, "ScreenType", $screenviewtype);
+	$doc_obj->add_metadata ($section, "Screen", "screenview.$screenviewtype");
+	
+	$doc_obj->add_utf8_metadata ($section, "screenicon", "<img src=\"_httpprefix_/collect/[collection]/index/assoc/[assocfilepath]/[Screen]\" width=[ScreenWidth] height=[ScreenHeight]>");
+    } else {
+	my $outhandle = $self->{'outhandle'};
+	print $outhandle "VideoPlugin: couldn't find \"$screenviewfilename\"\n";
+    }
+}
+
+
+# Create the keyframes, thumbnail and screenview images, and discover
+# the Video's size, width, and height using the ffmpeg utility.
+
+sub run_convert {
+    my $self = shift (@_);
+    my $base_dir = shift (@_);
+    my $filename = shift (@_);   # filename with full path
+    my $file = shift (@_);       # filename without path
+    my $doc_obj = shift (@_);
+
+    my $section = $doc_obj->get_top_section();
+    
+    my $verbosity = $self->{'verbosity'};
+    my $outhandle = $self->{'outhandle'};
+
+    # check the filename is okay
+    return 0 if ($file eq "" || $filename eq "");
+
+    my $minimumsize = $self->{'minimumsize'};
+    if (defined $minimumsize && (-s $filename < $minimumsize)) {
+        print $outhandle "VideoPlugin: \"$filename\" too small, skipping\n"
+	    if ($verbosity > 1);
+    }
+
+    my ($video_type, $video_width, $video_height, $video_duration, $durationDisplay, $video_size,
+	$vcodec,$vrate,$vfps,$atype,$afreq,$achan,$arate) 
+	= &VideoConverter::identify($filename, $outhandle, $verbosity);
+
+    if ($video_duration =~ m/^(\d\d):(\d\d):(\d\d)\.(\d\d)$/) {
+	$video_duration = $1*3600 + $2*60 + $3 + ($4/10.0);
+    }
+
+    if ($vfps eq "unknown") {
+	print $outhandle "Unknown framerate, defaulting to 25 frames per second.\n";
+	$vfps = 25;
+    }
+
+    #my ($dur_hour,$dur_min,$dur_sec) 
+	#= ($video_duration =~ m/(\d+):(\d+):(\d+\.\d+)/);
+    #my $total_dur_secs = $dur_hour*3600 + $dur_min*60 + $dur_sec;
+	
+    my $total_dur_secs = $video_duration / 1000;
+
+    $self->{'video-fps'} = $vfps;
+    $self->{'num-total-frames'} = $total_dur_secs * $vfps;
+    #print STDERR "****\nTotal_dur_secs= $total_dur_secs vfps= $vfps\n****\n";
+
+    # Convert the video to a new type (if required).
+    my $converttotype = $self->{'converttotype'};
+    my $converttosize = $self->{'converttosize'};
+
+    # shorten duration prcessed for experimentation purposes
+    my $exp_duration = undef;
+    
+    my $excerpt_duration = $self->{'excerpt_duration'};
+
+    if ((defined $excerpt_duration) && ($excerpt_duration ne "")) {
+	$exp_duration = $excerpt_duration;
+	my ($hh,$mm,$ss,$ms);
+
+	if ($exp_duration =~ m/^(\d\d):(\d\d):(\d\d)\.?(\d)$/) {
+	    $hh = $1;
+	    $mm = $2;
+	    $ss = $3;
+	    $ms = $4;
+	}
+	else {
+	    # assume value is in seconds
+	    $hh = 0;
+	    $mm = 0;
+	    $ss = $exp_duration;
+	    $ms = 0;
+	}
+
+	my $excerpt_dur_in_secs = $hh * 3600 + $mm * 60 + $ss;
+
+	if ($excerpt_dur_in_secs < $total_dur_secs) {
+	    $self->{'num-total-frames'} = $excerpt_dur_in_secs * $vfps; # override calculation for full length duration
+	}
+	else {
+	    # clip is already shorter than requested video excerpt duration
+	    # set exp_duration back to undefined
+	    $exp_duration = undef;
+	}
+    }
+
+
+    if (defined $exp_duration)
+    {
+	print $outhandle "Only encoding first $exp_duration of video.\n";
+	$self->{'exp_duration'} = $exp_duration;
+    }
+
+    my $ascii_only_filenames = $self->{'use_ascii_only_filenames'};
+    $self->init_cache_for_file($filename);
+
+    my $originalfilename = undef;
+    my $type = "unknown";
+
+    my $output_dir = $self->{'cached_dir'};
+    my $ivideo_root = $self->{'cached_file_root'};
+
+    my $convertto_regenerated = 0;
+    if (($converttotype ne "" && $filename =~ m/$converttotype$/i) ||
+	(($converttosize ne "" && $converttosize ne $video_width) || ($converttosize ne "" && $converttosize ne $video_height))) {
+	if ($converttotype eq "") {
+	    # in this block because the video width x height different to original
+	    # => set (for this call to run_convert only) converttotype
+	    #    to input file extension
+	    ($converttotype) = ($filename =~ m/\.(.*?)$/);
+	}
+
+	$originalfilename = $filename;
+
+	$file = "$ivideo_root.$converttotype";
+	$filename = &util::filename_cat($output_dir,$file);
+ 
+	my $s_opt = $self->optional_frame_scale($converttosize,$video_width,$video_height);
+	my $exp_duration = $self->{'exp_duration'};
+	my $t_opt = (defined $exp_duration) ? "-t $exp_duration" : "";
+	    
+	my $main_opts = "-y $t_opt $s_opt";
+
+
+	my $originalfilename_gsdlenv = $self->gsdlhome_independent($originalfilename);
+	my $filename_gsdlenv = $self->gsdlhome_independent($filename);
+
+
+	my $convertto_command = "ffmpeg $main_opts -i \"$originalfilename_gsdlenv\"";
+	$convertto_command .= " -ar 22050" if ($converttotype eq "flv");
+	$convertto_command .= " -y \"$filename_gsdlenv\"";
+
+	my $convertto_result;
+	my $convertto_error;
+	
+	my $convertto_options = { @{$self->{'ffmpeg_monitor'}},
+				  'message_prefix' => "Convert to",
+				  'message' => "Converting video to $converttotype" }; 
+
+	($convertto_regenerated,$convertto_result,$convertto_error)
+	    = $self->run_cached_general_cmd($convertto_command,
+					    $originalfilename,$filename,
+					    $convertto_options);
+						    
+	$type = $converttotype;
+    }
+    
+
+    # Add the video metadata
+
+    my $file_unicode = pack("U0C*", map { ord($_) } split(//,$file)); # force explicitly to unicode
+
+    $file_unicode =~ s/\x{2018}|\x{2019}|\x{201C}|\x{201D}//g; # remove smart quotes as cause problem in URL for video server
+    $file_unicode =~ s/\x{2013}/\-/g; # change en-dash to '-' as again causes problems for video server
+
+##    print STDERR "**** file metadata = ", &gsprintf::debug_unicode_string($file_unicode), "\n";
+
+    
+    # split filename on char if specified
+    if (0) {
+# don't do any more	
+#    if ($file_unicode =~ m/\-/) {
+
+	my @file_split = split(/\s*\-\s*/,$file_unicode);
+	my $creator = shift @file_split;
+	my $title = shift @file_split;
+
+	$title =~ s/\..*?$//;
+	$title =~ s/^(.)/\u$1/;
+
+	$doc_obj->add_utf8_metadata($section,"Title",$title);
+
+	my @creator_split = split(/\s+and\s+/,$creator);
+	foreach my $c (@creator_split) {
+	    $doc_obj->add_utf8_metadata($section,"Creator",$c);
+	}
+    }
+
+    $file = $file_unicode;
+    my $filemeta = $self->filename_to_utf8_metadata($file);
+    my $filemeta_url_safe = $self->url_safe($filemeta);
+
+    $doc_obj->add_utf8_metadata ($section, "Video", $filemeta_url_safe);
+
+    # Also want to set filename as 'Source' metadata to be
+    # consistent with other plugins
+    # $doc_obj->add_utf8_metadata ($section, "Source", $filemeta_url_safe);
+
+
+    if ($video_type ne " ") {
+	$type = $video_type;
+    }
+    
+    $doc_obj->add_metadata ($section, "FileFormat", $type);
+    $doc_obj->add_metadata ($section, "FileSize",   $video_size);
+
+    $doc_obj->add_metadata ($section, "VideoType",     $video_type);
+    $doc_obj->add_metadata ($section, "VideoWidth",    $video_width);
+    $doc_obj->add_metadata ($section, "VideoHeight",   $video_height);
+    $doc_obj->add_metadata ($section, "VideoDuration", $video_duration);
+    $doc_obj->add_metadata ($section, "VideoSize",     $video_size);
+
+    $doc_obj->add_metadata ($section, "VideoCodec",    $vcodec);
+    $doc_obj->add_metadata ($section, "VideoFPS",      $vfps);
+
+    $doc_obj->add_metadata ($section, "AudioType",     $atype);
+    $doc_obj->add_metadata ($section, "AudioFreq",     $afreq);
+    $doc_obj->add_metadata ($section, "AudioChannels", $achan);
+    $doc_obj->add_metadata ($section, "AudioRate",     $arate);
+
+    $doc_obj->add_utf8_metadata ($section, "srclink", 
+			    "<a href=\"_httpprefix_/collect/[collection]/index/assoc/[assocfilepath]/[Video]\">");
+    $doc_obj->add_utf8_metadata ($section, "/srclink", "</a>");
+    $doc_obj->add_metadata ($section, "srcicon", "[VideoType]");
+
+	
+    # Add the original file as an associated file
+	if ($self->{'keep_original_video'} eq "true") {
+		$doc_obj->associate_file($filename,$file,"video/$type",$section);
+	}
+
+
+    if ($self->{'extract_keyframes'}) {
+		$self->extract_keyframes($doc_obj,$originalfilename,$filename);
+    }
+
+    my $streamable_regenerated = 0;
+	
+	# Create a VP6+MP3 streaming ready video file with FFMpeg
+    if ($self->{'enable_streaming'} eq "flv") {	
+		$streamable_regenerated 
+			= $self->enable_full_streaming($doc_obj,
+					   $originalfilename,$filename,
+					   $convertto_regenerated,
+					   $video_width,$video_height);
+    }
+
+	# Create an H264+ACC streaming ready video file with Handbrake
+	if ($self->{'enable_streaming'} eq "mp4") {
+		$streamable_regenerated 
+			= $self->enable_h264_streaming($doc_obj,
+					   $originalfilename,$filename,
+					   $convertto_regenerated,
+					   $video_width,$video_height);
+    }
+
+    my $thumbnailsize = $self->{'thumbnailsize'} || 100;
+    my $thumbnailtype = $self->{'thumbnailtype'} || 'jpg';
+
+
+    if ($self->{'create_thumbnail'} eq "true") {
+
+	my $thumbnail_width; 
+	my $thumbnail_height;
+	
+	if ($video_width>$video_height) {
+	    my $scale_ratio = $video_height / $video_width;
+	    $thumbnail_width = $thumbnailsize;
+	    $thumbnail_height = int($thumbnailsize * $scale_ratio);
+	}
+	else {
+	    my $scale_ratio = $video_width / $video_height;
+	    $thumbnail_width = int($thumbnailsize * $scale_ratio);
+	    $thumbnail_height = $thumbnailsize;
+	}
+	
+	# for some video formats, extracted size needs to be multiple of 2
+	$thumbnail_width  = int($thumbnail_width/2) * 2;
+	$thumbnail_height = int($thumbnail_height/2) * 2;
+
+	$self->extract_thumbnail($doc_obj,$filename,$convertto_regenerated,
+				 $thumbnailtype, 
+				 $thumbnail_width,$thumbnail_height);
+    }
+
+    if ($self->{'extract_keyframes'}) {
+	$self->extract_keyframes_montage($doc_obj,$filename,$thumbnailtype);
+    }
+
+    my $screenviewsize = $self->{'screenviewsize'};
+    my $screenviewtype = $self->{'screenviewtype'} || 'jpeg';
+
+    # Make a screen-sized version of the picture if requested
+    if ($self->{'create_screenview'} eq "true") {
+
+	# To do: if the actual image smaller than the screenview size,
+	# we should use the original !
+
+	my $screenview_width; 
+	my $screenview_height;
+	
+	if ($video_width>$video_height) {
+	    my $scale_ratio = $video_height / $video_width;
+	    $screenview_width = $screenviewsize;
+	    $screenview_height = int($screenviewsize * $scale_ratio);
+	}
+	else {
+	    my $scale_ratio = $video_width / $video_height;
+	    $screenview_width = int($screenviewsize * $scale_ratio);
+	    $screenview_height = $screenviewsize;
+	}
+
+
+	# for some video formats, extracted size needs to be multiple of 2
+	$screenview_width  = int($screenview_width/2) * 2;
+	$screenview_height = int($screenview_height/2) * 2;
+	
+	$self->extract_screenview($doc_obj,$filename, $convertto_regenerated, 
+				  $screenviewtype,
+				  $screenview_width,$screenview_height);
+    }
+
+    return $type;
+}
+
+
+
+
+sub read_into_doc_obj {
+    my $self = shift (@_);  
+    my ($pluginfo, $base_dir, $file, $block_hash, $metadata, $processor, $maxdocs, $total_count, $gli) = @_;
+
+    $self->{'media_type'} = "video";
+
+    my ($rv,$doc_obj) = $self->SUPER::read_into_doc_obj(@_);
+
+    if ($rv != 1) {
+	return ($rv,$doc_obj);
+    }
+
+    my $enable_streaming 
+	= ($self->{'enable_flv_streaming'} || $self->{'enable_mp4_streaming'})
+	? 1 : 0;
+
+    if (($enable_streaming) && ($self->{'extract_keyframes'})) {
+	my $section = $doc_obj->get_top_section();
+	my $oflash_filename = $self->{'oflash_filename'};
+	my ($streamkeyframes_cmd,$ostreamkeyframes_filename) 
+	    = $self->streamkeyframes_cmd($oflash_filename,$doc_obj,$section);
+
+	my $verbosity = $self->{'verbosity'};
+
+	my $streamkeyframes_options = { @{$self->{'flvtool2_monitor'}},
+					'message_prefix' => "Stream Keyframes",
+					'message' => "Reprocessing video stream to add keyframes on timeline" }; 
+
+	$self->run_general_cmd($streamkeyframes_cmd,$streamkeyframes_options);
+    }
+
+    my ($filename_full_path, $filename_no_path) = &util::get_full_filenames($base_dir, $file);
+    my $section = $doc_obj->get_top_section();
+
+    $self->title_fallback($doc_obj,$section,$filename_no_path);
+    
+    $self->{'media_type'} = undef;
+
+    return ($rv,$doc_obj);
+}
+
+
+
+
+sub output_distributed_keyframes
+{
+    my ($self) = shift @_;
+    my ($timeline,$num_dist_frames) = @_;
+
+    my $num_total_frames = $self->{'num-total-frames'};
+
+    my $keep_timeline = {};
+
+    my $frame_delta;
+    if ($num_dist_frames eq "all") {
+	$frame_delta = undef;
+    }
+    else {
+	$frame_delta = $num_total_frames/$num_dist_frames;
+    }
+
+    my $closest_to = $frame_delta;
+    my $prev_t = 0;
+
+#    print STDERR "*** num total frames  = $num_total_frames\n";
+#    print STDERR "*** frame delta = $frame_delta\n";
+
+    my $keep_keyframe_num = 1;
+
+    foreach my $curr_t (sort { $a <=> $b } keys %$timeline)
+    {
+#	print STDERR "*** curr_t = $curr_t\n";
+	
+	my $timeline_rec = undef;
+
+	if (!defined $closest_to) {
+	    $timeline_rec = $timeline->{$curr_t};
+	    $keep_timeline->{$curr_t} = $timeline_rec;
+	}
+	elsif ($curr_t>$closest_to) {
+	    # decide if previous t (prev_t_ or current t (curr_t) is closest
+
+	    my $prev_t_dist = $closest_to - $prev_t;
+	    my $curr_t_dist = $curr_t - $closest_to;
+
+	    if ($curr_t_dist <= $prev_t_dist)
+	    {
+		$timeline_rec = $timeline->{$curr_t};
+		$keep_timeline->{$curr_t} = $timeline_rec;
+
+	    }
+	    else
+	    {
+		$timeline_rec = $timeline->{$prev_t};
+		$keep_timeline->{$prev_t} = $timeline_rec;
+	    }
+
+	    $closest_to += $frame_delta;
+	}
+
+	if (defined $timeline_rec) {
+
+	    my $name      = $timeline_rec->{'name'};
+	    my $timestamp = $timeline_rec->{'timestamp'};
+	    my $thumb     = $timeline_rec->{'thumb'};
+	    my $keyframe_num = $timeline_rec->{'keyframenum'};
+
+
+	    print CUEOUT "  <metatag event=\"onCuePoint\" overwrite=\"true\">\n";
+	    print CUEOUT "    <name>$name</name>\n";
+	    print CUEOUT "    <timestamp>$timestamp</timestamp>\n";
+	    print CUEOUT "    <parameters>\n";
+	    print CUEOUT "      <thumb>$thumb</thumb>\n";
+	    print CUEOUT "      <keyframeNum>$keyframe_num</keyframeNum>\n";
+	    print CUEOUT "      <keepKeyframeNum>$keep_keyframe_num</keepKeyframeNum>\n";
+	    print CUEOUT "    </parameters>\n";
+	    print CUEOUT "    <type>navigation</type>\n";
+	    print CUEOUT "  </metatag>\n";
+
+#	    my $testtime = $timestamp+1000;
+#	    print CUEOUT "  <metatag event=\"onCuePoint\" overwrite=\"true\">\n";
+#	    print CUEOUT "    <name>Test $name</name>\n";
+#	    print CUEOUT "    <timestamp>$testtime</timestamp>\n";
+#	    print CUEOUT "    <parameters>\n";
+#	    print CUEOUT "      <thumb>$thumb</thumb>\n";
+#	    print CUEOUT "      <secnum>1</secnum>\n";
+#	    print CUEOUT "      <subsecnum>0</subsecnum>\n";
+#	    print CUEOUT "    </parameters>\n";
+#	    print CUEOUT "    <type>event</type>\n";
+#	    print CUEOUT "  </metatag>\n";
+
+	    $keep_keyframe_num++;
+
+	}
+	$prev_t = $curr_t;
+    }
+
+    $self->{'keep_keyframe_timeline'} = $keep_timeline;
+}
+
+
+sub StartDocument {$_[0]->{'PluginObj'}->xml_start_document(@_);}
+sub XMLDecl {$_[0]->{'PluginObj'}->xml_xmldecl(@_);}
+sub Entity {$_[0]->{'PluginObj'}->xml_entity(@_);}
+sub Doctype {$_[0]->{'PluginObj'}->xml_doctype(@_);}
+sub StartTag {$_[0]->{'PluginObj'}->xml_start_tag(@_);}
+sub EndTag {$_[0]->{'PluginObj'}->xml_end_tag(@_);}
+sub Text {$_[0]->{'PluginObj'}->xml_text(@_);}
+sub PI {$_[0]->{'PluginObj'}->xml_pi(@_);}
+sub EndDocument {$_[0]->{'PluginObj'}->xml_end_document(@_);}
+sub Default {$_[0]->{'PluginObj'}->xml_default(@_);}
+
+
+# This Char function overrides the one in XML::Parser::Stream to overcome a
+# problem where $expat->{Text} is treated as the return value, slowing
+# things down significantly in some cases.
+sub Char {
+    use bytes;  # Necessary to prevent encoding issues with XML::Parser 2.31+
+    $_[0]->{'Text'} .= $_[1];
+    return undef;
+}
+
+sub xml_start_document {
+    my $self = shift(@_);
+    my ($expat, $name, $sysid, $pubid, $internal) = @_;
+
+}
+
+# Called for XML declarations
+sub xml_xmldecl {
+    my $self = shift(@_);
+    my ($expat, $version, $encoding, $standalone) = @_;
+}
+
+# Called for XML entities
+sub xml_entity {
+  my $self = shift(@_);
+  my ($expat, $name, $val, $sysid, $pubid, $ndata) = @_;
+}
+
+
+# Called for DOCTYPE declarations - use die to bail out if this doctype
+# is not meant for this plugin
+sub xml_doctype {
+    my $self = shift(@_);
+    my ($expat, $name, $sysid, $pubid, $internal) = @_;
+
+    # This test used to be done in xml_start_document
+    # Moved to here as seems more logical
+
+    if ($name !~ "seg") {	
+	die "VideoPlugin: Root tag $name does not match expected <seg>";
+    }
+}
+
+
+sub xml_start_tag {
+    my $self = shift(@_);
+    my ($expat, $element) = @_;
+
+    my %attr = %_;
+    
+    if ($element eq "seg") {
+	$self->{'keyframe_index'} = 0;
+	$self->{'keyframe_fnames'} = [];
+	$self->{'keyframe_timeline'} = {};
+
+	#$self->{'flowplayer_thumblist'} = "thumbs: \\[";
+
+	my $output_dir = $self->{'cached_dir'};
+	my $cue_filename = &util::filename_cat($output_dir,"on_cue.xml");
+
+	open(CUEOUT,">$cue_filename")
+	    || die "Unable to open $cue_filename: $!\n";
+	print CUEOUT "<tags>\n";
+    }
+    elsif ($element eq "trans") {
+	my $trans_type = $attr{'type'};
+	my $pre_frame_num = $attr{'preFNum'};
+	my $post_frame_num = $attr{'postFNum'};
+
+	my $avg_frame_num = int(($pre_frame_num+$post_frame_num)/2.0)+1;
+
+	my $output_dir = $self->{'cached_dir'};
+	my $ivideo_root = $self->{'cached_file_root'};
+
+	my $keyframe_index = $self->{'keyframe_index'};
+
+	my $fps = $self->{'video-fps'};
+
+	if ($keyframe_index==0)
+	{
+	    # hive actually generates one extra keyframe at the start,
+	    # which is half way between frame 0 and the frist pre_frame
+
+	    my $thumb_file = sprintf("%s_%04d.jpg",$ivideo_root,$keyframe_index);
+	    my $thumb_filename = &util::filename_cat($output_dir,$thumb_file);
+	    push(@{$self->{'keyframe_fnames'}},$thumb_file);
+
+	    my $half_frame_num = $pre_frame_num/2.0;
+	    my $time_msec = ($half_frame_num / $fps) * 1000;
+
+#	    print CUEOUT "  <metatag event=\"onCuePoint\" overwrite=\"true\">\n";
+#	    print CUEOUT "    <name>Keyframe $keyframe_index</name>\n";
+#	    print CUEOUT "    <timestamp>$time_msec</timestamp>\n";
+#	    print CUEOUT "    <parameters>\n";
+#	    print CUEOUT "      <thumb>$thumb_file</thumb>\n";
+#	    print CUEOUT "    </parameters>\n";
+#	    print CUEOUT "    <type>navigation</type>\n";
+#	    print CUEOUT "  </metatag>\n";
+
+
+	    my $timeline_rec = { 'name'=> "Keyframe $keyframe_index",
+				 'keyframeindex' => $keyframe_index,
+				 'timestamp' => $time_msec,
+				 'thumb' => $thumb_file,
+				 'keyframenum' => $keyframe_index};
+
+	    $self->{'keyframe_timeline'}->{$half_frame_num}=$timeline_rec;
+	}
+
+	$keyframe_index++;
+
+	my $thumb_file = sprintf("%s_%04d.jpg",$ivideo_root,$keyframe_index);
+	my $thumb_filename = &util::filename_cat($output_dir,$thumb_file);
+	push(@{$self->{'keyframe_fnames'}},$thumb_file);
+
+	my $time_msec = (($avg_frame_num) / $fps) * 1000;
+	my $time_sec = (($avg_frame_num)/ $fps);
+
+#	print CUEOUT "  <metatag event=\"onCuePoint\" overwrite=\"true\">\n";
+#	print CUEOUT "    <name>Keyframe $keyframe_index</name>\n";
+#	print CUEOUT "    <timestamp>$time_msec</timestamp>\n";
+#	print CUEOUT "    <parameters>\n";
+#	print CUEOUT "      <thumb>$thumb_file</thumb>\n";
+#	print CUEOUT "    </parameters>\n";
+#	print CUEOUT "    <type>navigation</type>\n";
+#	print CUEOUT "  </metatag>\n";
+
+
+	my $timeline_rec = { 'name'=> "Keyframe $keyframe_index",
+			     'keyframeindex' => $keyframe_index,
+			     'timestamp' => $time_msec,
+			     'thumb' => $thumb_file,
+			     'keyframenum' => $keyframe_index};
+
+	$self->{'keyframe_timeline'}->{$avg_frame_num}=$timeline_rec;
+
+	# $self->{'flowplayer_thumblist'} .= "\\{ thumbNail: '$thumb_file', time: $time_sec \\},"; 
+
+	$self->{'keyframe_index'}  = $keyframe_index;
+    }
+}
+
+sub xml_end_tag {
+    my $self = shift(@_);
+    my ($expat, $element) = @_;
+
+    if ($element eq "seg") {
+
+	$self->output_distributed_keyframes($self->{'keyframe_timeline'},$self->{'keep_keyframes'});
+
+
+	print CUEOUT "</tags>\n";
+	close(CUEOUT);
+
+	#$self->{'flowplayer_thumblist'} .= "\\]";
+    }
+}
+
+
+
+
+
+# Called just before start or end tags with accumulated non-markup text in
+# the $_ variable.
+sub xml_text {
+    my $self = shift(@_);
+    my ($expat) = @_;
+}
+
+# Called for processing instructions. The $_ variable will contain a copy
+# of the pi.
+sub xml_pi {
+    my $self = shift(@_);
+    my ($expat, $target, $data) = @_;
+}
+
+# Called at the end of the XML document.
+sub xml_end_document {
+    my $self = shift(@_);
+    my ($expat) = @_;
+
+    # ****
+    # $self->close_document();
+}
+
+
+# Called for any characters not handled by the above functions.
+sub xml_default {
+    my $self = shift(@_);
+    my ($expat, $text) = @_;
+}
+
+
+1;
+
+
+
+
+
+
+
+
+
+
+
Index: gs2-extensions/video-and-audio/trunk/src/perllib/strings.properties
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/perllib/strings.properties	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/perllib/strings.properties	(revision 23036)
@@ -0,0 +1,6 @@
+
+VideoPlugin.desc:This plugin processes video files, adding basic metadata
+
+MultimediaConverter.ffmpegnotinstalled:FFmpeg not installed
+
+MultimediaConverter.noconversionavailable:Video conversion not available
Index: gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/CASCADE-MAKE.bat
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/CASCADE-MAKE.bat	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/CASCADE-MAKE.bat	(revision 23036)
@@ -0,0 +1,19 @@
+@echo off
+
+for /D %%d IN (packages) do (
+  echo   Running %%d\CASCADE-MAKE.bat %*
+
+  (cd %%d && call CASCADE-MAKE.bat %* && cd ..)
+
+  if not ERRORLEVEL 0 (
+    echo   Error encountered running %%d\CASCADE-MAKE.bat
+    goto ErrorExitPos1
+  )
+)
+goto End
+
+:ErrorExitPos1
+set ERRORLEVEL=1
+
+:End
+
Index: gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/CASCADE-MAKE.sh
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/CASCADE-MAKE.sh	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/CASCADE-MAKE.sh	(revision 23036)
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+
+for d in packages ; do
+  echo "  Running $d/CASCADE-MAKE.sh $*"
+
+  (cd $d; ./CASCADE-MAKE.sh $*)
+
+  if [ $? != 0 ] ; then
+    echo "  Error encountered running $d/CASCADE-MAKE.sh"
+    exit 1
+  fi
+done
+
Index: gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/CASCADE-MAKE.bat
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/CASCADE-MAKE.bat	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/CASCADE-MAKE.bat	(revision 23036)
@@ -0,0 +1,20 @@
+@echo off
+
+for /D %%d IN (MOD_FLVX FLOWPLAYER) do (
+  echo     Running CASCADE-MAKE\%%d.bat %*
+
+  call CASCADE-MAKE\%%d.bat %*
+
+  if not ERRORLEVEL 0 (
+    echo   Error encountered running CASCADE-MAKE\%%d.bat
+    goto ErrorExitPos1
+  )
+)
+
+goto End
+
+:ErrorExitPos1
+set ERRORLEVEL=1
+goto End
+
+:End
Index: gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/CASCADE-MAKE.sh
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/CASCADE-MAKE.sh	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/CASCADE-MAKE.sh	(revision 23036)
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+
+for d in MOD_FLVX FLOWPLAYER ; do
+  echo "    Running CASCADE-MAKE/$d.sh $*"
+
+  ./CASCADE-MAKE/$d.sh $*
+
+  if [ $? != 0 ] ; then
+    echo "  Error encountered running CASCADE-MAKE/$d.sh"
+    exit 1
+  fi
+done
+
+
Index: gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/CASCADE-MAKE/FLOWPLAYER.bat
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/CASCADE-MAKE/FLOWPLAYER.bat	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/CASCADE-MAKE/FLOWPLAYER.bat	(revision 23036)
@@ -0,0 +1,40 @@
+@echo off
+
+set package=flowplayer
+set version=-gs
+
+set progname=%0
+
+REM call ../../cascade-make/bin/script/test-setup.bash
+REM call ../../cascade-make/lib/cascade-lib.bash
+
+set prefix=%GEXTVIDEO_INSTALLED%/%GSDLOS%
+set webprefix=..\..\web
+
+REM opt_run_untar $force_untar $auto_untar $package $version
+
+REM no make configure, or compile needed
+
+REM install
+if "%install%" == "1" (
+  if not exist "%webprefix%\flash" (
+    if not exist "%package%version%" (
+      echo Warning: %package%%version% does not appear to be unpacked
+    ) else (
+      echo Installing Flowplayer into %webprefix% folder
+      mkdir %webprefix%\flash
+      xcopy /Y %package%%version%\* %webprefix%\flash
+    )
+  )
+)
+
+REM installclean
+if "%installclean%" == "1" (
+  del /F %webprefix%\flash 
+)
+
+
+REM opt_run_tarclean $tarclean $package $version
+
+
+
Index: gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/CASCADE-MAKE/FLOWPLAYER.sh
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/CASCADE-MAKE/FLOWPLAYER.sh	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/CASCADE-MAKE/FLOWPLAYER.sh	(revision 23036)
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+package=flowplayer
+version=-gs
+
+progname=$0
+
+source ../../cascade-make/bin/script/test-setup.bash
+source ../../cascade-make/lib/cascade-lib.bash
+
+prefix=$GEXTVIDEO_INSTALLED/$GSDLOS
+webprefix=../../web
+
+opt_run_untar $force_untar $auto_untar $package $version
+
+# no make configure, or compile needed
+
+#install
+if [ $install = "1" ] ; then
+  if [ ! -d $webprefix/flash ] ; then
+    echo "Installing Flowplayer into $webprefix folder"
+    /bin/cp -rf $package$version $webprefix/flash
+  fi
+fi
+
+#installclean
+if [ $installclean = "1" ] ; then
+  /bin/rm -rf $webprefix/flash 
+fi
+
+
+
+opt_run_tarclean $tarclean $package $version
+
+
+
Index: gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/CASCADE-MAKE/GSLIGHTTPD.sh
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/CASCADE-MAKE/GSLIGHTTPD.sh	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/CASCADE-MAKE/GSLIGHTTPD.sh	(revision 23036)
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+progname=$0
+
+source ../../bin/script/test-setup.bash
+source ../../lib/cascade-lib.bash
+
+prefix=$GEXTVIDEO_INSTALLED/server
+
+
+# Ensure that the necessary directories exist within the 'installed' area
+
+
+for d in logs htdocs conf ; do
+  if [ ! -e $prefix/$d ] ; then
+    echo "Creating $prefix/$d"
+    mkdir $prefix/$d
+  fi
+done
+
+# Install necessary config files to work with Greenstone
+
+gsvidinstalled=$prefix
+
+( cd gslighttpd ; \
+  cat lighttpd.conf.in \
+  | sed "s%@GSVIDINSTALLED@%$gsvidinstalled%g" \
+  > $prefix/conf/lighttpd.conf )
+
+if [ $? != 0 ] ; then
+    echo "      Error encountered running *customise install* stage of GSLIGHTTPD.sh"
+    exit 1
+fi
+
+
+( cd gslighttpd ; \
+  /bin/cp index.html $prefix/htdocs/. )
+
+if [ $? != 0 ] ; then
+    echo "      Error encountered running *customise install* stage of GSLIGHTTPD.sh"
+    exit 1
+fi
+
Index: gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/CASCADE-MAKE/LIGHTTPD.sh
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/CASCADE-MAKE/LIGHTTPD.sh	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/CASCADE-MAKE/LIGHTTPD.sh	(revision 23036)
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+package=lighttpd
+version=-1.4.15
+
+progname=$0
+
+source ../../bin/script/test-setup.bash
+source ../../lib/cascade-lib.bash
+
+prefix=$GEXTVIDEO_INSTALLED/server
+
+
+opt_run_untar $force_untar $auto_untar $package $version
+opt_run_configure $force_config $auto_config $package $version "$prefix"
+
+opt_run_make $compile   $package $version
+opt_run_make $install   $package $version "install"
+opt_run_make $clean     $package $version "clean"
+opt_run_make $distclean $package $version "distclean"
+
+opt_run_tarclean $tarclean $package $version
Index: gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/CASCADE-MAKE/MOD_FLVX.bat
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/CASCADE-MAKE/MOD_FLVX.bat	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/CASCADE-MAKE/MOD_FLVX.bat	(revision 23036)
@@ -0,0 +1,23 @@
+@echo off
+
+set package=mod_flvx
+set version=-14-apr-2009
+
+set progname=%0
+
+REM call ../../cascade-make/bin/script/test-setup.bash
+REM call ../../cascade-make/lib/cascade-lib.bash
+
+set prefix=%GEXTVIDEO_INSTALLED%\%GSDLOS%
+
+REM opt_run_untar $force_untar $auto_untar $package $version
+
+REM opt_run_make $compile   $package "$version"
+REM opt_run_make $install   $package "$version" "install"
+REM opt_run_make $clean     $package "$version" "clean"
+
+REM opt_run_tarclean $tarclean $package $version
+
+echo *****
+echo ***** Need to add compile instructions for mod_flvx
+echo *****
Index: gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/CASCADE-MAKE/MOD_FLVX.sh
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/CASCADE-MAKE/MOD_FLVX.sh	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/CASCADE-MAKE/MOD_FLVX.sh	(revision 23036)
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+package=mod_flvx
+version=-14-apr-2009
+
+progname=$0
+
+source ../../cascade-make/bin/script/test-setup.bash
+source ../../cascade-make/lib/cascade-lib.bash
+
+prefix=$GEXTVIDEO_INSTALLED/$GSDLOS
+
+opt_run_untar $force_untar $auto_untar $package $version
+
+opt_run_make $compile   $package "$version"
+opt_run_make $install   $package "$version" "install"
+opt_run_make $clean     $package "$version" "clean"
+
+opt_run_tarclean $tarclean $package $version
Index: gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/gslighttpd/index.html
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/gslighttpd/index.html	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/gslighttpd/index.html	(revision 23036)
@@ -0,0 +1,13 @@
+<html>
+  <head>
+    <title>Greenstone Video Streaming Server</title>
+  </head>
+
+  <body>
+    <h3>Greenstone Video Streaming Server</h3>
+
+    <p>You are running the lighthtppd server with flash streaming enabled.</p>
+
+  </body>
+
+</html>
Index: gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/gslighttpd/lighttpd.conf.in
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/gslighttpd/lighttpd.conf.in	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/runtime-srcpack/packages/gslighttpd/lighttpd.conf.in	(revision 23036)
@@ -0,0 +1,327 @@
+# lighttpd configuration file
+#
+# use it as a base for lighttpd 1.0.0 and above
+#
+# $Id: lighttpd.conf,v 1.7 2004/11/03 22:26:05 weigon Exp $
+
+############ Options you really have to take care of ####################
+
+## modules to load
+# at least mod_access and mod_accesslog should be loaded
+# all other module should only be loaded if really neccesary
+# - saves some time
+# - saves memory
+server.modules              = (
+#                               "mod_rewrite",
+#                               "mod_redirect",
+#                               "mod_alias",
+                                "mod_access",
+				"mod_flv_streaming",
+#                               "mod_cml",
+#                               "mod_trigger_b4_dl",
+#                               "mod_auth",
+#                               "mod_status",
+#                               "mod_setenv",
+#                               "mod_fastcgi",
+#                               "mod_proxy",
+#                               "mod_simple_vhost",
+#                               "mod_evhost",
+#                               "mod_userdir",
+                                "mod_cgi",
+#                               "mod_compress",
+#                               "mod_ssi",
+#                               "mod_usertrack",
+#                               "mod_expire",
+#                               "mod_secdownload",
+#                               "mod_rrdtool",
+                                "mod_accesslog" )
+
+
+## a static document-root, for virtual-hosting take look at the
+## server.virtual-* options
+server.document-root        = "@GSVIDINSTALLED@/htdocs/"
+
+## where to send error-messages to
+server.errorlog             = "@GSVIDINSTALLED@/logs/error.log"
+
+# files to check for if .../ is requested
+index-file.names            = ( "index.php", "index.html",
+                                "index.htm", "default.htm" )
+
+## set the event-handler (read the performance section in the manual)
+# server.event-handler = "freebsd-kqueue" # needed on OS X
+
+# mimetype mapping
+mimetype.assign             = (
+  ".pdf"          =>      "application/pdf",
+  ".sig"          =>      "application/pgp-signature",
+  ".spl"          =>      "application/futuresplash",
+  ".class"        =>      "application/octet-stream",
+  ".ps"           =>      "application/postscript",
+  ".torrent"      =>      "application/x-bittorrent",
+  ".dvi"          =>      "application/x-dvi",
+  ".gz"           =>      "application/x-gzip",
+  ".pac"          =>      "application/x-ns-proxy-autoconfig",
+  ".swf"          =>      "application/x-shockwave-flash",
+  ".tar.gz"       =>      "application/x-tgz",
+  ".tgz"          =>      "application/x-tgz",
+  ".tar"          =>      "application/x-tar",
+  ".zip"          =>      "application/zip",
+  ".mp3"          =>      "audio/mpeg",
+  ".m3u"          =>      "audio/x-mpegurl",
+  ".wma"          =>      "audio/x-ms-wma",
+  ".wax"          =>      "audio/x-ms-wax",
+  ".ogg"          =>      "application/ogg",
+  ".wav"          =>      "audio/x-wav",
+  ".gif"          =>      "image/gif",
+  ".jpg"          =>      "image/jpeg",
+  ".jpeg"         =>      "image/jpeg",
+  ".png"          =>      "image/png",
+  ".xbm"          =>      "image/x-xbitmap",
+  ".xpm"          =>      "image/x-xpixmap",
+  ".xwd"          =>      "image/x-xwindowdump",
+  ".css"          =>      "text/css",
+  ".html"         =>      "text/html",
+  ".htm"          =>      "text/html",
+  ".js"           =>      "text/javascript",
+  ".asc"          =>      "text/plain",
+  ".c"            =>      "text/plain",
+  ".cpp"          =>      "text/plain",
+  ".log"          =>      "text/plain",
+  ".conf"         =>      "text/plain",
+  ".text"         =>      "text/plain",
+  ".txt"          =>      "text/plain",
+  ".dtd"          =>      "text/xml",
+  ".xml"          =>      "text/xml",
+  ".mpeg"         =>      "video/mpeg",
+  ".mpg"          =>      "video/mpeg",
+  ".mov"          =>      "video/quicktime",
+  ".qt"           =>      "video/quicktime",
+  ".avi"          =>      "video/x-msvideo",
+  ".asf"          =>      "video/x-ms-asf",
+  ".asx"          =>      "video/x-ms-asf",
+  ".wmv"          =>      "video/x-ms-wmv",
+  ".bz2"          =>      "application/x-bzip",
+  ".tbz"          =>      "application/x-bzip-compressed-tar",
+  ".tar.bz2"      =>      "application/x-bzip-compressed-tar"
+ )
+
+# Use the "Content-Type" extended attribute to obtain mime type if possible
+#mimetype.use-xattr        = "enable"
+
+
+## send a different Server: header
+## be nice and keep it at lighttpd
+# server.tag                 = "lighttpd"
+
+#### accesslog module
+accesslog.filename          = "@GSVIDINSTALLED@/logs/access.log"
+
+## deny access the file-extensions
+#
+# ~    is for backupfiles from vi, emacs, joe, ...
+# .inc is often used for code includes which should in general not be part
+#      of the document-root
+url.access-deny             = ( "~", ".inc" )
+
+#!!!!!
+#$HTTP["url"] =~ "\.pdf$" {
+#  server.range-requests = "disable"
+#}
+
+##
+# which extensions should not be handle via static-file transfer
+#
+# .php, .pl, .fcgi are most often handled by mod_fastcgi or mod_cgi
+static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )
+
+######### Options that are good to be but not neccesary to be changed #######
+
+## bind to port (default: 80)
+server.port                = 8081
+
+## bind to localhost (default: all interfaces)
+#server.bind                = "grisu.home.kneschke.de"
+
+## error-handler for status 404
+#server.error-handler-404   = "/error-handler.html"
+#server.error-handler-404   = "/error-handler.php"
+
+## to help the rc.scripts
+#server.pid-file            = "/var/run/lighttpd.pid"
+
+
+flv-streaming.extensions = ( ".flv" )
+
+###### virtual hosts
+##
+##  If you want name-based virtual hosting add the next three settings and load
+##  mod_simple_vhost
+##
+## document-root =
+##   virtual-server-root + virtual-server-default-host + virtual-server-docroot
+## or
+##   virtual-server-root + http-host + virtual-server-docroot
+##
+#simple-vhost.server-root   = "/home/weigon/wwwroot/servers/"
+#simple-vhost.default-host  = "grisu.home.kneschke.de"
+#simple-vhost.document-root = "/pages/"
+
+
+##
+## Format: <errorfile-prefix><status-code>.html
+## -> ..../status-404.html for 'File not found'
+#server.errorfile-prefix    = "/home/weigon/projects/lighttpd/doc/status-"
+
+## virtual directory listings
+dir-listing.activate       = "enable"
+
+## enable debugging
+#debug.log-request-header   = "enable"
+#debug.log-response-header  = "enable"
+#debug.log-request-handling = "enable"
+#debug.log-file-not-found   = "enable"
+
+### only root can use these options
+#
+# chroot() to directory (default: no chroot() )
+#server.chroot              = "/"
+
+## change uid to <uid> (default: don't care)
+#server.username            = "wwwrun"
+
+## change uid to <uid> (default: don't care)
+#server.groupname           = "wwwrun"
+
+#### compress module
+#compress.cache-dir         = "@GSVIDINSTALLED@/cache/compress/"
+#compress.filetype          = ("text/plain", "text/html")
+
+#### proxy module
+## read proxy.txt for more info
+#proxy.server               = ( ".php" =>
+#                               ( "localhost" =>
+#                                 (
+#                                   "host" => "192.168.0.101",
+#                                   "port" => 80
+#                                 )
+#                               )
+#                             )
+
+#### fastcgi module
+## read fastcgi.txt for more info
+## for PHP don't forget to set cgi.fix_pathinfo = 1 in the php.ini
+#fastcgi.server             = ( ".php" =>
+#                               ( "localhost" =>
+#                                 (
+#                                   "socket" => "/tmp/php-fastcgi.socket",
+#                                   "bin-path" => "/usr/local/bin/php"
+#                                 )
+#                               )
+#                            )
+
+#### CGI module
+cgi.assign                 = ( ".pl"  => "/usr/bin/perl",
+                               ".cgi" => "/usr/bin/perl" )
+
+
+#### SSL engine
+#ssl.engine                 = "enable"
+#ssl.pemfile                = "server.pem"
+
+#### status module
+#status.status-url          = "/server-status"
+#status.config-url          = "/server-config"
+
+#### auth module
+## read authentication.txt for more info
+#auth.backend               = "plain"
+#auth.backend.plain.userfile = "lighttpd.user"
+#auth.backend.plain.groupfile = "lighttpd.group"
+
+#auth.backend.ldap.hostname = "localhost"
+#auth.backend.ldap.base-dn  = "dc=my-domain,dc=com"
+#auth.backend.ldap.filter   = "(uid=$)"
+
+#auth.require               = ( "/server-status" =>
+#                               (
+#                                 "method"  => "digest",
+#                                 "realm"   => "download archiv",
+#                                 "require" => "user=jan"
+#                               ),
+#                               "/server-config" =>
+#                               (
+#                                 "method"  => "digest",
+#                                 "realm"   => "download archiv",
+#                                 "require" => "valid-user"
+#                               )
+#                             )
+
+#### url handling modules (rewrite, redirect, access)
+#url.rewrite                = ( "^/$"             => "/server-status" )
+#url.redirect               = ( "^/wishlist/(.+)" => "http://www.123.org/$1" )
+#### both rewrite/redirect support back reference to regex conditional using %n
+#$HTTP["host"] =~ "^www\.(.*)" {
+#  url.redirect            = ( "^/(.*)" => "http://%1/$1" )
+#}
+
+#
+# define a pattern for the host url finding
+# %% => % sign
+# %0 => domain name + tld
+# %1 => tld
+# %2 => domain name without tld
+# %3 => subdomain 1 name
+# %4 => subdomain 2 name
+#
+#evhost.path-pattern        = "/home/storage/dev/www/%3/htdocs/"
+
+#### expire module
+#expire.url                 = ( "/buggy/" => "access 2 hours", "/asdhas/" => "access plus 1 seconds 2 minutes")
+
+#### ssi
+#ssi.extension              = ( ".shtml" )
+
+#### rrdtool
+#rrdtool.binary             = "/usr/bin/rrdtool"
+#rrdtool.db-name            = "/var/www/lighttpd.rrd"
+
+#### setenv
+#setenv.add-request-header  = ( "TRAV_ENV" => "mysql://user@host/db" )
+#setenv.add-response-header = ( "X-Secret-Message" => "42" )
+
+## for mod_trigger_b4_dl
+# trigger-before-download.gdbm-filename = "/home/weigon/testbase/trigger.db"
+# trigger-before-download.memcache-hosts = ( "127.0.0.1:11211" )
+# trigger-before-download.trigger-url = "^/trigger/"
+# trigger-before-download.download-url = "^/download/"
+# trigger-before-download.deny-url = "http://127.0.0.1/index.html"
+# trigger-before-download.trigger-timeout = 10
+
+## for mod_cml
+## don't forget to add index.cml to server.indexfiles
+# cml.extension               = ".cml"
+# cml.memcache-hosts          = ( "127.0.0.1:11211" )
+
+#### variable usage:
+## variable name without "." is auto prefixed by "var." and becomes "var.bar"
+#bar = 1
+#var.mystring = "foo"
+
+## integer add
+#bar += 1
+## string concat, with integer cast as string, result: "www.foo1.com"
+#server.name = "www." + mystring + var.bar + ".com"
+## array merge
+#index-file.names = (foo + ".php") + index-file.names
+#index-file.names += (foo + ".php")
+
+#### include
+#include /etc/lighttpd/lighttpd-inc.conf
+## same as above if you run: "lighttpd -f /etc/lighttpd/lighttpd.conf"
+#include "lighttpd-inc.conf"
+
+#### include_shell
+#include_shell "echo var.a=1"
+## the above is same as:
+#var.a=1
Index: gs2-extensions/video-and-audio/trunk/src/setup.bash
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/setup.bash	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/setup.bash	(revision 23036)
@@ -0,0 +1,45 @@
+
+extdesc="the Video and Audio Extension"
+
+full_setup=`pwd`/${BASH_SOURCE}
+fulldir=${full_setup%/*}
+fulldir=${fulldir%/.}
+
+source cascade-make/lib/cascade-lib.bash
+
+if [ "x$GEXTVIDEO" = "x" ] ; then
+  export GEXTVIDEO=$fulldir
+  export GEXTVIDEO_INSTALLED=$GEXTVIDEO/$GSDLOS
+
+  export PATH=$GEXTVIDEO_INSTALLED/bin:$PATH
+
+  if [ "x$LD_LIBRARY_PATH" = "x" ] ; then
+    export LD_LIBRARY_PATH=$GEXTVIDEO_INSTALLED/lib
+  else
+    export LD_LIBRARY_PATH=$GEXTVIDEO_INSTALLED/lib:$LD_LIBRARY_PATH
+  fi
+  if [ "x$DYLD_LIBRARY_PATH" = "x" ] ; then
+    export DYLD_LIBRARY_PATH=$GEXTVIDEO_INSTALLED/lib
+  else
+    export DYLD_LIBRARY_PATH=$GEXTVIDEO_INSTALLED/lib:$DYLD_LIBRARY_PATH
+  fi
+
+  if [ -e "devel-srcpack" ] ; then
+    export LD_LIBRARY_PATH=$GEXTVIDEO_INSTALLED/devel/lib:$LD_LIBRARY_PATH
+    export DYLD_LIBRARY_PATH=$GEXTVIDEO_INSTALLED/devel/lib:$DYLD_LIBRARY_PATH
+  fi
+
+  
+  extdir=${GEXTVIDEO##*/}
+
+  if [ "x$GSDLEXTS" = "x" ] ; then
+    export GSDLEXTS=$extdir
+  else 
+    export GSDLEXTS=$GSDLEXTS:$extdir
+  fi
+
+
+  echo "+Your environment is now setup for $extdesc"
+else
+  echo "+Your environment is already setup for $extdesc"
+fi
Index: gs2-extensions/video-and-audio/trunk/src/setup.bat
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/setup.bat	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/setup.bat	(revision 23036)
@@ -0,0 +1,57 @@
+@echo off
+pushd "%CD%"
+CD /D "%~dp0"
+set GSDLLANG=en
+set extdesc="the Video and Audio Extension"
+
+if "%OS%" == "Windows_NT" goto WinNT
+if "%OS%" == "" goto Win95
+if "%GSDLLANG%" == "en" echo Setup failed - your PATH has not been set
+if "%GSDLLANG%" == "es" echo No se pudo realizar la configuraci¢n - no se ha establecido la RUTA.
+if "%GSDLLANG%" == "fr" echo Echc de l'installation - votre variable PATH n'a pas t ajuste
+if "%GSDLLANG%" == "ru" echo áâ ­®¢ª  ­¥ ã€ « áì -  ­¥ ¡ë« ãáâ ­®¢«¥­
+goto End
+
+:WinNT
+set GEXTVIDEO=%CD%
+set GEXTVIDEO_INSTALLED=%GEXTVIDEO%\windows
+
+set PATH=%GEXTVIDEO_INSTALLED%\bin;%PATH%
+set GS_CP_SET=yes
+goto Success
+
+:Win95
+if "%1" == "SetEnv" goto Win95Env
+REM We'll invoke a second copy of the command processor to make
+REM sure there's enough environment space
+COMMAND /E:2048 /K %0 SetEnv
+goto End
+
+:Win95Env
+set GEXTVIDEO=%CD%
+set PATH="%GEXTVIDEO_INSTALLED%\bin";"%PATH%"
+set GS_CP_SET=yes
+goto Success
+
+:Success
+
+set fulldir=%~dp0
+
+:: strip off everything up to (and including) ext dir
+set extdir=%fulldir:*ext\=%
+
+:: remove trailing slash
+set extdir=%extdir:\=%
+
+
+if "x%GSDLEXTS%" == "x" (
+  set GSDLEXTS=%extdir%
+) else (
+  set GSDLEXTS=%GSDLEXTS%:%extdir%
+)
+
+echo +Your environment is now setup for %extdesc%
+
+:End
+
+popd
Index: gs2-extensions/video-and-audio/trunk/src/video-and-audio-setup.pl
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/video-and-audio-setup.pl	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/video-and-audio-setup.pl	(revision 23036)
@@ -0,0 +1,69 @@
+
+BEGIN {
+    die "GSDLHOME not set\n" unless defined $ENV{'GSDLHOME'};
+    die "GSDLOS not set\n" unless defined $ENV{'GSDLOS'};
+    unshift (@INC, "$ENV{'GSDLHOME'}/perllib");
+    unshift (@INC, "$ENV{'GSDLHOME'}/perllib/cpan");
+}
+
+use Cwd;
+require util;    
+
+my $env_ext  = "GEXTVIDEO";
+my $ext_desc = "the Video and Audio Extension";
+
+if (!defined $ENV{$env_ext}) {
+    
+    my $ext_home = cwd();
+    my $ext_os = $ENV{'GSDLOS'};
+
+    my ($ext_dir,$_prefix_dir) = File::Basename::fileparse($ext_home);
+
+    print STDERR "*** ext_dir = $ext_dir\n\n\n";
+
+
+    my $ext_installed = $env_ext."_INSTALLED";
+
+    $ENV{$env_ext} = $ext_home;
+    
+    $ENV{$ext_installed} = &util::filename_cat($ext_home,$ext_os);
+
+    my $ext_bin = &util::filename_cat($ENV{$ext_installed},"bin");
+    my $ext_lib = &util::filename_cat($ENV{$ext_installed},"lib");
+
+    &util::envvar_append("PATH",$ext_bin);
+
+    if ($ext_os =~ m/darwin/i) {
+	&util::envvar_append("DYLD_LIBRARY_PATH",$ext_lib);
+    }
+    else {
+	&util::envvar_append("LD_LIBRARY_PATH",$ext_lib);
+    }
+
+    # GSDLEXTS always uses : as a separator
+    if (defined $ENV{'GSDLEXTS'}) {
+	$ENV{'GSDLEXTS'} .= ":$ext_dir";
+    }
+    else {
+	$ENV{'GSDLEXTS'} = $ext_dir;
+    }
+    
+    print STDERR "+Your environment is now setup for $ext_desc\n";
+}
+else {    
+    print STDERR "+Your environment is already setup for $ext_desc\n";
+}
+
+if (scalar(@ARGV>0)) {
+
+    print STDERR "\n";
+
+    my $cmd = join(" ",map {$_ = "\"$_\""} @ARGV);
+
+    if (system($cmd)!=0) {
+	print STDERR "\nError: Failed to run '$cmd'\n";
+	print STDERR "$!\n";
+    }
+}
+
+1;
Index: gs2-extensions/video-and-audio/trunk/src/web/flash/flowPlayer.js
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/web/flash/flowPlayer.js	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/web/flash/flowPlayer.js	(revision 23036)
@@ -0,0 +1,450 @@
+/*
+ * FlowPlayer external configuration file.
+ * Copyright 2005-2006 Anssi Piirainen
+ *
+ * All settings defined in this file can be alternatively defined in the 
+ * embedding HTML object tag (as flashvars variables). Values defined in the
+ * object tag override values defined in this file. You could use this 
+ * config file to provide defaults for multiple player instances that 
+ * are used in a Web site. Individual instances can be then customized 
+ * with their embedding HTML.
+ *
+ * Note that you should probably remove all the comments from this file
+ * before using it. That way the file will be smaller and will load faster.
+ */
+{
+	/*
+	 * Instructs the player to load the configuration from an external config file.
+	 * This can be a abosulte URL or a relative url (relative to the HTML page
+	 * where the player is embedded).
+	 */
+//	configFileName: 'flowPlayer.js',
+
+	/*
+	 * Instructs the player to load the configuration from a RTMP server.
+	 * The player connects to the server listening in the address specified
+	 * by this URL and calls a method 'getStreamPlayerConfig' that should return a
+	 * valid FP configuration object.
+	 */
+//	rtmpConfigUrl: 'rtmp://localhost/myapp',
+
+	/*
+	 * A param value to be passed to getStreamPlayerConfig(). A value 'foobar'
+	 * will make the player to call getStreamPlayerConfig('foobsr')
+	 */
+//	rtmpConfigParam: 'anssi',
+	
+	/*
+	 * Name of the video file. Used if only one video is shown.
+	 *
+	 * Note for testing locally: Specify an empty baseURL '', if you want to load
+	 * the video from your local disk from the directory that contains
+	 * FlowPlayer.swf. In this case the videoFile parameter value should start
+	 * with a slash, for example '/video.flv'.
+	 *
+	 * See also: 'baseURL' that affects this variable
+	 */
+//	 videoFile: 'honda_accord.flv',
+
+	/*
+	 * Clip to be used if the file specified with 'videoFile' or any of the clips in the playlist
+	 * was not found.  The missing video clips are replaced by this clip. This can be
+	 * an image or a FLV clip. Typically this will contain an image/video saying
+	 * "the video you requested cannot be found.....".
+	 *
+	 * The syntax for the value is the same is with the clips in a playlist
+	 * including the possibility to have start/end and duration properties.
+	 *
+	 * See also: 'baseURL' that affects this variable
+	 */
+	 noVideoClip: { url: 'main_clickToPlay.jpg', duration: 10 },
+	 //noVideoClip: { url: 'MiltonFriedmanonLimi.flv' },
+
+	/*
+	 * Playlist is used to publish several videos using one player instance.
+	 * You can also have images in the playlist. The playback pauses in the
+	 * image unless a 'duration' property is given for the image:
+
+ * 	 * The clips in the playlist may have following properties:
+	 *
+	 * name: Name for the clip to be shown in the playlist view. If this is
+	 *       not given, the clip will be hidden from the view.
+	 *
+	 * url: The URL used to load the clip.
+	 * 
+	 * type: One of 'video', 'flv', 'swf', 'jpg'. Optional, determined from the URL's filename extension
+	 *       if that is present. 'video' means a video file in any format supported by Flash.
+	 *       'flv' is present here for backward compatibility, use 'video' in new FlowPlayer installations
+	 *       now. Defaults to 'video' if the extension is not present in the URL.
+	 *
+	 * start: The start time (seconds) from where to start the playback. A nonzero
+	 *        value can only be used when using a streaming server!!
+	 * end: The end time (seconds) where to stop the playback.
+	 *
+	 * duration: The duration the image is to be shown. If not given the playback
+	 *           pauses when the image is reached in the list.
+	 * 
+	 * protected: (true/false) Apply inlinine linking protection for this clip?
+	 *            Optional, defaults to false.
+	 * 
+	 * linkUrl: Associates a hyperlink pointing to the specified URL. The linked
+	 *          document will be opened to the browser when the clip area is clicked.
+	 * 			Specifying this parameter will replace the normal pause/resume behavior
+	 * 			that is associated to clicking the display area. If you specify an empty
+	 * 			linkUrl '' the pause/resume behavior is disabled but no hyperlink
+	 * 			is created.
+	 * linkWindow: Specifies the name of the browser window or frame into which to load
+	 *   the linked document. Can be a custom name or one of presets: '_blank', 
+	 *   '_parent', '_self', '_top'. (optional, defaults to '_blank')
+	 * 
+	 * controlEnabled: (true/false) Enable transport control buttons for this clip?
+	 *                 Optional, defaults to true.
+	 *
+	 * allowResize: (true/false) Allow resizing this clip according to the menu selection.
+	 *              Optional, defaults to true.
+	 *              
+	 * overlay: A filename pointing to an image that will be placed on top of this image clip. This
+	 *          is only applicable to image clips (jpg or png files). Essentially this layers two images
+	 *          on top of each other. Typically the image on top is a big play button that is used on
+	 *          top of an image taken from the main movie.
+	 * 
+	 * overlayId: ID that specifies a built-in overlay to be used. Currently the player supports
+	 * 			  one built-in overlay with ID 'play'. It renders a large play button with mouse hover color change.
+	 * 			  You can use this on top of image clips (one clip with both the 'url' property and
+	 * 			  'overlayId' property). 
+	 * 			  You can also specify a clip that only has this ID. In that
+	 * 			  case you should place it immediately before or after a FLV clip. This overlay-only
+	 * 			  clip is then rendered on top of the first or the last frame of the FLV video.
+	 * 
+	 * live: (true/false) Is this a live stream (played from a media server)?
+	 * 
+	 * showOnLoadBegin: (true/false) If true, make this clip visible when the fist bits have been loaded.
+	 * If false, do not show this clip (show the background instead) before the buffer is filled
+	 * and the playback starts. Optional, defaults to true.
+	 * 
+	 * maxPlayCount: The maximum play count for this clip. The clip is removed from the playlist when
+	 * the playcount reaches this amount.
+	 * 
+	 * suggestedClipsInfoUrl:  URL used to fetch suggestions (related videos) information from the server
+	 * 
+	 * See also: 'baseURL' is prefixed with each URL
+	 */
+	playList: [
+	{ url: 'main_clickToPlay.jpg' },
+	{ name: 'Honda Accord', url: '!honda_accord.flv' },
+	{ name: 'River', url: 'river.flv' },
+	{ name: 'Ounasvaara', url: 'ounasvaara.flv' }
+	],
+	
+	/*
+	 * Specifies wether the playlist control buttons should be shown in the player SWF component or not.
+	 * Optional, defaults to the value of showPlayList. 
+	 */
+	showPlayListButtons: true,
+
+	/*
+	 * Streaming server connection URL. 
+	 * You don't need this with lighttpd, just use the streamingServer setting (see below) with it.
+	 */
+//	 streamingServerURL: 'rtmp://localahost:oflaDemo',
+	
+	/*
+	 * Is this a live stream? Only makes sense when using a streaming server.
+	 * Optional, defaults to false.
+	 */
+//	liveStream: true,
+	
+	/* 
+	 * baseURL specifies the URL that is appended in front of different file names
+	 * given in this file.
+	 * 
+	 * You don't need to specify this at all if you place the video next to
+	 * the player SWF file on the Web server (to be available under the same URL path).
+	 */
+//	 baseURL: 'http://flowplayer.sourceforge.net/video',
+	
+	
+	/*
+	 * What kind of streaming server? Available options: 'fms', 'red5', 'lighttpd'
+	 */
+//	streamingServer: 'fms',
+	
+	/*
+	 * Specifies whether thumbnail information is contained in the FLV's cue point 
+	 * metadata. Cue points can be injected into the FLV file using 
+	 * for example Flvtool2. See the FlowPlayer web site for more info.
+	 * (optional, defaults to false)
+	 * 
+	 * See also: cuePoints below for an alternative way of specifying thumb metadata
+	 */
+//	thumbsOnFLV: true,
+	
+	/*
+	 * Thumbnails specific to cue points. Use this if you don't want to
+	 * embed thumbnail metadata into the FLV's cue points. 
+	 * If you have thumbNails defined here you should have thumbsOnFLV: false !
+	 * thumb times are given in seconds
+	 */
+// 	thumbs: [
+// 	{ thumbNail:  'Thumb1.jpg', time: 10 },
+// 	{ thumbNail:  'Thumb2.jpg', time: 24 },
+// 	{ thumbNail:  'Thumb3.jpg', time: 54 },
+// 	{ thumbNail:  'Thumb4.jpg', time: 74 },
+// 	{ thumbNail:  'Thumb5.jpg', time: 94 },
+// 	{ thumbNail:  'Thumb6.jpg', time: 110 }
+// 	],
+	// Location of the thumbnail files
+// 	thumbLocation: 'http://www.kolumbus.fi/apiirain/video',
+	
+	/* 
+	 * 'autoPlay' variable defines whether playback begins immediately or not.
+	 * 
+	 * Note that currently with red5 you should not have false in autoPlay 
+	 * when you specify a nonzero starting position for the video clip. This is because red5
+	 * does not send FLV metadata when the playback starts from a nonzero value.
+	 * 
+	 * (optional, defaults to true)
+	 */
+	autoPlay: true,
+
+	/*
+	 * 'autoBuffering' specifies wheter to start loading the video stream into
+	 *  buffer memory  immediately. Only meaningful if 'autoPlay' is set to
+	 * false. (optional, defaults to true)
+	 */
+	autoBuffering: true,
+
+	/*
+	 * 'startingBufferLength' specifies the video buffer length to be used to kick
+	 * off the playback. This is used in the beginning of the playback and every time
+	 * after the player has ran out of buffer memory. 
+	 * More info at: http://www.progettosinergia.com/flashvideo/flashvideoblog.htm#031205
+	 * (optional, defaults to the value of 'bufferLength' setting)
+	 * 
+	 * see also: bufferLength
+	 */
+//	startingBufferLength: 5,
+
+	/*
+	 * 'bufferLength' specifies the video buffer length in seconds. This is used
+	 * after the playback has started with the initial buffer length. You should
+	 * use an arbitrary large value here to ensure stable playback.
+	 * (optional, defaults to 10 seconds)
+	 * 
+	 * see also: startingBufferLength
+	 */
+	bufferLength: 20,
+
+	/*
+	 * 'loop' defines whether the playback should loop to the first clip after
+	 * all clips in the playlist have been shown. It is used as the
+	 * default state of the toggle button that controls looping. (optional,
+	 * defaults to true)
+	 */
+	loop: true,
+
+	/*
+	 * Rewind back to the fist clip in the playlist when end of the list has been reached?
+	 * This option only has effect if loop is false (please see loop variable above).
+	 * (optional, defaults to false)
+	 */
+	autoRewind: true,
+	
+	/*
+	 * Specifies wether the loop toggle button should be shown in the player SWF component or not.
+	 * Optional, defaults to false. 
+	 */
+//	showLoopButton: true,
+	
+	/*
+	 * Specifies the height to be allocated for the video display. This is the
+	 * maximum height available for the different resizing options.
+	 */
+	videoHeight: 320,
+	
+	/*
+	 * Specifies the width for the control buttons area. Optiona, defaults to the
+	 * width setting used in the embedding code. 
+	 */
+//	controlsWidth: 480,
+	
+	/*
+	 * Specifies how the video is scaled initially. This can be then changed by
+	 * the user through the menu. (optional, defaults to 'fit')
+	 * Possible values:
+	 * 'fit'   Fit to window by preserving the aspect ratios encoded in the FLV metadata.
+	 *         This is the default behavior.
+	 * 'half'  Half size (preserves aspect ratios)
+	 * 'orig'  Use the dimensions encoded in FLV. If the video is too big for the 
+	 *         available space the video is scaled as if using the 'fit' option.
+	 * 'scale' Scale the video to fill all available space for the video. Ignores
+	 *         the dimensions in metadata.
+	 * 
+	 */
+	initialScale: 'fit',
+	
+	/*
+	 * Specifies if the menu containing the size options should be shown or not.
+	 * (optional, defaults to true)
+//	showMenu: false,
+	
+	/*
+	 * 'hideControls' if set to true, hides all buttons and the progress bar
+	 * leaving only the video showing (optional, defaults to false)
+	 */
+	hideControls: false,
+
+	/*
+	 * URL that specifies a base URL that points to a folder containing
+	 * images used to skin the player. You must specify this if you intend
+	 * to load external button images (see 'loadButtonImages' below).
+	 */
+	skinImagesBaseURL: 'http://flowplayer.sourceforge.net/resources'
+
+	/*
+	 * Will button images be loaded from external files, or will images embedded
+	 * in the player SWF component be used? Set this to false if you want to "skin"
+	 * the buttons. Optional, defaults to true.
+	 * 
+	 * NOTE: If you set this to false, you need to have the skin images available
+	 * on the server! Otherwise the player will not show up at all or will show
+	 * up corrupted.
+	 *
+	 * See also: 'skinImagesBaseURL' that affects this variable
+	 */
+//	useEmbeddedButtonImages: false,
+	
+	/*
+	 * 'splashImageFile' specifies an image file to be used as a splash image.
+	 * This is useful if 'autoPlay' is set to false and you want to show a
+	 * welcome image before the video is played. Should be in JPG format. The
+	 * value of 'baseURL' is used similarily as with the video file name and
+	 * therefore the video and the image files should be placed in the Web
+	 * server next to each other.
+	 * 
+	 * NOTE: If you set a value for this, you need to have the splash image available
+	 * on the server! Otherwise the player will not show up at all or will show
+	 * up corrupted.
+	 *
+	 * NOTE2: You can also specify the splash in a playlist. This is just
+	 * an alternative way of doing it. It was preserved for backward compatibility.
+	 *
+	 * See also: 'baseURL' that affects this variable
+	 */
+//	splashImageFile: 'main_clickToPlay.jpg',
+	
+	/*
+	 * Should the splash image be scaled to fit the entire video area? If false,
+	 * the image will be centered. Optional, defaults to false.
+	 */
+//	scaleSplash: false,
+
+	/*
+	 * 'progressBarColor1' defines the color of the progress bar at the bottom
+	 * and top edges. Specified in hexadecimal triplet form indicating the RGB
+	 * color component values. (optional)
+	 */
+//	progressBarColor1: 0xFFFFFF,
+
+
+	/*
+	 * 'progressBarColor2' defines the color in the middle of the progress bar.
+	 * The value of this and 'progressBarColor1' variables define the gradient
+	 * color fill of the progress bar. (optional)
+	 */
+//	progressBarColor2: 0xDDFFDD,
+
+	/*
+	 * 'bufferBarColor1' defines the color of the buffer size indicator bar at the bottom
+	 * and top edges. (optional)
+	 */
+//	bufferBarColor1: 0xFFFFFF,
+
+
+	/*
+	 * 'bufferBarColor2' defines the color of the buffer size indicator bar in the middle
+	 * of the bar. (optional)
+	 */
+//	bufferBarColor2: 0xDDFFDD,
+
+	/*
+	 * 'progressBarBorderColor1' defines the color of the progress bar's border at the bottom
+	 * and top edges. (optional)
+	 */
+//	progressBarBorderColor1: 0xDDDDDD,
+
+
+	/*
+	 * 'progressBarBorderColor2' defines the color of the progress bar's border in the middle
+	 * of the bar. (optional)
+	 */
+//	progressBarBorderColor2: 0xEEEEEE,
+
+	/*
+	 * 'bufferingAnimationColor' defines the color of the moving bars used in the buffering 
+	 * animation. (optional)
+	 */
+//	bufferingAnimationColor: 0x0000FF,
+
+	/*
+	 * 'controlsAreaBorderColor' defines the color of the border behind buttons and progress bar 
+	 * (optional)
+	 */
+//	controlsAreaBorderColor: 0x1234,
+
+	/*
+	 * 'timeDisplayFontColor' defines the color of the progress/duration time display 
+	 * (optional)
+	 */
+//	timeDisplayFontColor: 0xAABBCC,
+
+	/*
+	 * Height of the progress bar. (optional)
+	 */
+//	progressBarHeight: 10,
+
+	/*
+	 * Height of the progress bar area. (optional)
+	 */
+//	progressBarAreaHeight: 10,
+
+	/*
+	 * Name of the authentication code file name that is used to prevent inline linking
+	 * of video and image files. This can be a complete URL or just a file name relative
+	 * to the location from where the player is loaded. (optional, defaults to flowplayer_auth.txt)
+	 */
+//	authFileName: 'http://www.mytube.org/authCode.txt',
+
+	/*
+	 * The URL pointing to a sctipt that opens the player full screen. 
+	 * If this is not configured explicitly, the default script, 
+	 * http://flowplayer.sourceforge.net/fullscreen.js, is used.
+	 */
+//	fullScreenScriptURL: 'http://mysite.org/fullscreen.js'
+
+	/**
+	 * Specifies which menu items will be show. This is an array that contains a boolean
+	 * value for each of the items. By default shows them all except "full screen".
+	 */
+//	menuItems[
+//		true, // show 'Fit to window'
+//		true, // show 'Half size'
+//		true, // show 'Original size'
+//		true, // show 'Fill window'
+//		true, // show 'Full screen'
+//		false // hide 'Embed...'
+//	],
+
+
+	/*
+	 * Specifies wether the full screen button should be shown in the player SWF component or not.
+	 * Optional, defaults to true. 
+	 */
+//	showFullScreenButton: false,
+
+	/*
+	 * Use the Flash 9 native full screen mode.
+	 */
+//	useNativeFullScreen: true,
+}
+
Index: gs2-extensions/video-and-audio/trunk/src/web/flash/fullscreen.js
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/web/flash/fullscreen.js	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/web/flash/fullscreen.js	(revision 23036)
@@ -0,0 +1,15 @@
+/*
+ * Default FlowPlayer fullscreen opener.
+ * http://flowplayer.sourceforge.net
+ */
+
+function flowPlayerOpenFullScreen(config) {
+  var winWidth = window.screen.availWidth;
+  var winHeight = window.screen.availHeight;
+  var fullScreenWindow = window.open('http://flowplayer.org/video/1_20/fullscreen.html?config='+config, 'FlowPlayer', 'left=0,top=0,width='+winWidth+',height='+winHeight+',status=no,resizable=yes');
+}
+
+function flowPlayerExitFullScreen(config) {
+  self.close();
+}
+
Index: gs2-extensions/video-and-audio/trunk/src/web/flash/jwplayer/readme.html
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/web/flash/jwplayer/readme.html	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/web/flash/jwplayer/readme.html	(revision 23036)
@@ -0,0 +1,85 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+
+	<title>JW Player for Flash</title>
+
+	<style type="text/css">
+		body { background-color: #fff; padding: 0 20px; color:#000; font: 13px/18px Arial, sans-serif; }
+		a { color: #360; }
+		h3 { padding-top: 20px; }
+		ol { margin:5px 0 15px 16px; padding:0; list-style-type:square; }
+	</style>
+
+</head>
+<body>
+
+	<h1>Welcome to the World of LongTail Video</h1>
+	<p>Thanks for downloading the JW FLV Player, just one of many products created by Jeroen "JW" Wijering and his company, <a href="http://www.longtailvideo.com">LongTail Video</a>.  Before you get started, let us tell you about a few other products that might interest you:</p>
+
+<ul>
+<li><a href="http://www.longtailvideo.com/players/jw-desktop-player/">Desktop Player</a> - Our sleek new desktop player is the best way to watch all your music and video files.  Plus, you can even search the web for videos without ever opening your browser.  <a href="http://www.longtailvideo.com/players/jw-desktop-player/">Download it now</a>.</li>
+<li><a href="http://www.longtailvideo.com/referral.aspx?page=pubreferral&ref=oryixfvlxlptwph">AdSolution</a> - Monetize your videos with LongTail's AdSolution.  Integrate pre-roll, overlay mid-roll and post-roll ads into your site and starting making money today.  <a href="http://www.longtailvideo.com/referral.aspx?page=pubreferral&ref=oryixfvlxlptwph">Sign up now</a>.</li>
+<li><a href="http://www.bitsontherun.com/via/longtailvideo">Bits on the Run</a> - Upload, encode, store, manage and stream your videos with Bits on the Run, LongTail's end-to-end video content management system.  <a href="http://www.bitsontherun.com/via/longtailvideo">Sign up now</a>.</li>
+<li><a href="http://www.longtailvideo.com/addons/plugins">Plugins</a> - Add functionality to your player, like analytics tracking, embed links, sharing and more.  <a href="http://www.longtailvideo.com/addons/plugins">Find one for you</a>.</li>
+<li><a href="http://www.longtailvideo.com/addons/skins">Skins</a> - Change the look of your player to match your site.  <a href="http://www.longtailvideo.com/addons/skins">View our library</a>.</li>
+</ul>
+
+
+	<h3>Licensing</h3>
+	<p>The player is licensed under a <a href="http://creativecommons.org/licenses/by-nc-sa/2.0/">Creative Commons License</a>. It allows you to use, modify and redistribute the script, but only for <b>noncommercial</b> purposes. Examples of <b>commercial use</b> include: 
+	
+	<ul>
+	<li>websites with any advertisements; </li>
+	<li>websites owned or operated by corporations; </li>
+	<li>websites designed to promote other products, such as a band or artist;</li>
+	<li>products (e.g. a CMS) that bundle LongTail products into its offering. </li>
+	</ul>
+	
+	If any of the above conditions apply to you, <a href="http://www.longtailvideo.com/players/order" title="Order commercial licenses">please apply for a commercial license</a> now.  If you are still unsure whether you need to purchase a license, please <a href="http://www.longtailvideo.com/forum/">post your question</a> in the forum. </p>
+
+	<h3>Player Example</h3>
+	<p>Below you see a simple embedded example of the JW Player.  Copy and paste the source code and put the SWF, JS and FLV file on your site to get started.</p>
+
+	<!-- START OF THE PLAYER EMBEDDING TO COPY-PASTE -->
+
+	<script type="text/javascript" src="swfobject.js"></script>
+	<script type="text/javascript">
+		swfobject.registerObject("player","9.0.98","expressInstall.swf");
+	</script>
+
+	<object id="player" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" name="player" width="328" height="200">
+		<param name="movie" value="player.swf" />
+		<param name="allowfullscreen" value="true" />
+		<param name="allowscriptaccess" value="always" />
+		<param name="flashvars" value="file=video.flv&image=preview.jpg" />
+		<object type="application/x-shockwave-flash" data="player.swf" width="328" height="200">
+			<param name="movie" value="player.swf" />
+			<param name="allowfullscreen" value="true" />
+			<param name="allowscriptaccess" value="always" />
+			<param name="flashvars" value="file=video.flv&image=preview.jpg" />
+			<p><a href="http://get.adobe.com/flashplayer">Get Flash</a> to see this player.</p>
+		</object>
+	</object>
+
+	<!-- END OF THE PLAYER EMBEDDING -->
+
+
+	<h3>Quickstart</h3>
+	<p>The easiest way to get to know the player is by using our <a href="http://www.longtailvideo.com/support/jw-player-setup-wizard">setup wizard</a>. Select an example, set the file or playlist you want to play and copy-paste the embed code to your site. If you want to play Youtube content in the player, you also have to copy the <b>yt.swf</b> file along with the player.swf or player-viral.swf.</p>
+
+	<p>If you need any help, please visit our extensive support section, including <a href="http://www.longtailvideo.com/support/faq">FAQs</a>, a string of <a href="http://www.longtailvideo.com/support/tutorials">tutorials</a> and a very active <a href="http://www.longtailvideo.com/support/forum">support forum</a>.</p>
+
+	<h3>Wiki and Source Code</h3>
+	<p>The <a href="http://developer.longtailvideo.com">Developer Wiki</a> contains a wealth of info about the player, including:</p>
+	<ol>
+		<li><a href="http://developer.longtailvideo.com/trac/wiki/FlashFormats">Supported file formats</a> (and playlists).</li>
+		<li><a href="http://developer.longtailvideo.com/trac/wiki/FlashVars">Supported flashvars</a> (for customizing the player).</li>
+		<li><a href="http://developer.longtailvideo.com/trac/wiki/FlashAPI">Supported API calls</a> (for actionscript / javascript interaction).</li>
+		<li><a href="http://developer.longtailvideo.com/trac/wiki/FlashSkinning">Supported skinning elements</a> (for creating your own graphics).</li>
+		<li><a href="http://developer.longtailvideo.com/trac/roadmap/">Roadmap with full changelogs</a> for each version.</li>
+	</ol>
+	<p>The source code of all different versions of the player can <a href="http://developer.longtailvideo.com/trac/browser/tags">be found here</a>. You can click a version and download the ZIP files (the links are at the bottom).</p>
+
+</body>
+</html>
Index: gs2-extensions/video-and-audio/trunk/src/web/flash/jwplayer/swfobject.js
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/web/flash/jwplayer/swfobject.js	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/web/flash/jwplayer/swfobject.js	(revision 23036)
@@ -0,0 +1,8 @@
+/**
+ * SWFObject v1.5: Flash Player detection and embed - http://blog.deconcept.com/swfobject/
+ *
+ * SWFObject is (c) 2007 Geoff Stearns and is released under the MIT License:
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ */
+if(typeof deconcept=="undefined"){var deconcept=new Object();}if(typeof deconcept.util=="undefined"){deconcept.util=new Object();}if(typeof deconcept.SWFObjectUtil=="undefined"){deconcept.SWFObjectUtil=new Object();}deconcept.SWFObject=function(_1,id,w,h,_5,c,_7,_8,_9,_a){if(!document.getElementById){return;}this.DETECT_KEY=_a?_a:"detectflash";this.skipDetect=deconcept.util.getRequestParameter(this.DETECT_KEY);this.params=new Object();this.variables=new Object();this.attributes=new Array();if(_1){this.setAttribute("swf",_1);}if(id){this.setAttribute("id",id);}if(w){this.setAttribute("width",w);}if(h){this.setAttribute("height",h);}if(_5){this.setAttribute("version",new deconcept.PlayerVersion(_5.toString().split(".")));}this.installedVer=deconcept.SWFObjectUtil.getPlayerVersion();if(!window.opera&&document.all&&this.installedVer.major>7){deconcept.SWFObject.doPrepUnload=true;}if(c){this.addParam("bgcolor",c);}var q=_7?_7:"high";this.addParam("quality",q);this.setAttribute("useExpressInstall",false);this.setAttribute("doExpressInstall",false);var _c=(_8)?_8:window.location;this.setAttribute("xiRedirectUrl",_c);this.setAttribute("redirectUrl","");if(_9){this.setAttribute("redirectUrl",_9);}};deconcept.SWFObject.prototype={useExpressInstall:function(_d){this.xiSWFPath=!_d?"expressinstall.swf":_d;this.setAttribute("useExpressInstall",true);},setAttribute:function(_e,_f){this.attributes[_e]=_f;},getAttribute:function(_10){return this.attributes[_10];},addParam:function(_11,_12){this.params[_11]=_12;},getParams:function(){return this.params;},addVariable:function(_13,_14){this.variables[_13]=_14;},getVariable:function(_15){return this.variables[_15];},getVariables:function(){return this.variables;},getVariablePairs:function(){var _16=new Array();var key;var _18=this.getVariables();for(key in _18){_16[_16.length]=key+"="+_18[key];}return _16;},getSWFHTML:function(){var _19="";if(navigator.plugins&&navigator.mimeTypes&&navigator.mimeTypes.length){if(this.getAttribute("doExpressInstall")){this.addVariable("MMplayerType","PlugIn");this.setAttribute("swf",this.xiSWFPath);}_19="<embed type=\"application/x-shockwave-flash\" src=\""+this.getAttribute("swf")+"\" width=\""+this.getAttribute("width")+"\" height=\""+this.getAttribute("height")+"\" style=\""+this.getAttribute("style")+"\"";_19+=" id=\""+this.getAttribute("id")+"\" name=\""+this.getAttribute("id")+"\" ";var _1a=this.getParams();for(var key in _1a){_19+=[key]+"=\""+_1a[key]+"\" ";}var _1c=this.getVariablePairs().join("&");if(_1c.length>0){_19+="flashvars=\""+_1c+"\"";}_19+="/>";}else{if(this.getAttribute("doExpressInstall")){this.addVariable("MMplayerType","ActiveX");this.setAttribute("swf",this.xiSWFPath);}_19="<object id=\""+this.getAttribute("id")+"\" classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\" width=\""+this.getAttribute("width")+"\" height=\""+this.getAttribute("height")+"\" style=\""+this.getAttribute("style")+"\">";_19+="<param name=\"movie\" value=\""+this.getAttribute("swf")+"\" />";var _1d=this.getParams();for(var key in _1d){_19+="<param name=\""+key+"\" value=\""+_1d[key]+"\" />";}var _1f=this.getVariablePairs().join("&");if(_1f.length>0){_19+="<param name=\"flashvars\" value=\""+_1f+"\" />";}_19+="</object>";}return _19;},write:function(_20){if(this.getAttribute("useExpressInstall")){var _21=new deconcept.PlayerVersion([6,0,65]);if(this.installedVer.versionIsValid(_21)&&!this.installedVer.versionIsValid(this.getAttribute("version"))){this.setAttribute("doExpressInstall",true);this.addVariable("MMredirectURL",escape(this.getAttribute("xiRedirectUrl")));document.title=document.title.slice(0,47)+" - Flash Player Installation";this.addVariable("MMdoctitle",document.title);}}if(this.skipDetect||this.getAttribute("doExpressInstall")||this.installedVer.versionIsValid(this.getAttribute("version"))){var n=(typeof _20=="string")?document.getElementById(_20):_20;n.innerHTML=this.getSWFHTML();return true;}else{if(this.getAttribute("redirectUrl")!=""){document.location.replace(this.getAttribute("redirectUrl"));}}return false;}};deconcept.SWFObjectUtil.getPlayerVersion=function(){var _23=new deconcept.PlayerVersion([0,0,0]);if(navigator.plugins&&navigator.mimeTypes.length){var x=navigator.plugins["Shockwave Flash"];if(x&&x.description){_23=new deconcept.PlayerVersion(x.description.replace(/([a-zA-Z]|\s)+/,"").replace(/(\s+r|\s+b[0-9]+)/,".").split("."));}}else{if(navigator.userAgent&&navigator.userAgent.indexOf("Windows CE")>=0){var axo=1;var _26=3;while(axo){try{_26++;axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash."+_26);_23=new deconcept.PlayerVersion([_26,0,0]);}catch(e){axo=null;}}}else{try{var axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");}catch(e){try{var axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");_23=new deconcept.PlayerVersion([6,0,21]);axo.AllowScriptAccess="always";}catch(e){if(_23.major==6){return _23;}}try{axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash");}catch(e){}}if(axo!=null){_23=new deconcept.PlayerVersion(axo.GetVariable("$version").split(" ")[1].split(","));}}}return _23;};deconcept.PlayerVersion=function(_29){this.major=_29[0]!=null?parseInt(_29[0]):0;this.minor=_29[1]!=null?parseInt(_29[1]):0;this.rev=_29[2]!=null?parseInt(_29[2]):0;};deconcept.PlayerVersion.prototype.versionIsValid=function(fv){if(this.major<fv.major){return false;}if(this.major>fv.major){return true;}if(this.minor<fv.minor){return false;}if(this.minor>fv.minor){return true;}if(this.rev<fv.rev){return false;}return true;};deconcept.util={getRequestParameter:function(_2b){var q=document.location.search||document.location.hash;if(_2b==null){return q;}if(q){var _2d=q.substring(1).split("&");for(var i=0;i<_2d.length;i++){if(_2d[i].substring(0,_2d[i].indexOf("="))==_2b){return _2d[i].substring((_2d[i].indexOf("=")+1));}}}return "";}};deconcept.SWFObjectUtil.cleanupSWFs=function(){var _2f=document.getElementsByTagName("OBJECT");for(var i=_2f.length-1;i>=0;i--){_2f[i].style.display="none";for(var x in _2f[i]){if(typeof _2f[i][x]=="function"){_2f[i][x]=function(){};}}}};if(deconcept.SWFObject.doPrepUnload){if(!deconcept.unloadSet){deconcept.SWFObjectUtil.prepUnload=function(){__flash_unloadHandler=function(){};__flash_savedUnloadHandler=function(){};window.attachEvent("onunload",deconcept.SWFObjectUtil.cleanupSWFs);};window.attachEvent("onbeforeunload",deconcept.SWFObjectUtil.prepUnload);deconcept.unloadSet=true;}}if(!document.getElementById&&document.all){document.getElementById=function(id){return document.all[id];};}var getQueryParamValue=deconcept.util.getRequestParameter;var FlashObject=deconcept.SWFObject;var SWFObject=deconcept.SWFObject;
Index: gs2-extensions/video-and-audio/trunk/src/web/flash/suggestions.js
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/web/flash/suggestions.js	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/web/flash/suggestions.js	(revision 23036)
@@ -0,0 +1,10 @@
+{
+	clips: [
+		{ url: 'honda_accord.flv', name: 'Honda commercial', suggestedClipsInfoUrl: 'suggestions.js',
+					duration: 180, thumbnailUrl: 'Thumb1.jpg', 
+					info: { viewed: 631, category: 'ads' } },
+		{ url: 'river.flv', name: 'Skiing in Rovaniemi', suggestedClipsInfoUrl: 'suggestions.js',
+					duration: 180, thumbnailUrl: 'Thumb2.jpg',
+					info: { viewed: 2, category: 'sports' } }
+	]
+}
Index: gs2-extensions/video-and-audio/trunk/src/web/flash/swfobject.js
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/web/flash/swfobject.js	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/web/flash/swfobject.js	(revision 23036)
@@ -0,0 +1,138 @@
+/**
+ * SWFObject v1.4.4: Flash Player detection and embed - http://blog.deconcept.com/swfobject/
+ *
+ * SWFObject is (c) 2006 Geoff Stearns and is released under the MIT License:
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ * **SWFObject is the SWF embed script formerly known as FlashObject. The name was changed for
+ *   legal reasons.
+ */
+if(typeof deconcept=="undefined"){var deconcept=new Object();}
+if(typeof deconcept.util=="undefined"){deconcept.util=new Object();}
+if(typeof deconcept.SWFObjectUtil=="undefined"){deconcept.SWFObjectUtil=new Object();}
+deconcept.SWFObject=function(_1,id,w,h,_5,c,_7,_8,_9,_a,_b){if(!document.getElementById){return;}
+this.DETECT_KEY=_b?_b:"detectflash";
+this.skipDetect=deconcept.util.getRequestParameter(this.DETECT_KEY);
+this.params=new Object();
+this.variables=new Object();
+this.attributes=new Array();
+if(_1){this.setAttribute("swf",_1);}
+if(id){this.setAttribute("id",id);}
+if(w){this.setAttribute("width",w);}
+if(h){this.setAttribute("height",h);}
+if(_5){this.setAttribute("version",new deconcept.PlayerVersion(_5.toString().split(".")));}
+this.installedVer=deconcept.SWFObjectUtil.getPlayerVersion();
+if(c){this.addParam("bgcolor",c);}
+var q=_8?_8:"high";
+this.addParam("quality",q);
+this.setAttribute("useExpressInstall",_7);
+this.setAttribute("doExpressInstall",false);
+var _d=(_9)?_9:window.location;
+this.setAttribute("xiRedirectUrl",_d);
+this.setAttribute("redirectUrl","");
+if(_a){this.setAttribute("redirectUrl",_a);}};
+deconcept.SWFObject.prototype={setAttribute:function(_e,_f){
+this.attributes[_e]=_f;
+},getAttribute:function(_10){
+return this.attributes[_10];
+},addParam:function(_11,_12){
+this.params[_11]=_12;
+},getParams:function(){
+return this.params;
+},addVariable:function(_13,_14){
+this.variables[_13]=_14;
+},getVariable:function(_15){
+return this.variables[_15];
+},getVariables:function(){
+return this.variables;
+},getVariablePairs:function(){
+var _16=new Array();
+var key;
+var _18=this.getVariables();
+for(key in _18){_16.push(key+"="+_18[key]);}
+return _16;},getSWFHTML:function(){var _19="";
+if(navigator.plugins&&navigator.mimeTypes&&navigator.mimeTypes.length){
+if(this.getAttribute("doExpressInstall")){
+this.addVariable("MMplayerType","PlugIn");}
+_19="<embed type=\"application/x-shockwave-flash\" src=\""+this.getAttribute("swf")+"\" width=\""+this.getAttribute("width")+"\" height=\""+this.getAttribute("height")+"\"";
+_19+=" id=\""+this.getAttribute("id")+"\" name=\""+this.getAttribute("id")+"\" ";
+var _1a=this.getParams();
+for(var key in _1a){_19+=[key]+"=\""+_1a[key]+"\" ";}
+var _1c=this.getVariablePairs().join("&");
+if(_1c.length>0){_19+="flashvars=\""+_1c+"\"";}_19+="/>";
+}else{if(this.getAttribute("doExpressInstall")){this.addVariable("MMplayerType","ActiveX");}
+_19="<object id=\""+this.getAttribute("id")+"\" classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\" width=\""+this.getAttribute("width")+"\" height=\""+this.getAttribute("height")+"\">";
+_19+="<param name=\"movie\" value=\""+this.getAttribute("swf")+"\" />";
+var _1d=this.getParams();
+for(var key in _1d){_19+="<param name=\""+key+"\" value=\""+_1d[key]+"\" />";}
+var _1f=this.getVariablePairs().join("&");
+if(_1f.length>0){_19+="<param name=\"flashvars\" value=\""+_1f+"\" />";}_19+="</object>";}
+return _19;
+},write:function(_20){
+if(this.getAttribute("useExpressInstall")){
+var _21=new deconcept.PlayerVersion([6,0,65]);
+if(this.installedVer.versionIsValid(_21)&&!this.installedVer.versionIsValid(this.getAttribute("version"))){
+this.setAttribute("doExpressInstall",true);
+this.addVariable("MMredirectURL",escape(this.getAttribute("xiRedirectUrl")));
+document.title=document.title.slice(0,47)+" - Flash Player Installation";
+this.addVariable("MMdoctitle",document.title);}}
+if(this.skipDetect||this.getAttribute("doExpressInstall")||this.installedVer.versionIsValid(this.getAttribute("version"))){
+var n=(typeof _20=="string")?document.getElementById(_20):_20;
+n.innerHTML=this.getSWFHTML();return true;
+}else{if(this.getAttribute("redirectUrl")!=""){document.location.replace(this.getAttribute("redirectUrl"));}}
+return false;}};
+deconcept.SWFObjectUtil.getPlayerVersion=function(){
+var _23=new deconcept.PlayerVersion([0,0,0]);
+if(navigator.plugins&&navigator.mimeTypes.length){
+var x=navigator.plugins["Shockwave Flash"];
+if(x&&x.description){_23=new deconcept.PlayerVersion(x.description.replace(/([a-zA-Z]|\s)+/,"").replace(/(\s+r|\s+b[0-9]+)/,".").split("."));}
+}else{try{var axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");}
+catch(e){try{var axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");
+_23=new deconcept.PlayerVersion([6,0,21]);axo.AllowScriptAccess="always";}
+catch(e){if(_23.major==6){return _23;}}try{axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash");}
+catch(e){}}if(axo!=null){_23=new deconcept.PlayerVersion(axo.GetVariable("$version").split(" ")[1].split(","));}}
+return _23;};
+deconcept.PlayerVersion=function(_27){
+this.major=_27[0]!=null?parseInt(_27[0]):0;
+this.minor=_27[1]!=null?parseInt(_27[1]):0;
+this.rev=_27[2]!=null?parseInt(_27[2]):0;
+};
+deconcept.PlayerVersion.prototype.versionIsValid=function(fv){
+if(this.major<fv.major){return false;}
+if(this.major>fv.major){return true;}
+if(this.minor<fv.minor){return false;}
+if(this.minor>fv.minor){return true;}
+if(this.rev<fv.rev){
+return false;
+}return true;};
+deconcept.util={getRequestParameter:function(_29){
+var q=document.location.search||document.location.hash;
+if(q){var _2b=q.substring(1).split("&");
+for(var i=0;i<_2b.length;i++){
+if(_2b[i].substring(0,_2b[i].indexOf("="))==_29){
+return _2b[i].substring((_2b[i].indexOf("=")+1));}}}
+return "";}};
+deconcept.SWFObjectUtil.cleanupSWFs=function(){if(window.opera||!document.all){return;}
+var _2d=document.getElementsByTagName("OBJECT");
+for(var i=0;i<_2d.length;i++){_2d[i].style.display="none";for(var x in _2d[i]){
+if(typeof _2d[i][x]=="function"){_2d[i][x]=function(){};}}}};
+deconcept.SWFObjectUtil.prepUnload=function(){__flash_unloadHandler=function(){};
+__flash_savedUnloadHandler=function(){};
+if(typeof window.onunload=="function"){
+var _30=window.onunload;
+window.onunload=function(){
+deconcept.SWFObjectUtil.cleanupSWFs();_30();};
+}else{window.onunload=deconcept.SWFObjectUtil.cleanupSWFs;}};
+if(typeof window.onbeforeunload=="function"){
+var oldBeforeUnload=window.onbeforeunload;
+window.onbeforeunload=function(){
+deconcept.SWFObjectUtil.prepUnload();
+oldBeforeUnload();};
+}else{window.onbeforeunload=deconcept.SWFObjectUtil.prepUnload;}
+if(Array.prototype.push==null){
+Array.prototype.push=function(_31){
+this[this.length]=_31;
+return this.length;};}
+var getQueryParamValue=deconcept.util.getRequestParameter;
+var FlashObject=deconcept.SWFObject;
+var SWFObject=deconcept.SWFObject;
Index: gs2-extensions/video-and-audio/trunk/src/web/script/document.js
===================================================================
--- gs2-extensions/video-and-audio/trunk/src/web/script/document.js	(revision 23036)
+++ gs2-extensions/video-and-audio/trunk/src/web/script/document.js	(revision 23036)
@@ -0,0 +1,416 @@
+
+  function format_date (gsdate,mode) 
+  {
+       var year_num  = gsdate.substr(0,4);
+       var month_num = gsdate.substr(4,2);
+       var date_num  = gsdate.substr(6,2);
+
+       var date = new Date(year_num,month_num-1,date_num)
+
+       var date_str = date.toLocaleDateString();
+
+       if ((mode != undefined) && (mode == "alphanum"))
+       {
+           var dayname   = new Array("Sunday","Monday","Tuesday","Wednesday",
+                                     "Thursday","Friday","Saturday");
+           var monthname = new Array("Jan","Feb","Mar","Apr","May","Jun","Jul",
+                                     "Aug","Sep","Oct","Nov","Dec");
+
+           var day_alpha   = dayname[date.getDay()];
+           var month_alpha = monthname[date.getMonth()];
+
+           date_str = day_alpha +", "
+                       + date_num +" "+ month_alpha +" "+ year_num;
+        }
+
+       document.write(date_str);
+  }
+
+  function format_filesize (gsfilesize) 
+  {
+      var filesize = gsfilesize + " bytes";
+
+      if (gsfilesize > 1048576) {
+        filesize = Math.round(gsfilesize / 1048576.0) + " Mb"
+      }
+      else if (gsfilesize > 1024) {
+        filesize = Math.round(gsfilesize / 1024.0) + " Kb"
+      }
+
+      document.write(filesize);
+  }
+
+
+
+function getOpenBookTable() {
+
+        var tabTocA = document.getElementById("toc");
+
+        // book icon and toc wrapper
+        var tablesOuter = tabTocA.parentNode.getElementsByTagName("table")
+
+        return tablesOuter.item(0);
+
+}
+
+function getTocTable() {
+
+        var tabTocA = document.getElementById("toc");
+
+        // book icon and toc wrapper
+        var tablesOuter = tabTocA.parentNode.getElementsByTagName("table")
+        
+        var tabWrap = tablesOuter.item(1);
+
+        var tables = tabWrap.getElementsByTagName("table")
+
+        return tables.item(0);
+
+}
+
+
+
+
+function manipulateTocTable() {
+
+	var tabOB= getOpenBookTable();
+
+	if (tabOB != null) {
+	  var expandText = document.getElementById("db-expand-text");
+	  var highlight  = document.getElementById("db-highlight");
+	  var parentDB = expandText.parentNode;
+
+	  parentDB.removeChild(expandText);
+	  parentDB.removeChild(highlight);
+
+	  var tbodyOB = tabOB.firstChild;
+	  var trOB = tbodyOB.firstChild;
+	  trOB.appendChild(expandText);
+	  trOB.appendChild(highlight);
+
+	  tabOB.setAttribute("width","100%");
+	  
+  	  // tabOB.setAttribute("bgcolor","red");
+	}
+
+
+	var tabToc= getTocTable();
+
+	if (tabToc != null) {
+
+	    var tablePack = document.createElement("table");
+	    var tbodyPack = document.createElement("tbody");
+	    tablePack.appendChild(tbodyPack);
+
+	    var trPack = document.createElement("tr");
+	    trPack.setAttribute("bgColor","#e0f0ff");
+
+
+  	    var trList = tabToc.getElementsByTagName("tr")
+
+	    // repack into 3-col table
+	    var i;
+	    for (i=0; i<trList.length; i++) {
+	      if ((i>0) && (i % 3 == 0)) {
+	        tbodyPack.appendChild(trPack);
+	        trPack = document.createElement("tr");
+		trPack.setAttribute("bgColor","#e0f0ff");
+	      }
+	      var tr = trList.item(i);
+	      var tdList = tr.getElementsByTagName("td");
+
+	      //console.log("tdList = " + tdList.length);
+
+	      var tdLink = tdList.item(0);
+	      var href = tdLink.firstChild.href;
+
+	      href = "javascript:htmlLoad(\""+href+"&onlytext=1\",\"ajaxtext\")";
+
+	      var tdText = tdList.item(2);
+
+	      var aPack = document.createElement("a");
+	      aPack.setAttribute("href",href);
+	      aPack.setAttribute("class","toc");
+	      aPack.innerHTML = tdText.innerHTML;
+
+	      var iPack = document.createElement("i");
+	      iPack.appendChild(aPack);
+
+	      //var tdPack = tdText.cloneNode(true);
+	      var tdPack = document.createElement("td");
+	      tdPack.appendChild(iPack);
+
+	      // var textText = document.createTextNode(tdText.innerHTML);
+
+	      // tdPack.appendChild(textText);
+
+	      // tdPack.setAttribute("bgcolor","blue");
+	      trPack.appendChild(tdPack);
+
+	    }
+
+	    tbodyPack.appendChild(trPack);
+
+	    // find outer table element, and replace with new compact TOC
+	    var newTocLoc = tabToc.parentNode;
+	    while (!newTocLoc.nodeName.match(/^table$/i)) {
+	        newTocLoc = newTocLoc.parentNode;
+	    }
+
+	    
+	    // set up list of TOC chapter headings (used when video player is shown)
+
+	    //var tocList = document.createElement("div");
+	    //tocList.setAttribute("style","display: block");
+
+	    var tocList  = document.getElementById("toclist");
+
+	    for (i=0; i<trList.length; i++) {
+	      var tr = trList.item(i);
+	      var tdList = tr.getElementsByTagName("td");
+	      var tdText = tdList.item(1);
+
+	      // alert("tdText = " + tdText.innerHTML);
+
+	      var tocEntry = document.createElement("div");
+	      tocEntry.style.display = "none";
+
+	      tocEntry.innerHTML = tdText.innerHTML;
+
+	      //tocEntry.appendChild(tdText);
+
+	      tocList.appendChild(tocEntry);
+	    }
+	
+
+	    // delay replace until here so toclist can access trList
+	    // in IE, doing replace earlier means trList becomes empty
+	    newTocLoc.parentNode.replaceChild(tablePack,newTocLoc);    
+
+	}
+	else {
+	  var mess = "Warning: Didn't find TOC table where expected in DOM";
+	  mess += "\n Found <" + tabToc.nodeName + ">instead of <table>";
+	  mess += "\n Unable to manipulate: " + tabToc.nodeValue;
+ 
+	  alert(mess);
+	}
+
+}
+
+
+    var flowplayervisible = 1;
+    var storedFlowplayerHeight = null;
+
+    function flowplayershow() 
+     {
+        if (flowplayervisible) return; // already visible
+
+        divTOCtableElem = document.getElementById("toctable");
+	divTOCtableElem.style.display = "none";
+
+        divTOCnextprevElem = document.getElementById("tocnextprev");
+	divTOCnextprevElem.style.display = "block";
+
+        var divFlowplayerElem = document.getElementById("FlowPlayerDiv");
+	divFlowplayerElem.style.visibility = "visible";
+
+	if (storedFlowplayerHeight != null) {
+	    divFlowplayerElem.style.height = storedFlowplayerHeight;
+        }
+
+	var flowPlayer = document.getElementById("FlowPlayer");
+
+	if (flowPlayer.DoPlay) {
+	    flowPlayer.DoPlay();
+	}
+
+        flowplayervisible = 1;
+      }
+
+    function flowplayerhide() 
+     {
+        if (!flowplayervisible) return; // already hidden
+
+
+        divTOCnextprevElem = document.getElementById("tocnextprev");
+	divTOCnextprevElem.style.display = "none";
+
+        var divFlowplayerElem = document.getElementById("FlowPlayerDiv");
+
+	if (parseInt(divFlowplayerElem.style.height) != "0") {
+	    storedFlowplayerHeight = divFlowplayerElem.style.height;
+	}
+	divFlowplayerElem.style.height = "0px";
+	divFlowplayerElem.style.visibility = "hidden";
+
+        divTOCtableElem = document.getElementById("toctable");
+	divTOCtableElem.style.display = "block";
+
+        var flowPlayer = document.getElementById("FlowPlayer");
+
+	if (flowPlayer.Pause) {
+	    flowPlayer.Pause();
+	}
+
+        flowplayervisible = 0;
+      }
+
+    var storedseek;
+    var delayedSafetyLimit = 0;
+
+    function videoplayerseekDelayed() 
+     {
+          var flowPlayer = document.getElementById("FlowPlayer");
+
+          if (flowPlayer.Seek) {
+	     flowPlayer.Pause();
+	     flowPlayer.Seek(storedseek);
+	     flowPlayer.DoPlay()
+          }
+	  else {
+	     if (delayedSafetyLimit<10) {
+	       delayedSafetyLimit++;
+	       setTimeout("videoplayerseekDelayed()",2000);
+             }
+	     else {
+	       alert("Failed to start at seeked position " + storedseek);
+	     }
+	  }
+
+      }
+
+
+    function videoplayerseek(hh,mm,ss) 
+     {
+	  flowplayershow();
+
+          storedseek = hh*3600 + mm*60 + ss -2;
+
+          var flowPlayer = document.getElementById("FlowPlayer");
+
+          if (flowPlayer.Seek) {
+	      videoplayerseekDelayed();
+          }
+	  else {
+	     delayedSafetyLimit = 0;
+	     setTimeout("videoplayerseekDelayed()",7000);
+	  }
+
+      }
+
+    function audioplayerseek(hh,mm,ss) 
+     {
+          storedseek = hh*3600 + mm*60 + ss -2;
+
+          var flowPlayer = document.getElementById("FlowPlayer");
+
+          if (flowPlayer.Seek) {
+	      videoplayerseekDelayed();
+          }
+	  else {
+	     delayedSafetyLimit = 0;
+	     setTimeout("videoplayerseekDelayed()",7000);
+	  }
+
+      }
+
+
+      function onCuePoint(cuePoint) 
+      {
+           if (cuePoint.type != "event") { return; }
+
+	  var tocList = document.getElementById("toclist");
+
+	  var tocEntries = tocList.getElementsByTagName("div");
+	  var parameters = cuePoint.parameters;
+	  var secNum = cuePoint.parameters.sectionnum;
+
+	  var tocNum = secNum-1;
+
+	  tocLen = tocEntries.length;
+
+	  var i;
+	  for (i=0; i<tocLen; i++)
+	  {
+	        var tocItem = tocEntries.item(i);
+
+	        if (i==tocNum-1) {
+		  var prevImg = document.getElementById("tocless");
+		  prevImg.setAttribute("title",tocItem.innerHTML);
+		}
+
+
+		if (i!=tocNum) {
+	          tocItem.style.display = "none";
+		}
+		else {
+	          tocItem.style.display = "block";
+
+		  // load in matching document-section text
+		  var url = document.location.toString();
+
+		  var nexturl = url;
+		  var nextSecNum = tocNum + 1;
+
+		  var doccutoff = url.lastIndexOf("&d=");
+		  var dotcutoff = url.lastIndexOf(".");
+
+		  if (dotcutoff>doccutoff) {
+		    nexturl = url.substring(0,dotcutoff);
+		  }
+
+		  nexturl = nexturl + "." + nextSecNum + "&onlytext=1";
+		  //alert("nexturl = " + nexturl);
+
+		  htmlLoad(nexturl,"ajaxtext");
+
+		}
+
+	        if (i==tocNum+1) {
+		  var moreImg = document.getElementById("tocmore");
+		  moreImg.setAttribute("title",tocItem.innerHTML);
+		}
+
+	  }
+
+
+      }
+
+
+
+
+  function htmlLoad(url,locid)
+  {
+    var xmlHttp;
+    try {
+      // Firefox, Opera 8.0+, Safari
+      xmlHttp=new XMLHttpRequest();
+    }
+    catch (e) {
+      // Internet Explorer
+      try {
+        xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");
+      }
+      catch (e) {
+        try {
+          xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
+        }
+        catch (e) {
+          alert("Your browser does not support AJAX!");
+          return false;
+        }
+      }
+    }
+
+    xmlHttp.onreadystatechange=function() {
+      if(xmlHttp.readyState==4) {
+        var locelem = document.getElementById(locid);
+        
+        locelem.innerHTML = xmlHttp.responseText;
+      }
+    }
+
+    xmlHttp.open("GET",url,true);
+    xmlHttp.send(null);
+  }
Index: gs2-extensions/video-and-audio/trunk/video-and-audio-setup.pl
===================================================================
--- gs2-extensions/video-and-audio/trunk/video-and-audio-setup.pl	(revision 23035)
+++ 	(revision )
@@ -1,69 +1,0 @@
-
-BEGIN {
-    die "GSDLHOME not set\n" unless defined $ENV{'GSDLHOME'};
-    die "GSDLOS not set\n" unless defined $ENV{'GSDLOS'};
-    unshift (@INC, "$ENV{'GSDLHOME'}/perllib");
-    unshift (@INC, "$ENV{'GSDLHOME'}/perllib/cpan");
-}
-
-use Cwd;
-require util;    
-
-my $env_ext  = "GEXTVIDEO";
-my $ext_desc = "the Video and Audio Extension";
-
-if (!defined $ENV{$env_ext}) {
-    
-    my $ext_home = cwd();
-    my $ext_os = $ENV{'GSDLOS'};
-
-    my ($ext_dir,$_prefix_dir) = File::Basename::fileparse($ext_home);
-
-    print STDERR "*** ext_dir = $ext_dir\n\n\n";
-
-
-    my $ext_installed = $env_ext."_INSTALLED";
-
-    $ENV{$env_ext} = $ext_home;
-    
-    $ENV{$ext_installed} = &util::filename_cat($ext_home,$ext_os);
-
-    my $ext_bin = &util::filename_cat($ENV{$ext_installed},"bin");
-    my $ext_lib = &util::filename_cat($ENV{$ext_installed},"lib");
-
-    &util::envvar_append("PATH",$ext_bin);
-
-    if ($ext_os =~ m/darwin/i) {
-	&util::envvar_append("DYLD_LIBRARY_PATH",$ext_lib);
-    }
-    else {
-	&util::envvar_append("LD_LIBRARY_PATH",$ext_lib);
-    }
-
-    # GSDLEXTS always uses : as a separator
-    if (defined $ENV{'GSDLEXTS'}) {
-	$ENV{'GSDLEXTS'} .= ":$ext_dir";
-    }
-    else {
-	$ENV{'GSDLEXTS'} = $ext_dir;
-    }
-    
-    print STDERR "+Your environment is now setup for $ext_desc\n";
-}
-else {    
-    print STDERR "+Your environment is already setup for $ext_desc\n";
-}
-
-if (scalar(@ARGV>0)) {
-
-    print STDERR "\n";
-
-    my $cmd = join(" ",map {$_ = "\"$_\""} @ARGV);
-
-    if (system($cmd)!=0) {
-	print STDERR "\nError: Failed to run '$cmd'\n";
-	print STDERR "$!\n";
-    }
-}
-
-1;
