Commit 38bb5674 authored by Jan Gerber's avatar Jan Gerber
Browse files

cleanup frontend a bit, update status less often

parent 59e8d35a
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# vi:si:et:sw=2:sts=2:ts=2
# -*- coding: utf-8 -*-
# Written 2007 by j@v2v.cc
#
# see LICENSE.txt for license information
......@@ -32,31 +32,39 @@ class SimpleTheoraEncoder(wx.Frame):
_qd_key = {}
encodingQueueInitialized = False
inputFile = False
encoding = False
quit = 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.SetPosition(wx.Point(10,50))
self.encodingQueue.SetSize(wx.Size(440, 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)
buttonSize = wx.Size(80,-1)
self.addItem = wx.Button(self, wx.ID_ANY, "Add...", wx.Point(460, 70), 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.removeItem = wx.Button(self, wx.ID_ANY, "Remove", wx.Point(460, 100), 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)
self.buttonEncode = wx.Button(self, wx.ID_ANY, "Encode", wx.Point(460, 190), buttonSize)
self.Bind(wx.EVT_BUTTON, self.OnEncode, self.buttonEncode)
self.buttonEncode.Disable()
self.Bind(wx.EVT_CLOSE, self.OnClose)
#Title
self.title = wx.StaticText(self, -1, "Simple Theora Encoder", wx.Point(15, 10))
titleFont = wx.Font(14, wx.DEFAULT, wx.NORMAL, wx.NORMAL, False, u'Sans')
self.title = wx.StaticText(self, -1, "Simple Theora Encoder", wx.Point(10, 10))
self.title.SetFont(titleFont)
def __init__(self, parent, id, title, inputFile=None):
wx.Frame.__init__(self, parent, id, title, size=(559,260))
wx.Frame.__init__(self, parent, id, title, size=(550,230))
self.inputFile = inputFile
self.initMainInterface()
self.Show(True)
......@@ -69,7 +77,7 @@ class SimpleTheoraEncoder(wx.Frame):
q.ClearAll()
q.InsertColumn(0, "Name")
q.InsertColumn(1, "Stats")
q.SetColumnWidth(0, 195)
q.SetColumnWidth(0, 200)
q.SetColumnWidth(1, 240)
q.itemDataMap = self.queuedata
......@@ -106,8 +114,8 @@ class SimpleTheoraEncoder(wx.Frame):
itemID = item['itemID']
if item['status'] != status:
item['status'] = status
#self.title.SetLabel(os.path.basename(item['path']) +': '+ status)
self.encodingQueue.SetStringItem(itemID, 1, status)
now = time.mktime(time.localtime())
def getSettings(self, options):
settings = []
......@@ -131,12 +139,36 @@ class SimpleTheoraEncoder(wx.Frame):
settings.append('%s' % s['encoding'])
return settings
def addItemThread(self, item):
if not item['enc'].encode():
print "encoding failed"
return
return True
def encodeItem(self, item):
item['encoding'] = True
if self.currentItem == item['itemID']:
self.removeItem.SetLabel('Cancel')
self.setItemStatus(item['itemID'], 'encoding')
result = item['enc'].encode()
if not result:
self.setItemStatus(item['itemID'], 'encoding failed.')
else:
self.setItemStatus(item['itemID'], 'encoding done.')
item['encoded'] = True
item['encoding'] = False
return result
def encodeQueue(self, foo):
def nextItem():
items = self.queuedata.items()
for x in range(len(items)):
key, item = items[x]
if not item['encoded']:
return item
return None
next = nextItem()
while next and not self.quit:
self.encodeItem(next)
next = nextItem()
self.encoding = False
def addItemToQueue(self, videoFile, options):
name = os.path.basename(videoFile)
display_path = videoFile
......@@ -146,10 +178,12 @@ class SimpleTheoraEncoder(wx.Frame):
path = videoFile,
options = options,
display_path = display_path,
status = 'encoding... ',
status = 'waiting... ',
listID = 0,
name = name,
)
item['encoding'] = False
item['encoded'] = False
item['enc'] = theoraenc.TheoraEnc(videoFile, None, lambda x: self.updateItemStatus(name, x))
item['enc'].settings = self.getSettings(options)
......@@ -170,10 +204,15 @@ class SimpleTheoraEncoder(wx.Frame):
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
key = self.encodingQueue.GetItemData(self.currentItem)
item = self.queuedata[key]
if item['encoding']:
self.removeItem.SetLabel('Cancel')
else:
self.removeItem.SetLabel('Remove')
self.removeItem.Enable()
def OnClickAdd(self, event):
......@@ -181,23 +220,41 @@ class SimpleTheoraEncoder(wx.Frame):
time.sleep(0.5)
if result['ok']:
self.addItemToQueue(result['videoFile'], result)
if not self.encoding:
self.buttonEncode.Enable()
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 OnEncode(self, event):
if not self.encoding:
self.encoding = True
thread.start_new_thread(self.encodeQueue, ("foo", ))
self.buttonEncode.Disable()
def OnClose(self, event):
close = True
if self.encoding:
dlg = wx.MessageDialog(self,
"Videos are still encoded.\nDo you really want to close Simple Theora Encoder?",
"Confirm Exit", wx.OK|wx.CANCEL|wx.ICON_QUESTION)
result = dlg.ShowModal()
dlg.Destroy()
if result != wx.ID_OK:
close = False
if close:
self.quit = True
for key in self.queuedata:
if 'enc' in self.queuedata[key]:
try:
self.queuedata[key]['enc'].cancel()
except:
pass
self.Destroy()
def gui(inputFile = None):
app = wx.PySimpleApp()
......
......@@ -7,6 +7,8 @@ import time
from addSubtitlesDialog import addSubtitlesPropertiesDialog, SubtitlesList
import wx
import theoraenc
class AddVideoDialog(wx.Dialog):
def __init__(
......@@ -273,7 +275,7 @@ class AddVideoDialog(wx.Dialog):
self.btnOK = wx.Button(self, wx.ID_OK)
self.btnOK.SetDefault()
self.btnOK.Disable()
self.btnOK.SetLabel('Encode')
self.btnOK.SetLabel('Add to queue')
hbox.Add(self.btnOK, 0, wx.EXPAND|wx.ALL, padding)
hbox = wx.BoxSizer(wx.HORIZONTAL)
......@@ -304,13 +306,24 @@ class AddVideoDialog(wx.Dialog):
return filename
def selectVideoFile(self, videoFile):
self.videoFile = videoFile
lValue = videoFile
lLength = 45
if len(lValue) > lLength:
lValue = "..." + lValue[-lLength:]
self.btnVideoFile.SetLabel(lValue)
self.btnOK.Enable()
self.info = theoraenc.fileInfo(videoFile)
if self.info:
#FIXME: enable/disable options based on input
"""
if "video" in self.info: #source has video
#enable video options
if "audio" in self.info: #source has audio
#enable audio options
if "audio" in self.info: #source has audio
"""
self.videoFile = videoFile
lValue = videoFile
lLength = 45
if len(lValue) > lLength:
lValue = "..." + lValue[-lLength:]
self.btnVideoFile.SetLabel(lValue)
self.btnOK.Enable()
def CheckSubtitlesSelection(self, event):
idx=self.subtitles.GetFirstSelected()
......@@ -380,8 +393,22 @@ def addVideoDialog(parent, hasKate):
encoding = dlg.subtitles.GetItem(idx, 2).GetText()
file = dlg.subtitles.GetItem(idx, 3).GetText()
result['subtitles'].append({'encoding':encoding, 'language':language, 'category':category, 'file':file})
print result
else:
result['ok'] = False
dlg.Destroy()
return result
if __name__ == "__main__":
import sys
class Frame(wx.Frame):
inputFile = None
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "add video test", size=(559,260))
self.Show(True)
app = wx.PySimpleApp()
frame=Frame()
if len(sys.argv) > 1:
frame.inputFile = sys.argv[1]
result = addVideoDialog(frame, True)
print result
......@@ -7,8 +7,10 @@ import time
import sys
import signal
import subprocess
import threading
import simplejson
import wx
resourcePath = abspath(dirname(__file__))
......@@ -45,6 +47,22 @@ def timestr(seconds):
seconds = (seconds-((hours*3600)+(minutes*60)))
return '%02d:%02d:%02d' % (hours, minutes, seconds)
class ThreadWorker(threading.Thread):
def __init__(self, callable, *args, **kwargs):
super(ThreadWorker, self).__init__()
self.callable = callable
self.args = args
self.kwargs = kwargs
self.setDaemon(True)
def run(self):
try:
self.callable(*self.args, **self.kwargs)
except wx.PyDeadObjectError:
pass
except Exception, e:
print e
class TheoraEnc:
settings = []
p = None
......@@ -88,44 +106,52 @@ class TheoraEnc:
def encode(self):
cmd = self.commandline()
print cmd
p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, close_fds=True)
self.p = p
f = p.stdout
line = f.readline()
info = dict()
status = ''
self.warning_timeout = 0
while line:
now = time.time()
try:
data = simplejson.loads(line)
for key in data:
info[key] = data[key]
if 'WARNING' in info:
status = info['WARNING']
self.warning_timeout = now + 3
del info['WARNING']
def worker(pipe):
while True:
line = pipe.readline()
if line == '':
break
else:
status=None
if now >= self.warning_timeout:
if 'position' in info:
if 'duration' in info and float(info['duration']):
encoded = "encoding % 3d %% done " % ((float(info['position']) / float(info['duration'])) * 100)
else:
encoded = "encoded %s/" % timestr(float(info['position']))
if float(info['remaining'])>0:
status = encoded + '/ '+ timestr(float(info['remaining']))
else:
status = encoded
now = time.time()
try:
data = simplejson.loads(line)
for key in data:
info[key] = data[key]
if 'WARNING' in info:
status = info['WARNING']
self.warning_timeout = now + 3
del info['WARNING']
else:
status = "encoding.."
if status != None:
self.updateGUI(status)
except:
pass
line = f.readline()
f.close()
status=None
if now >= self.warning_timeout:
if 'position' in info:
if 'duration' in info and float(info['duration']):
encoded = "encoding % 3d %% done " % ((float(info['position']) / float(info['duration'])) * 100)
else:
encoded = "encoded %s/" % timestr(float(info['position']))
if float(info['remaining'])>0:
status = encoded + '/ '+ timestr(float(info['remaining']))
else:
status = encoded
status = "encoding % 3d %% done " % ((float(info['position']) / float(info['duration'])) * 100)
else:
status = "encoding.."
if status != None:
self.updateGUI(status)
except:
pass
stdout_worker = ThreadWorker(worker, p.stdout)
stdout_worker.start()
p.wait()
if info.get('result', 'no') == 'ok':
self.updateGUI('Encoding done.')
return True
......@@ -133,6 +159,20 @@ class TheoraEnc:
self.updateGUI(info.get('result', 'Encoding failed.'))
return False
def fileInfo(filename):
cmd = []
cmd.append(ffmpeg2theora)
cmd.append('--info')
cmd.append(filename)
p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, close_fds=True)
data, err = p.communicate()
try:
info = simplejson.loads(data)
except:
info = None
return info
ffmpeg2theora = probe_ffmpeg2theora()
hasKate = probe_kate(ffmpeg2theora)
......@@ -631,6 +631,7 @@ static double estimated_size(oggmux_info *info, double timebase) {
}
static void print_stats(oggmux_info *info, double timebase) {
static double last = -2;
int hundredths = timebase * 100 - (long) timebase * 100;
int seconds = (long) timebase % 60;
int minutes = ((long) timebase / 60) % 60;
......@@ -639,34 +640,37 @@ static void print_stats(oggmux_info *info, double timebase) {
int remaining_seconds = (long) remaining % 60;
int remaining_minutes = ((long) remaining / 60) % 60;
int remaining_hours = (long) remaining / 3600;
if (info->frontend) {
fprintf(info->frontend, "{\"duration\": %lf, \"position\": %.02lf, \"audio_kbps\": %d, \"video_kbps\": %d, \"remaining\": %.02lf}\n",
(double)info->duration,
timebase,
info->akbps, info->vkbps,
remaining
);
fflush (info->frontend);
}
else if (timebase > 0 ) {
if (!remaining) {
remaining = time(NULL) - info->start_time;
remaining_seconds = (long) remaining % 60;
remaining_minutes = ((long) remaining / 60) % 60;
remaining_hours = (long) remaining / 3600;
fprintf (stderr,"\r %d:%02d:%02d.%02d audio: %dkbps video: %dkbps, time elapsed: %02d:%02d:%02d ",
hours, minutes, seconds, hundredths,
if (timebase - last > 0.5) {
last = timebase;
if (info->frontend) {
fprintf(info->frontend, "{\"duration\": %lf, \"position\": %.02lf, \"audio_kbps\": %d, \"video_kbps\": %d, \"remaining\": %.02lf}\n",
(double)info->duration,
timebase,
info->akbps, info->vkbps,
remaining_hours, remaining_minutes, remaining_seconds
);
}
else {
fprintf (stderr,"\r %d:%02d:%02d.%02d audio: %dkbps video: %dkbps, ET: %02d:%02d:%02d, est. size: %.01lf MB ",
hours, minutes, seconds, hundredths,
info->akbps, info->vkbps,
remaining_hours, remaining_minutes, remaining_seconds,
estimated_size(info, timebase)
remaining
);
fflush (info->frontend);
}
else if (timebase > 0 ) {
if (!remaining) {
remaining = time(NULL) - info->start_time;
remaining_seconds = (long) remaining % 60;
remaining_minutes = ((long) remaining / 60) % 60;
remaining_hours = (long) remaining / 3600;
fprintf (stderr,"\r %d:%02d:%02d.%02d audio: %dkbps video: %dkbps, time elapsed: %02d:%02d:%02d ",
hours, minutes, seconds, hundredths,
info->akbps, info->vkbps,
remaining_hours, remaining_minutes, remaining_seconds
);
}
else {
fprintf (stderr,"\r %d:%02d:%02d.%02d audio: %dkbps video: %dkbps, ET: %02d:%02d:%02d, est. size: %.01lf MB ",
hours, minutes, seconds, hundredths,
info->akbps, info->vkbps,
remaining_hours, remaining_minutes, remaining_seconds,
estimated_size(info, timebase)
);
}
}
}
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment