Learning Python – Making a SVCD GUI

I was making a VCD the other day for my kid and rediscover how hard it can be to make one of those – in Ubuntu-. I love the OS but every application that I tried was from useless to lousy, as often happen in the Linux world I discover the absolute best tools are on the command line.

While I don’t mind playing with the terminal, the truth is there’s no way I’ll ever remember all those special flags, parameters and other strange things you need to re-encode videos, so I decided to try my freshly acquired Python knowledge to make a small tool to pass my commands to the terminal and encode the videos using mencoder.

I was harder than expected and the final result could be better, but it’s not bad for a first hands on!

To develop this project I used:

  • Geany. For coding
  • Glade. To design the interface

Functionalities:

  • Can convert a single video to VCD or SVCD
  • Can crop a single video (Time start – Time End) < Both in seconds
  • Can convert all videos in a directory to VCD or SVCD

Problems:

  • The progress bar is useless, it never seems to update
  • Better to be run from terminal so you can see the actual progress of the task

The source:
This code include the glade file, the final .xml to define the interface and the python file (Change the format after downloading)
VCDmaker.zip

The python code.

  1. #!/usr/bin/env python
  2.  
  3. # gtk-builder-convert tutorial.glade vcdmaker.xml
  4. # Then save this file as tutorial.py and make it executable using this command:
  5. # chmod a+x vcdtrigger.py
  6. # And execute it:
  7. # ./vcdtrigger.py
  8.  
  9. import pygtk
  10. pygtk.require("2.0")
  11. import gtk
  12. import os
  13. import subprocess
  14.  
  15. class vcd(object):      
  16.     def __init__(self):
  17.         builder = gtk.Builder()
  18.         builder.add_from_file("vcdmaker.xml")
  19.         builder.connect_signals(self)
  20.         self.window = builder.get_object("Video Maker")
  21.         self.saveas = builder.get_object("txt_saveas")
  22.         self.filesource = builder.get_object("file_source")
  23.         self.dirsource = builder.get_object("dirchooser")
  24.         self.dirsource.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
  25.         self.pbar = builder.get_object("progressbar1")
  26.         self.type = builder.get_object("sel_target")
  27.         self.type.set_active(1)
  28.         self.output = builder.get_object("txtoutput")
  29.         self.ini = builder.get_object("spin_start")
  30.         self.end = builder.get_object("spin_end")
  31.         self.window.show()
  32.  
  33.     def on_window_destroy(self, widget, data=None):
  34.         gtk.main_quit()
  35.    
  36.     def target_name(self,filename):
  37.         parts = os.path.split(filename)
  38.         name = parts[1]
  39.         name = name.replace(" ","_")
  40.         name = name.lower()
  41.         ext = os.path.splitext(name)[1]
  42.         ext = ext[1:]
  43.         ext = ext.lower()
  44.         videoformats = [‘mpg’,‘avi’,‘mpeg’, ‘flv’]
  45.         if (ext in videoformats):
  46.             return(parts[0] + os.sep + "VCD" + os.sep + "VCD_" + os.path.splitext(name)[0]+".mpg")
  47.         else:
  48.             return False
  49.    
  50.     def on_dirchooser_file_set(self, dir_chooser):
  51.         self.saveas.set_text("Multiple")
  52.    
  53.     def on_file_source_file_set(self, file_chooser):
  54.         if (self.filesource.get_filename() != None):
  55.             saveas = self.target_name(self.filesource.get_filename())
  56.             if (saveas != False):
  57.                 self.saveas.set_text(saveas)
  58.             else:
  59.                 self.error_message ("Please select a video file")
  60.    
  61.     def on_btnstart_clicked(self, button):
  62.         if (self.saveas.get_text() == ""):
  63.             print("Please select a file first")
  64.             self.error_message("Please select a file first")
  65.         elif self.saveas.get_text() == "Multiple":
  66.             print self.dirsource.get_filename()
  67.             self.convert_all(self.dirsource.get_filename(), self.type.get_active())
  68.         else:
  69.             targetdir = os.path.split(self.saveas.get_text())
  70.             print (targetdir)
  71.             self.check_target_dir(targetdir[0])
  72.             c = converter()
  73.             if self.type.get_active() == 1:
  74.                 c.svcd(self.filesource.get_filename(), self.saveas.get_text(), self.ini.get_value_as_int(), self.end.get_value_as_int())
  75.             else:
  76.                 c.vcd(self.filesource.get_filename(), self.saveas.get_text(), self.ini.get_value_as_int(), self.end.get_value_as_int())
  77.  
  78.  
  79.     def check_target_dir(self, dirname):
  80.         print (dirname)
  81.         if not os.path.isdir(dirname):
  82.             os.makedirs(dirname)
  83.  
  84.    
  85.     def convert_all(self, dir, format):
  86.         print ("dir: %s" % dir)
  87.         c = converter()
  88.         self.check_target_dir(dir + os.sep + "VCD")
  89.         x=0
  90.         for directory, subdirectories, files in os.walk(dir):
  91.             file_count = len(files)
  92.             if (x==0):
  93.                 self.pbar.set_text("Processing %d" % file_count)
  94.             for file in files:
  95.                 x +=1
  96.                 print ("Processing file %d of %d " % (x, file_count))
  97.                 self.pbar.set_fraction( x / file_count)
  98.                 print (x/file_count)
  99.                 source = os.path.join(directory,file)
  100.                 target = self.target_name(source)
  101.                 if self.type.get_active() ==1:
  102.                     c.svcd(source, target, 0, 0)
  103.                 else:
  104.                     c.vcd(source, target, 0, 0)
  105.  
  106.     def error_message(self, message):
  107.         print message
  108.         dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, message)
  109.         dialog.run()
  110.         dialog.destroy()
  111.        
  112. class converter:
  113.     def test(self):
  114.         print ("test ok")
  115.        
  116.     def get_common_cmd(self, source, ini, end):
  117.         command = [‘mencoder’, source]
  118.         if (ini != 0 ):
  119.             command.append("-ss")
  120.             command.append("%d" % ini)
  121.         if (end!=0):
  122.             command.append("-endpos")
  123.             command.append("%d " % end)
  124.         return command
  125.        
  126.     def vcd(self, source, target, ini=0, end=0):
  127.         command = self.get_common_cmd(source, ini,end)
  128.         command += [‘-oac’ ,‘lavc’ ,‘-ovc’ ,‘lavc’ ,‘-of’ ,‘mpeg’ ,‘-mpegopts’ ,‘format=xsvcd’ ,‘-vf’ ,‘scale=480:576,harddup’ ,‘-srate’ ,‘44100’ ,‘-af’ ,‘lavcresample=44100’ ,‘-lavcopts’  ,‘vcodec=mpeg2video:mbd=2:keyint=15:vrc_buf_size=917:vrc_minrate=600:vbitrate=2500:vrc_maxrate=2500:acodec=mp2:abitrate=224:aspect=16/9’ ,‘-ofps’ ,’25’ ,‘-o’, target]
  129.         self.convert(command)
  130.  
  131.     def svcd(self,source, target, ini=0, end=0):       
  132.         command = self.get_common_cmd(source, ini,end)
  133.         command += [‘-oac’, ‘lavc’ ,‘-ovc’, ‘lavc’ ,‘-of’ ,‘mpeg’ ,‘-mpegopts’ ,‘format=xvcd’ ,‘-vf’ ,‘scale=352:288,harddup’ ,‘-srate’, ‘44100’ ,‘-af’ ,‘lavcresample=44100’ ,‘-lavcopts’ ,‘vcodec=mpeg1video:keyint=15:vrc_buf_size=327:vrc_minrate=1152:vbitrate=1152:vrc_maxrate=1152:acodec=mp2:abitrate=224:aspect=16/9’ ,‘-ofps’ ,’25’ ,‘-o’, target]       
  134.         self.convert(command)
  135.        
  136.     def convert (self, command):
  137.         print (command)
  138.         #Execute the actual command and put result in text
  139.         #os.execl ("mencoder", command)
  140.         subprocess.call(command)
  141.        
  142. if __name__ == "__main__":
  143.     app = vcd()
  144.     gtk.main()
  145.  

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s