Commit ab5d57db authored by Jan Gerber's avatar Jan Gerber
Browse files

replace PythonCard with wxPython,

expose options in add video dialog,
needs cleanup to look ok but should work.
parent 3ba140ac
Simple Theora Encoder a PythonCard based cross platform GUI
Simple Theora Encoder a wxPython based cross platform GUI
for ffmpeg2theora.
for those that fear the command line
FIXME: figure out how to get smaller binaries
#!/usr/bin/env python
"""
__version__ = "1.0"
"""
from PythonCard import model, dialog
import wx
import os
from os.path import join, dirname, basename
import thread
"""
Format seconds
"""
def sec2time(seconds):
seconds = int(seconds)
minutes = int(seconds / 60)
seconds = seconds % 60
hours = int(minutes / 60)
minutes = minutes % 60
return "%02d:%02d:%02d" % (hours, minutes, seconds)
class Encoder:
working = False
inputfile = ''
outputfile = ''
settings = "-p preview"
ffmpeg2theora_path = os.path.abspath(join(dirname(__file__), 'ffmpeg2theora'))
def commandline(self):
cmd = "'%s' --frontend %s '%s'" % (self.ffmpeg2theora_path, self.settings, self.inputfile.replace("'", "\'"))
if self.outputfile:
cmd += " -o '%s'" % self.outputfile.replace("'", "\'")
cmd += " 2>&1"
return cmd
def encodeItem(self, item):
self.inputfile = self.queuedata[item]['path']
self.settings = self.queuedata[item]['settings']
itemID = self.queuedata[item]['itemID']
self.itemStatus(itemID, 'encoding...')
cmd = self.commandline()
f = os.popen(cmd)
line = f.readline()
info = dict()
while line:
if line.startswith('f2t'):
for o in line.split(';')[1:]:
oo = o.split(': ')
if len(oo) >= 2:
info[oo[0]] = ": ".join(oo[1:]).strip()
if info.has_key('position'):
encoded = "encoded %s/" % sec2time(float(info['position']))
if info.has_key('duration') and float(info['duration']):
encoded = "% 3d %% done, " % ((float(info['position']) / float(info['duration'])) * 100)
line = encoded + 'remaining: '+ sec2time(float(info['remaining']))
else:
line = "encoding.."
self.itemStatus(itemID, line)
line = f.readline()
f.close()
if info.get('result', 'no') == 'ok':
self.itemStatus(itemID, 'Done.')
else:
self.itemStatus(itemID, info.get('result', 'Failed.'))
def encodeQueueThread(self):
self.working = True
for key in self.queuedata:
if self.queuedata[key]['status'] != 'Done.':
self.encodeItem(key)
self.working = False
self.encodingFinished()
def encodeQueue(self, queuedata, itemStatus, encodingFinished):
self.itemStatus = itemStatus
self.encodingFinished = encodingFinished
self.queuedata = queuedata
encoding_thread = thread.start_new_thread(self.encodeQueueThread, ())
class SimpleTheoraEncoderBackground(model.Background):
encoder = Encoder()
queuedata = {}
encodingQueueInitialized = False
def on_initialize(self,event):
list = self.components.encodingQueue
list.InsertColumn(0, "Name")
list.InsertColumn(1, "Status")
pass
# so how much to wrap and how much to leave raw wxPython?
def initializeEncodingQueue(self, select = 0):
list = self.components.encodingQueue
list.Clear()
items = self.queuedata.items()
for x in range(len(items)):
key, item = items[x]
self.queuedata[key]['itemID'] = x
list.InsertStringItem(x, item['path'])
list.SetStringItem(x, 0, item['display_path'])
list.SetStringItem(x, 1, item['status'])
list.SetItemData(x, key)
list.SetColumnWidth(0, 180)
list.SetColumnWidth(1, 250)
list.SetItemDataMap(self.queuedata)
# show how to select an item
self.currentItem = select
if items:
list.SetItemState(self.currentItem, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)
self.encodingQueueInitialized = True
self.components.encodeQueue.enabled = True
def setItemStatus(self, itemID, value):
key = self.components.encodingQueue.GetItemData(self.currentItem)
self.queuedata[key]['status'] = value
self.components.encodingQueue.SetStringItem(itemID, 1, value)
def addItemToQueue(self, input_path, settings):
list = self.components.encodingQueue
display_path = input_path
if len(display_path) > 26:
display_path = "..." + input_path[-23:]
item = dict(
path = input_path,
settings = settings,
display_path = display_path,
status = 'waiting... ',
listID = 0
)
if self.encodingQueueInitialized:
x = list.GetItemCount()
if self.queuedata:
key = max(self.queuedata.keys()) + 1
else:
key = 1
item['itemID'] = x
self.queuedata[key] = item
list.InsertStringItem(x, item['path'])
list.SetStringItem(x, 0, item['display_path'])
list.SetStringItem(x, 1, item['status'])
list.SetItemData(x, key)
else:
key = 1
self.queuedata[key] = item
self.initializeEncodingQueue()
def on_addItem_mouseClick(self, event):
wildcard = "Video files|*.AVI;*.avi;*.OGG;*.ogg;*.mov;*.MOV;*.dv;*.DV;*.mp4;*.MP4;*.mpg;*.mpeg;*.wmv;*.MPG;*.flv;*.FLV|All Files (*.*)|*.*"
result = dialog.fileDialog(self, 'Add Video..', '', '', wildcard )
if result.accepted:
for input_path in result.paths:
settings = '-p preview'
self.addItemToQueue(input_path, settings)
def on_removeItem_mouseClick(self, event):
list = self.components.encodingQueue
print "remove", self.currentItem
key = self.components.encodingQueue.GetItemData(self.currentItem)
self.queuedata.pop(key)
self.initializeEncodingQueue(self.currentItem)
def on_editItem_mouseClick(self, event):
print "edit settings here"
list = self.components.encodingQueue
key = self.components.encodingQueue.GetItemData(self.currentItem)
result = dialog.textEntryDialog(self,
'These parameters are passed to ffmpeg2theora adjust to your needs',
'Encoding Settings',
self.queuedata[key]['settings'])
if result.accepted:
self.queuedata[key]['settings'] = result.text
def on_encodeQueue_mouseClick(self, event):
self.components.addItem.enabled = False
self.components.editItem.enabled = False
self.components.removeItem.enabled = False
self.components.encodeQueue.enabled = False
self.encoder.encodeQueue(self.queuedata, self.setItemStatus, self.encodingFinished)
def encodingFinished(self):
self.components.addItem.enabled = True
self.components.editItem.enabled = True
self.components.removeItem.enabled = True
self.components.encodeQueue.enabled = True
def on_encodingQueue_select(self, event):
self.currentItem = event.m_itemIndex
key = self.components.encodingQueue.GetItemData(self.currentItem)
print self.queuedata[key]
self.components.editItem.enabled = True
self.components.removeItem.enabled = True
if __name__ == '__main__':
app = model.Application(SimpleTheoraEncoderBackground)
app.MainLoop()
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# vi:si:et:sw=2:sts=2:ts=2
# Written 2007 by j@v2v.cc
#
# see LICENSE.txt for license information
#
__version__ = "1.0"
import os
from os.path import join, dirname, basename, abspath
import sys
import time
import thread
import xmlrpclib
import wx
try:
from xml.etree.ElementTree import Element, SubElement, ElementTree, parse
except:
from elementtree.ElementTree import Element, SubElement, ElementTree, parse
from theoraenc.addVideoDialog import addVideoDialog
from theoraenc import theoraenc
#overwrite location of resources in submodules
if os.name != 'nt':
theoraenc.resourcePath = abspath(dirname(__file__))
class SimpleTheoraEncoder(wx.Frame):
queuedata = {}
_qd_key = {}
encodingQueueInitialized = False
inputFile = False
def initMainInterface(self):
#TODO: addd menue
self.encodingQueue = wx.ListCtrl(self, -1, style=wx.LC_REPORT)
self.encodingQueue.SetPosition(wx.Point(15,50))
self.encodingQueue.SetSize(wx.Size(435, 165))
self.encodingQueue.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
buttonSize = wx.Size(88,-1)
self.addItem = wx.Button(self, wx.ID_ANY, "Add...", wx.Point(462, 71), buttonSize)
self.Bind(wx.EVT_BUTTON, self.OnClickAdd, self.addItem)
self.removeItem = wx.Button(self, wx.ID_ANY, "Remove", wx.Point(462, 106), buttonSize)
self.Bind(wx.EVT_BUTTON, self.OnClickRemove, self.removeItem)
self.removeItem.Disable()
self.buttonQuit = wx.Button(self, wx.ID_ANY, "Quit", wx.Point(462, 187), buttonSize)
self.Bind(wx.EVT_BUTTON, self.OnExit, self.buttonQuit)
#Title
self.title = wx.StaticText(self, -1, "Simple Theora Encoder", wx.Point(15, 10))
def __init__(self, parent, id, title, inputFile=None):
wx.Frame.__init__(self, parent, id, title, size=(559,260))
self.inputFile = inputFile
self.initMainInterface()
self.Show(True)
if self.addItem.IsEnabled:
self.OnClickAdd(None)
def initializeUploadQueue(self, selectItem = 0):
q = self.encodingQueue
q.ClearAll()
q.InsertColumn(0, "Name")
q.InsertColumn(1, "Stats")
q.SetColumnWidth(0, 195)
q.SetColumnWidth(1, 240)
q.itemDataMap = self.queuedata
items = self.queuedata.items()
for x in range(len(items)):
key, item = items[x]
self.queuedata[key]['itemID'] = x
self._qd_key[item['name']] = key
q.InsertStringItem(x, item['path'])
q.SetStringItem(x, 0, item['display_path'])
q.SetStringItem(x, 1, item['status'])
q.SetItemData(x, key)
# show how to select an item
self.currentItem = selectItem
if items:
q.SetItemState(self.currentItem, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)
else:
self.removeItem.Disable()
self.encodingQueueInitialized = True
def setItemStatus(self, itemID, value):
key = self.encodingQueue.GetItemData(itemID)
self.queuedata[key]['status'] = value
self.encodingQueue.SetStringItem(itemID, 1, value)
def updateItemStatus(self, name, status):
try:
item = self.queuedata[self._qd_key[name]]
except KeyError:
return
itemID = item['itemID']
if item['status'] != status:
item['status'] = status
self.encodingQueue.SetStringItem(itemID, 1, status)
now = time.mktime(time.localtime())
def getSettings(self, options):
settings = []
for key in ('width', 'height'):
if key in options and options[key]:
settings.append('--%s' % key)
settings.append("%s" % int(options[key]))
for key in ('videoquality', 'audioquality'):
if key in options and options[key]:
settings.append('--%s' % key)
settings.append("%s" % float(options[key]))
return settings
def addItemThread(self, item):
if not item['enc'].encode():
print "encoding failed"
return
return True
def addItemToQueue(self, videoFile, options):
name = os.path.basename(videoFile)
display_path = videoFile
if len(display_path) > 25:
display_path = "..." + display_path[-24:]
item = dict(
path = videoFile,
options = options,
display_path = display_path,
status = 'encoding... ',
listID = 0,
name = name,
)
item['enc'] = theoraenc.TheoraEnc(videoFile, None, lambda x: self.updateItemStatus(name, x))
item['enc'].settings = self.getSettings(options)
if self.encodingQueueInitialized:
x = self.encodingQueue.GetItemCount()
if self.queuedata:
key = max(self.queuedata.keys()) + 1
else:
key = 1
item['itemID'] = x
self.queuedata[key] = item
self.encodingQueue.InsertStringItem(x, item['path'])
self.encodingQueue.SetStringItem(x, 0, item['display_path'])
self.encodingQueue.SetStringItem(x, 1, item['status'])
self.encodingQueue.SetItemData(x, key)
else:
key = 1
self.queuedata[key] = item
self.initializeUploadQueue()
self._qd_key[name] = key
thread.start_new_thread(self.addItemThread, (item, ))
def OnItemSelected(self, event):
self.currentItem = event.m_itemIndex
self.removeItem.Enable()
def OnClickAdd(self, event):
result = addVideoDialog(self)
time.sleep(0.5)
if result['ok']:
self.addItemToQueue(result['videoFile'], result)
def OnClickRemove(self, event):
key = self.encodingQueue.GetItemData(self.currentItem)
print key
if 'enc' in self.queuedata[key]:
self.queuedata[key]['enc'].cancel()
del self.queuedata[key]
self.initializeUploadQueue(self.currentItem)
def OnExit(self, event):
for key in self.queuedata:
if 'enc' in self.queuedata[key]:
try:
self.queuedata[key]['enc'].cancel()
except:
pass
sys.exit()
def gui(inputFile = None):
app = wx.PySimpleApp()
frame=SimpleTheoraEncoder(None, wx.ID_ANY, 'Simple Theora Encoder', inputFile = inputFile)
app.MainLoop()
if __name__ == '__main__':
inputFile = None
if len(sys.argv) > 1 and not sys.argv[1].startswith('-'):
inputFile = sys.argv[1]
gui(inputFile)
{'application':{'type':'Application',
'name':'SimpleTheoraEncoder',
'backgrounds': [
{'type':'Background',
'name':'bgTemplate',
'title':'Simple Theora Encoder',
'size':(559, 260),
'style':['resizeable'],
'menubar': {'type':'MenuBar',
'menus': [
{'type':'Menu',
'name':'menuFile',
'label':'&File',
'items': [
{'type':'MenuItem',
'name':'menuFileExit',
'label':'E&xit',
'command':'exit',
},
]
},
]
},
'components': [
{'type':'Button',
'name':'encodeQueue',
'position':(462, 187),
'enabled':False,
'label':u'Encode...',
},
{'type':'Button',
'name':'editItem',
'position':(461, 104),
'enabled':False,
'label':u'Edit...',
},
{'type':'Button',
'name':'removeItem',
'position':(461, 136),
'enabled':False,
'label':u'Remove',
},
{'type':'Button',
'name':'addItem',
'position':(462, 71),
'label':u'Add...',
},
{'type':'StaticText',
'name':'title',
'position':(15, 9),
'font':{'faceName': u'Lucida Grande', 'family': 'sansSerif', 'size': 28},
'text':u'Simple Theora Encoder',
},
{'type':'MultiColumnList',
'name':'encodingQueue',
'position':(15, 49),
'size':(431, 164),
'backgroundColor':(255, 255, 255),
'columnHeadings':[],
'font':{'faceName': u'Lucida Grande', 'family': 'default', 'size': 12},
'items':[],
'maxColumns':20,
'rules':1,
},
] # end components
} # end background
] # end backgrounds
} }
......@@ -10,8 +10,8 @@ class mypy2app(py2app):
print ">>>>> installing ffmpeg2theora <<<<<<"
resourcesRoot = os.path.join(self.dist_dir, 'Simple Theora Encoder.app/Contents/Resources')
shutil.copy('ffmpeg2theora', os.path.join(resourcesRoot, 'ffmpeg2theora'))
rsrc_file = "Simple Theora Encoder.rsrc.py"
shutil.copy(rsrc_file, os.path.join(resourcesRoot, rsrc_file))
#rsrc_file = "Simple Theora Encoder.rsrc.py"
#shutil.copy(rsrc_file, os.path.join(resourcesRoot, rsrc_file))
imgPath = os.path.join(self.dist_dir, "Simple Theora Encoder.dmg")
os.system('''hdiutil create -srcfolder "%s" -volname "Simple Theora Encoder" -format UDZO "%s"''' %
......@@ -27,7 +27,6 @@ setup(
'strip': True,
'optimize': 2,
'iconfile': 'Simple Theora Encoder.icns',
'packages': ['PythonCard', 'wx'],
'plist': {'CFBundleIconFile': 'Simple Theora Encoder.icns'},
}},
cmdclass = {'py2app': mypy2app }
......
# -*- coding: utf-8 -*-
# vi:si:et:sw=2:sts=2:ts=2
import os
from os.path import basename
import time
import wx
class AddVideoDialog(wx.Dialog):
def __init__(
self, parent, ID, title, size=wx.DefaultSize, pos=wx.DefaultPosition,
style=wx.DEFAULT_DIALOG_STYLE,
):
self.videoFile = ''
pre = wx.PreDialog()
#pre.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP)
pre.Create(parent, ID, title, pos, size, style)
self.PostCreate(pre)
# Now continue with the normal construction of the dialog
padding = 4
section_padding=60
mainBox = wx.BoxSizer(wx.VERTICAL)
hbox = wx.BoxSizer(wx.HORIZONTAL)
mainBox.Add(hbox)
hbox.AddSpacer((8, 8))
#Video File
hbox = wx.BoxSizer(wx.HORIZONTAL)
mainBox.Add(hbox)
hbox.Add(wx.StaticText(self, -1, "Video File"), 0, wx.EXPAND|wx.ALL, 16)
self.btnVideoFile = wx.Button(self, size=(380, -1))
self.btnVideoFile.SetLabel('Select...')
self.Bind(wx.EVT_BUTTON, self.OnClickVideoFile, self.btnVideoFile)
hbox.Add(self.btnVideoFile, 0, wx.EXPAND|wx.ALL, padding)
#Quality
hbox = wx.BoxSizer(wx.HORIZONTAL)
mainBox.Add(hbox)
label = wx.StaticText(self, -1, "Video")
hbox.AddSpacer((12, 10))
hbox.Add(label, 0, wx.EXPAND|wx.ALL, padding)
hbox = wx.BoxSizer(wx.HORIZONTAL)
mainBox.Add(hbox)
hbox.AddSpacer((section_padding, 10))
label = wx.StaticText(self, -1, "Quality:")
hbox.Add(label, 0, wx.EXPAND|wx.ALL, padding)
self.videoquality = wx.TextCtrl(self, -1, '5.0', size=(32,-1))
hbox.Add(self.videoquality, 0, wx.EXPAND|wx.ALL, padding)
label = wx.StaticText(self, -1, "Bitrate (kbps):")
hbox.Add(label, 0, wx.EXPAND|wx.ALL, padding)
self.videobitrate = wx.TextCtrl(self, -1, '', size=(65,-1))
hbox.Add(self.videobitrate, 0, wx.EXPAND|wx.ALL, padding)
hbox = wx.BoxSizer(wx.HORIZONTAL)
mainBox.Add(hbox)
hbox.AddSpacer((section_padding, 10))
#Size
box=45
label = wx.StaticText(self, -1, "Size:")
hbox.Add(label, 0, wx.EXPAND|wx.ALL, padding)
self.width = wx.TextCtrl(self, -1, '', size=(65,-1))
hbox.Add(self.width, 0, wx.EXPAND|wx.ALL, padding)