Commits

boralyl committed b2a27b2 Draft

Initial release.

Comments (0)

Files changed (6)

+import os
 import wx
 
+from mkiso.mkiso import ISOCreator
+
 from ui import MakeIsoUi
 
 
 
     def __init__(self):
         app = wx.App()
-        make_iso_app = MakeIsoApp('MakeISO', (450, 450))
+        make_iso_app = MakeIsoApp('MakeISO', (450, 220))
+        make_iso_app.Show()
         app.MainLoop()
 
 
 
     def __init__(self, title, size):
         super(MakeIsoApp, self).__init__(title, size)
+        self.bind_events()
+        self.path = None
+        self.progress_dlg = None
+
+    def bind_events(self):
+        """
+        Binds all of the events to functions
+        """
+        self.Bind(wx.EVT_BUTTON, self.on_choose, self.choose_btn)
+        self.Bind(wx.EVT_BUTTON, self.on_create, self.create_btn)
+
+    def on_choose(self, event):
+        wildcard = "Video Files (*.mp4; *.mpg; *.avi; *.mkv)|" \
+            "*.mp4;*.mpg;*.avi;*.mkv|All files (*.*)|*.*"
+        dlg = wx.FileDialog(self, message="Choose a video", wildcard=wildcard,
+            style=wx.OPEN|wx.CHANGE_DIR)
+        if dlg.ShowModal() == wx.ID_OK:
+            self.path = dlg.GetPath()
+            self.create_btn.Show()
+        dlg.Destroy()
+        self.display_selected_file()
+
+    def display_selected_file(self):
+        """
+        Creates static text to display the selected file.
+        """
+        if self.selected_file:
+            self.selected_file.Destroy()
+        self.selected_file = wx.StaticText(self.main_panel, -1,
+            "Selected File: " + self.path, pos=(10, 50),
+            size=(self.width -20, 50))
+
+    def on_create(self, event):
+        """
+        Displays dialog and runs conversion to ISO
+        """
+        self.progress_dlg = wx.ProgressDialog("Making ISO", "Converting",
+            maximum=100, parent=self,
+            style=wx.PD_CAN_ABORT|wx.PD_APP_MODAL)
+        self.run_makeiso()
+        self.progress_dlg.Update(100, "Done!")
+        self.progress_dlg.Destroy()
+        self.progress_dlg = None
+        self.hide_convert()
+
+    def hide_convert(self):
+        """
+        Hides the selected file button and text
+        """
+        self.create_btn.Hide()
+        self.selected_file.Hide()
+
+    def run_makeiso(self):
+        """
+        Runs the makeiso module
+        """
+        make_iso = ISOCreator([self.path], '~/file.iso', True, 5)
+        self.progress_dlg.Update(25, "Converting to MPEG2...")
+        for video in make_iso.video_files:
+            video = os.path.abspath(video)
+            make_iso.convert_video(video)
+        self.progress_dlg.Update(65, "Creating DVD titles...")
+        make_iso.dvdauthor()
+        self.progress_dlg.Update(80, "Creating DVD ISO Image...")
+        make_iso.mkiso()
+        make_iso.cleanup()
 
     def run(self):
         app = wx.App()

mkiso/__init__.py

