Editor's WTF?! App Python

What it does

As a video editor you will inevitably get files from your client that will make you say WTF?! These are files in a strange format that you cannot open or transcode into a usable format. I have written this app to leverage the FFMPEG binaries to create a simple transcoding tool. The motivation is simple, take a file you cannot read and transcode it directly to ProRes compatible format while retaining the native frame rate and frame size.

You can see the actual code I wrote in the next tab. You can also see the screen shots from the app.

The code

# -*- coding: utf-8 -*- ########################################################################### ## Python code generated with wxFormBuilder (version Sep 12 2010) ## http://www.wxformbuilder.org/ ## ## PLEASE DO "NOT" EDIT THIS FILE! ########################################################################### from threading import * import os import signal from os.path import basename import datetime import subprocess import json import string import wx import wx.richtext app = wx.App() ########################################################################### ## Class WtfEditor ########################################################################### class WtfEditor ( wx.Frame ): def __init__( self, parent ): wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = u"WTF Video Converter", pos = wx.DefaultPosition, size = wx.Size( 650,500 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL, name = u"Editor's WTF?!" ) self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize ) fgSizer1 = wx.FlexGridSizer( 5, 1, 0, 0 ) fgSizer1.SetFlexibleDirection( wx.BOTH ) fgSizer1.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED ) self.src_file_picker = wx.FilePickerCtrl( self, wx.ID_ANY, u"Select a source file:", u"Select a file", u"*.*", wx.DefaultPosition, wx.Size( 600,-1 ), wx.FLP_DEFAULT_STYLE ) self.src_file_picker.SetMinSize( wx.Size( 600,-1 ) ) fgSizer1.Add( self.src_file_picker, 0, wx.ALL, 5 ) self.m_richText1 = wx.richtext.RichTextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size( -1,300 ), 0|wx.HSCROLL|wx.NO_BORDER|wx.VSCROLL|wx.WANTS_CHARS ) fgSizer1.Add( self.m_richText1, 1, wx.EXPAND |wx.ALL, 5 ) self.dest_dir_picker = wx.DirPickerCtrl( self, wx.ID_ANY, u"Select a destination:", u"Select a destination folder", wx.DefaultPosition, wx.Size( 600,-1 ), wx.DIRP_DEFAULT_STYLE ) self.dest_dir_picker.SetMinSize( wx.Size( 600,-1 ) ) fgSizer1.Add( self.dest_dir_picker, 0, wx.ALL, 5 ) self.transcode_button = wx.Button( self, wx.ID_ANY, u"Transcode", wx.DefaultPosition, wx.DefaultSize, 0 ) fgSizer1.Add( self.transcode_button, 0, wx.ALIGN_RIGHT|wx.ALL, 5 ) self.transcode_button.Enable( False ) self.output_textCtrl1 = wx.TextCtrl( self, wx.ID_ANY, u"testing me", wx.DefaultPosition, wx.Size( 600,-1 ), wx.TE_READONLY|wx.NO_BORDER ) self.output_textCtrl1.Enable( False ) fgSizer1.Add( self.output_textCtrl1, 0, wx.ALL, 5 ) self.SetSizer( fgSizer1 ) self.Layout() self.Centre( wx.BOTH ) # Connect Events self.src_file_picker.Bind( wx.EVT_FILEPICKER_CHANGED, self.ff_probe_file ) self.dest_dir_picker.Bind( wx.EVT_DIRPICKER_CHANGED, self.set_output ) self.transcode_button.Bind( wx.EVT_BUTTON, self.transcode_thread ) self.m_richText1.BeginTextColour('grey') self.m_richText1.BeginFontSize(20) self.m_richText1.WriteText("Editor's WTF!? - Video File Transcoder") self.m_richText1.EndFontSize() self.m_richText1.LineBreak() self.m_richText1.WriteText("Editor's WTF?! will convert virtually any video file to a ProRes compatible format. The next time your client gives you a file that makes you say 'WTF?!' you can transcode with confidence. Editor's WTF?! keeps the original file frame size and framerate while preserving as much quality as possible.") self.m_richText1.EndTextColour() self.m_richText1.LineBreak() global inputfileok global destinationok global transfile global totalframes inputfileok = False destinationok = False transfile = False def __del__( self ): pass def ok_to_transcode(self,infile,outfile, transfile): if infile == True and outfile == True and transfile == False: self.transcode_button.Enable( True ) print infile print outfile print transfile self.transcode_button.SetLabel( "Transcode" ) elif infile == True and outfile == True and transfile == True: self.transcode_button.SetLabel( "Stop!" ) print "It's a trans!" else: self.transcode_button.Enable( False ) print infile print outfile print transfile self.transcode_button.SetLabel( "Transcode" ) # Virtual event handlers, overide them in your derived class def ff_probe_file( self, event ): global inputfileok global totalframes apppath = os.path.dirname(os.path.realpath(__file__)) probepath = apppath + '/mac-binaries/' filename = self.src_file_picker.GetPath() probecmd = "%sffprobe -v quiet -print_format json -show_format -show_streams '%s'" % (probepath, filename) # tool = "2997/125" # print framerate_convert(tool) try: resultprobe = subprocess.check_output(probecmd, shell=True) parsed_result = json.loads(resultprobe) self.m_richText1.Clear() for stream in parsed_result['streams']: if stream['codec_type'] == "video": self.m_richText1.BeginBold() self.m_richText1.WriteText("Video Track") self.m_richText1.EndBold() self.m_richText1.LineBreak() self.m_richText1.WriteText("Codec: " + stream['codec_name']) self.m_richText1.LineBreak() self.m_richText1.WriteText("Dimensions: " + str(stream['width']) + "x" + str(stream['height'])) self.m_richText1.LineBreak() self.m_richText1.WriteText("Framerate: " + framerate_convert(stream['avg_frame_rate'])) framerate = float(framerate_convert(stream['avg_frame_rate'])) self.m_richText1.LineBreak() self.m_richText1.WriteText("Bitrate: " + str(stream['bit_rate'])[:-3] + " Kb/s") self.m_richText1.LineBreak() self.m_richText1.WriteText("Duration: " + str(stream['duration'])[:-3] + " seconds") duration = float(stream['duration']) self.m_richText1.LineBreak() self.m_richText1.LineBreak() totalframes = int(framerate*duration-1) print totalframes if stream['codec_type'] == "audio": self.m_richText1.BeginBold() self.m_richText1.WriteText("Audio Track") self.m_richText1.EndBold() self.m_richText1.LineBreak() self.m_richText1.WriteText("Codec: " + stream['codec_name']) self.m_richText1.LineBreak() self.m_richText1.WriteText("Sample Rate: " + stream['sample_rate']) self.m_richText1.LineBreak() self.m_richText1.WriteText("Channels: " + str(stream['channels'])) self.m_richText1.LineBreak() self.m_richText1.WriteText("Channel Layout: " + stream['channel_layout']) self.m_richText1.LineBreak() self.m_richText1.WriteText("Bitrate: " + str(stream['bit_rate'])[:-3] + " Kb/s") inputfileok = True except: self.m_richText1.Clear() self.m_richText1.BeginBold() self.m_richText1.WriteText("This is an invalid file type!") self.m_richText1.EndBold() inputfileok = False self.ok_to_transcode(inputfileok,destinationok,transfile) def transcode_thread( self, event ): t = Thread(target=self.ff_transcode) t.start() def set_output( self, event ): global destinationok destinationok = True self.ok_to_transcode(inputfileok,destinationok,transfile) def ff_transcode( self ): global transfile global inputfileok global result apppath = os.path.dirname(os.path.realpath(__file__)) probepath = apppath + '/mac-binaries/' filepath = self.src_file_picker.GetPath() filename = basename(filepath) outputpath = self.dest_dir_picker.GetPath() proresname = outputpath+"/"+filename+"PRORES.mov" filenumber = 1 if self.transcode_button.GetLabel() =="Transcode": while os.path.isfile(proresname): proresname = outputpath+"/"+str(filenumber)+"_"+filename+"_PRORES.mov" filenumber+=1 probecmd = "%sffmpeg -i '%s' -vcodec prores -profile:v 3 -an '%s'" % (probepath,filepath,proresname) transfile = True self.ok_to_transcode(inputfileok,destinationok,transfile) try: #wx.Yield() result = subprocess.Popen(probecmd, shell=True, preexec_fn=os.setsid,stdout=subprocess.PIPE) #self.output_textCtrl1.SetValue("Test me!") line = result.stdout.readline(10) #line = "Did this work" self.output_textCtrl1.SetValue(line) transfile = False self.ok_to_transcode(inputfileok,destinationok,transfile) except: self.m_richText1.Clear() self.m_richText1.BeginBold() self.m_richText1.WriteText("There was an error, perhaps you moved a file or destination folder?") self.m_richText1.EndBold() inputfileok = False transfile = False self.ok_to_transcode(inputfileok,destinationok,transfile) else: os.killpg(os.getpgid(result.pid), signal.SIGTERM) self.transcode_button.SetLabel( "Transcode" ) def framerate_convert(ratio): framerate = ratio.split("/") deducedrate = float(framerate[0])/float(framerate[1]) deducedrate = str(deducedrate) return deducedrate picker = WtfEditor(None) picker.Show() app.MainLoop()

The result

The app is functional, you can see the screenshots below. I'm just working on the last details of creating a progress bar.

Nagios Script

A custom Nagio Monitoring plugin for OS X machines.

Python Backup Script

A script to back up web domains from a Linux VPS.

Resolve Database

Script to back up multiple databases for a Resolve Server running Postgres.

Producer Portal

This is a PHP web application that is used by producers to start a job.

Editor's WTF?!

My effort to create a desktop app written in Python. This is a simple video file transcoder.