Archive
Varying the speed of animated GIFs for learners – the technology: Shell-scripting ImageMagick
Animated GIFs for visualizing teaching content were the first e-learning tool that I heard a conference presentation on, in the early nineties.
In the recent past, I have been trying to revive animated GIFs for use in minimalistic training materials.
Now, for different learner personalities, and also to accompany each learner in their practice of Chinese character stroke order, as they get more proficient in drawing, I wanted to provide varied animation speeds of the Chinese characters drawings.
This bash script can do this, using a downloaded set with several hundred source characters in one animation speed to produce 75000 animated GIF (via 95000 temporary) files:
#!/bin/sh
shopt -s nocasematch # shopt -s sets the option, whereas shopt -u disables it.; however, Find is an external command and not affected by shopt
echo `date -u`
# todo: parameterize
# -maindir todo: convert param to $maindir
blnskipcreated=1 # 1=do not recreate anims if _mydelaylimit exists
echo "blnskipcreated:" $blnskipcreated
mydelaystep=10
mydelaylimit=1010
 
# test: what happens if i add zi to maindir
maindir="/cygdrive/G/myfiles/doc/work/students/ms-office/charinput/mandarin_chinese/stroke-order/Zi/Animated-characters/azi"
# todo: the rootdir is unimportant, it contains only a shortcut which points to nothing
rootdir="/cygdrive/G/myfiles/doc/work/students/ms-office/charinput/mandarin_chinese/stroke-order/Zi/Animated-characters"
framefilefilepath=${rootdir}/azi/page1.htm # the actual framefile which loads the azi.html and the character is G:\myfiles\doc\work\students\ms-office\charinput\mandarin_chinese\stroke-order\Zi\Animated characters\azi\page1.htm
# wrong ${rootdir}/animated-characters.htm
framefilefileextension="htm"
framefilenamenopath="`expr "//$framefilefilepath" : '.*/\([^/]*\)'`" # remove path to file
framefilenamenoextension="`expr "$framefilenamenopath" : '\(.*\)\.[^.]*$'`" # remove last suffix ${i}
framefilefilepathnoextension=${rootdir}/${framefilenamenoextension}
echo "DOLLAR 1framefilefilepath, 2framefilefilepathnoextension, 3framefilenamenopath 4framefilenamenoextension:"1${framefilefilepath}:2${framefilefilepathnoextension}:3${framefilenamenopath}:4${framefilenamenoextension}:
# todo DOLLAR framefilefilepathnoextension, framefilenamenopath framefilenamenoextension:
 
cd $maindir
if [ -d "$maindir" ] ; then # needs "" around vriable, space before ]
echo "Good!"
else
echo "not a dir"
exit 1
fi
 
sourcehtmlfilename="azi" # todo: how to i need to reference (enclose) the variables so that can the maindir and sourcehtmlfilename can contain spaces
# todo: document: "give the string one has to add to the maindir to get to the html file that links to the gifs": complication: animated-characters is just al link to azi/azi.htm`- so why not change the maindir to include azi-subdir
sourcehtmlfileextension="htm"
sourcehtmlfilepathnoextension=${maindir}/${sourcehtmlfilename}
sourcehtmlfilepath=${sourcehtmlfilepathnoextension}.${sourcehtmlfileextension}
echo "dollar sourcehtmlfilepath =" $sourcehtmlfilepath # debug
# exit 0 # debug
if [ -e "$sourcehtmlfilepath" ] ; then # needs "" around vriable, space before ]
echo "Html File good!"
sourcehtmlfileswitch=1
else
echo "$sourcehtmlfilepath not an html file"
# no need, leave the html alone then, but set a switch exit 1
sourcehtmlfileswitch=0
fi
mfiles=`find $maindir -iname '*.gif'` # Simply enclosing the wildcard in single quotes makes it work! unix find is case-sensitiv
# todo: if you want to be able to resume gif creation, you have to remove (previously created) files that match pattern _[0-9]+.gif
# shopt -s extglob - does this work with find or only ls
# shopt -s extglob
# mfiles=`find $maindir -name '*!(_[0-9]+).gif'`
# shopt -u extglob
# Find is an external command, so its globbing isn't affected by bash shopt options, but you can use: find . ! -name 's.*.java'
# mfiles=`find $maindir -name '*!(_[0-9]+).gif'`
# i will include the full path
# done: skip some, not all gifs are animated, e.g. G:\myfiles\doc\work\students\ms-office\charinput\mandarin_chinese\stroke-order\Zi\Animated-characters\azi\18b.gif
k=0 #debug:break
# echo "mfiles:$mfiles"
for i in $mfiles ; do
if [[ $i =~ _([0-9]+|strip).gif ]]; then
echo "skipping previously produced file $i"
else
echo "Converting {$i}..." # this prints 1 line extra per space in dir - may cause problems with i down the road?
curdelay=0
delaystep=$mydelaystep # cs
delaylimit=$mydelaylimit # cs
filepath=$i
filenamenopath="`expr "//$i" : '.*/\([^/]*\)'`" # remove path to file
filenamenoextension="`expr "$filenamenopath" : '\(.*\)\.[^.]*$'`" # remove last suffix ${i}
filepathnoextension=${maindir}/${filenamenoextension} # test: w/o azi ${maindir}/azi/${filenamenoextension} # todo: stupid workaround, but i supsect i cannot just add /azi to maindir
echo "DOLLAR 1filepathnoextension,2filenamenopath,3filenamenoextension:"1${filepathnoextension}:2${filenamenopath}:3${filenamenoextension}:
# suffix="`expr "$name" : '.*\.\([^./]*\)$'`" # extract last suffix
# name="`expr "$name" : '\(.*\)\.[^.]*$'`" # remove last suffix
 
############################################################################################### BREAK APART
# path2name
# as a animation disassembler, producing a summary of animation in terms of IM options
# gif2anim [options] image_anim.gif.
#. gif2anim.sh -s MIFF -b $i -o ${i}.anim $i
# the s-switch of gif2anim.sh never worked, can i do without it ? ainim2gif.convert will complain: unable to open idid0_20.gif_001.-s
# -s seems to result in ext -s, miff seems to result in all params fro mmiff on being interpreted as filenames
#. gif2anim.sh -o ${i}.anim $i
framesfileext="xpm" # change this to give if you not use -x - CASESENSITIVE
. gif2anim.sh -x -o ${filenamenoextension}.anim $filepath
#. gif2anim.sh -s MIFF -b $i -o ${i}.anim $i
#. gif2anim.sh MIFF -b $i -o ${i}.anim $i
# -c coalesce animation before parsing
# -t Add time synchronization comment before each frame
# -l just list the anim file to stdout, no images
# -v Be verbose in animation conversions
# -n no images, just create the '.anim' file
#1 -g use an GIF suffix for frame images (default)
# -x use an XPM suffix for frame images
#todo: did this work? b0aej.gif_001.-s 1 MIFF -s suffix use this suffix for the frame images
# -i initframe number of the first frame image (def=1)
#needed? -b framename basename for the individual frames
# 1 -o file.anim output to this .anim file (- for stdout)
#
 
# grep 'delay ' ${i}.anim # check return 0exit good, 1exit bad - You can see what a commands exit status is by looking at the variable $?.
# might be safer
grep 'delay ' ${filenamenoextension}.anim
if [ $? == 1 ]; then # is no animated gif, skip
echo "NO ANIM GIF grep finds no delay in: " ${i}.anim
# do nothing: # done: if there is no delay in the gif.anim, do not enter while for curdelay, go to next file
else # is animated gif, curdelay
echo "since delay is matched for this gif, ENTERING curdelay WHILE"
while [ $curdelay -lt $delaylimit ] ; do # go in step 20cs from 20s = tile, to 40cs - default is 80cs\
echo "CURDELAY : " $curdelay
if [ $curdelay == 0 ]; then # branch into strip producing programm
############################################################################################## MAKE A STRIP
# The "gif_anim_montage" script also the special option '-u' which will also underlay a semi-transparent copy of the coalesced animation.
# interface: if you just output to samename.gif, you can leave the original links intact
# advantage: synchronous overview:
# disadvantage: viewing even more, and pseudo-signs
# gif_anim_montage [options] animation.gif [output_image]
# done: query how many frame files into a variable and use this as param 1x${frames} of call to gif_anim_montage.sh
if [ -e ${filepathnoextension}_strip.gif ] ; then
echo "skipping found ${filepathnoextension}_strip.gif"
else
striplength=$(ls ${filenamenoextension}.${framesfileext} 2> /dev/null | wc -l) # count matching files, errors redirected
echo "found ${striplength} of frames framesfileext with ${framesfileext}, calling montage 1x${striplength} -u -w ${filepath} ${filepathnoextension}_strip.gif"
if [ "striplength" != "0" ] ; then
. gif_anim_montage.sh 1x${striplength} -u -w -n ${filepath} ${filepathnoextension}_strip.gif
#1 -u Underlay a dimmed coaleased image (context for frame)
#0 -c Add checkerboard background for transparent areas
#0 -g Use granite for background
#1 -w Use a white background
#0 -b Use a black background
#0 -t image Use this image (or color image) for background
#0 -r Use a red border color rather than black
#0 todo: not USE DEFAULT, need column #x# tile the images (default one single row)
#0 -n Don't label the animation frames (not important)
else
echo "NO STRIP MADE!"
fi
fi # strip file already exists?
else # $curdelay > 0 -> branch into gif producing program
# first you need to stream edit the .anim textfile for each iteration match "-delay 80"/"-delay curdelay"
# echo "CALLING sed $curdelay ${i}.anim ${filepathnoextension}_${curdelay}.gif.anim" # debug
# sed 's/-delay 80/-delay '$curdelay'/g' ${i}.anim > ${filepathnoextension}_${curdelay}.gif.anim # todo: break delay loop if no match in gif.anim = no animated gif
echo "1:what is i now ${i} and filepathnoextension.anim is: ${filepathnoextension}.anim"
echo "CALLING sed $curdelay in: ${filepathnoextension}.anim out: ${filepathnoextension}_${curdelay}.anim" # debug
sed 's/\-delay\s.*/-delay '$curdelay'/g' ${filepathnoextension}.anim > ${filepathnoextension}_${curdelay}.anim # todo: break delay loop if no match in
# first stream edit the .anim textfile for each iteration match
# redirection: file is UNCHANGED the modified file is file.bak
# watch variable expansion '/'$license'/p' README.txt
# skip timeconsuming recreate
if [ $blnskipcreated == 1 ] ; then # if not needed (assuming generated files are correct - todo:parameterize!)
if [ -e ${filepathnoextension}_${mydelaylimit}.gif ] ; then
echo "skipping recreating gifs for ${filepathnoextension}_${mydelaylimit}.gif and below "
else
echo "NOT skipping recreating gifs for ${filepathnoextension}_${mydelaylimit}.gif and below "
############################################################################################### PUT TOGETHER AGAIN - w different delay AS PARAM -cs centiseconds
# read in prior output file .anim
# anim2gif [-b BASENAME] file.anim...
# anim2gif: Failed to convert "/cygdrive/G/myfiles/doc/work/students/ms-office/charinput/mandarin_chinese/stroke-order/Zi/Animated-characters/azi/b0b2_400.gif.anim" into "B0B2_400.GIF.GIF"
echo "CALLING CP ${filepathnoextension}_${curdelay}.anim ${filepathnoextension}.anim"
cp ${filepathnoextension}_${curdelay}.anim ${filepathnoextension}.anim #
# now try to call the ${filepathnoextension}.gif w/o${curdelay} to not muddy the output file name
echo "CALLING anim2gif.sh -g ${filepathnoextension}.gif" # debug exists: idid0.gif.anim - do we have curdelay?
. anim2gif.sh -g ${filepathnoextension}.anim # determine: we overwrite the ori gif here, does it matter? -> final cleanup
echo "CALLING mv4 ${filepathnoextension}.gif ${filepathnoextension}_${curdelay}.gif" # debug
mv ${filepathnoextension}.gif ${filepathnoextension}_${curdelay}.gif
# OPTIONS
#is this needed? not taken from the .anim ? -b framename basename for the individual frames
# 1 -g Add '.gif' to end of basename, not '_anim.gif'
# we rather want to overwrite the original gif: The "anim2gif" by default will re-create the GIF animation with a "_anim.gif" suffix.
# -c Input frames are coalesced, ignore any initial page size
# todo: since there is only one "animated characters.htm", one could append the _curdelay to all the output.gifs
# todo: and once per all gif files within one curdelay iteration, to the occurence of .gif within "animated characters.htm" and to the filename "animated characters_curdelay.htm"
fi # _mydelaysteplimit.gif already exists, can skip?
fi # blnskipcreated, allowed to skip?
fi # is $curdelay > 0 -> s or gif producing?
echo " before exec:" $curdelay "delaystep:" $delaystep
curdelay=$(( $curdelay + $delaystep )) # 0=20 command not found, does not work here: `expr $curdelay + $delaystep` # increment for next iteration #todo: command not found
# k=$(( $k + 1 ))
echo " afterexec:" $curdelay
if [ $sourcehtmlfileswitch == 1 ] ; then #################### update the azi-html that points to the _curdelay.gif
if [ -e ${sourcehtmlfilepathnoextension}_${curdelay}.${sourcehtmlfileextension} ] ; then # only once per pseudo $curdelay=0=strip
echo "file ${sourcehtmlfilepathnoextension}_${curdelay}.${sourcehtmlfileextension} already exists, nothing to do "
else
# todo: is das cp nicht überflüssig before sed
cp ${sourcehtmlfilepath} ${sourcehtmlfilepathnoextension}_${curdelay}.${sourcehtmlfileextension} # create file
echo "dollar sourcehtmlfilepath =" $sourcehtmlfilepath # debug
sed -e 's/\.gif/_'${curdelay}'\.gif/gI' $sourcehtmlfilepath > ${sourcehtmlfilepathnoextension}_${curdelay}.${sourcehtmlfileextension} # update gif links in html file to reflect curdelay
fi # if sourcehtmlfilename_curdelay already exists
 