Empty file added.
+#!/usr/bin/python
+"""
+Utility script to automate the process of converting multiple video files
+in different formats into a DVD ISO with chapters specified by the -c or --chapters
+switch (defaults to 10 minutes).
+
+TODO: What other command line args are useful?  Maybe a switch to remove
+      converted mpegs
+"""
+import argparse
+import os
+import re
+import shutil
+import subprocess
+
+from utils import get_chapters, parse_args, run_cmd
+from videotypes import AviFile, InvalidVideoFormatError, Mp4File, MkvFile
+
+
+VALID_EXTENSIONS = ('avi', 'mkv', 'mpg', 'mp4')
+
+
+class ISOCreator(object):
+    """
+    Class for converting videos into DVD ISOs
+    """
+
+    def __init__(self, video_files, output_file, verbose, chapters):
+        self.video_files = video_files
+        self.output_file = output_file
+        self.converted_videos = []
+        self.verbose = verbose
+        self.chapters = chapters
+        os.putenv("VIDEO_FORMAT", "NTSC")
+
+    def convert_video(self, video):
+        """
+        Converts videos into DVD MPEG2 formats
+        """
+        if self.verbose:
+            print "Converting %s to MPEG2..." % (video, )
+        ext = video.split('.')[-1]
+        mpg_file = ".".join(video.split('.')[:-1]) + ".mpg"
+        if ext not in VALID_EXTENSIONS:
+            raise InvalidVideoFormatError(video)
+        elif ext == 'avi':
+            avi = AviFile(video)
+            success = avi.convert()
+            if success:
+                self.converted_videos.append(mpg_file)
+        elif ext == 'mkv':
+            mkv = MkvFile(video)
+            success = mkv.convert()
+            if success:
+                self.converted_videos.append(mpg_file)
+        elif ext == 'mpg':
+            self.converted_videos.append(video)
+        elif ext == 'mp4':
+            mp4 = Mp4File(video)
+            success = mp4.convert()
+            if success:
+                self.converted_videos.append(mpg_file)
+
+    def dvdauthor(self):
+        """
+        Uses dvdauthor to create titles and chapters
+        """
+        if self.verbose:
+            print "Running DVDAuthor to create titles and chapters..."
+        xml = "<dvdauthor><vmgm /><titleset><titles><pgc>"
+        for vid in self.converted_videos:
+            xml += "<vob file=\"%s\" chapters=\"%s\" />" % (vid,
+                get_chapters(self.chapters))
+        xml += "</pgc></titles></titleset></dvdauthor>"
+        fp = open("/tmp/dvd.xml", "w")
+        fp.write(xml)
+        fp.close()
+        os.mkdir("/tmp/DVD")
+        cmd = "dvdauthor -o /tmp/DVD -x /tmp/dvd.xml"
+        run_cmd(cmd)
+        cmd2 = "dvdauthor -T -o /tmp/DVD"
+        run_cmd(cmd2)
+
+    def mkiso(self):
+        """
+        Creates an ISO
+        """
+        if self.verbose:
+            print "Creating the ISO..."
+        cmd = "mkisofs -dvd-video -o %s /tmp/DVD" % (self.output_file, )
+        run_cmd(cmd)
+
+    def run(self):
+        """
+        Runs the main loop
+        """
+        for video in self.video_files:
+            video = os.path.abspath(video)
+            self.convert_video(video)
+        self.dvdauthor()
+        self.mkiso()
+        self.cleanup()
+
+    def cleanup(self):
+        """
+        Removed mpg files and any other non-necessary files created
+        """
+        if self.verbose:
+            print "Cleaning up files..."
+        shutil.rmtree("/tmp/DVD")
+        os.remove("/tmp/dvd.xml")
+
+
+def main():
+    args = parse_args()
+    creator = ISOCreator(args.video_files, args.output, args.verbose, args.chapters)
+    creator.run()
+
+
+if __name__ == "__main__":
+    main()
+import argparse
+import subprocess
+
+
+def parse_args():
+    parser = argparse.ArgumentParser(description="Converts list of videos to"
+        " DVD compaitble iso")
+    parser.add_argument("-v", "--verbose", action="store_true", dest="verbose",
+        default=False, help="Enables verbose mode.")
+    parser.add_argument("-c", "--chapters", dest="chapters", type=int,
+        default=10, help="Specify the time in minutes between chapters.")
+    parser.add_argument("-o", required=True, dest="output",
+        help="Output file.")
+    parser.add_argument("video_files", nargs="+",
+        help="Video files to convert.")
+    return parser.parse_args()
+
+
+def get_chapters(x):
+    """
+    Returns a comma seperated list of chapters
+    for every x minutes in 2hr 30mins
+    example:
+    >>> get_chapters(30)
+    >>> 0:00:00,0:30:00,1:00:00,1:30:00,2:00:00,2:30:00
+    """
+    max_time = 150
+    num_chapters = (max_time / x) + 1
+    chapters = []
+    for i in xrange(0, num_chapters):
+        m = i * x
+        h = m / 60
+        if h > 0:
+            m = m - (h * 60)
+        chapters.append("%s:%s:00" % (h, str(m).zfill(2)))
+    return ",".join(chapters)
+
+
+def run_cmd(cmd, show_output=False):
+    """
+    Runs an arbitrary command, and handles any errors that occur
+    TODO: Make this more robust.
+    """
+    cmd = cmd.split(' ')
+    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+        stderr=subprocess.STDOUT)
+    output, error = proc.communicate()
+    if show_output:
+        return output
+    if error:
+        print error
+        return False
+    else:
+        return True
+#!/usr/bin/python
+"""
+This file contains classes of possible video types and each class is
+responsible for defining how it converts it's filetype to a MPEG2 compaitable
+format.
+"""
+import re
+
+from utils import run_cmd
+
+
+class InvalidVideoFormatError(Exception):
+
+    def __str__(self):
+        return "%s is not a valid video format." % (self.args[0], )
+
+
+class VideoFile(object):
+
+    def __init__(self, filename):
+        self.filename = filename
+        self.mpg_file = ".".join(filename.split('.')[:-1]) + ".mpg"
+
+    def get_convert_cmd(self):
+        pass
+
+    def convert(self):
+        return run_cmd(self.get_convert_cmd())
+
+
+class MkvFile(VideoFile):
+
+    STREAM_RE = re.compile(
+        "Stream\s\#(?P<stream>0\.[01]{1})(\(eng\))?\:\s(?P<type>[a-zA-Z]+)\:")
+
+    def get_convert_cmd(self):
+        vstream, astream = self.get_streams()
+        return "ffmpeg -threads 2 -v 1 -i %s -r ntsc -target dvd -b 4771k -s 720x480 -acodec copy -copyts -aspect 16:9 %s -map %s -map %s" % (
+            self.filename, self.mpg_file, vstream, astream)
+
+    def get_streams(self):
+        cmd = "ffmpeg -i %s" % (self.filename, )
+        info = run_cmd(cmd, show_output=True)
+        matches = self.STREAM_RE.findall(info)
+        if matches[0][-1] == 'Audio':
+            audio = matches[0][0]
+            video = matches[1][0]
+        else:
+            audio = matches[1][0]
+            video = matches[0][0]
+        return (video, audio)
+
+
+class AviFile(VideoFile):
+
+    def get_convert_cmd(self):
+        return "ffmpeg -i %s -target ntsc-dvd -aspect 16:9 -sameq %s" % (
+            self.filename, self.mpg_file)
+
+
+class Mp4File(VideoFile):
+
+    def get_convert_cmd(self):
+        return "ffmpeg -i %s -target ntsc-dvd -aspect 16:9 -s 720x480 %s" % (
+            self.filename, self.mpg_file)
+
 
 
 class MakeIsoUi(wx.Frame):
+    """
+    Creates all of the GUI elements
+    """
 
     def __init__(self, title, size):
         super(MakeIsoUi, self).__init__(None, title=title, size=size,
             style=wx.CLOSE_BOX|wx.CAPTION)
+        self.size = size
+        self.width = size[0]
+        self.height = size[1]
+        self.main_panel = wx.Panel(self, pos=(0, 0), size=size)
+        self.create_buttons()
+        self.selected_file = None
+
+    def create_buttons(self):
+        """
+        Creates the buttons for the UI
+        """
+        btn_width = self.width - 20
+        self.choose_btn = wx.Button(self.main_panel, -1, "Select File...",
+            pos=(10, 10), size=(btn_width, 30))
+        self.create_btn = wx.Button(self.main_panel, -1, "Create ISO",
+            pos=(10, 110), size=(btn_width, 100))
+        self.create_btn.Hide()
+