[open-allure-ds] push by jg07...@gmail.com - Addition of SlideSpeech icons, change of application names, Mac packag... on 2011-11-28 08:39 GMT

6 views
Skip to first unread message

open-al...@googlecode.com

unread,
Nov 28, 2011, 3:39:54 AM11/28/11
to open-al...@googlegroups.com
Revision: c66311166e16
Author: John Graves
Date: Mon Nov 28 00:38:44 2011
Log: Addition of SlideSpeech icons, change of application names, Mac
packaging project
http://code.google.com/p/open-allure-ds/source/detail?r=c66311166e16

Added:
/odp2wts/odp2ss.py
/odp2wts/slidespeech.icns
/wikitospeech/SlideSpeech.packproj
/wikitospeech/SlideSpeech.py
/wikitospeech/blurb.txt
/wikitospeech/slidespeech.icns
/wikitospeech/ss2app.py
Deleted:
/wikitospeech/BeautifulSoup.pyc
/wikitospeech/objects.pyc
/wikitospeech/scriptParser.pyc
/wikitospeech/voice.pyc
Modified:
/odp2wts/CHANGES.txt
/odp2wts/odp2app.py
/odp2wts/odp2wts.py
/wikitospeech/forms.py
/wikitospeech/wts2app.py

=======================================
--- /dev/null
+++ /odp2wts/odp2ss.py Mon Nov 28 00:38:44 2011
@@ -0,0 +1,1011 @@
+# -*- coding: utf-8 -*-
+"""
+odp2ss.py
+a component of SlideSpeech.py
+
+Extract speaker notes from .odp file and prepare script.txt for SlideSpeech
+
+Copyright (c) 2011 John Graves
+
+MIT License: see LICENSE.txt
+
+20110825 Add version to title bar
+20110901 Add direct to video output
+20110901 Switch to jpg, mklink with /h
+20110902 Switch to jpg for Windows and png for Mac
+20110907 More tweaks to joinContents
+20110909 Allow over 20 slides in MP4Box to cat for Mac
+20110910 Coping with unavailable mklink in Windows and path names
containing spaces
+20110913 Remove [] from script output and wrap ctypes import with win32
test
+20110915 Added boilerplate script comments including version number
+20110916 Read Unicode
+20110917 Write out bits of Question/Answer/Response
+20111118 Show image along with question. Requires slide with comment first.
+ Example:
+ Comment on Slide4
+ [questions=on]
+ Question for slide 4:
+ Answer 1 ; Response 1
+ [questions=off]
+
+ NOTE: last slide must not have questions
+
+img1.png > img1.htm > img1.mp3
+[questions=on]
+How many slides have we seen? > q/img1q1.htm > q/img1q1.mp3
+One ;; > q/img1q1a1.mp3
+Two ;; > q/img1q1a1.mp3
+
+What slide is next? > q/img1q2.htm > q/img1q2.mp3
+Third ;; > q/img1q2a1.mp3
+Fourth; No, just the third. > q/img1q2a2.mp3, > q/img1q2r2.mp3
+
+[questions=off]
+
+20111112 If script has [Source=http:// ...] add this link to the question
page
+20111121 Turn off debug2.txt and put quotes around calls in makeVid.bat
+20111128 Working Linux version once dependencies are installed
+ Linux dependencies include:
+ sox
+ ffmpeg
+ mencode
+ espeak
+"""
+__version__ = "0.1.28"
+
+import BeautifulSoup
+from BeautifulSoup import BeautifulStoneSoup
+from ConfigParser import ConfigParser
+import codecs
+import easygui
+import math
+import os
+import os.path
+import shutil
+import scriptParser
+import stat
+import subprocess
+import sys
+import time
+import webbrowser
+from zipfile import ZipFile
+
+def ensure_dir(d):
+ """Make a directory if it does not exist"""
+ if not os.path.exists(d):
+ os.makedirs(d)
+
+# Find location of Windows common application data files for odp2ss.ini
+iniDirectory = None
+if sys.platform.startswith("win"):
+ import ctypes
+ from ctypes import wintypes, windll
+ CSIDL_COMMON_APPDATA = 35
+
+ _SHGetFolderPath = windll.shell32.SHGetFolderPathW
+ _SHGetFolderPath.argtypes = [wintypes.HWND,
+ ctypes.c_int,
+ wintypes.HANDLE,
+ wintypes.DWORD, wintypes.LPCWSTR]
+
+
+ path_buf = wintypes.create_unicode_buffer(wintypes.MAX_PATH)
+ result = _SHGetFolderPath(0, CSIDL_COMMON_APPDATA, 0, 0, path_buf)
+ iniDirectory = path_buf.value+os.sep+"SlideSpeech"
+else:
+ iniDirectory = os.path.expanduser('~')+os.sep+".SlideSpeech"
+
+ensure_dir(iniDirectory)
+
+if sys.platform.startswith("win"):
+ imageFileSuffix = ".jpg"
+else:
+ imageFileSuffix = ".png"
+
+## Obtain odpFile name and directory
+
+# Check for last .odp file in config file
+lastOdpFile = '~/*.odp'
+config = ConfigParser()
+try:
+ config.read(iniDirectory+os.sep+'odp2ss.ini')
+ lastOdpFile = config.get("Files","lastOdpFile")
+except:
+ config.add_section("Files")
+ config.set("Files","lastOdpFile","")
+ with open(iniDirectory+os.sep+'odp2ss.ini', 'wb') as configfile:
+ config.write(configfile)
+
+if not os.path.isfile(lastOdpFile):
+ lastOdpFile = None
+
+odpFilePath = easygui.fileopenbox(title="SlideSpeech from ODP
Converter "+__version__, msg="Select an .odp file",
+ default=lastOdpFile, filetypes=None)
+if odpFilePath == None:
+ sys.exit()
+
+(odpFileDirectory, odpFile) = os.path.split(odpFilePath)
+(odpName, odpSuffix) = odpFile.split(".")
+
+## Find list of .png or .jpg files
+
+odpFileSubdirectory = odpFileDirectory+os.sep+odpName
+# Create a subdirectory for generated files (if needed)
+ensure_dir(odpFileSubdirectory)
+
+# Look for .jpg files (slide images) in the odpName subdirectory
+dir = os.listdir(odpFileSubdirectory)
+imageFileList = [file for file in dir if
file.lower().endswith(imageFileSuffix)]
+
+# If no image files found there ...
+if len(imageFileList)==0:
+ # ... look for image files in odpFileDirectory and copy to odpName
subdirectory
+ dir = os.listdir(odpFileDirectory)
+ imageFileList = [file for file in dir if
file.lower().endswith(imageFileSuffix)]
+ # If still no image files, request some.
+ if len(imageFileList)==0:
+ easygui.msgbox("Need some slide image files for this
presentation.\n.jpg for Windows or .png for Mac OSX.")
+ sys.exit()
+ else:
+ for file in imageFileList:
+ shutil.copy(odpFileDirectory+os.sep+file, odpFileSubdirectory)
+
+# Find minimum value for slide number for linking to First Slide
+# Find maximum value for slide number for linking to Last Slide
+# Find imageFilePrefix, imageFileSuffix
+# Default values
+minNum = 0
+maxNum = 0
+wrongStem = False
+# Test contents of image file list
+print imageFileList
+for file in imageFileList:
+ # Parse out file name stem (which includes number) and imageFileSuffix
+ (stem, imageFileSuffix) = file.split(".")
+
+ # Parse out just number (num) and imageFilePrefix
+ if stem.startswith("Slide"):
+ # PowerPoint Slide images are output to jpg with starting index of
1
+ imageFilePrefix = "Slide"
+ minNum=1
+ num = int(stem[5:])
+ elif stem.startswith("img"):
+ # ODP slide images are output to img with starting index of 0
+ imageFilePrefix = "img"
+ num = int(stem[3:])
+ else:
+ wrongStem = True
+ if num>maxNum:
+ maxNum=num
+
+if wrongStem:
+ easygui.msgbox("Need slide image files for this presentation\n"+
+ "with consistent stem: Slide* or img*\n\nCheck
in "+odpFileSubdirectory)
+ sys.exit()
+
+## Step 1 - parse the .odp file, prepare script.txt and .zip file
+
+def joinContents(textPList):
+ """Combine tagged XML into single string
+
+Needs to handle this from PowerPoint:
+ <text:p text:style-name="a785" text:class-names=""
text:cond-style-name="">
+ <text:span text:style-name="a783" text:class-names="">Voice over
1</text:span>
+ <text:span text:style-name="a784" text:class-names=""/>
+ </text:p>
+
+or worse, this:
+ <text:p text:style-name="a786" text:class-names=""
text:cond-style-name="">
+ <text:span text:style-name="a783" text:class-names="">
+ Voice
+ <text:s text:c="1"/>
+ </text:span>
+ <text:span text:style-name="a784" text:class-names="">
+ over 1
+ <text:s text:c="1"/>
+ asdf
+ </text:span>
+ <text:span text:style-name="a785" text:class-names=""/>
+ </text:p>
+
+ """
+ # item is list of all the XML for a single slide
+ joinedItems = ""
+ if len(textPList)>0:
+ textItems = []
+ i = 0
+ for textP in textPList:
+ textSpans = []
+ # break the XML into a list of tagged pieces (text:span)
+ for item in textP:
+ if type(item)==BeautifulSoup.Tag:
+ tagContents = item.contents
+ if type(tagContents)==type([]):
+ for item2 in tagContents:
+ if type(item2)==BeautifulSoup.Tag:
+ textSpans.append([item2.contents])
+ else:
+ textSpans.append([item2])
+ else:
+ textSpans.append([tagContents])
+ else:
+ textSpans.append([item])
+
+ # flatten list
+ textSpans1 = [item for sublist in textSpans for item in
sublist]
+ # clean up
+ textSpans1b = []
+ for item in textSpans1:
+ if type(item)==BeautifulSoup.NavigableString:
+ textSpans1b.append(item)
+ elif type(item)==type([]):
+ if len(item)==0:
+ pass
+ elif len(item)==1:
+ textSpans1b.append(item[0])
+ else:
+ for itemInList in item:
+ textSpans1b.append(itemInList)
+ # find the contents of these pieces if they are still tagged
(text:s)
+ textSpans2 = []
+ for textSpan in textSpans1b:
+ if type(textSpan)==BeautifulSoup.Tag:
+ textSpans2.append(textSpan.text)
+ else:
+ if (type(textSpan)==type([]) and len(textSpan)>0):
+ textSpans2.append(unicode(textSpan[0]))
+ else:
+ textSpans2.append(unicode(textSpan))
+
+ justText = u""
+ for item in textSpans2:
+ # deal with single quote and double quotes and dashes
+ # \u2018 LEFT SINGLE QUOTATION MARK
+ justText = justText + item + u" "
+ textItems.append(justText)
+ joinedItems = "\n".join(textItems)
+ return joinedItems
+
+if ((0 != len(odpFile)) and (os.path.exists(odpFilePath))):
+ # Save file name to config file
+ config.set("Files","lastOdpFile",odpFilePath)
+ with open(iniDirectory+os.sep+'odp2ss.ini', 'wb') as configfile:
+ config.write(configfile)
+
+ odpName = odpFile.replace(".odp","")
+ odp = ZipFile(odpFilePath,'r')
+ f = odp.read(u'content.xml')
+ soup = BeautifulStoneSoup(f)
+ notes = soup.findAll(attrs={"presentation:class":"notes"})
+ noteTextPLists = [item.findAll("text:p") for item in notes]
+ noteText = [joinContents(noteTextPList) for noteTextPList in
noteTextPLists]
+else:
+ sys.exit()
+
+# Create script.txt file
+scriptFile = codecs.open(odpFileSubdirectory+os.sep+'script.txt',
encoding='utf-8', mode='w+')
+scriptFile.write("""#[path=]
+#
+# Script created with SlideSpeech from ODP version """+__version__+
+"\n# http://slidespeech.org\n"+
+"# Date: "+time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())+"""
+#
+# Title:
+# Author:
+#
+# SlideSpeech Slide show version
+# http://
+#
+# SlideSpeech Video version
+# http://
+#
+# SlideSpeech script:
+""")
+onImg = minNum
+for item in noteText:
+ if onImg-minNum == 0: # first slide
+ # insert line with link to first slide image after parameter lines
+ # For example, noteText could start with [path=...]
+ lines = item.split("\n")
+ slideOnLine = -1
+ for linenum, line in enumerate(lines):
+ if len(line.strip())>0:
+ if line.startswith("["):
+ scriptFile.write(line+"\n")
+ elif slideOnLine == -1:
+
scriptFile.write(imageFilePrefix+str(onImg)+"."+imageFileSuffix+"\n")
+ slideOnLine = linenum
+ scriptFile.write(line+"\n")
+ else:
+ scriptFile.write(line+"\n")
+ else:
+ scriptFile.write("\n")
+ else:
+ # Add a line with a link to each slide
+
scriptFile.write(imageFilePrefix+str(onImg)+"."+imageFileSuffix+"\n")
+ # followed by the voice over text for the slide
+ scriptFile.write(item+"\n")
+ scriptFile.write("\n")
+ onImg += 1
+scriptFile.close()
+
+# Collect script and image files into ZIP file
+outputFile = ZipFile(odpFileDirectory+os.sep+odpName+".zip",'w')
+savePath = os.getcwd()
+os.chdir(odpFileSubdirectory)
+outputFile.write("script.txt")
+for file in imageFileList:
+ outputFile.write(file)
+os.chdir(savePath)
+easygui.msgbox("Zipped script.txt and image files
to "+odpFileDirectory+os.sep+odpName+".zip")
+
+## Step 2 - Sequence script and make and run convert.bat
+def convertItem(f,item,onImgStr):
+ if sys.platform.startswith("win"):
+ # For Windows
+
f.write('"'+savePath+os.sep+'sapi2wav.exe" '+imageFilePrefix+onImgStr+'.wav
1 -t "')
+ lines = item.split("\n")
+ for linenum, line in enumerate(lines):
+ if not line.startswith("["):
+ line.replace('"',' ').replace('`',' ').replace(';',' ')
+ if not line.startswith("#"):
+ f.write(line+" ")
+ elif linenum>0:
+ break
+ f.write('"\n')
+ f.write('"'+savePath+os.sep+'lame.exe"
-h '+imageFilePrefix+onImgStr+'.wav '+ '"' + \
+
odpFileSubdirectory+os.sep+imageFilePrefix+onImgStr+'.mp3"\n')
+
f.write('"'+savePath+os.sep+'sox.exe" '+imageFilePrefix+onImgStr+'.wav '+ '"'
+ \
+
odpFileSubdirectory+os.sep+imageFilePrefix+onImgStr+'.ogg"\n')
+ f.write('del '+imageFilePrefix+onImgStr+'.wav\n')
+ elif sys.platform.startswith("darwin"):
+ # For Mac OSX
+ f.write("/usr/bin/say -o "+imageFilePrefix+onImgStr+'.aiff "')
+ lines = item.split("\n")
+ for linenum, line in enumerate(lines):
+ line.replace('"',' ').replace('`',' ').replace(';',' ')
+ if not line.startswith("["):
+ f.write(line+" ")
+ elif linenum>0:
+ break
+ # f.write(item)
+ f.write('"\n')
+ f.write("~/bin/sox "+imageFilePrefix+onImgStr+'.aiff "'+
+ odpFileSubdirectory+os.sep+imageFilePrefix+onImgStr+'.ogg"\n')
+ f.write("~/bin/sox "+imageFilePrefix+onImgStr+'.aiff "'+
+ odpFileSubdirectory+os.sep+imageFilePrefix+onImgStr+'.mp3"\n')
+ f.write("rm "+imageFilePrefix+onImgStr+'.aiff\n')
+
+ else:
+ # For Linux
+ f.write("/usr/bin/espeak -w "+imageFilePrefix+onImgStr+'.wav "')
+ lines = item.split("\n")
+ for linenum, line in enumerate(lines):
+ line.replace('"',' ').replace('`',' ').replace(';',' ')
+ if not line.startswith("["):
+ f.write(line+" ")
+ elif linenum>0:
+ break
+ # f.write(item)
+ f.write('"\n')
+ f.write("/usr/bin/sox "+imageFilePrefix+onImgStr+'.wav "'+
+ odpFileSubdirectory+os.sep+imageFilePrefix+onImgStr+'.ogg"\n')
+ f.write("/usr/bin/sox "+imageFilePrefix+onImgStr+'.wav "'+
+ odpFileSubdirectory+os.sep+imageFilePrefix+onImgStr+'.mp3"\n')
+ # f.write("rm "+imageFilePrefix+onImgStr+'.wav\n')
+
+def writeHtmlHeader(htmlFile):
+ htmlFile.write('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01
Transitional//EN"' + "\n")
+ htmlFile.write('"http://www.w3.org/TR/html4/transitional.dtd">' + "\n")
+ htmlFile.write("<html>\n<head>\n")
+ htmlFile.write('<meta HTTP-EQUIV=CONTENT-TYPE CONTENT="text/html;
charset=utf-8">' + "\n")
+ htmlFile.write('<title>SlideSpeech</title>\n')
+
+def writeHtmlHeader2(htmlFile):
+ htmlFile.write('</head>\n')
+ htmlFile.write('<body text="#000000" bgcolor="#FFFFFF" link="#000080"
vlink="#0000CC" alink="#000080">' + "\n")
+ htmlFile.write('<center>' + "\n")
+
+def writeHtmlFileNavigation(htmlFile, questionFileNames, maxNum,
position):
+ # First page and Back navigation
+ # First page
+ if position==0:
+ htmlFile.write("""First page Back """)
+ # Second page
+ elif position==1:
+ htmlFile.write("""<a href="../""" + odpName +""".htm">First
page</a> <a href="../""" +
+ odpName +""".htm">Back</a> """)
+ # Rest of pages
+ else:
+ htmlFile.write("""<a href="../""" + odpName +""".htm">First
page</a> <a href=""" + '"' +
+
questionFileNames[position-1]+""".htm">Back</a> """)
+
+ # Continue and Last Page navigation
+ # Last page
+ if position==maxNum:
+ htmlFile.write('Continue Last page<br>\n')
+ # First page
+ elif position==0:
+ htmlFile.write( \
+ '<a href="'+
+ odpName+"/"+questionFileNames[position+1]+
+ '.htm">Continue</a> ')
+ htmlFile.write( \
+ '<a href="'+
+ odpName+"/"+questionFileNames[-1]+
+ '.htm">Last page</a><br>\n')
+ # Rest of pages
+ else:
+ htmlFile.write( \
+ '<a href="'+
+ questionFileNames[position+1]+
+ '.htm">Continue</a> ')
+ htmlFile.write( \
+ '<a href="' +
+ questionFileNames[-1] +
+ '.htm">Last page</a><br>\n')
+
+def writeHtmlJavascript(htmlFile,
+ questionFileNames,
+ question,
+ position,
+ audioFileTimes):
+ """
+ <script language="javascript" type="text/javascript">
+ var t;
+ function respond0()
+ {
+ clearTimeout(t)
+ document.getElementById("a0").innerHTML = "Separators of plus
and quotes";
+ document.getElementById("a0").style.color = "grey";
+ document.getElementById('playaudio').innerHTML='<audio
controls autoplay><source src="img8q1r0.mp3" /><source src="img8q1r0.ogg"
/>Your browser does not support the <code>audio</code>
element.</audio><!--[if lte IE 8]><embed src="img8q1r0.mp3"
autostart="true"><![endif]-->';
+ }
+ function respond1()
+ {
+ clearTimeout(t)
+ document.getElementById('playaudio').innerHTML='<audio
controls autoplay><source src="img8q1r1.mp3" /><source src="img8q1r1.ogg"
/>Your browser does not support the <code>audio</code>
element.</audio><!--[if lte IE 8]><embed src="img8q1r1.mp3"
autostart="true"><![endif]-->';
+ t=setTimeout("advance1()",12762);
+ }
+ function advance1()
+ {
+ location.href="img8q2.htm";
+ }
+
+ </script>
+ """
+ htmlFile.write('<script language="javascript"
type="text/javascript">\nvar t;\n')
+ for answerNum, answer in enumerate(question.answers):
+ if len(answer.answerText)>0:
+ htmlFile.write('function respond'+
+ str(answerNum)+
+ '()\n{\n clearTimeout(t);\n')
+ if not answer.action > 0:
+ htmlFile.write(' document.getElementById("a'+
+ str(answerNum)+'").innerHTML = "'+
+ answer.answerText+
+ '";\n')
+ htmlFile.write(' document.getElementById("a'+
+ str(answerNum)+
+ '").style.color = "grey";\n')
+
+ if len(answer.responseText)>0:
+ if position==0:
+ pathToAudio =
odpName+'/'+questionFileNames[position]+'r'+str(answerNum)
+ else:
+ pathToAudio =
questionFileNames[position]+'r'+str(answerNum)
+
+ htmlFile.write( \
+ " document.getElementById('playaudio').innerHTML=" +
+ "'<audio controls autoplay><source src=" +
+ '"' + pathToAudio +
+ '.mp3" />')
+ htmlFile.write('<source src="' +
+ pathToAudio +
+ '.ogg" />')
+ htmlFile.write( \
+ "<embed src=" +
+ '"' + pathToAudio +
+ '.mp3' +
+ '" autostart="true"></audio>' + "';\n")
+ if answer.action > 0:
+ htmlFile.write(' t=setTimeout("advance'+
+ str(answerNum)+
+ '()",'+
+ str(audioFileTimes[pathToAudio]+1000)+
+ ');\n')
+ elif answer.action > 0:
+ htmlFile.write(" advance"+
+ str(answerNum)+
+ '();\n')
+ htmlFile.write('}\n')
+
+ if (answer.action > 0 and position+answer.action <
len(questionFileNames)):
+ htmlFile.write('function advance'+
+ str(answerNum)+
+ '()\n{\n')
+ htmlFile.write(' location.href="'+
+ questionFileNames[position+answer.action]+
+ '.htm";\n}\n')
+ htmlFile.write('</script>\n')
+
+
+def makeConvert(sequence):
+ # Make list of question file names for navigation
+ questionFileNames = []
+ onImg = minNum
+ onImgStr = str(onImg)
+ onQ = 0
+ for question in sequence:
+ if len(question.answers)==0:
+ questionFileNames.append(imageFilePrefix+onImgStr)
+ onImg += 1
+ onImgStr = str(onImg)
+ onQ = 0
+ else:
+ onQ += 1
+ questionFileNames.append(imageFilePrefix+onImgStr+"q"+str(onQ))
+ maxNum = len(questionFileNames)-1
+
+ # Make convert.bat to convert questionText into audio files
+ f = codecs.open(odpFileDirectory+os.sep+"convert.bat",
encoding='utf-8', mode='w+')
+ os.chmod(odpFileDirectory+os.sep+"convert.bat",stat.S_IRWXU)
+ onImg = minNum
+ onImgStr = str(onImg)
+ onQ = 0
+ oggList = []
+ for position, question in enumerate(sequence):
+
+ # write convert.bat
+ if len(question.answers)==0:
+ convertItem(f," ".join(question.questionTexts),onImgStr)
+ oggList.append(onImgStr)
+ onImg += 1
+ onImgStr = str(onImg)
+ onQ = 0
+ else:
+ onQ += 1
+
convertItem(f," ".join(question.questionTexts),onImgStr+"q"+str(onQ))
+ oggList.append(onImgStr+"q"+str(onQ))
+ onAns = 0
+ for answer in question.answers:
+
convertItem(f,answer.answerText,onImgStr+"q"+str(onQ)+"a"+str(onAns))
+ oggList.append(onImgStr+"q"+str(onQ)+"a"+str(onAns))
+ if len(answer.responseText)>0:
+
convertItem(f,answer.responseText,onImgStr+"q"+str(onQ)+"r"+str(onAns))
+ oggList.append(onImgStr+"q"+str(onQ)+"r"+str(onAns))
+ onAns += 1
+
+ # Write concatenation of all .ogg files into all.ogg
+ f.write('cd "'+odpFileSubdirectory+'"\n')
+ if sys.platform.startswith("win"):
+ f.write('"'+savePath+os.sep+'sox.exe" ')
+ elif sys.platform.startswith("darwin"):
+ f.write("~/bin/sox ")
+ else:
+ f.write("/usr/bin/sox ")
+ for item in oggList:
+ f.write(imageFilePrefix+item+".ogg ")
+ f.write('"'+savePath+os.sep+'silence.ogg" ')
+ f.write("all.ogg\n")
+ f.close()
+
+def fetchAudioFileTimes():
+ os.chdir(odpFileSubdirectory)
+ dir = os.listdir(odpFileSubdirectory)
+ ogg = [file for file in dir if file.lower().endswith(".ogg")]
+ oggDict = {}
+
+ for file in ogg:
+ # Parse out file name stem
+ (stem, audioFileSuffix) = file.split(".")
+ # soxi -D returns the duration in seconds of the audio file as a
float
+ if sys.platform.startswith("win"):
+ # Unfortunately, there is a requirement that soxi (with an
implict .exe)
+ # be the command to check audio file duration in Win32
+ # but soxi is the name of the unix version of this utility
+ # So we need to delete the (unix) file called soxi so the
command line call
+ # to soxi will run soxi.exe
+ if os.path.isfile(savePath+os.sep+"soxi"):
+ os.remove(savePath+os.sep+"soxi")
+ command =
[savePath+os.sep+"soxi","-D",odpFileSubdirectory+os.sep+file]
+ elif sys.platform.startswith("darwin"):
+ if os.path.isfile(savePath+os.sep+"soxi"):
+ command =
[savePath+os.sep+"soxi","-D",odpFileSubdirectory+os.sep+file]
+ elif os.path.isfile(savePath+os.sep+"Contents/Resources/soxi"):
+ command =
[savePath+os.sep+"Contents/Resources/soxi","-D",odpFileSubdirectory+os.sep+file]
+ else:
+ command = ["soxi","-D",odpFileSubdirectory+os.sep+file]
+ else :
+ command = ["soxi","-D",odpFileSubdirectory+os.sep+file]
+ print command
+ process = subprocess.Popen(command,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ universal_newlines=True)
+ output = process.communicate()
+ retcode = process.poll()
+ if retcode:
+ print "No time available"
+ oggDict[stem]=int(float(output[0].strip())*1000)
+ return oggDict
+
+def writeHtml(sequence, audioFileTimes):
+
+ # Make list of question file names for navigation
+ questionFileNames = []
+ onImg = minNum
+ onImgStr = str(onImg)
+ onQ = 0
+ for question in sequence:
+ if len(question.answers)==0:
+ questionFileNames.append(imageFilePrefix+onImgStr)
+ onImg += 1
+ onImgStr = str(onImg)
+ onQ = 0
+ else:
+ onQ += 1
+ questionFileNames.append(imageFilePrefix+onImgStr+"q"+str(onQ))
+ maxNum = len(questionFileNames)-1
+
+ onImg = minNum
+ onImgStr = str(onImg)
+ onQ = 0
+ for position, question in enumerate(sequence):
+
+ if position==0:
+ # Create first .htm file in same directory as odpFile
+ htmlFile = codecs.open(odpFileDirectory+os.sep+odpName+".htm",
encoding='utf-8', mode='w+')
+ else:
+ # Create subsequent .htm files in folder in same directory as
odpFile
+ htmlFile =
codecs.open(odpFileSubdirectory+os.sep+questionFileNames[position]+".htm",
encoding='utf-8', mode='w+')
+
+ writeHtmlHeader(htmlFile)
+
+ if len(question.answers)==0:
+ writeHtmlHeader2(htmlFile)
+ writeHtmlFileNavigation(htmlFile, questionFileNames, maxNum,
position)
+ else:
+ writeHtmlJavascript(htmlFile, questionFileNames, question,
position,
+ audioFileTimes)
+ writeHtmlHeader2(htmlFile)
+ writeHtmlFileNavigation(htmlFile, questionFileNames, maxNum,
position)
+
+ if len(question.answers)==0:
+ # image src and link to next slide
+ # Last page which is not (also) the first page
+ if (position==maxNum and position>0):
+ # src but no link
+ htmlFile.write( \
+ '<img src="' +
+ questionFileNames[position] + '.' + imageFileSuffix +
+ '" style="border:0px"><br>\n')
+ # Last page which is also the first page
+ elif (position==maxNum and position==0):
+ # src but no link
+ htmlFile.write( \
+ '<img src="' +
+ odpName+"/"+questionFileNames[position] + '.' +
imageFileSuffix +
+ '" style="border:0px"><br>\n')
+ # First page
+ elif position==0:
+ htmlFile.write( \
+ '<a href="' +
+ odpName+"/"+questionFileNames[position+1] +
+ '.htm">')
+ htmlFile.write( \
+ '<img src="' +
+ odpName +"/" + questionFileNames[position] + '.' +
imageFileSuffix +
+ '" style="border:0px"></a><br>\n')
+ # Rest of pages
+ else:
+ htmlFile.write( \
+ '<a href="' +
+ questionFileNames[position+1] +
+ '.htm">')
+ htmlFile.write( \
+ '<img src="' +
+ questionFileNames[position] + '.' + imageFileSuffix +
+ '" style="border:0px"></a><br>\n')
+
+ # Add source link, if any
+ if 0<len(question.sourceLink):
+ htmlFile.write( \
+ '<a href="' + question.sourceLink + '">' +
question.sourceLink + '</a><br>\n')
+
+ else:
+ htmlFile.write("<br><br><hr><br><center>\n")
+ if len(question.linkToShow)>0:
+ # src but no link
+ htmlFile.write( \
+ '<img src="' +
+ question.linkToShow +
+ '" style="border:0px"><br>\n')
+ htmlFile.write("""<table width="400"
style="text-align:left"><tbody>
+<tr><td>""")
+
htmlFile.write(" ".join(question.questionTexts)+ "</td></tr>\n" )
+
+ for answerNumber, answer in enumerate(question.answers):
+ if len(answer.answerText)>0:
+ htmlFile.write('<tr><td><div id="a'+
+ str(answerNumber)+
+ '"><a href="javascript:respond'+
+ str(answerNumber)+
+ '();">'+
+ answer.answerText +
+ '</a></div></td></tr>\n')
+ htmlFile.write("""</tbody>
+</table>
+</center><br><hr>""")
+
+ # include audio
+ # First page
+ if position==0:
+ pathToAudio = odpName+'/'+questionFileNames[position]
+ else:
+ pathToAudio = questionFileNames[position]
+ # For Safari
+ htmlFile.write( \
+ '<p id="playaudio">' +
+ '<audio controls autoplay><source src="' +
+ pathToAudio +
+ '.mp3" />')
+ # For Firefox
+ htmlFile.write( \
+ '<source src="' +
+ pathToAudio +
+ '.ogg" />\n')
+ # For others
+ htmlFile.write( \
+ 'Your browser does not support the <code>audio</code>
element.\n</audio>\n')
+ htmlFile.write( \
+ '</p>\n')
+ # For Internet Explorer
+ htmlFile.write( \
+ '<!--[if lte IE 8]>\n' +
+ '<script>\n' +
+ 'document.getElementById("playaudio").innerHTML=' + "'" +
+ '<embed src="' +
+ pathToAudio +
+ '.mp3" autostart="true">' + "'" + ';\n' +
+ '</script>\n' +
+ '<![endif]-->\n')
+
+ htmlFile.write('</center>' + "\n")
+ htmlFile.write('</body>\n</html>\n')
+ htmlFile.close()
+
+
+sequence =
scriptParser.parseTxtFile(odpFileSubdirectory+os.sep+"script.txt")
+makeConvert(sequence)
+os.chdir(odpFileDirectory)
+p =
subprocess.Popen('"'+odpFileDirectory+os.sep+'convert.bat"',shell=True).wait()
+audioFileTimes = fetchAudioFileTimes()
+writeHtml(sequence, audioFileTimes)
+
+
+## Step 3 - create makeVid.bat
+os.chdir(odpFileSubdirectory)
+dir = os.listdir(odpFileSubdirectory)
+ogg = [file for file in dir if file.lower().endswith(".ogg")]
+oggDict = {}
+for file in ogg:
+ # Parse out file name stem (which includes number) and imageFileSuffix
+ (stem, audioFileSuffix) = file.split(".")
+
+ # Parse out just number (num) and imageFilePrefix
+ if stem.startswith("Slide"):
+ numberPart = file[5:].split(".")[0]
+ if numberPart.isdigit():
+ oggDict[int(numberPart)] = file
+ else:
+ # imgXX.ogg
+ numberPart = file[3:].split(".")[0]
+ if numberPart.isdigit():
+ oggDict[int(file[3:].split(".")[0])] = file
+sortedOgg = oggDict.values()
+times = []
+for file in sortedOgg:
+ # soxi -D returns the duration in seconds of the audio file as a float
+ if sys.platform.startswith("win"):
+ # Unfortunately, there is a requirement that soxi (with an
implict .exe)
+ # be the command to check audio file duration in Win32
+ # but soxi is the name of the unix version of this utility
+ # So we need to delete the (unix) file called soxi so the command
line call
+ # to soxi will run soxi.exe
+ if os.path.isfile(savePath+os.sep+"soxi"):
+ os.remove(savePath+os.sep+"soxi")
+ command =
[savePath+os.sep+"soxi","-D",odpFileSubdirectory+os.sep+file]
+ elif sys.platform.startswith("darwin"):
+ if os.path.isfile(savePath+os.sep+"soxi"):
+ command =
[savePath+os.sep+"soxi","-D",odpFileSubdirectory+os.sep+file]
+ elif os.path.isfile(savePath+os.sep+"Contents/Resources/soxi"):
+ command =
[savePath+os.sep+"Contents/Resources/soxi","-D",odpFileSubdirectory+os.sep+file]
+ else:
+ command = ["soxi","-D",odpFileSubdirectory+os.sep+file]
+ else:
+ command = ["soxi","-D",odpFileSubdirectory+os.sep+file]
+ process = subprocess.Popen(command,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ universal_newlines=True)
+ output = process.communicate()
+ retcode = process.poll()
+ if retcode:
+ print "No time available"
+ times.append(float(output[0].strip()))
+
+# Create makeVid.bat in odpFileDirectory for Windows
+f = open(odpFileDirectory+os.sep+"makeVid.bat","w")
+os.chmod(odpFileDirectory+os.sep+"makeVid.bat",stat.S_IRWXU)
+
+if sys.platform.startswith("win"):
+ # find out if mklink is available
+ mklinkAvailable = False
+ if os.path.isfile("win32_mklink_test"):
+ subprocess.Popen(["del","win32_mklink_test"],shell=True)
+ os.chdir(odpFileDirectory)
+
subprocess.Popen(["mklink","/h","win32_mklink_test","convert.bat"],shell=True).wait()
+ if os.path.isfile("win32_mklink_test"):
+ mklinkAvailable = True
+ subprocess.Popen(["del","win32_mklink_test"],shell=True)
+
+ f.write("echo off\ncls\n")
+ f.write("if exist output.mp4 (del output.mp4)\n")
+ if os.path.isfile(savePath+os.sep+"MP4Box.exe"):
+ catCommand = '"'+savePath+os.sep+'MP4Box"'
+ else:
+ catCommand = "MP4Box"
+ for i, file in enumerate(sortedOgg):
+ stem, suffix = file.split(".")
+ # Add the slide video to the list of videos to be concatenated
+ catCommand += " -cat "+stem+".mp4"
+ tenthsOfSeconds = int(math.floor(times[i]*10))
+ # If we are on the last slide, add enough frames
+ # to give audio time to finish
+ if sortedOgg[i]==sortedOgg[-1]:
+ tenthsOfSeconds += 20
+ # Make a symlink to the slide image for each second the audio runs
+ # Only 999 symlinks are allowed per image, so, if there are more
+ # than this number, we need to also make additional copies of the
+ # slide image to link to
+ for j in range(tenthsOfSeconds):
+ if ((j > 0) and (j % 900 == 0)):
+ f.write("copy "+stem+'.jpg '+stem+str(j)+'.jpg\n')
+ extraStem = ""
+ for j in range(tenthsOfSeconds):
+ if ((j > 0) and (j % 900 == 0)):
+ extraStem = str(j)
+ if mklinkAvailable:
+ f.write("mklink
/h "+stem+'_'+str(j).zfill(5)+'.jpg '+stem+extraStem+'.jpg\n')
+ else:
+
f.write("copy "+stem+'.jpg '+stem+'_'+str(j).zfill(5)+'.jpg\n')
+ # Convert the images to a video of that slide with voice over
+ # NOTE: Little trick here -- Windows wants to substitute the batch
file name
+ # into %0 so we use %1 and pass %0 as the first parameter
+ f.write('"'+savePath+os.sep+'ffmpeg" -i '+stem+'.mp3 -r 10
-i "'+stem+'_%15d.jpg" -ab 64k '+stem+".mp4\n")
+ # Delete the symlinks
+ for j in range(tenthsOfSeconds):
+ f.write("del "+stem+'_'+str(j).zfill(5)+'.jpg\n')
+ # Delete any extra copies
+ for j in range(tenthsOfSeconds):
+ if ((j > 0) and (j % 900 == 0)):
+ f.write("del "+stem+str(j)+'.jpg\n')
+ # Add an output file name for the concatenation
+ catCommand += " output.mp4\n"
+ f.write(catCommand)
+ # Delete all the single slide videos
+ for file in sortedOgg:
+ stem, suffix = file.split(".")
+ f.write('del '+stem+'.mp4\n')
+ f.close()
+
+elif sys.platform.startswith("darwin"):
+ # For Mac OSX
+ # ffmpeg -i Slide1.mp3 -r 1 -i Slide1_%03d.png -ab 64k output.mp4
+ f.write("clear\n")
+ f.write("if [ -f output.mp4 ]\n")
+ f.write("then rm output.mp4\n")
+ f.write("fi\n")
+ if os.path.isfile(savePath+os.sep+"MP4Box"):
+ # for uncompiled run
+ catCommand = '"'+savePath+os.sep+'MP4Box"'
+ elif os.path.isfile(savePath+os.sep+"Contents/Resources/MP4Box"):
+ # for compiled run
+ catCommand = '"'+savePath+os.sep+'Contents/Resources/MP4Box"'
+ else:
+ # for when MP4Box is not distributed but is installed
+ catCommand = "MP4Box"
+ # save another copy for subsequent cat lines if more than 20 slides
+ catCommand2 = catCommand
+ tempFilesToDelete = []
+ for i, file in enumerate(sortedOgg):
+ stem, suffix = file.split(".")
+ # Add the slide video to the list of videos to be concatenated
+ catCommand += " -cat "+stem+".mp4"
+ if ((i>0) and (i % 18 == 0)):
+ tempFilesToDelete.append("temp"+ str(i) +".mp4")
+ # add a temp.mp4 for output and then input on next line
+ catCommand += " temp" + str(i) +".mp4\n"+catCommand2+" -cat
temp" + str(i) +".mp4"
+ tenthsOfSeconds = int(math.floor(times[i]*10))
+ # If we are on the last slide, add enough frames
+ # to give audio time to finish
+ if sortedOgg[i]==sortedOgg[-1]:
+ tenthsOfSeconds += 20
+ # Make a symlink to the slide image for each second the audio runs
+ for j in range(tenthsOfSeconds):
+ # ln -s Slide2.png Slide2_001.png
+ f.write("ln
-s "+stem+'.png '+stem+'_'+str(j).zfill(5)+'.png\n')
+ f.write('"'+savePath+os.sep+'ffmpeg" -i '+stem+'.mp3 -r 10
-i "'+stem+'_%05d.png" -ab 64k '+stem+".mp4\n")
+ # Delete the symlinks
+ for j in range(tenthsOfSeconds):
+ f.write("rm "+stem+'_'+str(j).zfill(5)+'.png\n')
+ # Add an output file name for the concatenation
+ catCommand += " output.mp4\n"
+ f.write(catCommand)
+ # Delete all the single slide videos
+ for file in sortedOgg:
+ stem, suffix = file.split(".")
+ f.write('rm '+stem+'.mp4\n')
+ for file in tempFilesToDelete:
+ f.write('rm '+file+"\n")
+ f.close()
+
+else:
+ # For Linux
+ # ffmpeg -i Slide1.mp3 -r 1 -i Slide1_%03d.png -ab 64k output.mp4
+ f.write("clear\n")
+ f.write("if [ -f output.mp4 ]\n")
+ f.write("then rm output.mp4\n")
+ f.write("fi\n")
+
+ # We need to do this:
+ # cat img0.avi img1.avi > output.avi
+ # mencoder output.avi -o final.avi -forceidx -ovc copy -oac copy
+ catCommand = "cat "
+ catCommand2= "cat "
+
+ # save another copy for subsequent cat lines if more than 20 slides
+ tempFilesToDelete = []
+ for i, file in enumerate(sortedOgg):
+ stem, suffix = file.split(".")
+ # Add the slide video to the list of videos to be concatenated
+ catCommand += " " + stem+".avi"
+ if ((i>0) and (i % 18 == 0)):
+ tempFilesToDelete.append("temp"+ str(i) +".avi")
+ # add a temp.mp4 for output and then input on next line
+ catCommand += " temp" + str(i) +".avi\n"+catCommand2+" temp" +
str(i) +".avi"
+ tenthsOfSeconds = int(math.floor(times[i]*10))
+ # If we are on the last slide, add enough frames
+ # to give audio time to finish
+ if sortedOgg[i]==sortedOgg[-1]:
+ tenthsOfSeconds += 20
+ # Make a symlink to the slide image for each second the audio runs
+ for j in range(tenthsOfSeconds):
+ # ln -s Slide2.png Slide2_001.png
+ f.write("ln
-s "+stem+'.png '+stem+'_'+str(j).zfill(5)+'.png\n')
+ f.write('ffmpeg -i '+stem+'.mp3 -r 10 -i "'+stem+'_%05d.png" -ab
64k '+stem+".avi\n")
+ # Delete the symlinks
+ for j in range(tenthsOfSeconds):
+ f.write("rm "+stem+'_'+str(j).zfill(5)+'.png\n')
+ # Add an output file name for the concatenation
+ catCommand += " > output.avi\n mencoder output.avi -o final.avi
-forceidx -ovc copy -oac copy\n"
+ f.write(catCommand)
+ # Delete all the single slide videos
+ for file in sortedOgg:
+ stem, suffix = file.split(".")
+ f.write('rm '+stem+'.avi\n')
+ for file in tempFilesToDelete:
+ f.write('rm '+file+"\n")
***The diff for this file has been truncated for email.***
=======================================
--- /dev/null
+++ /odp2wts/slidespeech.icns Mon Nov 28 00:38:44 2011
Binary file, no diff available.
=======================================
--- /dev/null
+++ /wikitospeech/SlideSpeech.packproj Mon Nov 28 00:38:44 2011
@@ -0,0 +1,565 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST
1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Hierarchy</key>
+ <dict>
+ <key>Attributes</key>
+ <dict>
+ <key>Documents</key>
+ <dict>
+ <key>Background Image</key>
+ <dict>
+ <key>IFPkgFlagBackgroundAlignment</key>
+ <integer>4</integer>
+ <key>IFPkgFlagBackgroundScaling</key>
+ <integer>1</integer>
+ <key>Mode</key>
+ <integer>0</integer>
+ <key>Path</key>
+ <string></string>
+ </dict>
+ <key>License</key>
+ <dict>
+ <key>International</key>
+ <dict>
+ <key>Mode</key>
+ <integer>0</integer>
+ <key>Path</key>
+ <string></string>
+ </dict>
+ </dict>
+ <key>ReadMe</key>
+ <dict>
+ <key>International</key>
+ <dict>
+ <key>Mode</key>
+ <integer>0</integer>
+ <key>Path</key>
+ <string></string>
+ </dict>
+ </dict>
+ <key>Welcome</key>
+ <dict>
+ <key>International</key>
+ <dict>
+ <key>Mode</key>
+ <integer>0</integer>
+ <key>Path</key>
+ <string></string>
+ </dict>
+ </dict>
+ </dict>
+ <key>Files</key>
+ <dict>
+ <key>Compress</key>
+ <true/>
+ <key>Hierarchy</key>
+ <dict>
+ <key>Children</key>
+ <array>
+ <dict>
+ <key>Children</key>
+ <array>
+ <dict>
+ <key>Children</key>
+ <array/>
+ <key>GID</key>
+ <integer>80</integer>
+ <key>Path</key>
+ <string>../SlideSpeech from ODP.app</string>
+ <key>Path Type</key>
+ <integer>2</integer>
+ <key>Privileges</key>
+ <integer>493</integer>
+ <key>Type</key>
+ <integer>3</integer>
+ <key>UID</key>
+ <integer>0</integer>
+ </dict>
+ <dict>
+ <key>Children</key>
+ <array/>
+ <key>GID</key>
+ <integer>80</integer>
+ <key>Path</key>
+ <string>../SlideSpeech.app</string>
+ <key>Path Type</key>
+ <integer>2</integer>
+ <key>Privileges</key>
+ <integer>493</integer>
+ <key>Type</key>
+ <integer>3</integer>
+ <key>UID</key>
+ <integer>0</integer>
+ </dict>
+ <dict>
+ <key>Children</key>
+ <array/>
+ <key>GID</key>
+ <integer>80</integer>
+ <key>Path</key>
+ <string>Utilities</string>
+ <key>Path Type</key>
+ <integer>1</integer>
+ <key>Privileges</key>
+ <integer>509</integer>
+ <key>Type</key>
+ <integer>1</integer>
+ <key>UID</key>
+ <integer>0</integer>
+ </dict>
+ </array>
+ <key>GID</key>
+ <integer>80</integer>
+ <key>Path</key>
+ <string>Applications</string>
+ <key>Path Type</key>
+ <integer>1</integer>
+ <key>Privileges</key>
+ <integer>509</integer>
+ <key>Type</key>
+ <integer>1</integer>
+ <key>UID</key>
+ <integer>0</integer>
+ </dict>
+ <dict>
+ <key>Children</key>
+ <array>
+ <dict>
+ <key>Children</key>
+ <array/>
+ <key>GID</key>
+ <integer>80</integer>
+ <key>Path</key>
+ <string>Application Support</string>
+ <key>Path Type</key>
+ <integer>1</integer>
+ <key>Privileges</key>
+ <integer>509</integer>
+ <key>Type</key>
+ <integer>1</integer>
+ <key>UID</key>
+ <integer>0</integer>
+ </dict>
+ <dict>
+ <key>Children</key>
+ <array/>
+ <key>GID</key>
+ <integer>80</integer>
+ <key>Path</key>
+ <string>Documentation</string>
+ <key>Path Type</key>
+ <integer>1</integer>
+ <key>Privileges</key>
+ <integer>509</integer>
+ <key>Type</key>
+ <integer>1</integer>
+ <key>UID</key>
+ <integer>0</integer>
+ </dict>
+ <dict>
+ <key>Children</key>
+ <array/>
+ <key>GID</key>
+ <integer>80</integer>
+ <key>Path</key>
+ <string>Filesystems</string>
+ <key>Path Type</key>
+ <integer>1</integer>
+ <key>Privileges</key>
+ <integer>509</integer>
+ <key>Type</key>
+ <integer>1</integer>
+ <key>UID</key>
+ <integer>0</integer>
+ </dict>
+ <dict>
+ <key>Children</key>
+ <array/>
+ <key>GID</key>
+ <integer>80</integer>
+ <key>Path</key>
+ <string>Frameworks</string>
+ <key>Path Type</key>
+ <integer>1</integer>
+ <key>Privileges</key>
+ <integer>509</integer>
+ <key>Type</key>
+ <integer>1</integer>
+ <key>UID</key>
+ <integer>0</integer>
+ </dict>
+ <dict>
+ <key>Children</key>
+ <array/>
+ <key>GID</key>
+ <integer>80</integer>
+ <key>Path</key>
+ <string>Internet Plug-Ins</string>
+ <key>Path Type</key>
+ <integer>1</integer>
+ <key>Privileges</key>
+ <integer>509</integer>
+ <key>Type</key>
+ <integer>1</integer>
+ <key>UID</key>
+ <integer>0</integer>
+ </dict>
+ <dict>
+ <key>Children</key>
+ <array/>
+ <key>GID</key>
+ <integer>80</integer>
+ <key>Path</key>
+ <string>PreferencePanes</string>
+ <key>Path Type</key>
+ <integer>1</integer>
+ <key>Privileges</key>
+ <integer>509</integer>
+ <key>Type</key>
+ <integer>1</integer>
+ <key>UID</key>
+ <integer>0</integer>
+ </dict>
+ <dict>
+ <key>Children</key>
+ <array/>
+ <key>GID</key>
+ <integer>80</integer>
+ <key>Path</key>
+ <string>Preferences</string>
+ <key>Path Type</key>
+ <integer>1</integer>
+ <key>Privileges</key>
+ <integer>509</integer>
+ <key>Type</key>
+ <integer>1</integer>
+ <key>UID</key>
+ <integer>0</integer>
+ </dict>
+ <dict>
+ <key>Children</key>
+ <array/>
+ <key>GID</key>
+ <integer>80</integer>
+ <key>Path</key>
+ <string>Printers</string>
+ <key>Path Type</key>
+ <integer>1</integer>
+ <key>Privileges</key>
+ <integer>509</integer>
+ <key>Type</key>
+ <integer>1</integer>
+ <key>UID</key>
+ <integer>0</integer>
+ </dict>
+ <dict>
+ <key>Children</key>
+ <array/>
+ <key>GID</key>
+ <integer>80</integer>
+ <key>Path</key>
+ <string>QuickTime</string>
+ <key>Path Type</key>
+ <integer>1</integer>
+ <key>Privileges</key>
+ <integer>509</integer>
+ <key>Type</key>
+ <integer>1</integer>
+ <key>UID</key>
+ <integer>0</integer>
+ </dict>
+ <dict>
+ <key>Children</key>
+ <array/>
+ <key>GID</key>
+ <integer>80</integer>
+ <key>Path</key>
+ <string>Scripts</string>
+ <key>Path Type</key>
+ <integer>1</integer>
+ <key>Privileges</key>
+ <integer>509</integer>
+ <key>Type</key>
+ <integer>1</integer>
+ <key>UID</key>
+ <integer>0</integer>
+ </dict>
+ </array>
+ <key>GID</key>
+ <integer>80</integer>
+ <key>Path</key>
+ <string>Library</string>
+ <key>Path Type</key>
+ <integer>1</integer>
+ <key>Privileges</key>
+ <integer>1021</integer>
+ <key>Type</key>
+ <integer>1</integer>
+ <key>UID</key>
+ <integer>0</integer>
+ </dict>
+ <dict>
+ <key>Children</key>
+ <array>
+ <dict>
+ <key>Children</key>
+ <array>
+ <dict>
+ <key>Children</key>
+ <array/>
+ <key>GID</key>
+ <integer>0</integer>
+ <key>Path</key>
+ <string>Extensions</string>
+ <key>Path Type</key>
+ <integer>1</integer>
+ <key>Privileges</key>
+ <integer>493</integer>
+ <key>Type</key>
+ <integer>1</integer>
+ <key>UID</key>
+ <integer>0</integer>
+ </dict>
+ </array>
+ <key>GID</key>
+ <integer>0</integer>
+ <key>Path</key>
+ <string>Library</string>
+ <key>Path Type</key>
+ <integer>1</integer>
+ <key>Privileges</key>
+ <integer>493</integer>
+ <key>Type</key>
+ <integer>1</integer>
+ <key>UID</key>
+ <integer>0</integer>
+ </dict>
+ </array>
+ <key>GID</key>
+ <integer>0</integer>
+ <key>Path</key>
+ <string>System</string>
+ <key>Path Type</key>
+ <integer>1</integer>
+ <key>Privileges</key>
+ <integer>493</integer>
+ <key>Type</key>
+ <integer>1</integer>
+ <key>UID</key>
+ <integer>0</integer>
+ </dict>
+ </array>
+ <key>GID</key>
+ <integer>80</integer>
+ <key>Path</key>
+ <string>/</string>
+ <key>Path Type</key>
+ <integer>1</integer>
+ <key>Privileges</key>
+ <integer>1021</integer>
+ <key>Type</key>
+ <integer>1</integer>
+ <key>UID</key>
+ <integer>0</integer>
+ </dict>
+ <key>IFPkgFlagDefaultLocation</key>
+ <string>/</string>
+ <key>Imported Package</key>
+ <false/>
+ <key>Package Path</key>
+ <string></string>
+ <key>Split Forks</key>
+ <true/>
+ </dict>
+ <key>Plugins</key>
+ <dict>
+ <key>PluginsList</key>
+ <array>
+ <dict>
+ <key>Path</key>
+ <string>Introduction</string>
+ <key>Type</key>
+ <integer>0</integer>
+ </dict>
+ <dict>
+ <key>Path</key>
+ <string>ReadMe</string>
+ <key>Type</key>
+ <integer>0</integer>
+ </dict>
+ <dict>
+ <key>Path</key>
+ <string>License</string>
+ <key>Type</key>
+ <integer>0</integer>
+ </dict>
+ <dict>
+ <key>Path</key>
+ <string>Target</string>
+ <key>Type</key>
+ <integer>0</integer>
+ </dict>
+ <dict>
+ <key>Path</key>
+ <string>PackageSelection</string>
+ <key>Type</key>
+ <integer>0</integer>
+ </dict>
+ <dict>
+ <key>Path</key>
+ <string>Install</string>
+ <key>Type</key>
+ <integer>0</integer>
+ </dict>
+ <dict>
+ <key>Path</key>
+ <string>FinishUp</string>
+ <key>Type</key>
+ <integer>0</integer>
+ </dict>
+ </array>
+ </dict>
+ <key>Scripts</key>
+ <dict>
+ <key>Additional Resources</key>
+ <dict>
+ <key>International</key>
+ <array/>
+ </dict>
+ <key>Installation Scripts</key>
+ <dict>
+ <key>IFInstallationScriptsPostflight</key>
+ <dict>
+ <key>Path</key>
+ <string></string>
+ <key>Status</key>
+ <false/>
+ </dict>
+ <key>IFInstallationScriptsPostinstall</key>
+ <dict>
+ <key>Path</key>
+ <string></string>
+ <key>Status</key>
+ <false/>
+ </dict>
+ <key>IFInstallationScriptsPostupgrade</key>
+ <dict>
+ <key>Path</key>
+ <string></string>
+ <key>Status</key>
+ <false/>
+ </dict>
+ <key>IFInstallationScriptsPreflight</key>
+ <dict>
+ <key>Path</key>
+ <string></string>
+ <key>Status</key>
+ <false/>
+ </dict>
+ <key>IFInstallationScriptsPreinstall</key>
+ <dict>
+ <key>Path</key>
+ <string></string>
+ <key>Status</key>
+ <false/>
+ </dict>
+ <key>IFInstallationScriptsPreupgrade</key>
+ <dict>
+ <key>Path</key>
+ <string></string>
+ <key>Status</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>Requirements</key>
+ <array/>
+ </dict>
+ <key>Settings</key>
+ <dict>
+ <key>Description</key>
+ <dict>
+ <key>International</key>
+ <dict>
+ <key>IFPkgDescriptionDeleteWarning</key>
+ <string></string>
+ <key>IFPkgDescriptionDescription</key>
+ <string></string>
+ <key>IFPkgDescriptionTitle</key>
+ <string>SlideSpeech</string>
+ <key>IFPkgDescriptionVersion</key>
+ <string>1.0</string>
+ </dict>
+ </dict>
+ <key>Display Information</key>
+ <dict>
+ <key>CFBundleGetInfoString</key>
+ <string>SlideSpeech 1.0 Copyrights © 2011 My Great Company</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.mygreatcompany.pkg.SlideSpeech</string>
+ <key>CFBundleName</key>
+ <string>SlideSpeech</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ </dict>
+ <key>Options</key>
+ <dict>
+ <key>IFPkgFlagAllowBackRev</key>
+ <false/>
+ <key>IFPkgFlagAuthorizationAction</key>
+ <integer>0</integer>
+ <key>IFPkgFlagFollowLinks</key>
+ <false/>
+ <key>IFPkgFlagInstallFat</key>
+ <false/>
+ <key>IFPkgFlagIsRequired</key>
+ <false/>
+ <key>IFPkgFlagOverwritePermissions</key>
+ <false/>
+ <key>IFPkgFlagRelocatable</key>
+ <false/>
+ <key>IFPkgFlagRestartAction</key>
+ <integer>0</integer>
+ <key>IFPkgFlagRootVolumeOnly</key>
+ <false/>
+ <key>IFPkgFlagUpdateInstalledLanguages</key>
+ <false/>
+ </dict>
+ <key>Version</key>
+ <dict>
+ <key>IFMajorVersion</key>
+ <integer>1</integer>
+ <key>IFMinorVersion</key>
+ <integer>0</integer>
+ </dict>
+ </dict>
+ </dict>
+ <key>IFPkgFlagPackageSelection</key>
+ <integer>0</integer>
+ <key>Name</key>
+ <string>SlideSpeech</string>
+ <key>Status</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <integer>1</integer>
+ </dict>
+ <key>Name</key>
+ <string>Project</string>
+ <key>Settings</key>
+ <dict>
+ <key>10.1 Compatibility</key>
+ <true/>
+ <key>Build Path</key>
+ <string>build</string>
+ <key>Build Path Type</key>
+ <integer>2</integer>
+ <key>Comment</key>
+ <string></string>
+ <key>Remove .DS_Store</key>
+ <true/>
+ <key>Remove .pbdevelopment</key>
+ <true/>
+ </dict>
+</dict>
+</plist>
=======================================
--- /dev/null
+++ /wikitospeech/SlideSpeech.py Mon Nov 28 00:38:44 2011
@@ -0,0 +1,218 @@
+# -*- coding: utf-8 -*-
+"""
+SlideSpeech.py
+
+Use browser and local text-to-speech engine
+to display and play Wiki-to-Speech scripts
+
+Copyright (c) 2011 John Graves
+
+MIT License: see LICENSE.txt
+
+20110818 Changes to enable reading
http://aucklandunitarian.pagekite.me/Test20110818b
+20110819 Tested on Mac:
+http://aucklandunitarian.pagekite.me/Test20110814
+http://aucklandunitarian.pagekite.me/Test20110819 which calls another
script
+http://aucklandunitarian.pagekite.me/Test20110819b which has
[path=pathToImageFiles] and
+ combined image with question
+20110822 Added version number to input form
+20110824 Pass __version__ to input form
+ Ensure static directory exists
+20110825 Add __version__ to title
+20110909 Added question number to showQuestion (so going back should work)
+20110913 Make symbolic links from static directory to location of
script.txt png images
+20111126 Titanpad in addition to iEtherpad. Jump to 0.1.26 to sync with
odp2wts (SlideSpeech)
+20111128 Changed name to SlideSpeech. Revised voice.py to work under Linux.
+"""
+import cherrypy
+import os.path
+import Queue
+import threading
+import webbrowser
+
+import forms
+import objects
+import scriptParser
+import sys
+import voice
+
+__version__ = "0.1.28"
+
+if not os.path.exists('static'):
+ os.makedirs('static')
+
+class WelcomePage:
+
+ def index(self):
+ # Ask for the script name.
+ return forms.scriptInputFormWithErrorMessage(__version__,"")
+ index.exposed = True
+ webbrowser.open_new_tab('http://localhost:8080')
+
+ def SlideSpeech_Exit_Complete(self):
+ webbrowser.open_new_tab('http://slidespeech.org')
+ sys.exit()
+ SlideSpeech_Exit_Complete.exposed = True
+
+ def getScriptName(self, name = None):
+ #name = "http://dl.dropbox.com/u/12838403/20110428a.txt"
+ if name:
+ if name=="exit":
+ sys.exit()
+ seq.sequence = scriptParser.parseScript(name)
+ if seq.sequence:
+ seq.onQuestion = 0
+ return speakAndReturnForm()
+ else:
+ return forms.scriptInputFormWithErrorMessage( \
+ __version__,
+ "<i>Could not open "+name+"</i>")
+ else:
+ # No name was specified
+ return forms.scriptInputFormWithErrorMessage( \
+ __version__,
+ "<i>Please enter a file name or link on the
web.</i>")
+ getScriptName.exposed = True
+
+ def nextSlide(self):
+ clearQueue()
+ seq.onQuestion += 1
+ if seq.onQuestion > len(seq.sequence) - 1:
+ return forms.scriptInputFormWithErrorMessage(__version__,"")
+ else:
+ return speakAndReturnForm()
+ nextSlide.exposed = True
+
+ def nextSlideFromAnswer0(self, q):
+ return respondToAnswer(0, q)
+ nextSlideFromAnswer0.exposed = True
+
+ def nextSlideFromAnswer1(self, q):
+ return respondToAnswer(1, q)
+ nextSlideFromAnswer1.exposed = True
+
+ def nextSlideFromAnswer2(self, q):
+ return respondToAnswer(2, q)
+ nextSlideFromAnswer2.exposed = True
+
+ def nextSlideFromAnswer3(self, q):
+ return respondToAnswer(3, q)
+ nextSlideFromAnswer3.exposed = True
+
+ def nextSlideFromAnswer4(self, q):
+ return respondToAnswer(4, q)
+ nextSlideFromAnswer4.exposed = True
+
+ def nextSlideFromAnswer5(self, q):
+ return respondToAnswer(5, q)
+ nextSlideFromAnswer5.exposed = True
+
+ def nextSlideFromAnswer6(self, q):
+ return respondToAnswer(6, q)
+ nextSlideFromAnswer6.exposed = True
+
+seq = objects.Sequence()
+voiceInstance = voice.Voice()
+
+def speakAndReturnForm():
+ # Check for visited answers. If found, do not re-read question
+ noVisitedAnswers = True
+ for a in seq.sequence[seq.onQuestion].answers:
+ if a.visited:
+ noVisitedAnswers = False
+ if noVisitedAnswers:
+ speakList(seq.sequence[seq.onQuestion].questionTexts)
+ for a in seq.sequence[seq.onQuestion].answers:
+ speakList([a.answerText])
+ linkToShow = seq.sequence[seq.onQuestion].linkToShow
+
+ if linkToShow.lower().endswith(".pdf"):
+ return forms.showPDFSlide(seq.sequence[seq.onQuestion].linkToShow)
+
+ elif linkToShow.lower().endswith(".jpg") or
linkToShow.lower().endswith(".png"):
+ if linkToShow.startswith("Slide") or linkToShow.startswith("img")
or \
+ linkToShow.find("\Slide")!=-1 or linkToShow.find("/img")!=-1:
+ if len(seq.sequence[seq.onQuestion].pathToImageFiles)>0:
+ linkToShow = seq.sequence[seq.onQuestion].pathToImageFiles
+ linkToShow
+ else:
+ linkToShow = "static/" + linkToShow
+ if len(seq.sequence[seq.onQuestion].answers)>0:
+ return forms.showJPGSlideWithQuestion(linkToShow, \
+ seq.sequence[seq.onQuestion] )
+ else:
+ return forms.showJPGSlide(linkToShow)
+
+ elif linkToShow.lower().endswith(".htm"):
+ return forms.showDHTML()
+ elif linkToShow.lower().endswith(".swf"):
+ return forms.showSWF()
+ elif len(linkToShow)>0:
+ #return forms.showWebsite(seq.sequence[seq.onQuestion])
+ return forms.showQuestionAndWebsite(seq.sequence[seq.onQuestion],
seq.onQuestion)
+ else: # no match for linkToShow
+ return forms.showQuestion(seq.sequence[seq.onQuestion],
seq.onQuestion)
+
+def respondToAnswer(n, q):
+ clearQueue()
+ response = ""
+ seq.onQuestion = int(q)
+ if n<len(seq.sequence[seq.onQuestion].answers):
+ # mark answer as visited
+ seq.sequence[seq.onQuestion].answers[n].visited = True
+ # say what there is to say
+ response = seq.sequence[seq.onQuestion].answers[n].responseText
+ if response != "":
+ speakList([response])
+ # follow any response side link
+ responseSideLink =
seq.sequence[seq.onQuestion].answers[n].responseSideLink
+ if len(responseSideLink)>0:
+ seq.sequence = scriptParser.parseScript(responseSideLink)
+ if seq.sequence:
+ seq.onQuestion = 0
+ return speakAndReturnForm()
+ #TODO error recovery
+ # move to whichever question comes next
+ if seq.sequence[seq.onQuestion].answers[n].action != 0:
+ nextQ = seq.onQuestion +
seq.sequence[seq.onQuestion].answers[n].action
+ if 0 <= nextQ <= len(seq.sequence):
+ seq.onQuestion = nextQ
+ if seq.onQuestion<len(seq.sequence):
+ return speakAndReturnForm()
+ else:
+ # past end of sequence
+ speakList(["You have reached the end. Please select another
script."])
+ return forms.scriptInputFormWithErrorMessage(__version__,"")
+
+def clearQueue():
+ while not q.empty():
+ q.get()
+
+def worker():
+ while True:
+ text = q.get()
+ voiceInstance.speak(text, "")
+ q.task_done()
+
+q = Queue.Queue()
+t = threading.Thread(target=worker)
+t.daemon = True
+t.start()
+
+def speakList(textList):
+ for item in textList:
+ q.put(item)
+ #q.join() # block until all tasks are done
+
+if __name__ == '__main__':
+ # CherryPy always starts with app.root when trying to map request URIs
+ # to objects, so we need to mount a request handler root. A request
+ # to '/' will be mapped to HelloWorld().index().
+ cherrypy.config.update({'server.socket_host': '127.0.0.1',
+ 'server.socket_port': 8080,
+ 'server.thread_pool': 10,
+ })
+ config = {'/': {'tools.staticdir.root': os.path.abspath(os.curdir)},
+ '/static':{'tools.staticdir.on':True,
+ 'tools.staticdir.dir':"static"}}
+ cherrypy.quickstart(WelcomePage(), config=config)
+
=======================================
--- /dev/null
+++ /wikitospeech/blurb.txt Mon Nov 28 00:38:44 2011
@@ -0,0 +1,7 @@
+SlideSpeech and utility software SlideSpeech from ODP for Mac
+
+Download the .dmg and open the SlideSpeech package to install two programs:
+
+SlideSpeech - play wiki-based scripts using the voice selected in Mac
System Preferences. Copy/paste the address of a script into the input area.
For example: http://code.google.com/p/wiki-to-speech/wiki/HiKim
+
+SlideSpeech from ODP - utility to convert an open document presentation
(ODP) file into a script, a zip file, a slideshow with voice over and a
video using the voice selected in Mac System Preferences.
=======================================
--- /dev/null
+++ /wikitospeech/slidespeech.icns Mon Nov 28 00:38:44 2011
Binary file, no diff available.
=======================================
--- /dev/null
+++ /wikitospeech/ss2app.py Mon Nov 28 00:38:44 2011
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#-------------------------------------------------------------------------------
+# Name: ss2app.py
+# Purpose: Compile SlideSpeech.py into Mac OSX Application
+#
+# Author: John Graves
+#
+# Created: 24 August 2011
+# Modified: 28 November 2011
+# Copyright: (c) John Graves 2011
+# License: MIT License
+#-------------------------------------------------------------------------------
+
+from setuptools import setup
+import operator
+import os
+import shutil
+import sys
+
+
+class BuildApp:
+ def __init__(self):
+ self.APP = ['SlideSpeech.py']
+ self.DATA_FILES =
['CHANGES.txt','ethics_notice.txt','README.txt','LICENSE.txt']
+ self.OPTIONS = {'argv_emulation':
True, 'iconfile': 'slidespeech.icns',}
+ #Dist directory
+ self.dist_dir ='dist'
+
+ def run(self):
+ if os.path.isdir(self.dist_dir): #Erase previous destination dir
+ shutil.rmtree(self.dist_dir)
+
+ setup(
+ app=self.APP,
+ data_files=self.DATA_FILES,
+ options={'py2app': self.OPTIONS},
+ setup_requires=['py2app'],
+ )
+
+ if os.path.isdir('build'): #Clean up build dir
+ shutil.rmtree('build')
+
+if __name__ == '__main__':
+ if operator.lt(len(sys.argv), 2):
+ sys.argv.append('py2app')
+ BuildApp().run() #Run generation
+ raw_input("Press any key to continue") #Pause to let user see that
things ends
=======================================
--- /wikitospeech/BeautifulSoup.pyc Sun Nov 27 16:43:36 2011
+++ /dev/null
Binary file, no diff available.
=======================================
--- /wikitospeech/objects.pyc Sun Nov 27 16:43:36 2011
+++ /dev/null
Binary file, no diff available.
=======================================
--- /wikitospeech/scriptParser.pyc Sun Nov 27 16:43:36 2011
+++ /dev/null
@@ -1,152 +0,0 @@
-Ñò
-ÜÖÒNc @ sØ d d k l Z d d k Z d d k Z d d k Z d d k Z d d k Z d d k Z d d k Z d d k l
Z
l
- Z
- d „ Z d „ Z d „ Z
- d „ Z d „ Z d „ Z d
- „ Z e d j o e d ƒ n d S(
- iÿÿÿÿ( t
- BeautifulSoupN( t gmtimet strftimec C s
- d „ } t i d | | ƒ S( Nc
- S s¹ | i d ƒ } | d d j oa yG | d d j o t t |
d d !d ƒ ƒ S t t | d d !ƒ ƒ SWqµ t j
- o qµ Xn5 y
- t t i | d d ! ƒ } Wn t j
- o n X| S(
Ni i s &#i s &#xiÿÿÿÿi i ( t groupt unichrt intt
- ValueErrort htmlentitydefst name2codepointt KeyError( t mt text( ( sA /home/stefan/software/open-allure-ds/wikitospeech/scriptParser.pyt fixup s

- s &#?\w+;( t ret sub( R R ( ( sA /home/stefan/software/open-allure-ds/wikitospeech/scriptParser.pyt unescape s
c C s– |
i d ƒ o\ | i d ƒ d j o t |
ƒ } q’ | i d ƒ d j o t | ƒ } q’ t | ƒ } n' |
i d ƒ o t | ƒ } n d } |
S( Nt httpt etherpadi t titanpads .txt( t
- startswitht findt
- parseEtherpadt
parseHtmlt endswitht parseTxtFilet None( t namet sequence( ( sA /home/stefan/software/open-allure-ds/wikitospeech/scriptParser.pyt parseScript+ s c
-
- C s( y t i d } Wn
- d } n X| d j o& h d d 6} t i | d | ƒ } n t i |
d h ƒ } t | i ƒ ƒ } | i d h d d 6ƒ } d } y t |
ƒ } Wn t j
- o d S Xt
- | ƒ d j o@ | | i d
- ƒ d | i d ƒ d
- !} t | ƒ i d ƒ } n d St
- | ƒ } | S( Nt
- HTTP_PROXYt s http://cache.aut.ac.nz:3128R t proxiest divt postbodyt classi u "initialAttributedText":{"text"i! u ,"attribs":i s \n( t ost environt urllibt urlopenR t readR t strt UnicodeDecodeErrorR t lenR t splitt
parseText(
- R t proxyR t urlOpent soupR! t
- soupStringt cleanUnicodeTextStrR R ( ( sA /home/stefan/software/open-allure-ds/wikitospeech/scriptParser.pyR E s,
-
-
- c C sÎ y t i d } Wn
- d } n X| d j o& h d d 6} t i | d | ƒ } n t i |
d h ƒ } | i d ƒ o | i ƒ i d ƒ } n‘ t | i ƒ ƒ } |
i } | d j od t
- | ƒ i d d ƒ } | i d d ƒ } t | ƒ } t d i
- | i i d
- t ƒ ƒ ƒ i ƒ } n d St d d ƒ } | i d
- t d t ƒ ƒ d ƒ | i t
- t | ƒ ƒ d ƒ | i t
- t | ƒ ƒ d ƒ x% | D]
- } | i | i ƒ d ƒ q“ W| i ƒ t | ƒ }
- |
- S( NR
- R
- s http://cache.aut.ac.nz:3128R R s .txts
-s <br />
-s <br />R s debug.txtt ws test run at
s %d %b %Y %H:%M( R# R$ R% R& R R' R+ R t preR R( t replaceR t joint findAllt Truet
- splitlinest opent writeR R t typeR* t stript closeR, ( R R- R R. t urlTextR/ t
taggedPret taggedPreStrt ft lR ( ( sA /home/stefan/software/open-allure-ds/wikitospeech/scriptParser.pyR c s8
-
-
-
- . !
-
-
- c
- C sd y t | ƒ } Wn d SX|
i ƒ } t i i d ƒ p g } | D]' } | i d ƒ o | |
i ƒ qE qE ~ } t i i
- | ƒ \ } } t i ƒ } t i d ƒ t i
- d ƒ } g }
- | D]! } | i d ƒ o |
- | qÀ qÀ ~
- } xV | D]N }
- |
- | j o t i d |
- g ƒ n t i d d | t i |
- |
- g ƒ qñ Wt i | ƒ n t | ƒ } | S( Nt wins .png
-t statict .s .pngt rmt lns -s( R9 R t
readlinest syst platformR R R< R# t pathR+ t getcwdt chdirt listdirt
- subprocesst Popent sepR, ( R RA R t _[1]t itemt pngst
scriptDirt
- scriptFilet savePatht
staticDirt _[2]t filet pngsInStatict pngR ( ( sA /home/stefan/software/open-allure-ds/wikitospeech/scriptParser.pyR ˆ s( ;
- 5
- ( c C s | d i ƒ i d ƒ p | d i d ƒ o
- t } n t } t i ƒ } g | _ d } t i ƒ } t i ƒ } x¨ |
D]  } | i
- ƒ } | i d ƒ p~ | i d ƒ og | i d ƒ oV t |
i ƒ d j o) | i i
- | ƒ t i ƒ } | | _ n | i d ƒ } | d j oÞ | d | !i
- ƒ i ƒ } | | d | i d
- ƒ !i
- ƒ i ƒ } | d j o5 | d j o
- t } n | d
- j o
- t } qx qx q
- | d j oL | i t i ƒ p | i d ƒ o | t i 7} n |
} | | _ qx q
- q | d | i d
- ƒ !| _ q | i
- ƒ } | t j o× t | ƒ d j oÀ | i d ƒ pP | i d ƒ p@ |
i d ƒ p0 | i d ƒ p | i d ƒ p | i d ƒ oL t |
i ƒ d j o) | i i
- | ƒ t i ƒ } | | _ n | | _ qý | i i
- | ƒ q q t | ƒ d j oÁ | i d ƒ p0 | i d ƒ p |
i d ƒ p | i d ƒ oL t | i ƒ d j o) | i i
- | ƒ t i ƒ } | | _ n | | _ q | i d ƒ }
- d |
- j o | i i
- | ƒ q | d |
- !i
- ƒ | _ | |
- d i
- ƒ } t | ƒ d j o‹ x4 | i d ƒ o# | i d 7 _ | d i
- ƒ } q W| i d ƒ o7 | i d
- ƒ } | d | !| _ | | d i
- ƒ } n | | _ n | i d j o
- d | _ n | i i
- | ƒ t i ƒ } q t | i ƒ d j o) | i i
- | ƒ t i ƒ } | | _ q qx qx Wt | i ƒ d j o | i i
- | ƒ n g }
- | i D] } |
- | i i ƒ qT ~
- } x t | i ƒ D]p \ } } xa | i D]V } | i d j o@ |
i i ƒ | j o& | i | i ƒ | | _ d | _ qï q™ q™ Wqƒ Wt |
| ƒ | i S( Ni s .pngs [path=R
- t #t [t ;t =i t ]t
questionst ont offRK s \R s .htmls .jpgs .JPGs .PNGiÿÿÿÿs [next](
- t lowerR R t FalseR7 t objectst SequenceR t Questiont AnswerR< R* t
- questionTextst appendt pathToImageFilesR R# RQ t tagt
- linkToShowt
- answerTextt actiont responseSideLinkt responseTextt answerst
enumeratet indext dumpSequence( R t questionModet seqRm t questiont answert linet equalsAtt
- parameterNamet parameterValuet semicolonAtt responseSidet rightbracketAtRR t tagst qnum( ( sA /home/stefan/software/open-allure-ds/wikitospeech/scriptParser.pyR, £ s¾ .
- !
-
- &
-
-
-
-
-
- %
-
-
-
-
-
-
-
-
- -
-
- c C sª t oŸ t d d ƒ } | i d t d t ƒ ƒ ƒ |
i d t | ƒ ƒ xK t | i ƒ D]: \ } } | i d t |
ƒ d d ƒ | i d | i ƒ | i d
- | i ƒ | i d | i
- ƒ x" | i D] } | i d | ƒ qË Wx« t | i ƒ D]š \ } } |
i d
- t | ƒ d | i
- ƒ | i d t | ƒ d | i ƒ | i d t | ƒ d | i ƒ |
i d t | ƒ d t | i ƒ ƒ qö WqZ W| i ƒ n d S( Ns
- debug2.txtR2 s test run at s %d %b %Y %H:%Ms
-questionMode is s
-
-Question t -i
- s
- tag: s
- linkToShow: s
- pathToImageFiles: s
- questionText: s
- answerTexts : s
- responseSideLinks
- responseTexts
-
action( R7 R9 R: R R R( Ru R Rn Ro Rm Rk Rt Rp Rr Rs Rq R= ( Ry Rx RA t it qRB t jt a( ( sA /home/stefan/software/open-allure-ds/wikitospeech/scriptParser.pyRw
s(
-
- " " " 0 t __main__s
- 20110819b.txt( R R Rg R# R
- RO RI R% t timeR R R R
- R R R R, Rw t __name__( ( ( sA /home/stefan/software/open-allure-ds/wikitospeech/scriptParser.pyt <module> s"


- % }
-
=======================================
--- /wikitospeech/voice.pyc Sun Nov 27 16:43:36 2011
+++ /dev/null
@@ -1,43 +0,0 @@
-Ñò
-ÚØÒNc @ s{ d Z d d k Z d d k Z d d k Z d d k l Z l Z d e f d „ ƒ YZ d „ Z
e
- d j o e ƒ n d S( sŒ
-voice.py
-a component of openallure.py
-
-Function for rendering text-to-speech
-
-Copyright (c) 2011 John Graves
-
-MIT License: see LICENSE.txt
-iÿÿÿÿN( t gmtimet strftimet Voicec B s
e Z d Z d „ Z d „ Z RS( s Text-to-speech
classc C s
- d | _ d S( Ni ( t
- pid_status( t self( ( s: /home/stefan/software/open-allure-ds/wikitospeech/voice.pyt __init__ s c C s® |
i ƒ } |
i d d ƒ } t | ƒ d j o d S |
d j o d S t i d j o” d } d | d | d
- } | i d j o t i | d t ƒ i | _ qª y t
- i | i d ƒ d | _ Wn n Xt i | d t ƒ i | _ n¿ t i d
- j oI y t i d | g d t ƒ } Wn t j
- o d GHn X| i
- ƒ d S t d d ƒ } | i d t d t ƒ ƒ d | ƒ |
i ƒ t i d d
- | d
- g ƒ } | i
- ƒ d S( sÿ Say or print phrase using text-to-speech engine or
stdout.
-
- An empty phrase returns without calling the speech engine.
-
- espeak (engine) could take an optional Voice:language parameter
-
- say (engine) could take a systemVoice
- s http://www.t i Ns [next]t darwint Alexs say
-v s "t "t shelli t win32s
- SayStatic s Call to SayStatic faileds
- debug3.txtt ws test run at s %d %b %Y %H:%Ms
-t espeak( t stript replacet lent syst platformR t
- subprocesst Popent Truet pidt ost waitpidt OSErrort waitt opent writeR R t close( R t phraset systemVoicet commandLinet proct f( ( s: /home/stefan/software/open-allure-ds/wikitospeech/voice.pyt speak s8

-
-
- %
-
- ( t __name__t
- __module__t __doc__R R# ( ( ( s: /home/stefan/software/open-allure-ds/wikitospeech/voice.pyR s
c C s
- t ƒ } | i d d ƒ d S( s9
- Create a Voice instance and check that it works
- s This is a
testR N( R R# ( t voice( ( s: /home/stefan/software/open-allure-ds/wikitospeech/voice.pyt
testVoiceG s
t __main__( R& R R R t timeR R t objectR R( R$ ( ( ( s: /home/stefan/software/open-allure-ds/wikitospeech/voice.pyt <module> s 5

-
=======================================
--- /odp2wts/CHANGES.txt Sun Sep 4 19:39:35 2011
+++ /odp2wts/CHANGES.txt Mon Nov 28 00:38:44 2011
@@ -1,4 +1,5 @@
Change history::
+ v0.1.28 28 November 2011 Mac/Windows/Linux all produce video with
appropriate dependencies installed
v0.1.15 5 September 2011 More unicode character fixes in joinContents
v0.1.12 1 September 2011 Direct to video output for Windows only
v0.1.11 31 August 2011 Continue link steps down into subdirectory
correctly
=======================================
--- /odp2wts/odp2app.py Fri Nov 25 16:43:11 2011
+++ /odp2wts/odp2app.py Mon Nov 28 00:38:44 2011
@@ -2,11 +2,12 @@
# -*- coding: utf-8 -*-

#-------------------------------------------------------------------------------
# Name: odp2app.py
-# Purpose: Compile odp2wts.py into Mac OSX Application
+# Purpose: Compile odp2ss.py into Mac OSX Application
#
# Author: John Graves
#
# Created: 26 November 2011
+# Modified: 28 November 2011
# Copyright: (c) John Graves 2011
# License: MIT License

#-------------------------------------------------------------------------------
@@ -21,10 +22,10 @@

class BuildApp:
def __init__(self):
- self.APP = ['odp2wts.py']
+ self.APP = ['odp2ss.py']
self.DATA_FILES = ['CHANGES.txt','ethics_notice.txt',
'README.txt','LICENSE.txt','soxi','MP4Box','ffmpeg','silence22kHz.ogg']
- self.OPTIONS = {'argv_emulation': True}
+ self.OPTIONS = {'argv_emulation':
True, 'iconfile': 'slidespeech.icns',}
#Dist directory
self.dist_dir ='dist'

@@ -47,6 +48,6 @@
sys.argv.append('py2app')
BuildApp().run() #Run generation
raw_input("Press any key to continue") #Pause to let user see that
things ends
-
os.chmod(os.getcwd()+os.sep+"dist/odp2wts.app/Contents/Resources/soxi",stat.S_IRWXU)
-
os.chmod(os.getcwd()+os.sep+"dist/odp2wts.app/Contents/Resources/ffmpeg",stat.S_IRWXU)
-
os.chmod(os.getcwd()+os.sep+"dist/odp2wts.app/Contents/Resources/MP4Box",stat.S_IRWXU)
+
os.chmod(os.getcwd()+os.sep+"dist/odp2ss.app/Contents/Resources/soxi",stat.S_IRWXU)
+
os.chmod(os.getcwd()+os.sep+"dist/odp2ss.app/Contents/Resources/ffmpeg",stat.S_IRWXU)
+
os.chmod(os.getcwd()+os.sep+"dist/odp2ss.app/Contents/Resources/MP4Box",stat.S_IRWXU)
=======================================
--- /odp2wts/odp2wts.py Sun Nov 27 18:36:38 2011
+++ /odp2wts/odp2wts.py Mon Nov 28 00:38:44 2011
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
"""
odp2wts.py
-a component of Wiki-to-Speech.py
-
-Extract speaker notes from .odp file and prepare script.txt for
Wiki-to-Speech
+a component of SlideSpeech.py
+
+Extract speaker notes from .odp file and prepare script.txt for SlideSpeech

Copyright (c) 2011 John Graves

@@ -44,8 +44,14 @@

20111112 If script has [Source=http:// ...] add this link to the question
page
20111121 Turn off debug2.txt and put quotes around calls in makeVid.bat
+20111128 Working Linux version once dependencies are installed
+ Linux dependencies include:
+ sox
+ ffmpeg
+ mencode
+ espeak
"""
-__version__ = "0.1.27"
+__version__ = "0.1.28"

import BeautifulSoup
from BeautifulSoup import BeautifulStoneSoup
@@ -85,9 +91,9 @@

path_buf = wintypes.create_unicode_buffer(wintypes.MAX_PATH)
result = _SHGetFolderPath(0, CSIDL_COMMON_APPDATA, 0, 0, path_buf)
- iniDirectory = path_buf.value+os.sep+"Wiki-to-Speech"
+ iniDirectory = path_buf.value+os.sep+"SlideSpeech"
else:
- iniDirectory = os.path.expanduser('~')+os.sep+".Wiki-to-Speech"
+ iniDirectory = os.path.expanduser('~')+os.sep+".SlideSpeech"

ensure_dir(iniDirectory)

@@ -113,7 +119,7 @@
if not os.path.isfile(lastOdpFile):
lastOdpFile = None

-odpFilePath = easygui.fileopenbox(title="ODP2WTS Converter "+__version__,
msg="Select an .odp file",
+odpFilePath = easygui.fileopenbox(title="SlideSpeech from ODP
Converter "+__version__, msg="Select an .odp file",
default=lastOdpFile, filetypes=None)
if odpFilePath == None:
sys.exit()
@@ -280,20 +286,20 @@
scriptFile = codecs.open(odpFileSubdirectory+os.sep+'script.txt',
encoding='utf-8', mode='w+')
scriptFile.write("""#[path=]
#
-# Script created with Wiki-to-Speech version """+__version__+
-"\n# http://wikitospeech.org\n"+
+# Script created with SlideSpeech from ODP version """+__version__+
+"\n# http://slidespeech.org\n"+
"# Date: "+time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())+"""
#
# Title:
# Author:
#
-# Wiki-to-Speech Slide show version
+# SlideSpeech Slide show version
# http://
#
-# Wiki-to-Speech Video version
+# SlideSpeech Video version
# http://
#
-# Wiki-to-Speech script:
+# SlideSpeech script:
""")
onImg = minNum
for item in noteText:
@@ -393,7 +399,7 @@
htmlFile.write('"http://www.w3.org/TR/html4/transitional.dtd">' + "\n")
htmlFile.write("<html>\n<head>\n")
htmlFile.write('<meta HTTP-EQUIV=CONTENT-TYPE CONTENT="text/html;
charset=utf-8">' + "\n")
- htmlFile.write('<title>Wiki-to-Speech</title>\n')
+ htmlFile.write('<title>SlideSpeech</title>\n')

def writeHtmlHeader2(htmlFile):
htmlFile.write('</head>\n')
=======================================
--- /wikitospeech/forms.py Fri Nov 25 16:43:11 2011
+++ /wikitospeech/forms.py Mon Nov 28 00:38:44 2011
@@ -2,15 +2,16 @@
import urllib

def scriptInputFormWithErrorMessage(version, errorMessage):
- return '''<html><head><title>Wiki-to-Speech {0}</title></head>
+ return '''<html><head><title>SlideSpeech {0}</title></head>
<body><br><br><hr><br><center><form action="getScriptName"
method="GET">
- Wiki-to-Speech Script:<br>
+ SlideSpeech Script:<br>
<input type="text" name="name" size="80" /><br>
<input type="submit" value="Open"/><br><br>
- <small>Version {0}<br>
- Enter <i>exit</i> to stop localhost server</small><br>
- <italic>{1}</italic>
- </form></center><br><hr></body></html>'''.format(version,
errorMessage)
+ </form>
+ <small>Version {0}<br><form action="SlideSpeech_Exit_Complete"
method="GET">
+ <input type="submit" value="Exit"/><br><br>
+ </form><br>
+
<italic>{1}</italic></center><br><hr></body></html>'''.format(version,
errorMessage)

def loading(name):
return "Loading script: " + name + " ..."
=======================================
--- /wikitospeech/wts2app.py Wed Aug 24 19:13:11 2011
+++ /wikitospeech/wts2app.py Mon Nov 28 00:38:44 2011
@@ -7,6 +7,7 @@
# Author: John Graves
#
# Created: 24 August 2011
+# Modified: 28 November 2011
# Copyright: (c) John Graves 2011
# License: MIT License

#-------------------------------------------------------------------------------
@@ -22,7 +23,7 @@
def __init__(self):
self.APP = ['Wiki-to-Speech.py']
self.DATA_FILES =
['CHANGES.txt','ethics_notice.txt','README.txt','LICENSE.txt']
- self.OPTIONS = {'argv_emulation': True}
+ self.OPTIONS = {'argv_emulation':
True, 'iconfile': 'slidespeech.icns',}
#Dist directory
self.dist_dir ='dist'

Reply all
Reply to author
Forward
0 new messages