#################### update the frames-html that points to azi.html
if [ -e ${framefilefilepathnoextension}_${curdelay}.${framefilefileextension} ] ; then # only once per pseudo $curdelay=0=strip
echo "framefile already exists, nothing to do "
else
# todo: is das cp nicht überflüssig before sed
echo "CALLING CP ${framefilefilepath} ${framefilefilepathnoextension}_${curdelay}.${framefilefileextension}"
cp ${framefilefilepath} ${framefilefilepathnoextension}_${curdelay}.${framefilefileextension} # create file
echo "dollar framefilefilepath =" $framefilefilepath # debug
# todo: magic string
sed -e 's/azi.htm/azi_'${curdelay}'\.htm/gI' $framefilefilepath > ${framefilefilepathnoextension}_${curdelay}.${framefilefileextension} # update gif links in html file to reflect curdelay
fi # if framefilefilename_curdelay already exists
fi # if $sourcehtmlfileswitch=1
# does break jump across below
# obsolete echo " NOBREAK ${i}.anim $curdelay "
done # all curdelay steps to limit
echo " could be BREAK ${i}.anim $curdelay "
# does break go here?
echo " before exec $k:" $k
k=$(( $k + 1 )) # `expr $k + 1` #debug: test only
echo " after exec $k:" $k
 
if [ $k -gt 6 ] ; then # increased to 2 since 1 is skipped then # the "if [ ... ]" and the "then" commands must be on different lines. Alternatively, the semicolon ";" can separate the
echo "let it run freely , or uncomment the next line"
# exit 0
fi # debug
fi # is animated gif?
 
# final cleanup todo: only if the ori gif and ori htmlsource do not get deleted
if [ -e ${filepathnoextension}_${curdelay}.gif ] ; then # this errors now for undeleted non-animated gifs (break)
# todo: ${i} something wrgon is in ${i}
# echo "5: what is in ${i} now"?
rm ${filepathnoextension}.gif # the ori gif name, whatever is not in there is also in ${i}_${curdelay}.gif
fi
fi #skipping previously produced?
done # all gif files
# final cleanup delete the compromised ori gif and ori htmlsource
# debug this causes to many probs rm $sourcehtmlfilepath # the ori html name, whatever is not in there is also in ${sourcehtmlfilename}_${curdelay}.${sourcehtmlfileextension}
echo 'animated gif conversion complete!'
# todo: azi.htm is the file to be made into azi_10...1010.htm, ot animated characters
# todo:b1caf_strip.gif wont displaty inphotoviewer, strip gets way too long with empty spots at end
# -> redo only the strips and
# deleate all (after variable creation and loopstarting) and (before grep 'delay ' ${filenamenoextension}.anim)
# delete all after echo "NO STRIP MADE!" except fi and loop closing
# todo:labelling of strips - can one also label the animated gifs
#todo: der erste rame sollte nicht blank sein, sonst kann erst lange gar nichts sichtbar sein
#todo: found 0 of frames, calling montage 1x0 -u -w /cygdrive/G/myfiles/doc/work/students/ms-office/charinput/mandarin_chinese/stroke-order/Zi/Animated-characters/azi/a1f5.gif/cygdrive/G/myfiles/doc/work/students/ms-office/charinput/mandarin_chinese/stroke-order/Zi/Animated-characters/azi/a1f5_strip.gif
# afterexec:10
#dollar sourcehtmlfilepath = /cygdrive/G/myfiles/doc/work/students/ms-office/charinput/mandarin_chinese/stroke-order/Zi/Animated-characters/azi/azi.htm
#CALLING CP _10.htm
# cp: missing destination file operand after `_10.htm'
#/cygdrive/g/conf/lang/bat/imagemagick/animated-gifs-slow-down.sh: line 26: /cygdrive\
#G\myfiles\doc\work\students\ms-office\charinput\mandarin_chinese\stroke-order\Zi\Animated-characters\animated-characters.htm: No such file or directory
#G:\myfiles\doc\work\students\ms-office\charinput\mandarin_chinese\stroke-order\Zi\Animated-characters\animated-characters.htm
One day, I hope to generalize this script for adapting other animated GIF collections from the Web, e.g . parameterizing it. For now, it can run out of the box on the Chinese website if you setup the necessary environment.
I am running this from mintty using Cygwin’s ImageMagick, to avoid having to port the great shell ImageMagick scripts gif2anim.sh, anim2gif.sh and gif_anim_montage.sh by Anthony Thyssen which you need to put in the same directory as my animated-gifs-slow-down-pub.sh
And if you change the rootdir and maindir to point to the disk location where you downloaded the website package.
Call the script with “animated-gifs-slow-down.sh &>slowing.log” to log the chatty debug information.
If you use it and/or adapt it to process other websites that use animated GIFs, kindly link back.
Hint: When done, be careful when moving such a large set of files. InfoZip’s zip utility ate the last 10000 of my gif files, without warning, as if it can handle only 64k files? Since this happened during a “move into zip-file”operation, this was costly, at least in CPU cycles required to rerun the gif animation script…
AviSynth scripting should be next…
How to build a digital recorder with GUI for free with Windows-Media-Encoder
- Merely a bit of systems integration. Context: An interpreting study program centered around a conference-interpreting lab that, in 2009, was still analog (tape-based) and could not be upgraded to digital, but each interpreter booth had unintegrated Windows PCs – an opportunity.
- Uses AutoIT and Windows-COM programming, as exemplified in Windows-media-encoder-SDK, against Windows-Media-Encoder.
- make sure that you have
- have all AutoIt packages referenced with #includes below;
- have the windows-media-encoder installed – the windows-media-encoder-SDK should not be needed , except for programming examples;
- make sure you adapt “PUTYOUROWN” to your own environment
- provide your own (equipment-specific) WME files for the Windows-Media-Encoder configuration.
- (While there exists an AutoIt brush for Alex Gorbatchev’s SyntaxHighlighter, WordPress.com does not seem to include it, hence the poor highlighting below:)
#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Outfile=g:\myfiles\bureaucracy\proposals\interpreting\digitization\local_hacks\recording.au3\trprecord.exe
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Sample: How to build a digital recorder with GUI for free with Windows-Media-Encoder for an analog interpreting lab that cannot be digitized
; prerequisites:
; make sure you have all autoit packages referenced with #includes below
; make sure you have the windows-media-encoder enstalled - the windows-media-encoder-sdk should not be needed , except for programming examples
; make sure you adapt "PUTYOUROWN" to your own environment
; provide your own WME files
; sorry for the magic strings!
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; $GW_HWNDNEXT $FORMAT_MESSAGE_FROM_SYSTEM $GW_CHILD T:\old\trp_l\lang\autoit\Include\Constants.au3(200): Global Const $GW_CHILD = 5
; $GW_HWNDNEXT $FORMAT_MESSAGE_FROM_SYSTEM $GW_CHILD T:\old\trp_l\lang\autoit\Include\Constants.au3(200): Global Const $GW_CHILD = 5
; $EM_LINEFROMCHAR etc EditConstants.au3
#include <Constants.au3>
#include <GUIConstants.au3>
#include <WindowsConstants.au3>
; for gui's
#include <ButtonConstants.au3>
#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <ProgressConstants.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>
#include <GUIConstantsEx.au3>
#include <_CSVLib_V1.3.au3> ;
#include <Array.au3>
; #include <Array2D.au3> $WS_CLIPSIBLINGS, $WS_OVERLAPPEDWINDOW, WS_VISIBLE: used before declaration, must be outdated
#include <date.au3>
#include <debug.au3>
#include <File.au3>
#include <GuiComboBox.au3>
#include <GuiListBox.au3>
#include <GuiListView.au3>
#include <GuiTab.au3>
#include <Hash.au3>
#include <Screencapture.au3>
#include <trpCombo.au3> ; not anymore inline
#include <trpListClick.au3>
#include <trpOCRTextCapture.au3>
#include <utils_array.au3>
#include <utils_csv.au3>
#include <utils_file.au3>
#include <utils_Hash.au3>
#include <utils_window.au3>
#include <ftp.au3> ; for stream
#include <Misc.au3> ; singleton
;#include <PopupChecker.au3>
; #include <trpAuralog.au3>
Opt("MustDeclareVars", 0) ; i cannot find where i set thinks to 1, but aat a certain development point autoit would not run w/o complaining about vars not being declaured, including in include files
Opt("TrayIconDebug", 1)
Opt("WinWaitDelay", 250) ; Alters how long a script should briefly pause after a successful window-related operation; default is250ms
Opt("WinTitleMatchMode", 1) ;1=start, 2=subStr, 3=exact, 4=advanced, -1 to -4=Nocase
; removed may 2008 Opt("RunErrorsFatal", 0)
Global $thissingletonid = "trprecord"
Global $thiswindowtitle = "TRPRecord"
Global $debugtype = "debugconsolescreen"; "debugconsole" ; or run -> screenshots and debug out; consider "release" --> log
; _DebugSetup("debug")
_ScreenCapture_SetBMPFormat(0) ; smallest, with default = 24bit, each 1280*1024 (seems to do only primary) is 3.5mb
; dolater: add cmdline: the program looks for params /admin and /video and loads the gui with buttons disabled and with video checked - there are prior solutions
; donot: options/ini: do you have to allow not to use default audio/video device? set the default device in the wme and edit it in there instead of the ini
; donot: logging - just store debug screenshots with names in the local temp, will get erased on restart
; beyond logging into a file for admin (debugging) (1 per student -> cannot be monitored by teacher),
; log for teacher onto a network folder which the instructor can monitor important errors like encoding start failed
; (not filecopy to instructors network folder, that is redundant, since log files are in the same folder)
; yes filecopy to student x: drive -> instructorr can tell students to clean up their x drives
; this functionality to write messages to filenames i devleoped already for auralog screenshots
Global $g_eventerror = 0 ; to be checked to know if com error occurs. Must be reset after handling.
global $oMyError
Global $szDrive, $szDir, $szFName, $szExt
Global $arrAutoitExe = _PathSplit(@AutoItExe, $szDrive, $szDir, $szFName, $szExt); Returns an array with 5 elements where 0 = original path, 1 = drive, 2 = directory, 3 = filename, 4 = extension
Global $scriptfilewithexe = $arrAutoitExe[3] & $arrAutoitExe[4] ; autoit3.exe if <>@compiled
Global $windowtitletrpRecord = "TRPRecord:Set Options, press Start to begin"
Global $nowstring = StringRegExpReplace(_NowCalc(), "[\s\/\:]", "_") ; fill this here only for logfile, but later again when record button is pressed
Global $networknamefile = ""
Global $networkpathfile = "" ; can instead differentiate audio/video in backupRecording, using $ext
Global $ext = ""
Global $pathfilelog = FileNameEscape("rec" & "_" & $nowstring & "_" & @ComputerName & "_" & @UserName & "_" & ".log", @TempDir)
; wmeditor.exe is not in the path
Global $wmeditorpath = "C:\Program Files\Windows Media Components\Encoder\wmeditor.exe" ; dolater: put in central ini
Global $wmencexe = "wemenc.exe"
Global $wmcmdvbspath = "C:\WMSDK\WMEncSDK9\samples\vb\wmcmd\Wmcmd.vbs"
Global $webinterfacestring = "https://thomasplagwitz.com/2009/08/04/digitization-of-the-saville-analogue-conference-interpreting-recording-facility-booths-end-user/" ; "http://plagwitz1.spaces.live.com/blog/cns!4FA3329905D7E1CE!961.entry"
Global $recordingstopstring = "stopRecordingAndPlay.exe"
Global $ret = 0
Global $set = 0
; just use TEMPdir=c:\temp
; just use @ComputerName =MORLIB--PCC6392
; just use @USERNAME instead of $username =plagwitt
; just use @TempDir - $tempdir = "c:\temp"; todo: what if @TempDir dioes not point to c:\temp, but network
Global $uncpathwritestudent = "PUTYOUROWN" ; "\\venus\homes\My Documents\My Music" ; todo: handle spaces
; not this, since can't probably be mapped under student account already "\\lgu.ac.uk\lgu$\multimedia student\mmedia\mmedia1\language_services\recordings"
Global $usernameinstructor = "PUTYOUROWN"; "plagwitt" ; dolater: todo: move into network ini
Global $passwordinstructor = "PUTYOUROWN" ; encrypted "7B5912DCA17955CECED6668C78303A0B" ; todo: 7A5D13D9A10555BCCED462FC7830, dolater:move into network ini
Global $passworddecrypt = "record"; todo:ini: leave blank if you do not know how to encrypt your pwd with autoit; level was 1
Global $wmcmdpath = "C:\WMSDK\WMEncSDK9\samples\vb\wmcmd\Wmcmd.vbs"
Global $uncpathsharewriteinstructoronly = "PUTYOUROWN" ; "\\lgu.ac.uk\lgu$\londonmet departments" ; old network backup destination for student recordings, replaced by k-drive;
; this worked for thp0136 (with filecopy to $driveletterkreplacementforstudentwrite returned from addinstructornetworkshares?) ; cannot replicate this anymore
Global $dirsharewriteinstructoronly = "Humanities arts and languages (HAL)\Language_services\recordings" ; old network backup destination for student recordings, replaced by k-drive;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; done: @LogonDomain is lgu, which suffices - do students and instructors use the same domain? done: test with thp0136, has the same domain
Global $domain = "lgu.ac.uk"
Global $serverwriteinstructor = "PUTYOUROWN" ; "\\stushare_server" ; we need to disconnect form the entire server computer to be able to reconneec with different permissions
Global $sharewriteinstructor = "PUTYOUROWN" ; "StuShare"
Global $uncpathsharewriteinstructor = $serverwriteinstructor & "\" & $sharewriteinstructor ; when we reconnect wiht instrucoter permission, this share should receive a driveletter
; done: replace $driveletterkreplacementforstudentwrite & "\" & $dirsharestudentreadconfig by $driveletterkreplacementforstudentwrite & "\" & $dirsharestudentreadconfig
Global $serverstudentreadconfig = "PUTYOUROWN" ; "\\lgu.ac.uk" ; we need to disconnect form the entire server computer to be able to reconneec with different permissions
Global $sharestudentreadconfig = "PUTYOUROWN" ; "lgu$\multimedia student\mmedia" ; \\lgu.ac.uk\lgu$\multimedia student\mmedia
Global $uncpathsharestudentreadconfig = $serverstudentreadconfig & "\" & $sharestudentreadconfig ;
Global $dirsharestudentreadconfig = "PUTYOUROWN" ; "mmedia1\language_services\configuration" ; now on o:, was on k: "Humanities, Arts and Languages\Language_Services\configuration"
; done: have to put this on K:-uncpath, wmenc runs under student, student cannot read from j
; "\\lgu.ac.uk\lgu$\multimedia student\mmedia\mmedia1\language_services\configuration" ; wmenc.exe running under studentuser needs to be able to read config files;
; done: replace $driveletterkreplacementforstudentwrite & "\" & $dirsharestudentreadmedia by $driveletterkreplacementforstudentwrite & "\" & $dirsharestudentreadmedia
Global $uncpathstudentreadmedia = "PUTYOUROWN" ; "\\stushare_server\StuShare\Humanities, Arts and Languages\Language_Services\media" ; studentuser can read
Global $dirsharestudentreadmedia = "PUTYOUROWN"; "Humanities, Arts and Languages\Language_Services\media"
Global $dirsharewriteinstructor = "PUTYOUROWN"; "Humanities, Arts and Languages\Language_Services\recordings"
Global $uncpathwritemedia = $uncpathsharewriteinstructor & "\" & $dirsharewriteinstructor ; useful? we currently do not use unc but the driveletter
; done: replace $driveletterwriteinstructor by $driveletterkreplacementforstudentwrite
Global $driveletterkreplacementforstudentwrite = ""; will be assigned in setupdrives()
Global $driveletterkreplacementforstudentwritenetuse = "K:"; todo: hack for net use
Global $driveletterreadconfig = ""; will be assigned in setupdrives()
; todo: build wme out of $driveletterreadconfig and trprecord
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; all the following .wme-files should be in the same network dir $driveletterkreplacementforstudentwrite & "\" & $dirsharestudentreadconfig - not neccessary anymore
Global $wmefilename = "" ; ; will set by the differentiated following
Global $wmefilenamerecordaudiolive = "recordaudiolive.wme" ; 64kbps,44khz,stereo,cbr (a default file archive profile)saving to c:\temp\recordaudio.wma - overwrite?
Global $wmefilenamerecordvideolive = "recordvideolive.wme"
Global $wmefilenamerecordaudiofile = "recordaudiofile.wme"
Global $wmefilenamerecordvideofile = "recordvideofile.wme"
; indexing is required for all recordstop()ed files that are not (like audiofile, videofile) postprocessed
; if for indexing you reencode (to same as input) using wmenc.xe with .wme, you need to differentiate between audio, video as source
Global $wmefilenamerecordaudioindexing = "recordaudioindexing.wme"
Global $wmefilenamerecordvideoindexing = "recordvideoindexing.wme"
; streamudio() sample
Global $fullpath, $name, $drv
Global $Label_1
Global $Label_2
Global $input_1
Global $button2
Global $button1
Global $guiMsg
Global $progbar
Global $prioruser
Global $wmencoderPID = 0
; new approach from here using encoder-object.start/stop
; config files for audio-video and audio onlyrecording: edit in windows media encoder/ properties to make changes; assumes exists and have write permissions to "c:\temp"
;todo: either: refer to the drive letter or to the unc path? former should be more stable, but latter is in practice
Global $strWMEFileVideoName = "trprecord_av.wme"
Global $strWMEFileVideo ; can only be done after setupdrives = $driveletterreadconfig & "\" & $dirsharestudentreadconfig & "\" & $strWMEFileVideoName ; $driveletterkreplacementforstudentwrite & "\" & $dirsharestudentreadconfig & "\" & $strWMEFileVideoName ; "K:\Humanities, Arts and Languages\Language_Services\configuration\trprecord_av.wme"
Global $strWMEFileAudioName = "trprecord_a.wme"
Global $strWMEFileAudio ; can only be done after setupdrives = $driveletterreadconfig & "\" & $dirsharestudentreadconfig & "\" & $strWMEFileAudioName ; $driveletterkreplacementforstudentwrite & "\" & $dirsharestudentreadconfig & "\" & $strWMEFileAudioName ; "K:\Humanities, Arts and Languages\Language_Services\configuration\trprecord_a.wme"
Global $objEncoder, $objIWMEncFile, $g_objEncoder, $g_ObjEvent, $g_objIWMEncFile
; done: rather than hardcoding read the tempfilename from the encoder-object.localfilename, which sets it according to load(wmefile)
Global $tempfilename = ""
Global $wmeoutputbasefilename = "trprecord" ; e.g. trprecord within c:\temp\trprecord.wma
Global $wmeoutputbasefilename = "c:\temp"
main()
;test()
Func test()
EndFunc ;==>test
Func main()
; MsgBox(0, "test", "test")
; allow only one instance of the program to run
If _Singleton($thissingletonid, 1) == 0 Then
myDebugOut(@ScriptLineNumber, "An occurence of trprecord is already running")
; following test seems a bit superfluous, but just to make sure:
If WinExists($thiswindowtitle) Then ; does work, when case-sensitivity is observed
WinActivate($thiswindowtitle) ; remind the user of the running instance by showing it
EndIf
; test
Exit
myDebugOut(@ScriptLineNumber, "singleton @error=" & @error)
; ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : Exit = ' & Exit & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console
EndIf
; Msgbox(0,"OK","the first occurence of trprecord is running")
killpriorInstance() ; before building a new gui
; was: test:non-interactive
setupdrives()
; create an encoder-object here for the entire duration of the program
$objEncoder = ObjCreate("WMEncEng.WMEncoder") ; this does not show up in the tasklist
$oMyError = ObjEvent("AutoIt.Error", "MyErrFunc") ; must be called only once (and the objcreate happens only once, with the gui-creation)
; release: $g_ObjEvent=ObjEvent("AutoIt.Error","nothing"); Equal to VBscript's On Error Resume Next
If Not IsObj($objEncoder) Then
$set = GUICtrlSetData($guiMsg, "Cannot create encoder, try restarting computer." & @CRLF & "If app still fails, reinstall windwos media encoder with sdk.")
myDebugOut(@ScriptLineNumber, "Cannot create encoder")
Else
; $objEncoder.Start
;
EndIf
; works MsgBox(0, "WMEncEng.WMEncoder", $ret)
; wmeobjects($objEncoder,$strWMEFileAudio)
guioptions()
; obsolete removeInstructorNetworkShares()
trpCleanup()
EndFunc ;==>main
Func setupdrives()
addStudentReadconfigNetworkshares() ; wme cannot reside on k:, but o: can also be read by student, but wmencoder needs a path
deleteStudentShare() ; before we reconnect with different permissions, we need to close all connections with current permissions (unless we were given the ip of the server)
; obsolete disconnectserverwriteinstructor() ; how can this be done? hardly drivemapdel is enough? net use? are local admin credentials necessary?
; determine: do this here?
$driveletterkreplacementforstudentwrite = addInstructorNetworkshares() ; returns a driveletter
$strWMEFileAudio = $driveletterreadconfig & "\" & $dirsharestudentreadconfig & "\" & $strWMEFileAudioName ; $driveletterkreplacementforstudentwrite & "\" & $dirsharestudentreadconfig & "\" & $strWMEFileAudioName ; "K:\Humanities, Arts and Languages\Language_Services\configuration\trprecord_a.wme"
$strWMEFileVideo = $driveletterreadconfig & "\" & $dirsharestudentreadconfig & "\" & $strWMEFileVideoName ; $driveletterkreplacementforstudentwrite & "\" & $dirsharestudentreadconfig & "\" & $strWMEFileVideoName ; "K:\Humanities, Arts and Languages\Language_Services\configuration\trprecord_av.wme"
EndFunc ;==>setupdrives
Func recordstart(ByRef $g_objEncoder, $g_strWMEFile = "") ; this works in principle, with an existing wme-session file,
; done: unlike the gui-wmencoder, the object will overwrite an existing output filename, without need for interaction, even if it is open in wmplayer
; $OEvent=ObjEvent("AutoIt.Error","nothing"); Equal to VBscript's On Error Resume Next
;If a WME file is provided, load the encoding configuration from this WME file.
If $g_strWMEFile <> "" Then
If Not (FileExists($g_strWMEFile)) Then
$set = GUICtrlSetData($guiMsg, "Cannot find config file " & $g_strWMEFile & ", will stop.")
myDebugOut(@ScriptLineNumber, "Cannot find wme config file" )
Else
; recordstart($objEncoder, $g_strWMEFile)
; test only, rather stop interactively by passing byref $objEncoder to recordstop
; Sleep(5000)
; recordstop($objEncoder)
; EndIf
If IsObj($g_objEncoder) Then
$set = GUICtrlSetData($guiMsg, "Now preparing recording...")
; $oMyError = ObjEvent("AutoIt.Error", "MyErrFunc") ; done: crashes when called repeatedly = when recordstart is called repeatedly -> moved to after objcreate(encoder)
; Load enocder session from a WME file
myDebugOut(@ScriptLineNumber, "attempting to load encoder profile $g_strWMEFile=" & $g_strWMEFile)
$g_objEncoder.Load($g_strWMEFile) ; ( g_objFileSystem.GetAbsolutePathName( g_strWMEFile ) )
; todo: on error returns 0x8007000E Memory cannot be allocated. - how can i sue this for debuging with ObjEvent
; if you try over-load [what? the output filename?], error: "the requested action with this object has failed.""
$set = GUICtrlSetData($guiMsg, "One moment, please...")
Sleep(2000) ; the mgb-36 computers are so slow, especially at startup (mcafee?) that we seem to need to go slower than 1000
; todo: can you shave sth off? rather give the user feedback when recording started
; donot: fiddle with WMEncoder.PrepareToEncode , SynchronizeOperation = false
$tempfilename = $g_objEncoder.File.LocalFileName ; not anymore: The requested action with this object has failed.:
$g_objEncoder.start
$set = GUICtrlSetData($guiMsg, "Now recording to localfilename:" & $tempfilename)
myDebugOut(@ScriptLineNumber, "recording to localfilename:" & $tempfilename)
Else
$set = GUICtrlSetData($guiMsg, "Cannot find encoder, try restarting the application.")
myDebugOut(@ScriptLineNumber, "error:encoder not isobject:" & $set)
EndIf
EndIf
Else
$set = GUICtrlSetData($guiMsg, "No configuration file specified, will stop. Try restarting the application.")
myDebugOut(@ScriptLineNumber, "No wme configuration file specified" )
EndIf
EndFunc ;==>recordstart
Func recordstop(ByRef $g_objEncoder)
; done: interactively stop (without presetting an encoding duration)
$g_objEncoder.stop ; This method does not return a value.
; tested: works myDebugOut(@ScriptLineNumber, "is the encoder stopped? how to tell.. ")
; todo: the network file cannot be played, but the tempfile can, what gives?
; is this cause=d by a mere encoder.stop? does not make sense, should be caused by copy
; is there another method short of destroy encoder to flush the file?
; todo: is it not also possible to rename the tempfile, or will another encoder.start otherwise add to it? but encoder.stop is more than encoder.pause
; donot: destroy the object - only destroy the object when gui/window/app is closed
; this cannot work with byref $g_objEncoder = 0
; todo: i would need to know when the objEncoder is finished = safe to start work on its outputfile
; test: can i override the filename -> no
; Retrieve an IWMEncFile object and override the name of the archive file specified in the predefined configuration.
; $objIWMEncFile = ObjCreate("WMEncEng.WMEncoder.File")
; fails to create an object $objIWMEncFile = ObjCreate($objEncoder.File) ; müssen wir doch mit dem existierenden object arbeiten
; fails to create an object $objIWMEncFile = ObjCreate("WMEncEng.WMEncoder.File") ; müssen wir doch nicht mit dem existierenden object arbeiten
; $g_objIWMEncFile = $g_objEncoder.File ; this creates an object
; $ret = IsObj($g_objIWMEncFile)
; MsgBox(0,"WMEncEng.WMEncoder.File",$ret)
; File = Encoder.File;
; File.LocalFileName = "C:\\file_name.wmv";
; $g_objIWMEncFile.LocalFileName = "C:\\tmp\\file_name.wmv"; error: Variable must be of type "Object" even though but ObjCreate did not error
; $objEncoder.File = $objIWMEncFile ;test:andersrum
; $g_objEncoder.start
; Sleep(15000)
; $g_objEncoder.stop
EndFunc ;==>recordstop
Func backupRecording() ; try to read
; todo: create source file path
$ret = FileExists($tempfilename)
If $ret Then
If (StringRight($tempfilename, 4) = ".wma") Then; audio
$ext = ".wma" ; handle a wma file
ElseIf (StringRight($tempfilename, 4) = ".wmv") Then
$ext = ".wmv";handle a wmv file
Else ;error
$set = GUICtrlSetData($guiMsg, "Not a legal temp file, cannot determine if audio or video.")
myDebugOut(@ScriptLineNumber, "$set=" & $set)
EndIf
; donot: leave it to the student to make manual backup copies in personal network home
; make instructor permanent copy of audio file
; done: create target file name from computername & username & datetime & ext
; done: create target file path
$networknamefile = "trprecord" & "_" & $nowstring & "_" & @ComputerName & "_" & @UserName & $ext
myDebugOut(@ScriptLineNumber, "$networknamefile is: " & $networknamefile)
$networkpathfile = FileNameEscape($networknamefile, $driveletterkreplacementforstudentwrite & "\" & $dirsharewriteinstructor); YYYY/MM/DD HH:MM:SS
myDebugOut(@ScriptLineNumber, "$networkpathfile is: " & $networkpathfile)
; todo: impersonate
; todo: copy
$ret = FileCopy($tempfilename, $networkpathfile, 9) ; FileCopy ( "source", "dest" [, flag] )
; todo: handle exceptions
If ($ret == 0) Then
myDebugOut(@ScriptLineNumber, "failed to filecopy: " & $networkpathfile)
; todo: this does not $ret 0, but the networkfile is not full size and does not have the complete audio even when it plays in wmp - is the recorded file closed?
$set = GUICtrlSetData($guiMsg, "Failed to save to network, make sure you save your recording : " & $tempfilename)
myDebugOut(@ScriptLineNumber, "$set=" & $set)
Else
$set = GUICtrlSetData($guiMsg, "Saved to network: " & $networknamefile)
myDebugOut(@ScriptLineNumber, "$set=" & $set)
EndIf
Else ; todo: what to try if not fileexists
$set = GUICtrlSetData($guiMsg, "tmp recording " & $tempfilename & ": does not exist, cannot save to network.")
myDebugOut(@ScriptLineNumber, "$set=" & $set)
EndIf
; todo: return 0 if error, 1 if success
Return $ret
EndFunc ;==>backupRecording
Func killpriorInstance()
$wmencoderPID = ProcessExists("wmencoder.exe")
If $wmencoderPID <> 0 Then
$prioruser = _ProcessGetOwner($wmencoderPID)
If $prioruser <> @UserName Then ; is owner of wmencoder = currentuser than taskkill
; myDebugOut(@ScriptLineNumber, "prior $wmencoderPID ="& $wmencoderPID & ", $prioruser = " & $prioruser & ", @Username =" & @Username)
$ret = MsgBox(1, "Error", "Looks like " & $prioruser & " should stop their trprecord on this computer first. Try again afterwards, I will exit now.")
Exit
Else ; if priorinstance belongs to @username, just kill it
If WinExists($windowtitletrpRecord, "") Then WinKill($windowtitletrpRecord) ; always returns1
; todo: killing the prior script instance is not enough - the instance may have spawned a wmencoder from the cmdline (since we do not use encoder objects here which would presumably be killed with the creating script)
; we need to kill any (todo: w/o warning to user?) wmencoder.exe process
; determine: which privileges are needed to execute taskkill? tested: a non-admin can use taskkill to kill their own tasks
; test: an admin or system account (teacher-> synchroneyes) should also be able to taskkill other admin and student task
$ret = RunWait('taskkill /F /fi "imagename eq wmencoder*" /im *') ; return 0 seems good, sometimes?
; determine: do we need to kill other spawned processes like wmeditor (indexing)? or rather give them time to finish, and the calling script to copy the resulting audiofile
EndIf
Else ; no wmencoder process, nothing to kill
EndIf
EndFunc ;==>killpriorInstance
Func guioptions()
Opt("GUICoordMode", 1) ; 1 = absolute coordinates (default) still relative to the dialog box. 0 = relative position to the start of the last control (upper left corner).
; GUICreate ( "title" [, width [, height [, left [, top [, style [, exStyle [, parent]]]]]]] )
GUICreate($windowtitletrpRecord, 800, 180)
GUISetHelp(@ProgramFilesDir & "\Internet Explorer\iexplore.exe http://plagwitz1.spaces.live.com"); todo: add the page to the actual blog entry
Dim $msg ; which the user sends through the gui - essentially that he is done updating the controls
; Create the controls
; SET controls by using variables already read into from ini
; groups represent the sections of the ini
; A group control is the thin line you see around controls (usually only Radio button) that visually groups them together.
; If you want to have multiple groups without the visible line then you must use GUIStartGroup() to group your Radio buttons.
; horizontal
; GUICtrlCreateGroup ( "text", left, top [, width [, height [, style [, exStyle]]]] )
Dim $actiontyperecordingvideolivestatus = 0
Dim $actiontyperecordingaudioliveStatus = 1
Dim $actiontyperecordingaudiofileStatus = 0
Dim $actiontyperecordingvideofileStatus = 0
Dim $group_action = GUICtrlCreateGroup("Action", 10, 5, 780, 160); creategroup seems just a canvas in the gui
GUIStartGroup()
Dim $actiontyperecordingaudioliveCtrl = GUICtrlCreateRadio("Audio Live", 20, 20, 80, 15)
Dim $actiontyperecordingvideoliveCtrl = GUICtrlCreateRadio("Video Live", 20, 50, 80, 15)
Dim $actiontyperecordingaudiofileCtrl = GUICtrlCreateRadio("Audio with File,Path:", 20, 80, 110, 15)
Dim $actiontyperecordingvideofileCtrl = GUICtrlCreateRadio("Audio w/ Video File,Path:", 20, 110, 140, 15)
GUICtrlSetState($actiontyperecordingvideoliveCtrl, $actiontyperecordingvideolivestatus)
GUICtrlSetState($actiontyperecordingaudioliveCtrl, $actiontyperecordingaudioliveStatus)
GUICtrlSetState($actiontyperecordingaudiofileCtrl, $actiontyperecordingaudiofileStatus)
GUICtrlSetState($actiontyperecordingvideofileCtrl, $actiontyperecordingvideofileStatus)
; todo: enable this if $actiontyperecordingaudiofile chosen
Dim $inputAudiofilepath = $driveletterkreplacementforstudentwrite & "\" & $dirsharestudentreadconfig
; dim $audiofilelabelctrl = GUICtrlcreateLabel( "File with Audio" , 300, 180, 280,15)
Dim $inputAudiofilepathCtrl = GUICtrlCreateInput($inputAudiofilepath, 160, 80, 580, 15)
Dim $inputvideofilepath = $driveletterkreplacementforstudentwrite & "\" & $dirsharestudentreadconfig
; dim $audiovideolabelctrl = GUICtrlcreateLabel( "Video File with Audio" , 300, 210, 280,15)
Dim $inputvideofilepathCtrl = GUICtrlCreateInput($inputAudiofilepath, 160, 110, 580, 15)
Dim $button_start = GUICtrlCreateButton("&Start", 130, 20, 50, 50)
Dim $button_stop = GUICtrlCreateButton("Sto&p", 190, 20, 50, 50)
Dim $button_play = GUICtrlCreateButton("Play", 250, 20, 50, 50) ; todo: fails: Dim $button_play = GUICtrlCreateButton("Play" & @CRLF & " &Last", 250, 20, 50, 50, $BS_MULTILINE)
Dim $button_open = GUICtrlCreateButton("&Open" & @CRLF & "Folder", 310, 20, 50, 50, $BS_MULTILINE) ; todo:fails
; Dim $statuslabelcontrol GUICtrlcreateLabel( "For trpRecording starting, Choose Action and press Start" , 300, 180, 280,15)
;global
$guiMsg = GUICtrlCreateLabel("Look here for important messages. Press F1 for help.", 360, 20, 450, 50) ; use with GUICtrlSetData ($guimsg, "my message")
GUICtrlSetColor($guiMsg, 0xff0000) ; Red
GUICtrlSetState($button_start, $GUI_FOCUS + $GUI_DEFBUTTON)
GUICtrlSetState($button_stop, $GUI_DISABLE)
GUICtrlSetState($button_play, $GUI_DISABLE)
GUICtrlSetState($actiontyperecordingaudiofileCtrl, $GUI_DISABLE)
GUICtrlSetState($actiontyperecordingvideofileCtrl, $GUI_DISABLE)
GUICtrlSetState($inputAudiofilepathCtrl, $GUI_DISABLE)
GUICtrlSetState($inputvideofilepathCtrl, $GUI_DISABLE)
GUISetState()
While 1
$msg = GUIGetMsg()
Select
Case $msg = $GUI_EVENT_CLOSE
; todo: here also?
; donot: destroy the encoder object - only destroy the object when gui/window/app is closed
$g_objEncoder = 0
trpCleanup(); could do ExitLoop, but probably better to exit
; donot: cannot happen, $actiontyperecordingaudiofileCtrl disabled, action not implemented
Case $msg = $actiontyperecordingaudiofileCtrl ; todo: how to check for radio button $actiontyperecordingaudiofile selected?
$set = GUICtrlSetData($guiMsg, "")
myDebugOut(@ScriptLineNumber, "$set=" & $set)
; todo: enable $inputAudiofilepathCtrl
GUICtrlSetState($inputAudiofilepathCtrl, $GUI_ENABLE)
; todo: disable $inputAudiofilepathCtrl
GUICtrlSetState($inputvideofilepathCtrl, $GUI_DISABLE)
$actiontyperecordingvideofileStatus = 0
$actiontyperecordingvideolivestatus = 0
$actiontyperecordingaudioliveStatus = 0
$actiontyperecordingaudiofileStatus = 1
; donot: cannot happen, $actiontyperecordingvideofileCtrl disabled, action not implemented
Case $msg = $actiontyperecordingvideofileCtrl ; todo: how to check for radio button $actiontyperecordingaudiofile selected?
$set = GUICtrlSetData($guiMsg, "")
myDebugOut(@ScriptLineNumber, "$set=" & $set)
GUICtrlSetState($inputvideofilepathCtrl, $GUI_ENABLE)
; todo: disable $inputAudiofilepathCtrl
GUICtrlSetState($inputAudiofilepathCtrl, $GUI_DISABLE)
$actiontyperecordingvideofileStatus = 1
$actiontyperecordingvideolivestatus = 0
$actiontyperecordingaudioliveStatus = 0
$actiontyperecordingaudiofileStatus = 0
Case $msg = $actiontyperecordingvideoliveCtrl ; todo: how to check for radio button $actiontyperecordingvideofile selected?
$set = GUICtrlSetData($guiMsg, "")
myDebugOut(@ScriptLineNumber, "clerared gui:" & $set)
; todo: disable $inputvideofilepathCtrl
GUICtrlSetState($inputvideofilepathCtrl, $GUI_DISABLE)
; todo: disable $inputAudiofilepathCtrl
GUICtrlSetState($inputAudiofilepathCtrl, $GUI_DISABLE)
$actiontyperecordingvideofileStatus = 0
$actiontyperecordingvideolivestatus = 1
$actiontyperecordingaudioliveStatus = 0
$actiontyperecordingaudiofileStatus = 0
Case $msg = $actiontyperecordingaudioliveCtrl ; todo: how to check for radio button $actiontyperecordingaudiofile selected?
$set = GUICtrlSetData($guiMsg, "")
myDebugOut(@ScriptLineNumber, "$set=" & $set)
; todo: disable $inputAudiofilepathCtrl
; was sollte das? ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : todo: = ' & todo: & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console
GUICtrlSetState($inputAudiofilepathCtrl, $GUI_DISABLE)
; todo: disable $inputAudiofilepathCtrl
GUICtrlSetState($inputAudiofilepathCtrl, $GUI_DISABLE)
$actiontyperecordingvideolivestatus = 0
$actiontyperecordingaudioliveStatus = 1
$actiontyperecordingaudiofileStatus = 0
Case $msg = $button_start
GUICtrlSetState($button_start, $GUI_DISABLE)
GUICtrlSetState($button_stop, $GUI_ENABLE)
$set = GUICtrlSetData($guiMsg, "Please wait...")
myDebugOut(@ScriptLineNumber, "asking to wait=" & $set)
If $actiontyperecordingaudiofileStatus Then ; dolater: not implemented
If Not FileExists($inputAudiofilepath) Then
MsgBox(0, "Error", "Enter a valid file name!")
Else
MsgBox(0, "Error", "Not implemented yet!")
; todo: set param, like this recordingstart($actiontyperecording = "audiofile", $inputAudiofilepath)
EndIf
ElseIf $actiontyperecordingvideolivestatus Then
; todo: set param, like this recordingstart($actiontyperecording = "videolive")
; $ret = recordingstart("videolive")
; old approac $ret = recordingstart("videolive")
; new approach with encoder-object
; done: capture datetime for file-postprocessing
$nowstring = StringRegExpReplace(_NowCalc(), "[\s\/\:]", "_")
; done: does now get written, since no illegal chars get passed anymore paassed filenameescape myDebugOut(@ScriptLineNumber, $nowstring & ": trying to encode with $strWMEFileVideo=" & $strWMEFileVideo)
myDebugOut(@ScriptLineNumber, "calling recordstart to encode with strWMEFileVideo=" & $strWMEFileVideo)
$ret = recordstart($objEncoder, $strWMEFileVideo)
ElseIf $actiontyperecordingaudioliveStatus Then
; $ret = recordingstart("audiolive")
; old approach $ret = recordingstart("audiolive")
;new approach encoder-object
; done: capture datetime for file-postprocessing
$nowstring = StringRegExpReplace(_NowCalc(), "[\s\/\:]", "_")
myDebugOut(@ScriptLineNumber, "calling recordstart to encode with $strWMEFileAudio=" & $strWMEFileAudio)
$ret = recordstart($objEncoder, $strWMEFileAudio)
ElseIf $actiontyperecordingvideofileStatus Then ; dolater: not implemented
If Not FileExists($inputvideofilepath) Then
MsgBox(0, "Error", "Enter a valid file name!")
Else
; todo: set param, like this recordingstart($actiontyperecording = "videofile")
MsgBox(0, "Error", "Not implemented yet!")
EndIf
EndIf
Case $msg = $button_stop
GUICtrlSetState($button_stop, $GUI_DISABLE)
$set = GUICtrlSetData($guiMsg, "Please wait...") ; GUICtrlSetData($guiMsg, "Now recording to localfilename:" & $tempfilename)
myDebugOut(@ScriptLineNumber, "$set=" & $set)
; not together, make a separate play button; $ret = recordingstopandplay()
; donot: (rather use encoder-object) differentiate whether indedexing or postprocessing required and whether to index audio or video
; new approach using encoder-object
$ret = recordstop($objEncoder)
; todo: backup file to network, using impersonation
Sleep(3000) ; half-done: the backuprecording is not complete when you do not give the enco der time to finish its saving after stop
; todo: is sleep(2000) enough time for large video files? maybe, filecopy comes later, i do not thinkg the encoder flushes all from memory when stopped
$ret = backupRecording()
GUICtrlSetState($button_start, $GUI_ENABLE) ; enable record again, the object-encoder has been stopped
; done: enable "play last"button
GUICtrlSetState($button_play, $GUI_ENABLE)
If Not ($ret == 0) Then
$set = GUICtrlSetData($guiMsg, "Finished. Play your recording now or open folder to work with it.")
myDebugOut(@ScriptLineNumber, "$set=" & $set)
Else
$set = GUICtrlSetData($guiMsg, "There was a problem saving your file to the network. You should save the local copy manually from " & @TempDir); todo
myDebugOut(@ScriptLineNumber, "$set=" & $set)
EndIf
Case $msg = $button_play ; todo: this does not seem to do anything, not even guictrlsetdata
$set = GUICtrlSetData($guiMsg, "Please wait...")
myDebugOut(@ScriptLineNumber, "$set=" & $set)
; GUICtrlSetState($button_play, $GUI_DISABLE); donot: you do not know when the player has been close
; not together, make a separate play button; $ret = recordingstopandplay()
; donot: (rather use encoder-object) differentiate whether indexing or postprocessing required and whether to index audio or video
; new approach using encoder-object
; todo: change to proper variable names $networkpath & $currentfilename
If FileExists($tempfilename) Then
; todo: i want to open thefile not with the default player which might be vlc, but with wmplayer
myDebugOut(@ScriptLineNumber, "$tempfilename exists") ; this is reached
$set = GUICtrlSetData($guiMsg, "Playing last recording from local temp recording.")
myDebugOut(@ScriptLineNumber, "$set=" & $set)
$ret = ShellExecute($tempfilename, "", @TempDir, "open") ; i do not think guimsg will be meaningful
ElseIf FileExists($networkpathfile) Then
; fails ElseIf FileExists($driveletterkreplacementforstudentwrite & "\" & $dirsharewriteinstructor & "\" & $networknamefile) Then
;fails $ret = Run($driveletterkreplacementforstudentwrite & "\" & $dirsharewriteinstructor & "\" & $networknamefile, @TempDir, @SW_HIDE, 8) ; i do not think guimsg will be meaningful
myDebugOut(@ScriptLineNumber, "$networkpathfile exists")
$ret = ShellExecute($networkpathfile, "", @TempDir, "open") ;
$set = GUICtrlSetData($guiMsg, "ATTENTION: no local copy") ; todo: & @CRLF & ", instead playing saved file from network.")
myDebugOut(@ScriptLineNumber, "$set=" & $set)
Else
$set = GUICtrlSetData($guiMsg, "ERROR: last recording not found: " & @CRLF & $networknamefile & " or " & $tempfilename)
myDebugOut(@ScriptLineNumber, "$set=" & $set)
EndIf
Case $msg = $button_open
$set = GUICtrlSetData($guiMsg, "Please wait...")
myDebugOut(@ScriptLineNumber, "$set=" & $set)
; GUICtrlSetState($button_stop, $GUI_DISABLE) ; donot: cannot check whether explore window has been closed, need not, if run open folder reissued, old on will be activatd
; not together, make a separate play button; $ret = recordingstopandplay()
; donot: (rathe use encoder-object) differentiate whether indedexing or postprocessing required and whether to index audio or video
; new approach using encoder-object
; todo: what is the proper variable names
$ret = ShellExecute($driveletterkreplacementforstudentwrite & "\" & $dirsharewriteinstructor, "", @TempDir, "open") ; i do not think guimsg will be meaningful
; todo: enable "play last"button
GUICtrlSetState($button_start, $GUI_ENABLE)
GUICtrlSetState($button_play, $GUI_ENABLE)
$set = GUICtrlSetData($guiMsg, "Done...")
myDebugOut(@ScriptLineNumber, "$set=" & $set)
Case Else
; ExitLoop rubbish, this will lead to an exit
EndSelect
WEnd
; todo: here?
; donot: destroy the encoder object - only destroy the object when gui/window/app is closed
$g_objEncoder = 0
GUIDelete()
EndFunc ;==>guioptions
Func recordingstart($actiontyperecording = "audiolive", $inputAudiofilepath = "")
Dim $arrinputAudiofilepath
If $actiontyperecording = "audiolive" Then
$wmefilename = $wmefilenamerecordaudiolive
ElseIf $actiontyperecording = "videolive" Then
$wmefilename = $wmefilenamerecordvideolive ; todo: this uses the default video source for live input - what ever shows there through the crestron, interpreting student or interpreter
ElseIf $actiontyperecording = "audiofile" Then
; dolater: file - you cannot omit or override ion the cmdline filesnames in a wme
; - so either work with the cmdline input/output and not with wme
; or (if you need the wme for params that the cmdline cannot have, like timestretch), work with renaming files before inputting/after outputting to wme
$wmefilename = $wmefilenamerecordaudiofile ;recordaudiofile.wme ; dolater: me needs to point to the defaultfile source.ext
; dolater: for file, the duration needs to be read from the file and the $wmefilename duration overwritten on the cmdline - is this possible?
; wmcmd.vbs says [-duration] <seconds> Amount of time in seconds to encode. Use when sourcing from devices.- ok with files?
; dolater: rip the audio (also get the duration - needs to be done first) with wemenc:!!
; dolater: how will you also play the file to the student? run(), opening with the default player
; dolater: is it not a restriction that wme can not do multi-channel
; donot: copy the file to a local temp location to avoid concurrency issues and with a generic name equal the the name p filesource hardcoded in $wmefilenamerecord = "recordaudiofile.wme"
$ret = FileCopy2LocalTemp($arrinputAudiofilepath)
If $ret = 0 Or @error Then
; dolater: making local copy failed
EndIf
; we do not need to know whether audio or video, wmencoder can use both as audio source $inputAudiofilepathnameext = ; get file extension
;$ret = FileCopy($inputAudiofilepath, @tempdir & "\"& $inputAudiofilepathname ) ; would need to check whether copy is same as original
; dolater: audiofile (&videofile, audio only):
; wme out of the box does not allow combine audio channels (of pre-existing file and live recording)
; so you need to open the file (with the defaultapp)
; wmenc:captureaudiomonowav.wme: record the live student input, ; save the recording as MONO +WAV
; -> left.wav
; wmenc:rippaudiomonowav.wme rips from the pre-existing file the audio as MONO +WAV (you could use a video directly, but only if it is an WMV->AVI with mono audio)
; -> right.wav
; WavAviMux.exe can combine 2? mono (->monotize) wav(-> unpack! up to 2gb) channels (including from avi, w/o video, w/o 2gb limit)
; -> stereowav.avi: To wrap a stereo file with no video- >: wavavimux -o stereowav.avi -iwav 2 left.wav right.wav -mask 3
; wmeenc:stereowav2wma.wme can compress avi:audio -> mp3 or wma: stereowav.avi -> [regular filename].wma
ElseIf $actiontyperecording = "videofile" Then
$wmefilename = $wmefilenamerecordaudiofile ;recordvideofile.wme ; dolater: wme needs to point to the defaultfile source.ext
; dolater: for file, the duration needs to be read from the file and the $wmefilename duration overwritten on the cmdline - is this possible?
; wmcmd.vbs says [-duration] <seconds> Amount of time in seconds to encode. Use when sourcing from devices.- ok with files?
; optional: videofile, with video - $actiontyperecordingavideofileStatus
; videofile optional can also do:
; wmenc:rippvideoavi.wme rips from the pre-existing file the audio as MONO +WAV (you could use a video directly, but only if it is an WMV->AVI with mono audio)
; WavAviMux.exe can combine 2? mono (->monotize) wav(-> unpack! up to 2gb) channels (including from avi, w/o video, w/o 2gb limit)
; To wrap a stereo file with video- >: wavavimux -iavi [input AVI file name] -o videostereowav.avi -iwav 2 left.wav right.wav -mask 3
; the following indexing postprocessing actions are not needed, if we use encoderobject.stop instead of taskkill
; the following actions are internal (to recordingstopandplay) and not visible in the GUI
ElseIf $actiontyperecording = "audioindex" Then ; there is not control for indexing in the gui, it is chose indirectly through button stop
$wmefilename = $wmefilenamerecordaudioindexing
ElseIf $actiontyperecording = "videoindex" Then ; there is not control for indexing in the gui, it is chose indirectly through button stop
$wmefilename = $wmefilenamerecordaudioindexing
EndIf
; IDENTIFY THE WMENC PROCESS
;this should check for a wmencoder process initated by admin - a remote controlled version of this script - and exit if there is one
; this should also check for another wemencoder process by current user - maybe user clicked twice, return message to "first stop prior recording session", then
If ProcessExists($wmencexe) Then
; who is the owner of the prior wmenc.exe? "user name"of "image name"
$strUser = _ProcessGetOwner($wmencexe); todo:requires wmi , does wmi require admin rights?
If @error Then ; we cannot determine the user to the process -> play it safe
$ret = MsgBox(0, "Error", "First stop current recording session, or talk to admin.")
trpCleanup()
EndIf ; error
If $strUser = @UserName Then ; the process belongs to the current user -> guide her
$ret = MsgBox(0, "Error", "First stop your current recording session, by running " & $recordingstopstring)
; browse to web-interface for $recordingstopstring
ShellExecute($webinterfacestring)
trpCleanup()
Else ; todo: is this automatically the admin? what about logging in as different user?
$ret = MsgBox(0, "Error", "Smile, you are already being recorded.")
Exit ; nor sute i can trpCleanup() here
EndIf
Else ; no other wmenc process -> proceed normally
EndIf ; processexists
; donot: provide an indicator that recording is running
; $ret =
; done: have 2 buttons start/stop and start gets greyed if pressed or, - indicator style only, no gui - , with stop, if admin started from the cmdline
; the trprecord.exe needs to remains running after start on client - whethe started from client or server
; another instance checks not only for wmeenc but also for filename.exe and stops a prior filename.exe also
; should this be autoit
; - then $recordingstopstring needs not only stop wmenc.exe, but also record.exe
; then indicator should contain a link to stop recording, which ends record.exe after launching $recordingstopstring
; or a visible wmcmd.vbs window is enough?
; that however could be stopped by user (could it if launched remotely by admin? but then not visible? psexec not, synchroneyes yes? ) manually also, not tragic while we have no remote control, but once we do, bad
EndFunc ;==>recordingstart
Func recordingstopandplay()
; todo: destroy gui
; todo: what is a good way to pass the filename to the $recordingstopstring
; mostrecent wma in tempdir?
; there might be more than one unindexed wma (if computer logged off? does this not clean the tempdir?)
; overwrite path to create time-specific (that does not overwrite) local file
; done: do not create file on network (can be down) or local (would probably be lost on restart - copy to network at end)
; find local file
; run
; donot: check homedir
; donot:copy local file to homedir
addInstructorNetworkshares()
; identify a prior GUI and stop it (not myself)
If ProcessExists($scriptfilewithexe) Then ; it is Not me
; todo
EndIf
; IDENTIFY THE WMENC PROCESS
;this should check for a wmencoder process initiated by admin/instructor-user/synchroneyes-user - a remote controlled version of this script - and exit if there is one
; this should also check for another wmencoder process by current user - maybe user clicked twice, return message to "first stop prior recording session", then
If ProcessExists($wmencexe) Then
; who is the owner of the prior wmenc.exe? "user name"of "image name"
$strUser = _ProcessGetOwner($wmencexe); todo:requires wmi , does wmi require admin rights?
If @error Then
$ret = MsgBox(0, "Error", "First stop current recording session, or talk to admin.")
Exit ; nor sure i can trpCleanup() here
EndIf ; error
If $strUser = @UserName Then
$ret = MsgBox(0, "Error", "First stop your current recording session, by running " & $recordingstopstring)
; browse to web-interface for $recordingstopstring
ShellExecute($webinterfacestring)
Exit
Else ; todo: is this automatically the admin? what about logging in as different user?
$ret = MsgBox(0, "Error", "Smile, you are being recorded (let's hope it's your admin).")
Exit
EndIf
Else
EndIf ; processexists
; IDENTIFY THE AUDIO FILE WE NEED TO WORK ON
$myfiles = _FileListToArray(@TempDir, "rec_*.wma", 1) ; _FileListToArray($sPath[, $sFilter = "*"[, $iFlag = 0]])
; @Error: 1 = Path not found or invalid
; $iFlag=1 Return files only
; sort the array, excluding the element=0=elemenetcounter, ascending
; todo: how does this arraysort identify the audiofil to work on? only if the audiofilename contains a datetimecreationstring, but then the sort should be descending, to put the largest=latest audiofile first
; todo: better upon fiel creration store the current (datetime) filename in a variable an refer to this variable throughout
_ArraySort($myfiles, 1, 1) ; ArraySort(ByRef $avArray[, $iDescending = 0[, $iStart = 0[, $iEnd = 0[, $iSubItem = 0]]]])
; array elements contain just the filenames, plus the counter, which will always be first element-counter (besides, we exclude the element-counter from sort) after sort (since filenames start with rec_)
; INDEXING (the audio file will (todo: really?) always be unindexed due to taskkill of wmenc to stop)
; donot: open the audio file in wmeditor to index, alt+_file /_save and index, alt+_file /e_xit and close : awkard, and can you issue keystrokes with a hidden window
; wmeditor can not be called form the cmdline, you cannot even run wmeditor with a file to open, so also open has to be done with keystrokes
; rather todo: wmenc can and has an AutoIndex Specifies and retrieves a value indicating whether the archive file will be indexed after the archiving process is completed.
; but it is overkill to reencode and save as the file just to index it
; WMEncoder Encoder;
; Encoder = new WMEncoder();
; attempt
; oWMEncoder = objcreate("WMEncEng.WMEncoder")
; If @error then
; $ret = msgbox("failed to created object")
; else
; Encoder.Load ("C:\filename.wme") ; basics, same as record.wme, or even entire, but then change source to files and output to new filename, on
; set inputfile
; set outputfile
; bool bAutoIndex;
; // Configure the encoding session to archive encoded content.
; // For a complete example, see either the IWMEncFile object or the IWMEncFileArchiveStats object.
; // Specify the AutoIndex property.
; Encoder.AutoIndex = true;
; Encoder.Start
; endif ; @error objecreate?
; ODER SO, laut chm
; using WMEncoderLib;
; Create the WMEncBasicEdit object.
;todo: use this: WMEncBasicEdit BasicEdit = new WMEncBasicEdit();
; Specify the input and output files.
;BasicEdit.MediaFile = "C:\\InputFile.wmv"; ; %2
;BasicEdit.OutputFile = "C:\\OutputFile.wmv"; ; %3
; Specify a configuration file.
;BasicEdit.ConfigFile = "C:\\ConfigFile.txt"; ; %1
; Add indexing to the file.
;BasicEdit.Index = True;don't you need to start the encoding bei claling one of tis method after setting its properties?
; oder doch wieder run-wmcmd.vbs
; [-wme] <Windows Media Encoder session file>
; [-profile] <profile code>
; [-loadprofile] <profile file name> // what is the difference between -profile and -loadprofile? a64: Profile_AudioOnly_CDQuality_64K (64 Kbps)
; [-input]
; [-output] <file or directory name>
; cscript.exe c:\windows\wmcmd.vbs -input g:\videoinput.avs -output g:\videooutput.wmv -v_codec WVC1 -videoonly -v_mode 0 -v_preset better -v_bitrate 8000000 -v_keydist 3 -v_buffer 600000 -v_quality 100 -s_config own.weu
; Run ( "filename" [, "workingdir" [, flag[, standard_i/o_flag]]] )
$ret = Run("cscript.exe " & $wmcmdvbspath & " - wme " & $driveletterkreplacementforstudentwrite & "\" & $dirsharestudentreadconfig & "\" & $wmefilename & " -input " & @TempDir & "\" & $myfiles[1] & "-output " & @TempDir & "\" & "i" & $myfiles[1], @TempDir, @SW_HIDE, 8)
If $ret = 0 Then
myDebugOut(@ScriptLineNumber, "failed to run indexing vbs: " & $wmcmdvbspath & " - wme " & $driveletterkreplacementforstudentwrite & "\" & $dirsharestudentreadconfig & "\" & $wmefilename & " -input " & @TempDir & "\" & $myfiles[1] & "-output " & @TempDir & "\" & "i" & $myfiles[1])
Else ; no error : replace unindexed original audio file
$ret = FileCopy(@TempDir & "\" & "i" & $myfiles[1], @TempDir & "\" & $myfiles[1])
EndIf ; @error indexing?
; todo:open output from local in wmplayer - either the indexed, or, if error, the unindexed version
$ret = Run(@TempDir & "\" & $myfiles[1], @TempDir, @SW_HIDE) ; do this first, this will take some time and focus the attention of the user away from the script finishing
If $ret = 0 Then myDebugOut(@ScriptLineNumber, "failed to open indexed audio file:" & @TempDir & "\" & "i" & $myfiles[1])
; make student permanent copy of audio file
$ret = FileCopy(@TempDir & "\" & $myfiles[1], $uncpathwritestudent & "\" & $myfiles[1], 9) ; FileCopy ( "source", "dest" [, flag] )
If $ret = 0 Then myDebugOut(@ScriptLineNumber, "failed to filecopy:" & $uncpathwritestudent & "\" & $myfiles[1])
; make instructor permanent copy of audio file
$ret = FileCopy(@TempDir & "\" & $myfiles[1], $driveletterkreplacementforstudentwrite & "\" & $dirsharewriteinstructor & "\" & $myfiles[1], 9) ; FileCopy ( "source", "dest" [, flag] )
If $ret = 0 Then myDebugOut(@ScriptLineNumber, "failed to filecopy:" & $driveletterkreplacementforstudentwrite & "\" & $dirsharewriteinstructor & "\" & $myfiles[1])
; cleanup before next iteration
$ret = DriveMapDel($driveletterkreplacementforstudentwrite)
If $ret = 0 Then myDebugOut(@ScriptLineNumber, "failed to DriveMapDel for: " & $driveletterkreplacementforstudentwrite & ", @error=" & @error)
Exit
EndFunc ;==>recordingstopandplay
Func streamVideo()
Dim $WMENC_VIDEO = 2
Dim $WMENC_AUDIO = 1
Dim $WMENC_PROTOCOL_HTTP = 1
$object = ObjCreate("WMEncEng.WMEncoder")
If @error Then
MsgBox(0, "Error", "Couldn't create object! Exiting...")
Else
$SrcGrpColl = $object.SourceGroupCollection
$SrcGrp = $SrcGrpColl.Add("SG_1")
$SrcVid = $SrcGrp.AddSource($WMENC_VIDEO)
$SrcAud = $SrcGrp.AddSource($WMENC_AUDIO)
$SrcVid.SetInput("DEVICE://Default_Video_Device")
$SrcAud.SetInput("DEVICE://Default_Audio_Device")
$ProColl = $object.ProfileCollection
$lLength = $ProColl.Count
For $i = 0 To $lLength - 1
$Pro = $ProColl.Item($i)
If $Pro.Name = "Windows Media Video 8 for Local Area Network (384 Kbps)" Then
$SrcGrp.Profile = $Pro
ExitLoop
EndIf
Next
$Broadcast = $object.Broadcast
$Broadcast.PortNumber($WMENC_PROTOCOL_HTTP) = 8010
$object.Start
While 1
Sleep(100)
WEnd
$object.Stop
EndIf
EndFunc ;==>streamVideo
Func streamAudio()
; Overview: This script opens a default.wme file and edits it to suit
; our needs. Then it runs the encoder application in hidden mode
; and begins both broadcasting a live stream
; and saving a copy to a network drive for archival purposes.
; When the user stops the broadcast
; the application is closed and the archived file is FTP'd to another server
GUICreate("Audio Encoder", 410, 200)
$Label_1 = GUICtrlCreateLabel("Type in a title for the broadcast and then click the button below", 30, 20, 300, 21, 0x00000020)
$Label_2 = GUICtrlCreateLabel("", 30, 130, 300, 21, 0x00000020)
$input_1 = GUICtrlCreateInput("", 30, 40, 350, 21)
$button2 = GUICtrlCreateButton("Stop Broadcasting", 150, 80, 100, 30)
GUICtrlSetState($button2, $GUI_HIDE)
$button1 = GUICtrlCreateButton("Begin Broadcasting", 150, 80, 100, 30)
$progbar = GUICtrlCreateProgress(30, 150, 350, 30, $PBS_SMOOTH)
GUISetState()
While 1
$msg = GUIGetMsg()
Select
Case $msg = $button1 ; begin broadcasting
$data = GUICtrlRead($input_1) ; title for broadcast
GUICtrlSetData($Label_1, $data)
GUICtrlDelete($input_1)
GUICtrlDelete($button1)
GUICtrlSetState($button2, $GUI_SHOW)
Initiate($data)
Case $msg = $button2
GUICtrlSetData($Label_2, "Stopping Encoder")
trpCleanup()
Case $msg = $GUI_EVENT_CLOSE
GUICtrlSetData($Label_2, "Stopping Encoder")
trpCleanup()
EndSelect
WEnd
EndFunc ;==>streamAudio
Func Initiate($var)
elog("=============================")
GUICtrlSetData($Label_2, "Configuring System")
GUICtrlSetData($progbar, 0)
$newtitle = $var
elog(day() & time() & " User input Title: " & Chr(34) & $newtitle & Chr(34))
; open files for reading / re-writing
; todo: this opens wme-gui and automates it, not what i want
$default = FileOpen("C:\encode\script\default.wme", 0) ; todo: remove strings
$start = FileOpen("C:\encode\script\start.wme", 2); todo: remove strings
If $default = -1 Then
MsgBox(0, "Error", "Unable to open 'default.wme' file.")
trpCleanup()
EndIf
If $start = -1 Then
MsgBox(0, "Error", "Unable to open 'start.wme' file.")
myCleanup()
EndIf
; map network drive for temp storing the archive file
; If you specify "*" an unused drive letter will be automatically selected.
$drv = DriveMapAdd("*", "\\Server\ShareName$") ; todo: remove string
elog(day() & time() & " Attempting to map Network Drive - " & $drv)
Sleep(2000)
If $drv = "" Then ; todo: seems wrong check, DriveMapAdd Returns 0 if a new mapping could not be created and sets @error (see below).
$drv = "C:\encode\archive"
Select
Case @error = 1
$comment = "1. Undefined / Other error"
Case @error = 2
$comment = "2. Access to the remote share was denied"
Case @error = 3
$comment = "3. The device is already assigned"
Case @error = 4
$comment = "4. Invalid device name"
Case @error = 5
$comment = "5. Invalid remote share"
Case @error = 6
$comment = "6. Invalid password"
EndSelect
elog(day() & time() & " Could not map network drive. Error was: " & $comment)
EndIf
GUICtrlSetData($Label_2, "Creating system parameters")
GUICtrlSetData($progbar, 25)
; format necessary info
$oldtitle = "CHANGE_TITLE"
$oldpath = "C:\CHANGE_PATH.wma"
$name = StringLeft($newtitle, 10) ; $newtitle is what the user entered
$name = StringReplace($name, " ", "")
$name = StringReplace($name, "/", "")
$name = StringReplace($name, ";", "")
$name = StringReplace($name, ",", "")
$name = StringReplace($name, "'", "")
$name = StringReplace($name, ":", "")
$name = StringReplace($name, "?", "")
$name = StringReplace($name, "~", "")
$name = StringReplace($name, "@", "")
$name = StringReplace($name, "!", "")
$name = StringReplace($name, "$", "")
$name = StringReplace($name, "*", "")
$name = StringReplace($name, "<", "")
$name = StringReplace($name, ">", "")
$name = day() & $name & ".wma"
$fullpath = $drv & "\" & $name
elog(day() & time() & " Archive file = " & $fullpath)
; create a custom start.wme file
While 1
$line = FileReadLine($default)
If @error = -1 Then ExitLoop; -1 means the end of the file
If StringInStr($line, $oldtitle) Then
$line = StringReplace($line, $oldtitle, $newtitle)
ElseIf StringInStr($line, $oldpath) Then
$line = StringReplace($line, $oldpath, $fullpath)
EndIf
FileWriteLine($start, $line)
WEnd
FileClose($default)
FileClose($start)
GUICtrlSetData($Label_2, "Starting Encoder Program")
GUICtrlSetData($progbar, 50)
; run encoding program in hidden mode
Run("C:\Program Files\Windows Media Components\Encoder\wmenc.exe c:\encode\script\start.wme", "", @SW_HIDE)
WinActivate("Audio Encoder"); shift focus back to our app
While 1
Sleep(500)
If WinExists("start - Windows Media Encoder") Then
Sleep(2000); give it a couple seconds before proceeding
ExitLoop
EndIf
WEnd
GUICtrlSetData($Label_2, "Initiating Encoder")
GUICtrlSetData($progbar, 75)
; Send keys to begin broadcasting
ControlSend("start - Windows Media Encoder", "", "Titan:CToolBar1", "^E", 0)
elog(day() & time() & " Started Encoding")
; Do some error checking to ensure the archive file is being created
$count = 0
While 1
If FileExists($fullpath) Then
elog(day() & time() & " Archive file took " & $count & " sec to create")
ExitLoop
Else
Sleep(1000)
$count = $count + 1
If $count > 10 Then
elog(day() & time() & " File not found within 10 seconds, killing process")
WinKill("start - Windows Media Encoder")
DriveMapDel($drv)
$msg = MsgBox(0, "Error", "There has been an error, please " _
& "contact an administrator if this problem persists")
Exit
EndIf
EndIf
WEnd
GUICtrlSetData($progbar, 100)
GUICtrlSetData($Label_2, "Broadcasting Live")
EndFunc ;==>Initiate
Func myCleanup()
ControlSend("start - Windows Media Encoder", "", "Titan:CToolBar1", "^S", 0)
GUICtrlSetData($progbar, 0)
GUICtrlSetData($Label_2, "Ending Session")
; give the encoder enough time to stop (move progress bar while waiting)
$movebar = 0
While 1
GUICtrlSetData($progbar, $movebar)
Sleep(1000)
$movebar = $movebar + 10
If $movebar > 99 Then ExitLoop
WEnd
WinClose("start - Windows Media Encoder")
; wait for encoder program to close
GUICtrlSetData($Label_2, "Closing Encoder Application")
$movebar = 0
While 1
Sleep(500)
If WinExists("start - Windows Media Encoder") Then
GUICtrlSetData($progbar, $movebar)
$movebar = $movebar + 10
If $movebar > 99 Then $movebar = 0
Else
ExitLoop
EndIf
WEnd
elog(day() & time() & " Begin FTP")
; FTP file to media server
If FileExists($fullpath) Then
GUICtrlSetData($progbar, 60)
GUICtrlSetData($Label_2, "Sending Archive file to Media Server")
$server = '192.168.1.1'; IP of server
$username = 'UserName'
$pass = "PassWord"
$Open = _FTPOpen('MyFTP Control')
$Conn = _FTPConnect($Open, $server, $username, $pass)
$Ftpp = _FtpPutFile($Conn, $fullpath, $name)
$Ftpc = _FTPClose($Open)
elog(day() & time() & " Completed FTP of temp file")
FileDelete($fullpath)
elog(day() & time() & " Deleted temp file")
GUICtrlSetData($Label_2, "Transfer Completed... Closing Application")
GUICtrlSetData($progbar, 100)
DriveMapDel($drv)
Sleep(2000)
Exit
Else
MsgBox(0, "Done", "Archived File not found.")
elog(day() & time() & " Temp file did not exist")
DriveMapDel($drv)
Exit
EndIf
elog(day() & time() & " Closing Application Successfully")
EndFunc ;==>myCleanup
Func elog($txt)
$ix = FileOpen("C:\encode\script\errorlog.txt", 1)
FileWriteLine($ix, $txt)
If $ix = -1 Then
MsgBox(0, "Error", "Unable to open 'error.txt' file.")
Exit
EndIf
FileClose($ix)
EndFunc ;==>elog
Func day()
$iday = @YEAR & @MON & @MDAY
Return $iday
EndFunc ;==>day
Func time()
$iTime = @HOUR & @MIN & @SEC
Return $iTime
EndFunc ;==>time
; Function Name: _ProcessGetOwner()
; Description: Get the owner of an open process
; Parameter(s): $vProcess - PID of a process.
; $strComputer - Name of target computer
; Requirement(s): AutoIt Beta v3.2.+
; cimwin32.dll (included with Windows)
; Return Value(s): On Success - Returns owners username of process
; 0 - Successful, returns username
;
; On Failure: Returns friendly errormessage and sets @Error to:
; 2 - Access denied
; 3 - Insufficient privilege
; 8 - Unknown failure
; 21 - Path not found
; Author(s): Andreas Fälldin
;===============================================================================
Func _ProcessGetOwner($vProcess, $strComputer = "")
Local $i_PID = ProcessExists($vProcess)
If Not $i_PID Then
SetError(1)
Return -1
EndIf
Local $wbemFlagReturnImmediately = 0x10
Local $wbemFlagForwardOnly = 0x20
Local $colItems = ""
Local $strUser = ""
; The local computer name in the object path can be replaced with '.'
; The computer name can also be omitted from the object path, in which case the local computer is assumed.
If $strComputer = "" Then $strComputer = "localhost"
$objWMIService = ObjGet("winmgmts:{impersonationLevel=impersonate}!\\" & $strComputer & "\root\CIMV2")
$colItems = $objWMIService.ExecQuery("SELECT * FROM Win32_Process WHERE ProcessID = " & $i_PID, "WQL", _
$wbemFlagReturnImmediately + $wbemFlagForwardOnly)
If IsObj($colItems) Then
For $objItem In $colItems
$rc = $objItem.GetOwner($strUser)
Switch $rc
Case 0
SetError(0)
Return $strUser
Case 2
SetError(2)
Return "ACCESS DENIED"
Case 3
SetError(3)
Return "INSUFFICIENT PRIVILEGE"
Case 8
SetError(8)
Return "UNKNOWN FAILURE"
Case 9
SetError(9)
Return "PATH NOT FOUND"
Case 21
SetError(21)
Return "INVALID PARAMETER"
Case Else
SetError(1)
Return -1
EndSwitch
Next
EndIf
EndFunc ;==>_ProcessGetOwner
; automates adding contextinfo to the debug msg (screenshot, scriptlinenumber)
; parameters: $ScriptLineNumber needs to be set to @ScriptLineNumber
; better with $tempdir
; requires screencapture.au3, debug.au3 with _debugsetup
; todo: would be nicer if $ScriptLineNumber need not be set explicitly, but default value would work (test), but then the parameter sequence needs to be switched
; i think $ScriptLineNumber = @ScriptLineNumber does not work
; updated ver below not documented above
Func myDebugOut($ScriptLineNumber = @ScriptLineNumber, $msg = "Error") ; requires global setting of $debugtype = "debug"
Local $debugstring
Local $debugfilename
Local $tabhandle
Local $tabmaxnumber
Local $tempdir
; must be global local $pathfilelog
$tempdir = @TempDir
If $tempdir == "" Then $tempdir = "c:\temp"
$debugstring = $ScriptLineNumber & "~" & _Now() & "~" & $msg ; changed : to ~to have delimiters -> tab -> excel -> filter
If $debugtype = "debug" Then
Local $activewindowtitle = WinGetTitle("") ; todo: test: is using this necessary to give focus back to the window after oking msgbox?
MsgBox(0, "debug", $debugstring)
WinActivate($activewindowtitle)
ElseIf $debugtype = "debugconsole" Then
ConsoleWrite($debugstring & Chr(13))
ElseIf $debugtype = "run" Then
If $tempdir == "" Then $tempdir = "c:\temp"
$debugfilename = FileNameEscape($debugstring, $tempdir) & ".jpg" ; ".bmp" ; can this do jpg also, and jsut by changing the extension?
; works _DebugOut("$debugfilename:" & $debugfilename)
_ScreenCapture_Capture($debugfilename); [$sFileName = ""[, $iLeft = 0[, $iTop = 0[, $iRight = -1[, $iBottom = -1[, $fCursor = True]]]]]])
_DebugOut($debugstring)
ElseIf $debugtype = "debugconsolescreen" Then ; do both
ConsoleWrite($debugstring & Chr(13))
$debugfilename = FileNameEscape($debugstring, $tempdir) & ".jpg" ; ".bmp" ; can this do jpg also, and jsut by changing the extension?
; works _DebugOut("$debugfilename:" & $debugfilename)
; todo: filenameescape does not result in a legit filename, it seems: debug - problem is the \
; works better now that backslash is escaped ConsoleWrite("$debugfilename:" & $debugfilename & Chr(13))
_ScreenCapture_Capture($debugfilename); [$sFileName = ""[, $iLeft = 0[, $iTop = 0[, $iRight = -1[, $iBottom = -1[, $fCursor = True]]]]]])
ElseIf $debugtype = "releasetextlog" Then ; todo
; todo: consider "release" --> log
; file open append file close
If $pathfilelog == "" Then $pathfilelog = @TempDir & "\" & "autoitlogfile.log"
$loghandle = FileOpen($pathfilelog, 1); Success: Returns a file "handle" for use with subsequent file functions.
If $ret = 0 Or @error Then Exit ; Failure: Returns - 1 If error occurs.
$ret = FileWriteLine($loghandle, $debugstring & Chr(13))
If $ret = 0 Or @error Then Exit ; Failure: Returns - 1 If error occurs.
$ret = FileClose($loghandle)
ElseIf $debugtype = "releasewordpad" Then ; todo
Else ; do nothing or error
; can one paste behind the scenes screenshots into wordpad? eaiser for the betatester to send
SetError(1)
EndIf
EndFunc ;==>myDebugOut
Func FileNameEscape($filename, $tempdir) ; return a filename which has no special chars - todo: should be unescapable
; StringReplace ( "string", "searchstring" or start, "replacestring" [, count [, casesense]] )
; added for mydebugout()
$filename = StringReplace($filename, ":", "_"); cannot have : in filename
$filename = StringReplace($filename, ">", "larger_than"); cannot have > in filename
$filename = StringReplace($filename, "<", "smaller_than"); cannot have > in filename
; end added for mydebugout()
$filename = StringReplace($filename, " ", "_"); have no spaces in filenames ?
$filename = StringReplace($filename, "/", "&"); "Picture/Word_Association"
; todo: & is not save on the commandline - does + work better
$filename = StringReplace($filename, "/", "+"); "Picture/Word_Association"
$filename = StringReplace($filename, "&", "+"); occurs in some titles
$filename = StringReplace($filename, Chr(92), "_") ; tabs - todo: what about other space characters
$filename = StringReplace($filename, Chr(9), "_") ; tabs - todo: what about other space characters
; todo: fails again, without warning, file save dialogue remains open:
; Language=1[British_English]_Family=2[Intermediate]_Contenttype=1[Activity]_ActivityOrUnitOrCulture=1[Dialogue]_Exercise=10['Still_hungry?'_Intermediate_1elements]_error171
; replace questionmark, exclamation mark, quotationmark
$filename = StringReplace($filename, "?", "questionmark")
$filename = StringReplace($filename, "!", "exclamationmark")
$filename = StringReplace($filename, "'", "quotationmark") ; todo: should say single or sth else unique
$filename = StringReplace($filename, Chr(34), "doublequotationmark") ; 34 Quotation mark (" in HTML)
$filename = StringReplace($filename, "__", "_") ; todo: can stringreplace handle multicharacter?
$filename = StringReplace($filename, "__", "_") ;
$filename = StringReplace($filename, "__", "_") ;
$filename = StringReplace($filename, "__", "_") ;
$filename = $tempdir & "\" & $filename
; end filename routine
Return $filename
EndFunc ;==>FileNameEscape
Func FileCopy2LocalTemp($filepath) ; ex:
Dim $arrfilepath, $filepathname, $arrfilepathname, $filepathnameext
$blnValid = True
If Not FileExists($filepath) Then
If Not DriveMapAdd("", $filepath) Then ; todo: try also unc paths
$blnValid = False
EndIf
EndIf
If $blnValid Then
$arrfilepath = _StringSplit($filepath, "\")
If @error Then
SetError(1)
myDebugOut(@ScriptLineNumber, "error stringsplit filepath")
EndIf
$filepathname = $arrfilepath(UBound($arrfilepath) - 1); get !!filename
; ould be used to give the file a new basename, which would have to be passed as 2nd param $basename
;$arrfilepathname = _StringSplit($filepathname,".")
;if @error then
; myDebugOut(@ScriptLineNumber, "error stringsplit filepathname")
; seterror(1)
;endif
;$filepathnameext = $arrfilepathname(UBound($arrfilepathname) -1); get ext
$ret = FileCopy($filepath, @TempDir & "\" & $filepathname)
Else
SetError(1)
EndIf
EndFunc ;==>FileCopy2LocalTemp
Func taskkillsamples()
Local $ret3, $ret4, $filename, $windowtitlePDFCreate
myDebugOut(@ScriptLineNumber, "trying to use taskkill to bypass window:" & $windowtitlePDFCreate & " for: " & $filename)
$ret3 = RunWait('taskkill /F /fi "imagename eq acro*" /im *') ; return 0 seems good, sometimes?
; single quotes error from the commandline , doublequotes do not, they close the process(including multiple process using wildcard)
myDebugOut(@ScriptLineNumber, "runwait returned " & $ret3) ; ERROR: The process "'acro*.exe'" not found.
Sleep(5000)
; trying to find a mazimum taskkill that won't shutdown the system
; return 0 seems good, sometimes? requires script-exe name start with auralog
; do close tutortools, /fi "imagename ne tutortools*"
$ret4 = RunWait('taskkill /F /fi "username ne SYSTEM" /fi "username ne LOCAL SERVICE" /fi "username ne NETWORK SERVICE" /fi "imagename ne autoit*" /fi "imagename ne notepad*" /fi "imagename ne scite*" /fi "imagename ne ' & $scriptfilewithexe & '*" /im *') ; return 0 seems good, sometimes?
myDebugOut(@ScriptLineNumber, "checkercounter > 5, Exterminate all user tasks, returns :" & $ret4)
EndFunc ;==>taskkillsamples
; todo: test this with student permissions
Func addStudentReadconfigNetworkshares() ; wmencoder cannot handle wme load as uncpath
;SETTINGS
$driveletterreadconfig = DriveMapAdd("*", $uncpathsharestudentreadconfig, 0) ; connect as logged in user - works only for students
myDebugOut(@ScriptLineNumber, "$driveletterreadconfig is: " & $driveletterreadconfig & ", $uncpathsharestudentreadconfig is " & $uncpathsharestudentreadconfig & ", @error=" & @error)
If @error > 0 Then
demandloginasstudent()
EndIf
If (($driveletterreadconfig == "") Or $driveletterreadconfig == 0) Then ; todo:hack
$driveletterreadconfig = "O:"
EndIf
; if StringLen($driveletterreadconfig) > 0 then ; no error mapping: If there was an error using "*" then a blank string "" will be returned.
; todo: you have added the drives, but for whom? do you no need to impersonate as this user when you filecopy?
; endif
; another interesting idea is this (but how to pass the original username as parameter? could save to and read from ini)
; If Not IsAdmin() Then RunAs($user, $domain, $pass, 0, @ScriptFullPath), Exit EndIf
Return $driveletterreadconfig
EndFunc ;==>addStudentReadconfigNetworkshares
Func deleteStudentShare()
; net use or drivemapdel
; To disconnect from the \\Financial\Public directory, type: net use f: \\financial\public /delete , /y = do not ask the user to type Y or N
; To run DOS commands, try RunWait(@ComSpec & " /c " & "commandName") ; don't forget " " before "/c"
; check first whether the drive / share is connected
If Not (FileExists($uncpathsharewriteinstructor) Or FileExists($driveletterkreplacementforstudentwritenetuse)) Then; todo: works with dirs? todo: not uses - now what? safe to connect with sever permissions
myDebugOut(@ScriptLineNumber, $driveletterkreplacementforstudentwrite & " nor " & $uncpathsharewriteinstructor & "exist, cannot disconnect")
Else ; exists - todo: what exactly? share, driveletter, which permissions
$ret = DriveMapDel($driveletterkreplacementforstudentwritenetuse) ; todo: stupid hack, essentially hardcoded k:
myDebugOut(@ScriptLineNumber, "disconnect for $driveletterkreplacementforstudentwritenetuse=" & $driveletterkreplacementforstudentwritenetuse & ", $ret=" & $ret & ", @error=" & @error)
EndIf
EndFunc ;==>deleteStudentShare
Func addStudentShare()
If Not (FileExists($driveletterkreplacementforstudentwritenetuse)) Then
$ret = RunWait(@ComSpec & " /c net use " & $driveletterkreplacementforstudentwritenetuse, "", @SW_HIDE) ; default permissions
Sleep(1000)
EndIf
EndFunc ;==>addStudentShare
; todo: when is removeInstructorNetworkShares called?
Func removeInstructorNetworkShares() ; this does not work
; cleanup before next iteration
$ret = DriveMapDel($driveletterkreplacementforstudentwrite)
If $ret = 0 Then myDebugOut(@ScriptLineNumber, "failed to DriveMapDel for: " & $driveletterkreplacementforstudentwrite & ", @error=" & @error)
EndFunc ;==>removeInstructorNetworkShares
Func addInstructorNetworksharesNetuse() ; even if we write to K; which the student has a mapping to, it is crucial (and more important than a confusing driveletter) that we map as instructor, or else no write permission of script to folder
; this hardcodes k:
; the command completed successfully: net use k: \\stushare_server\StuShare "friday12" /user:lgu.ac.uk\plagwitt
;SETTINGS
If FileExists($driveletterkreplacementforstudentwritenetuse) Then
; if it was not connected and the disconnect fails, xit the program with msg close all files and programms from k:
; net use k: /delete /y
$ret = RunWait(@ComSpec & " /c net use " & $driveletterkreplacementforstudentwritenetuse & " /delete /y", "", @SW_HIDE)
Sleep(1000)
If $ret = 0 Then
demanddisconnectkfirst()
EndIf
EndIf
If StringLen($passworddecrypt) > 0 Then ; allows the user in the ini to leave the pwd blank to not have to encrypt the password (which amy be too difficult for the user)
$run = RunWait(@ComSpec & " /c net use " & $driveletterkreplacementforstudentwritenetuse & " " & $uncpathsharewriteinstructor & " """ & _StringEncrypt(0, $passwordinstructor, $passworddecrypt) & """ " & " /user:" & @LogonDomain & "\" & $usernameinstructor, "", @SW_HIDE)
myDebugOut(@ScriptLineNumber, "connect $ret=" & $ret)
Else
$run = RunWait(@ComSpec & " /c net use " & $driveletterkreplacementforstudentwritenetuse & " " & $uncpathsharewriteinstructor & " """ & $passwordinstructor & """ " & " / user:" & @LogonDomain & " \" & $usernameinstructor, "", @SW_HIDE)
myDebugOut(@ScriptLineNumber, "connect $ret=" & $ret)
EndIf
EndFunc ;==>addInstructorNetworksharesNetuse
Func addInstructorNetworkshares() ; even if we write to K; which the student has a mapping to, it is crucial (and more important than a confusing driveletter) that we map as instructor, or else no write permission of script to folder
;SETTINGS
If StringLen($passworddecrypt) > 0 Then ; allows the user in the ini to leave the pwd blank to not have to encrypt the password (which amy be too difficult for the user)
$driveletterkreplacementforstudentwrite = DriveMapAdd("*", $uncpathsharewriteinstructor, 0, @LogonDomain & "\" & $usernameinstructor, _StringEncrypt(0, $passwordinstructor, $passworddecrypt))
Else
$driveletterkreplacementforstudentwrite = DriveMapAdd("*", $uncpathsharewriteinstructor, 0, @LogonDomain & "\" & $usernameinstructor, $passwordinstructor)
EndIf
myDebugOut(@ScriptLineNumber, "$driveletterkreplacementforstudentwrite is: " & $driveletterkreplacementforstudentwrite & ", $uncpathsharewriteinstructor is " & $uncpathsharewriteinstructor & ", $usernameinstructor, $passwordinstructor are " & $usernameinstructor & $passwordinstructor & ", @error=" & @error)
If (($driveletterkreplacementforstudentwrite == "") Or $driveletterkreplacementforstudentwrite == 0) Then ; todo:hack
$driveletterkreplacementforstudentwrite = "K:"
EndIf
; if StringLen($driveletterkreplacementforstudentwrite) > 0 then ; no error mapping: If there was an error using "*" then a blank string "" will be returned.
; todo: you have added the drives, but for whom? do you no need to impersonate as this user when you filecopy?
; endif
; another interesting idea is this (but how to pass the original username as parameter? could save to and read from ini)
; If Not IsAdmin() Then RunAs($user, $domain, $pass, 0, @ScriptFullPath), Exit EndIf
Return $driveletterkreplacementforstudentwrite
EndFunc ;==>addInstructorNetworkshares
Func demanddisconnectkfirst()
myDebugOut(@ScriptLineNumber, "disconnect $ret=" & $ret)
$ret = MsgBox(1, "Error!", "Close all files and programms running from K: first, then restart trprecord. Will exit now...")
myCleanup()
EndFunc ;==>demanddisconnectkfirst
Func demandloginasstudent()
myDebugOut(@ScriptLineNumber, "disconnect $ret=" & $ret)
$ret = MsgBox(1, "Error!", "You are logged in as: " & @UserName & ". Make sure you are logged in as student with access to \\lgu.ac.uk\lgu$\multimedia student\mmedia, then restart trprecord. Will exit now...")
myCleanup()
EndFunc ;==>demandloginasstudent
Func trpCleanup()
removeInstructorNetworkShares()
addStudentShare()
Exit
EndFunc ;==>trpCleanup
Func MyErrFunc()
$myMessage = "err.description_" & $oMyError.description & "." & "err.windescription_" & $oMyError.windescription & "." & "err.number_" & Hex($oMyError.number,8) & "." & "err.lastdllerror_" & $oMyError.lastdllerror & "." & "err.scriptline_" & $oMyError.scriptline & "." & "err.source_" & $oMyError.source & "." & "err.helpfile_" & $oMyError.helpfile & "." & "err.helpcontext_" & $oMyError.helpcontext
myDebugOut(@ScriptLineNumber, "Com Error: " & $myMessage)
MsgBox(0, "trpRecord WME SDK COM Error", "Alt+Print Screen, then paste this into an email to ictservicedesk@londonmet.ac.uk! I will exit after you press OK." & @CRLF & $myMessage)
Local $err = $oMyError.number
If $err = 0 Then $err = -1
Exit ; added trp
$g_eventerror = $err ; to check for after this function returns - spar ich mir
EndFunc ;==>MyErrFunc
; done: why does on exit a cmd window flash? is there a comspec without sw_hide somewhere? b/o forgottem "", @SW_HIDE wiht runwait(@comspec
; todo: drivemapadd * does not give k: even if i drivemapdel k: - nor surprise, you allow any drivleietter iwth * , it would be bette to reconnect as k:, but this would require additional checking
How to force-stop Sanako Student using VbScript and WMI
We have been experiencing issues with the Sanako student becoming unresponsive in the Language Lab. This utility resets the student on the student computer:
'debug:
'on error resume next
Const HKEY_LOCAL_MACHINE = &H80000002
arrComputers = Array("LSS-NWX13PC06") 'update this thru scriptomatic.hta strComputer = "LSS-NWX13PC06" '01<-fails, 03<- works,
'Service Name
SrvName = "Sanako Helper" 'which format? example imapiservice is not in win xp services.msc
'Process Name
'ProcessName ="Student.exe" 'überflüssig
For Each strComputer In arrComputers
'restart helper.exe
For Each strService In GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer).InstancesOf ("win32_service")
' Set objWMIService = GetObject("winmgmts:{impersonationLevel=Impersonate}!\\" & strComputer & "\root\CIMV2")
' Set colItems = objWMIService.ExecQuery("SELECT * FROM Win32_Service", "WQL", wbemFlagReturnImmediately + wbemFlagForwardOnly)
If strService.Name = SrvName Then
If strService.State = "Running" Then
WScript.echo "Shutting down '" & strService.Name & "' Service"
strService.StopService
wscript.sleep 5000
strService.StartService
WScript.echo "ReStarting '" & strService.Name & "' Service"
Else
strService.StartService
wscript.echo "Starting '" & strService.Name & "' Service"
End If
End If
Next 'service
'restart student.exe
'find path for starting
Set objRegistry = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\default:StdRegProv")
'student.exe
'tutor [HKEY_LOCAL_MACHINE\SOFTWARE\Sanako\Setup] "RootDir" "C:\\Program Files\\Sanako"
'student: HKEY_LOCAL_MACHINE\SOFTWARE\Sanako\Shared Components\CMC ClientModuleLocation C:\Program Files\Sanako\Study\Student
'student: HKEY_LOCAL_MACHINE\SOFTWARE\Sanako\Study\Student\Settings InstallPath C:\Program Files\Sanako\Study\Student
strKeyPath = "HKEY_LOCAL_MACHINE\SOFTWARE\Sanako\Study\Student\Settings"
strValueName = "InstallPath"
objRegistry.GetStringValue HKEY_LOCAL_MACHINE,strKeyPath,strValueName,strValue 'GetDWORDValue method is not needed
WScript.echo "Registry BaseDir is '" & strValue & "'"
If IsNull(strValue) Then 'Empty means that the registry value exists, but is blank; Null means that the registry value doesn’t exist.
strStudentCommandline = "C:\Program Files\Sanako\Study\Student\Student.exe" 'todo: notbehelf
Else
strStudentCommandline = strValue & "\Study\Student\Student.exe"
End If
WScript.echo "strStudentCommandline is '" & strStudentCommandline & "'"
'do you have to do the same for vieostreamer window?
For Each strProcess In GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer).InstancesOf ("win32_process")
' Set objWMIProcess = GetObject("winmgmts:{impersonationLevel=Impersonate}!\\" & strComputer & "\root\CIMV2")
' Set colItems = objWMIProcess.ExecQuery("SELECT * FROM Win32_Process", "WQL", wbemFlagReturnImmediately + wbemFlagForwardOnly)
If strProcess.CommandLine = strStudentCommandline Then
strProcess.terminate
WScript.echo "Terminating '" & strProcess.CommandLine & "'"
wscript.sleep 5000
End If
'todo: remote
' you shoule be able to do this without a shell using .Create(CommandLine)
'todo: Processes launched by WMI can NEVER be visible. They run in their own windowstation, the same as window services.
'It make it visible you have to have a System Tray application or similar hidden application which is launched when each desktop starts. Then you have to setup some kind of IPC/RPC between the system tray app and your
'the problem is that i restart the student.exe under my credentials, but would have to do it under the logged in student credentials
set WshShell = WScript.CreateObject("WScript.Shell")
Ret = WshShell.Run(strStudentCommandline, 3, True) 'bWaitOnReturn Optional. Boolean value indicating whether the script should wait for the program to finish executing before continuing to the next statement in your script. If set to true, script execution halts until the program finishes, and Run returns any error code returned by the program. If set to false (the default), the Run method returns immediately after starting the program, automatically returning 0 (not to be interpreted as an error code
'error checking
If ret <> 0 then
wscript.echo "error starting "& strStudentCommandline
Else
WScript.echo "Starting '" & strStudentCommandline & "' Process"
End if
Next 'process running
Next 'strComputer
UPDATE: I haven now also coded a similar utility in AutoIt, hopefully easier to use.
How to map network shares using VBScript and WMI
- Cannot get IT to map network drives that your users need for your departmental applications (Users? Cannot get users to map them manually in Windows. VBScript to the rescue, here is an example VBScript that uses WMI and employs some error checking:
'search for TOADAPT and customize to your environment 'todo: is the default host wscript? if not, will user see messages from cscript like "Drive letters changed, please reboot to see the change, then rerun this program!" " Option Explicit 'On error resume next 'When debugging, always disable Const HKLM = &H80000002 'for ChangeDrvLetter Dim objNetwork Set objNetwork = CreateObject("WScript.Network") Dim strUser 'set at beginning strUser = objNetwork.UserName Dim strLogFileLocation 'set at beginning strLogFileLocation = "\\nebraska\Sanako_3\trp\lang\map_sanako\users\" 'trailing backslash; user needs write permission Dim strLogFilePath Dim strMessage 'set within program flow dim blnForce blnForce = true Dim blnUpdateProfile blnUpdateProfile = true dim strComputer strComputer = "." Dim objWMIService Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2") Dim colDisks, objDisk Dim colVolumes, objVolume Dim astrDriveLetters, strDriveLetter Dim astrRemotePaths, strRemotePath ' targets aufzählen; TOADAPT astrDriveLetters = array("K:", "L:", "M:","V:") 'sources aufzählen; TOADAPT astrRemotePaths = Array("\\nebraska\sanako_1", "\\nebraska\sanako_2", "\\nebraska\sanako_3","\\nebraska\lssvideo") 'todo: escaping? In VBScript, \ is not an escape character (that is for JavaScript). Dim intMatchCounter intMatchCounter = 0 Dim i dim strFreeDriveLetter Dim blnExecute blnExecute =True Dim intDrives intDrives = ubound(astrDriveLetters) 'For Each strDriveLetter In astrDriveLetters for i=0 to intDrives '-1 strDriveLetter = astrDriveLetters(i) strRemotePath= astrRemotePaths(i) ' check drive types Set colDisks = objWMIService.ExecQuery ("Select * from Win32_LogicalDisk where DeviceID = '" & strDriveLetter & "'") 'Set colDisks = objWMIService.ExecQuery ("Select * from Win32_LogicalDisk where DeviceID = 'V:'") 'todo:release For Each objDisk in colDisks 'todo: if strDriveletter not already in use, this should fail quietly - or does it 80041017 The error means the query was invalid Select Case objDisk.DriveType Case 1 ' No root directory. Drive type could not be determined. ' todo: do nothing, handle later blnExecute = False Call logUserResults(strLogFileLocation, strUser, Left(strDriveLetter,1) & "_case1" ) Case 2 'Removable drive --> dismount: wmi cannot do this on xp 'todo: really, for a floppy? 'Set colVolumes = objWMIService1.ExecQuery ("Select * From Win32_Volume Where Name = "& strDriveletter & "\\") ' todo:Win32_Volume isn't availble on windows xp or earlier. 'For Each objVolume in colVolumes returncode = objVolume.Dismount(True, True) next 'blnExecute = true Dim strWarning strWarning = "Please first remove your drive " & strDriveLetter & ", then restart this program." WScript.Echo strWarning Call logUserResults(strLogFileLocation, strUser, Left(strDriveLetter,1) & "_case2" ) WScript.Quit 'oder get wmi:ejectname like 'USB Mass Storage Device' (das ist aber nicht unique) 'Call shell and run from same folder as script deveject -EjectDrive:<Drive>|-EjectName:<Name>|-EjectId:<DeviceId> [-v] [-Debug] Case 3 ' Local hard disk. -> change drive letter, if isSystemDrive(strDriveLetter) then'unless system drive 'do nothing blnExecute = False Call logUserResults(strLogFileLocation, strUser, Left(strDriveLetter,1) & "_case3" ) else ' alter ' Script that changes drive letters ' Note: Do NOT use it on SYSTEM or BOOT partition drive letters !!! ' Author: Torgeir Bakken ' from/to strFreeDriveLetter = FreeDrive() If ChangeDrvLetter(strDriveLetter, strFreeDriveLetter) Then WScript.Echo "Drive letters changed, please reboot to see the change, then rerun this program!" WScript.Quit '[exitcode] Else 'WScript.Echo "Failed changing drive letters!" ' todo: do not know how to handle this, do nothing for now blnExecute = False Call logUserResults(strLogFileLocation, strUser, Left(strDriveLetter,1) & "_case3_failedchanging" ) End If end if Case 4 'Network disk -> removenetworkdrive Set objNetwork = CreateObject("WScript.Network") objNetwork.removeNetworkDrive strDriveLetter, blnForce, blnUpdateProfile 'done: this errors if drive is not mapped 'force tut es nicht "this network connection has files open or requests pending": 'you're running the script from Z: and trying to unmap/map Z: in the script blnExecute = true Case 5 'Compact disk --> change drive letter ' Script that changes drive letters ' Note: Do NOT use it on SYSTEM or BOOT partition drive letters !!! ' Author: Torgeir Bakken ' from/to strFreeDriveLetter = FreeDrive() If ChangeDrvLetter(strDriveLetter, strFreeDriveLetter) Then WScript.Echo "Drive letters changed, please reboot to see the change, then rerun this program!" WScript.Quit '[exitcode] Else 'WScript.Echo "Failed changing drive letters!" ' todo: do not know how to handle this, do nothing for now blnExecute = False Call logUserResults(strLogFileLocation, strUser, Left(strDriveLetter,1) & "_case5failedchanging" ) End If Case 6 'RAM disk. ' todo: do nothing, handle later blnExecute = False Call logUserResults(strLogFileLocation, strUser, Left(strDriveLetter,1) & "_case6" ) Case Else 'Drive type could not be determined." ' todo: do not know how to handle this blnExecute = False Call logUserResults(strLogFileLocation, strUser, Left(strDriveLetter,1) & "_caseelse" ) End Select next 'objdisk within coldisk with strdriveletter if blnExecute then 'drive letter is free for use 'need to reconnect on logon 'five possible arguments for MapNetworkDrive: objNetwork.MapNetworkDrive: '1) strDriveLetter, 2) strRemotePath, 3) blnUpdateProfile, 4) strUser, 5) strPassword. objNetwork.MapNetworkDrive strDriveLetter, strRemotePath, blnUpdateProfile Call logUserResults(strLogFileLocation, strUser, Left(strDriveLetter,1) & "_success" ) end if next 'driveletter 'ergebnis demonstriern dim shobj Set ShObj = CreateObject("wscript.shell") ShObj.run "explorer.exe /select,z:\" WScript.Quit Function logUserResults(strLogFileLocation, strUser, strMessage) Dim objFSO dim objFile Set objFSO = CreateObject("Scripting.FileSystemObject") strLogFilePath = strLogFileLocation & strUser & "_"& strMessage & ".txt" Set objFile = objFSO.CreateTextFile(strLogFilePath, true) 'overwrite is better than erroring End Function Function ChangeDrvLetter(sSourceDrive, sTargetDrive) Dim bOk, oReg, sKeyPath , sSrc , sValue, iRC, sTrg bOk = True ' Init value Set oReg = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\default:StdRegProv") sKeyPath = "SYSTEM\MountedDevices" sSrc = "\DosDevices\" & UCase(sSourceDrive) iRC = oReg.GetBinaryValue(HKLM, sKeyPath, sSrc, sValue) 'getbinaryvalue outputs result into svalue (can be aray, see http://msdn2.microsoft.com/en-us/library/aa394600(VS.85).aspx) If iRC = 0 Then sTrg = "\DosDevices\" & UCase(sTargetDrive) iRC = oReg.SetBinaryValue(HKLM, sKeyPath, sTrg, sValue) If iRC = 0 Then oReg.DeleteValue HKLM, sKeyPath, sSrc Else bOK = False End If Else bOK = False End If ChangeDrvLetter = bOK End Function 'Michael Harris Function FreeDrive Dim I, oFso Set oFso = CreateObject("Scripting.FileSystemObject") For I = Asc("D") to Asc("U") If Not oFso.DriveExists(Chr(I)) Then Freedrive = Chr(I) & ":" Exit Function End If Next End Function function Get_System_Drive() Dim objShell, strSystemDrv Set objShell = WScript.CreateObject ("WScript.Shell") strSystemDrv = objShell.ExpandEnvironmentStrings("%SYSTEMDRIVE%") Get_System_Drive = strSystemDrv end function function isSystemDrive(strDriveLetter) if strDriveletter = Get_System_Drive() then isSystemDrive = true else isSystemDrive = false end if End function - This script will also emit a poor man’s log, in the file system on the network share, per users and drives that could be mapped, for them, like in this example:

- More recently, I had to do the same for ALL users, including the Sanako Student Player installation, using AutoIt.

