1import sys 2import os 3import wx 4from ui_editor import UIEditor 5from ui_options import UIOptions 6 7from constants import * 8from tools import Tools 9from demuxer import Demuxer 10from muxer import Muxer 11from finder import FindKateStreams 12from options import Options 13 14base_width=480 15base_height=240 16serial_width=(base_width/4) 17language_width=(base_width/4) 18category_width=(base_width/2) 19padding=8 20button_width=(base_width-padding*2)/3 21button_height=32 22list_height=96 23 24class UIMain(wx.Frame): 25 def __init__(self): 26 wx.Frame.__init__(self,None,wx.ID_ANY,kdj_name_version) 27 28 self.options=Options(wx.StandardPaths.Get().GetUserConfigDir()) 29 30 try: 31 self.tools=Tools(wx.BeginBusyCursor,wx.EndBusyCursor) 32 except Exception,e: 33 wx.MessageBox('Failed to find necessary tools:\n'+str(e),'Error',style=wx.OK|wx.CENTRE|wx.ICON_ERROR) 34 sys.exit(1) 35 36 self.filename=None 37 self.demuxer=None 38 39 vbox=wx.BoxSizer(wx.VERTICAL) 40 self.SetSizer(vbox) 41 42 self.action_button=self.AddActionButton() 43 vbox.Add(self.action_button,0,wx.EXPAND|wx.ALL,border=padding) 44 45 self.list=self.AddList() 46 vbox.Add(self.list,1,wx.EXPAND|wx.ALL,border=padding) 47 48 bbox=wx.BoxSizer(wx.HORIZONTAL) 49 vbox.Add(bbox,0,wx.EXPAND|wx.ALL,border=padding) 50 self.options_button=self.AddOptionsButton() 51 bbox.Add(self.options_button,0,wx.EXPAND) 52 bbox.Add((padding,0),1,wx.EXPAND) 53 self.help_button=self.AddHelpButton() 54 bbox.Add(self.help_button,0,wx.EXPAND) 55 self.quit_button=self.AddQuitButton() 56 bbox.Add((padding,0),1,wx.EXPAND) 57 bbox.Add(self.quit_button,0,wx.EXPAND) 58 59 self.SetMinSize((base_width+2*padding,base_height)) 60 self.Fit() 61 self.Show(True) 62 63 def SetupActionButton(self,button): 64 if self.filename==None: 65 button.SetLabel('Load Ogg stream') 66 button.Bind(wx.EVT_BUTTON,self.OnLoadButton,button) 67 elif self.demuxer!=None: 68 button.SetLabel('Remux file from parts') 69 button.Bind(wx.EVT_BUTTON,self.OnMuxButton,button) 70 else: 71 button.SetLabel('Demux file') 72 button.Bind(wx.EVT_BUTTON,self.OnDemuxButton,button) 73 74 def AddButton(self,text): 75 button=wx.Button(self,wx.ID_ANY,text) 76 return button 77 78 def AddLoadButton(self): 79 button=self.AddButton('Load file') 80 button.Bind(wx.EVT_BUTTON,self.OnLoadButton,button) 81 return button 82 83 def AddActionButton(self): 84 button=self.AddButton('') 85 self.SetupActionButton(button) 86 return button 87 88 def AddOptionsButton(self): 89 button=wx.Button(self,wx.ID_ANY,'Options',size=(button_width,button_height)) 90 button.Bind(wx.EVT_BUTTON,self.OnOptionsButton,button) 91 return button 92 93 def AddHelpButton(self): 94 button=wx.Button(self,wx.ID_HELP,'Help',size=(button_width,button_height)) 95 button.Bind(wx.EVT_BUTTON,self.OnHelpButton,button) 96 return button 97 98 def AddQuitButton(self): 99 button=wx.Button(self,wx.ID_EXIT,'Quit',size=(button_width,button_height)) 100 button.Bind(wx.EVT_BUTTON,self.OnQuitButton,button) 101 return button 102 103 def OnListSizeChanged(self,event): 104 self.Refresh() 105 event.Skip() 106 107 def Edit(self,list_idx=-1): 108 wx.BeginBusyCursor() 109 dlg=UIEditor(self,self.tools,format=self.options.format) 110 try: 111 kate_streams=FindKateStreams(self.demuxer.GetDirectory()) 112 if list_idx>=0: 113 idx=self.list.GetItemData(list_idx) 114 dlg.addStream(kate_streams[str(idx)]['filename']) 115 else: 116 for idx2 in kate_streams: 117 dlg.addStream(kate_streams[idx2]['filename']) 118 dlg.ShowModal() 119 except Exception,e: 120 print 'Exception: %s' % str(e) 121 dlg.Destroy() 122 wx.EndBusyCursor() 123 124 def OnListDoubleClicked(self,event): 125 idx=self.list.GetFirstSelected() 126 if idx>=0: 127 self.Edit(idx) 128 event.Skip() 129 130 def AddList(self): 131 list=wx.ListCtrl(self,style=wx.LC_REPORT) 132 list.ClearAll() 133 list.InsertColumn(0,'Serial') 134 list.InsertColumn(1,'Language') 135 list.InsertColumn(2,'Category') 136 list.SetColumnWidth(0,serial_width) 137 list.SetColumnWidth(1,language_width) 138 list.SetColumnWidth(2,category_width) 139 list.Bind(wx.EVT_SIZE,self.OnListSizeChanged) 140 list.Bind(wx.EVT_LEFT_DCLICK,self.OnListDoubleClicked) 141 return list 142 143 def CheckAndContinue(self): 144 if self.filename!=None and self.demuxer!=None: 145 ret=wx.MessageBox('Loaded file not remuxed - Quit anyway ?','Warning',parent=self,style=wx.YES|wx.NO_DEFAULT|wx.CENTRE|wx.ICON_QUESTION) 146 if ret!=wx.YES: 147 return False 148 return True 149 150 def SetFilename(self,dir,file): 151 filename=os.path.join(dir,file) 152 # TODO: some *quick* oggness checks 153 self.filename=filename 154 self.SetupActionButton(self.action_button) 155 156 def OnLoadButton(self,event): 157 if not self.CheckAndContinue(): 158 return 159 extensions=['ogg','oga','ogv','ogx'] 160 spec='Ogg streams|' 161 for ext in extensions: 162 spec+='*.'+ext+';' 163 spec+='*.'+ext.capitalize()+';' 164 spec+='*.'+ext.upper()+';' 165 spec+='|All files|*;*.*' 166 loader=wx.FileDialog(self,message='Load Ogg stream',wildcard=spec) 167 ret=loader.ShowModal() 168 if ret==wx.ID_OK: 169 filename=loader.GetFilename() 170 dirname=loader.GetDirectory() 171 self.SetFilename(dirname,filename) 172 173 def FillKateList(self,demuxer): 174 streams=FindKateStreams(demuxer.GetDirectory()) 175 for idx in streams: 176 stream=streams[idx] 177 self.list.Append(['','','']) 178 list_idx=self.list.GetItemCount()-1 179 self.list.SetStringItem(list_idx,0,stream['serial']) 180 self.list.SetStringItem(list_idx,1,stream['language']) 181 self.list.SetStringItem(list_idx,2,stream['category']) 182 self.list.SetItemData(list_idx,int(idx)) 183 return self.list.GetItemCount()>0 184 185 def OnDemuxButton(self,event): 186 try: 187 demuxer=Demuxer(self.tools,self.filename,self.options.format) 188 except Exception,e: 189 wx.MessageBox('Failed to demux file:\n'+str(e),'Error',parent=self,style=wx.OK|wx.CENTRE|wx.ICON_ERROR) 190 return 191 192 if not self.FillKateList(demuxer): 193 wx.MessageBox('No Kate streams found, or not an Ogg stream','Error',parent=self,style=wx.OK|wx.CENTRE|wx.ICON_ERROR) 194 self.filename=None 195 self.SetupActionButton(self.action_button) 196 return 197 198 self.demuxer=demuxer 199 self.SetupActionButton(self.action_button) 200 path=os.path.abspath(self.demuxer.GetDirectory()) 201 wx.MessageBox( 202 'Demuxed files are in '+path+'.\n'+ 203 'Double click on any entry to edit it.\n'+ 204 'You may also edit them with your favorite text editor.', 205 'Edit files', 206 parent=self, 207 style=wx.OK|wx.CENTRE 208 ) 209 210 def RemoveTemporaryFiles(self,directory): 211 streams=FindKateStreams(directory) 212 for idx in streams: 213 stream=streams[idx] 214 filename=stream['filename'] 215 os.unlink(filename) 216 if os.access(filename+'.ogg',os.F_OK): 217 os.unlink(filename+'.ogg') 218 os.unlink(os.path.join(directory,'misc.ogg')) 219 os.rmdir(directory) 220 221 def OnMuxButton(self,event): 222 directory=self.demuxer.GetDirectory() 223 try: 224 if self.options.save_as_copy: 225 root,ext=os.path.splitext(self.filename) 226 remuxed_name=root+'.remuxed'+ext 227 else: 228 remuxed_name=self.filename 229 muxer=Muxer(self.tools,remuxed_name,directory,'kate') 230 while self.list.GetItemCount()>0: self.list.DeleteItem(0) 231 self.demuxer=None 232 self.SetupActionButton(self.action_button) 233 try: 234 self.RemoveTemporaryFiles(directory) 235 except: 236 wx.MessageBox('Failed to remove all temporary files from\n%s' % directory,'Error',parent=self,style=wx.OK|wx.CENTRE|wx.ICON_ERROR) 237 except Exception,e: 238 wx.MessageBox('Failed to remux file:\n'+str(e),'Error',parent=self,style=wx.OK|wx.CENTRE|wx.ICON_ERROR) 239 240 def OnOptionsButton(self,event): 241 dlg=UIOptions(self,self.options) 242# dlg.CenterOnScreen() 243 dlg.ShowModal() 244 dlg.Destroy() 245 246 def OnHelpButton(self,event): 247 wx.MessageBox( 248 kdj_name+' is a remuxing program that allows extracting and decoding Kate tracks from an Ogg stream, '+ 249 'and recreating that Ogg stream after the Kate streams have been altered.\n'+ 250 '\n'+ 251 kdj_name+' requires both the Kate tools (kateenc and katedec) and the oggz tools.'+ 252 '\n\n'+ 253 'Click \'Load Ogg stream\' to load an Ogg file from disk. All types of Ogg streams are supported '+ 254 '(eg, audio, video, etc).\n'+ 255 '\n'+ 256 'Then, click \'Demux file\' to extract Kate streams, and decode them to a temporary place on disk. '+ 257 'You may now edit the decoded Kate streams, either by double clicking on the one you wish to change, '+ 258 'or with your text editor of choice.\n'+ 259 '\n'+ 260 'When done, click \'Remux file from parts\' to reconstruct the Ogg stream with the modified Kate streams. '+ 261 'If you are notified of any error (eg, syntax errors in the modified Kate streams), then go back and '+ 262 'correct any mistakes, and click \'Remux file from parts\' until no errors are reported.\n' 263 '\n'+ 264 'You may now play the rebuilt Ogg stream in your audio/video player, and repeat the demux/remux two step '+ 265 'process until you are happy with the resulting stream.\n'+ 266 '', 267 kdj_name+' help', 268 parent=self, 269 style=wx.OK|wx.CENTRE 270 ) 271 272 def OnQuitButton(self,event): 273 if not self.CheckAndContinue(): 274 return 275 if self.demuxer!=None and self.options.remove_temporary_files: 276 directory=self.demuxer.GetDirectory() 277 try: 278 self.RemoveTemporaryFiles(directory) 279 except: 280 wx.MessageBox('Failed to remove all temporary files from\n%s' % directory,'Error',parent=self,style=wx.OK|wx.CENTRE|wx.ICON_ERROR) 281 sys.exit(0) 282 283