Update of /cvsroot/quickrip/quickrip In directory sc8-pr-cvs1:/tmp/cvs-serv26132 Modified Files: cli.py config.py quickrip.py quickriprc Added Files: base.py Removed Files: dvd.py gui.py guimain.py guiprogressdialogue.py guisettings.py Log Message: Added base.py, Removed dvd.py, Moved qt files into "qt" subdirectory, Changed config.py to fit new base.py Changed quickrip.py to handle qt subdirectory A LOT IS NOW BROKEN! CLI AND QT INTERFACES NEED WORK TO MAKE THEM WORK WITH NEW BASE CLASS, DUE TO SOME MAJOR CHANGES, AND THE REPLACEMENT OF 'TRACK' WITH 'TITLE' --- NEW FILE: base.py --- #!/usr/bin/env python """ base.py - QuickRip base code copyright: (C) 2003, Tom Chance license: GNU General Public License (GPL) (see LICENSE file) web: http://quickrip.sf.net email: tom...@gm... """ from __future__ import generators from time import sleep import sys, os, re, popen2, ConfigParser, copy import config # QuickRip global configuration. class QuickRip: """QuickRip base class, including the following methods: loadConfig - loads the user configuration file cautiousLoad - loads a config variable in an exception clause findProgram - takes a program name, returns the full path calcRate - calculates a video bitrate from length, abr and file size calcFileSize - calculates file size from length, abr and video bitrate""" def __init__(self): """Replace this with your own init, but include the one line in your own""" self.initialise() def initialise(self): """the 'actual' init function""" self.cwd = os.getcwd() self.numtitles = 0 self.alangs = [] self.slangs = [] self.titles = [] self.dio = config.dio self.aro = config.dio self.langs = config.langs self.configfile = os.path.join(os.path.expanduser("~"), ".quickriprc") self.loadConfig() def loadConfig(self): """Load user configuration file""" self.config = {} self.parser = ConfigParser.ConfigParser() if not os.path.isfile(self.configfile): self.parser.write(open(self.configfile, 'w')) self.parser.readfp(open(self.configfile, 'r')) variables = { \ 'mplayer': ['paths', self.findProgram("mplayer")], \ 'mencoder': ['paths', self.findProgram("mencoder")], \ 'tcprobe': ['paths', self.findProgram("tcprobe")], \ 'transcode': ['paths', self.findProgram("transcode")], \ 'ogmmerge': ['paths', self.findProgram("ogmmerge")], \ 'outputdir': ['paths', os.path.expanduser("~")], \ 'dvd_device': ['paths', os.path.join("/", "dev", "dvd")], \ 'deinterlacing': ['vencode', 0], \ 'aspectratio': ['vencode', 0], \ 'videocodec': ['vencode', 0], \ 'pdamode': ['vencode', 0], \ 'audiocodec': ['aencode', 0], \ 'volumead': ['aencode', 0], \ 'audiobitrate': ['aencode', 1] \ } for key in variables.keys(): self.cautiousLoad(variables[key][0], key, variables[key][1]) def cautiousLoad(self, section, var, default): """Load a configurable variable within an exception clause, in case variable is not in configuration file""" try: self.config[var] = int(self.parser.get(section, var)) except: self.config[var] = default try: self.parser.set(section, var, default) except: self.parser.add_section(section) self.parser.set(section, var, default) self.parser.write(open(self.configfile, 'w')) def findProgram(self, program): """Looks for program in path, and returns full path if found""" for path in config.paths: if os.path.isfile(os.path.join(path, program)): return os.path.join(path, program) self.int_configError(program) def calcRate(self, length, audiobr, filesize): """Calculate bitrate for video based on: - length of title (seconds) - audio bitrate (kbps) - size of resultant file (MB)""" try: self.filesize = int(filesize) self.length = int(length) self.audiobr = int(audiobr) self.rate = ((self.filesize * 8192) / self.length) - self.audiobr self.rate = self.rate * 0.98 # Correction based on addition of credits and usual error return self.rate except: return 1 def calcFileSize(self, length, audiobr, bitrate): """Calculate vilesize for video based on: - length of title (seconds) - audio bitrate (kbps) - bitrate of title (Kbps)""" try: self.length = int(length) self.audiobr = int(audiobr) self.bitrate = int(bitrate) self.filesize = ((self.bitrate + self.audiobr) * self.length) / 8192 self.filesize = self.filesize * (1 / 0.98) # Correction based on addition of credits and usual error return self.filesize except: return 1 def run(self, program, arguments, flushbuffer=0): """Runs a program; supply program name (string) and arguments (list)""" command = arguments command[:0] = [self.config[program]] self.pipe = popen2.Popen4(command) pid = self.pipe.pid while 1: try: if flushbuffer: line = self.pipe.fromchild.read(1000) else: line = self.pipe.fromchild.readline() if not line: break yield line except: # For PyGtk... weird! sleep(1) continue # Clean up process table try: self.kill_pipe() except: pass try: os.waitpid(pid, os.WNOHANG) except: pass def kill_pipe(self): """Kills current process (pipe)""" try: os.kill(self.pipe.pid, 9) os.waitpid(self.pipe.pid, os.WNOHANG) except: pass def getDVDInfo(self): """Find number of titles on DVD""" arguments = ['-vo', 'null', '-ao', 'null', '-v', 'dvd://', '-identify', '-quiet', '-nocache'] re_title = re.compile('There are (\d*) titles on this DVD') re_error = re.compile('libdvdread: Could not open device') for line in self.run('mplayer', arguments): if re_error.search(line): self.int_noTitles() if re_title.search(line): numtitles = re_title.search(line).group(1) return numtitles def getTitleInfo(self, no): """Find number of chapters, audio/subtitle languages & length for title""" arguments = ['-i', self.config['dvd_device'], '-T', str(no)] re_error = re.compile('No such file or directory') re_time = re.compile('.*title playback time: (.*):(.*):(\d*)\.\d* (\d*) sec') re_chap = re.compile('(\d*) chapter\(s\), ') re_alang = re.compile('.* ac3 (\w*) ') re_slang = re.compile('.* subtitle \d*=<(\w*)>') alangs = [] slangs = [] for line in self.run('tcprobe', arguments): if re_error.search(line): self.int_noTitles() return if re_time.search(line): hours = re_time.search(line).group(1) minutes = re_time.search(line).group(2) seconds = re_time.search(line).group(3) time = re_time.search(line).group(4) time_label = "%2s hrs %2s mins %2s secs" % (hours, minutes, seconds) if re_alang.search(line): alang = re_alang.search(line).group(1) alang_full = self.langs[alang] if alang != "xx": alangs.append([alang, alang_full]) if re_slang.search(line): slang = re_slang.search(line).group(1) slang_full = self.langs[slang] if slang != "xx": slangs.append([slang, slang_full]) if re_chap.search(line): chapters = re_chap.search(line).group(1) return chapters, alangs, slangs, time, time_label def scanDVD(self): """Scan the DVD and build up a data structure for the titles""" self.int_startScanning() # Reset values to default (in case user scans two different discs in same session) self.titles = [] self.numtitles = 0 self.numtitles = self.getDVDInfo() for i in range(int(self.numtitles) + 1): if i is not 0: print i # Get title information from disc chapters, alangs, slangs, time, time_label = self.getTitleInfo(i) # Clean up title if i < 10: title_label = "".join(["0", str(i)]) else: title_label = str(i) title = {} title['id'] = i title['name'] = title_label title['numchapters'] = int(chapters) title['time'] = int(time) title['timelabel'] = time_label title['size'] = 680 title['vbr'] = self.calcRate(int(time), 96, 680) title['rip'] = "no" title['alangs'] = alangs title['alang'] = 'en' title['slangs'] = slangs title['slang'] = '' self.titles.append(title) self.int_dispTitle(title) self.int_finishScanning() def ripDVD(self): """Rip the DVD titles based on gathered settings""" self.int_startRipping() self.state = "ripping" try: os.chdir(self.config['outputdir']) except OSError, msg: print "Unable to change to directory %s: %s" % (self.config['outputdir'], msg) # Build list of titles to rip self.torip = [] for title in self.titles: if title['rip'] == 'yes': self.torip.append(title) self.numrips = len(self.torip) # Check PDA mode (universal to all ripping methods) if int(self.config['pdamode']) is 0: resolution = "720" else: resolution = "320" i = 0 for title in self.torip: self.int_newTitle(title['name'], i, self.numrips, title['vbr']) i = i + 1 # Look for cropping sstep = int(title['time']) / 31 if not sstep: sstep = 1 arguments = ["-dvd ", str(title['id']), "-vop", "cropdetect", "-nosound", "-vo", "null", "-frames", "10", "-sstep", str(sstep)] re_crop = re.compile('.*-vop crop=(\d*:\d*:\d*:\d*).*') crop_options = {} common_crop = "" cc_hits = 0 for line in self.run('mplayer', arguments): if re_crop.search(line): crop = re_crop.search(line).group(1) try: crop_options[crop] = crop_options[crop] + 1 if crop_options[crop] > cc_hits: common_crop = crop except: crop_options[crop] = 1 title['crop'] = common_crop # Clean up output dir in case QuickRip crashed out there os.popen("".join(["rm ", self.config['outputdir'], "frameno.avi 2>/dev/null"])) if self.config['videocodec'] is 2: self.scvd(title, output, resolution) elif self.config['audiocodec'] is 1: output = os.path.join(self.config['outputdir'], "".join([str(title['name']), ".ogm"])) self.oggAudio(title) self.aviVideo(title, output, resolution, 0) else: output = os.path.join(self.config['outputdir'], "".join([str(title['name']), ".avi"])) self.mp3Audio(title) self.aviVideo(title, output, resolution, 1) def mencode(self, arguments, passtype): """Run mencoder with given arguments and report progress""" perc = 0 trem = 0 re_progress = re.compile('(\d+)\%\) .*Trem:\s*(\d+\w+)\s+') for line in self.run('mencoder', arguments, 1): if re_progress.search(line): perc = re_progress.search(line).group(1) trem = re_progress.search(line).group(2) self.int_updateProgress(perc, trem, passtype) def mp3Audio(self, title): """Rip the audio from a DVD title and encode as MP3""" self.int_newPass("mp3audio") try: os.popen("".join(["rm ", os.path.join(self.config['outputdir'], "frameno.avi")])) except: pass if not self.config['volumead']: vol = "".join([":vol=", str(self.config['volumead'])]) else: vol = "" lameopts = "".join(["cbr=", str(config.abr[self.config['audiobitrate']]), str(vol)]) arguments = ["-dvd", str(title['id']), "-alang", title['alang'], "-oac", \ "mp3lame", "-lameopts", lameopts, "-ovc", "frameno", "-o", \ os.path.join(self.config['outputdir'], "frameno.avi")] self.mencode(arguments, "mp3audio") def oggAudio(self, title): """Rip the audio from a DVD title and encode as OGG""" import output print output.bold("NOT IMPLEMENTED YET!") def aviVideo(self, title, output, resolution, merge=1): """Rip the video from a DVD title, and optionally merge with audio""" self.int_newPass("video") if self.config['videocodec']: ovc = "xvid" ovc_opts_type = "-xvidencopts" ovc_opts = "".join(["4mv:me_quality=6:mod_quant:quant_range=1-31/1-31:bitrate=", str(int(title['vbr']))]) else: ovc = "lavc" ovc_opts_type = "-lavcopts" ovc_opts = "".join(["vcodec=mpeg4:vhq:vbitrate=", str(int(title['vbr']))]) vop = "".join(["scale,crop=", title['crop']]) if int(self.config['deinterlacing']) is not 0: vop = "".join([vop, ",", self.dio[self.config['deinterlacing']]]) if merge: oac = "frameno" else: oac = "null" arguments = ["-dvd", str(title['id']), "-alang", title['alang'], "-oac", oac, \ "-ovc", ovc, ovc_opts_type, ovc_opts, "-vop", vop, "-zoom", \ "-xy", resolution, "-o", output] if int(self.config['aspectratio']) is not 0: arguments.insert(3, "-aspect") arguments.insert(4, self.aro[self.config['aspectratio']]) if title['slang']: arguments.insert(3, "-slang") arguments.insert(4, title['slang']) self.mencode(arguments, "video") def ogmerge(self, number, title, output, resolution): """Merge the OGG audio and MPEG4 video using ogmmerge""" import output print output.bold("NOT IMPLEMENTED YET!") def svcd(self, number, title, output, resolution): """Rip the title to an SVCD MPEG using transcode""" import output print output.bold("NOT IMPLEMENTED YET!") ## INHERIT THE CLASS AND SUBSTITUE THESE CLASS METHODS WITH YOUR ## OWN UI HOOKS def int_startScanning(self): pass def int_noTitles(self): pass def int_dispDVD(self): pass def int_dispTitle(self, title): pass def int_finishScanning(self): pass def int_startRipping(self): pass def int_newTitle(self, name, number, total, vbr): pass def int_newPass(self, passtype): #pass print passtype def int_updateProgress(self, perc, trem, tpass): #pass print "%s percent, %s remaining in %s" % (perc, trem, tpass) def int_finishRipping(self): pass Index: cli.py =================================================================== RCS file: /cvsroot/quickrip/quickrip/cli.py,v retrieving revision 1.17 retrieving revision 1.18 diff -C2 -d -r1.17 -r1.18 *** cli.py 1 Aug 2003 11:51:44 -0000 1.17 --- cli.py 5 Aug 2003 17:50:36 -0000 1.18 *************** *** 11,15 **** try: import sys, os, re, output ! from dvd import * except ImportError, desc: print "Unable to load module (", desc, ")" --- 11,15 ---- try: import sys, os, re, output ! from base import * except ImportError, desc: print "Unable to load module (", desc, ")" Index: config.py =================================================================== RCS file: /cvsroot/quickrip/quickrip/config.py,v retrieving revision 1.10 retrieving revision 1.11 diff -C2 -d -r1.10 -r1.11 *** config.py 30 Jul 2003 11:07:10 -0000 1.10 --- config.py 5 Aug 2003 17:50:36 -0000 1.11 *************** *** 22,23 **** --- 22,56 ---- icon = os.path.join("icons", "quickrip.xpm") qr_dir = "/usr/share/quickrip" + aro = {0: 'Default', 1: '4:3', 2: '16:9', 3: '2.35:1'} + dio = {0 : 'None', 1: 'dint', 2: 'il', 3: 'lavcdeint', 4: 'pp=lb'} + abr = {0: 64, 1: 96, 2: 128, 3: 160, 4: 190} + langs = { \ + " ": "Not Specified", "aa": "Afar", "ab": "Abkhazian", "af": "Afrikaans", "am": "Amharic", \ + "ar": "Arabic", "as": "Assamese", "ay": "Aymara", "az": "Azerbaijani", "ba": "Bashkir", \ + "be": "Byelorussian", "bg": "Bulgarian", "bh": "Bihari", "bi": "Bislama", "bn": "Bengali; Bangla", \ + "bo": "Tibetan", "br": "Breton", "ca": "Catalan", "co": "Corsican", "cs": "Czech", \ + "cy": "Welsh", "da": "Dansk", "de": "Deutsch", "dz": "Bhutani", "el": "Greek", "en": "English", \ + "eo": "Esperanto", "es": "Espanol", "et": "Estonian", "eu": "Basque", "fa": "Persian", \ + "fi": "Suomi", "fj": "Fiji", "fo": "Faroese", "fr": "Francais", "fy": "Frisian", "ga": "Gaelic", \ + "gd": "Scots Gaelic", "gl": "Galician", "gn": "Guarani", "gu": "Gujarati", "ha": "Hausa", \ + "he": "Hebrew", "hi": "Hindi", "hr": "Hrvatski", "hu": "Magyar", "hy": "Armenian", \ + "ia": "Interlingua", "id": "Indonesian", "ie": "Interlingue", "ik": "Inupiak", "in": "Indonesian", \ + "is": "Islenska", "it": "Italiano", "iu": "Inuktitut", "iw": "Hebrew", "ja": "Japanese", \ + "ji": "Yiddish", "jw": "Javanese", "ka": "Georgian", "kk": "Kazakh", "kl": "Greenlandic", \ + "km": "Cambodian", "kn": "Kannada", "ko": "Korean", "ks": "Kashmiri", "ku": "Kurdish", \ + "ky": "Kirghiz", "la": "Latin", "ln": "Lingala", "lo": "Laothian", "lt": "Lithuanian", \ + "lv": "Latvian, Lettish", "mg": "Malagasy", "mi": "Maori", "mk": "Macedonian", "ml": "Malayalam", \ + "mn": "Mongolian", "mo": "Moldavian", "mr": "Marathi", "ms": "Malay", "mt": "Maltese", \ + "my": "Burmese", "na": "Nauru", "ne": "Nepali", "nl": "Nederlands", "no": "Norsk", "oc": "Occitan", \ + "om": "Oromo", "or": "Oriya", "pa": "Punjabi", "pl": "Polish", "ps": "Pashto, Pushto", \ + "pt": "Portugues", "qu": "Quechua", "rm": "Rhaeto-Romance", "rn": "Kirundi", "ro": "Romanian" , \ + "ru": "Russian", "rw": "Kinyarwanda", "sa": "Sanskrit", "sd": "Sindhi", "sg": "Sangho", \ + "sh": "Serbo-Croatian", "si": "Sinhalese", "sk": "Slovak", "sl": "Slovenian", "sm": "Samoan", \ + "sn": "Shona" , "so": "Somali", "sq": "Albanian", "sr": "Serbian", "ss": "Siswati", \ + "st": "Sesotho", "su": "Sundanese", "sv": "Svenska", "sw": "Swahili", "ta": "Tamil", \ + "te": "Telugu", "tg": "Tajik", "th": "Thai", "ti": "Tigrinya", "tk": "Turkmen", "tl": "Tagalog", \ + "tn": "Setswana", "to": "Tonga", "tr": "Turkish", "ts": "Tsonga", "tt": "Tatar", "tw": "Twi", \ + "ug": "Uighur", "uk": "Ukrainian", "ur": "Urdu", "uz": "Uzbek", "vi": "Vietnamese", \ + "vo": "Volapuk", "wo": "Wolof", "xh": "Xhosa", "yi": "Yiddish", "yo": "Yoruba", "za": "Zhuang", \ + "zh": "Chinese", "zu": "Zulu" \ + } \ No newline at end of file Index: quickrip.py =================================================================== RCS file: /cvsroot/quickrip/quickrip/quickrip.py,v retrieving revision 1.9 retrieving revision 1.10 diff -C2 -d -r1.9 -r1.10 *** quickrip.py 31 Jul 2003 18:03:20 -0000 1.9 --- quickrip.py 5 Aug 2003 17:50:36 -0000 1.10 *************** *** 60,65 **** # Fallback to GUI mode... if 'DISPLAY' in os.environ: ! import gui ! gui.main() else: print "No $DISPLAY found. Using CLI interface" --- 60,66 ---- # Fallback to GUI mode... if 'DISPLAY' in os.environ: ! sys.path.append("qt") ! import qtgui ! qtgui.main() else: print "No $DISPLAY found. Using CLI interface" Index: quickriprc =================================================================== RCS file: /cvsroot/quickrip/quickrip/quickriprc,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** quickriprc 13 Jun 2003 20:19:50 -0000 1.2 --- quickriprc 5 Aug 2003 17:50:36 -0000 1.3 *************** *** 14,22 **** # Paths to binaries tcprobe = /usr/bin/tcprobe mplayer = /usr/bin/mplayer mencoder = /usr/bin/mencoder ! [mencoder] # Aspect ratio for DVD # (0 = default, 4:3 --- 14,27 ---- # Paths to binaries tcprobe = /usr/bin/tcprobe + transcode = /usr/bin/transcode mplayer = /usr/bin/mplayer mencoder = /usr/bin/mencoder + ogmmerge = /usr/bin/ogmmerge + # Path to DVD device + dvd_device = /dev/dvd ! ! [vencode] # Aspect ratio for DVD # (0 = default, 4:3 *************** *** 34,43 **** deinterlacing = 0 ! # Number of mencoder passes ! # Can be 1, 2 or 3 ! passes = 3 #PDA mode (changes output resolution to suitable for PDAs) # 0 = off # 1 = on ! pdamode = 0 \ No newline at end of file --- 39,69 ---- deinterlacing = 0 ! # Video Codec, can be: ! # 0 - DivX ! # 1 - XviD ! # 2 - (S)VCD (MPEG-2) ! videocodec = 0 #PDA mode (changes output resolution to suitable for PDAs) # 0 = off # 1 = on ! pdamode = 0 ! ! ! [aencode] ! # Audio codec, can be: ! # 0 - MP3 ! # 1 - OGG ! audiocodec = 0 ! ! # Audio bitrate, can be: ! # 0 - 64kbps ! # 1 - 96kbps ! # 2 - 128kbps ! # 3 - 160kbps ! # 4 - 190kbps ! audiobitrate = 1 ! ! # Volume adjustment, can be: ! # 0 (none) - 10 ! volumead = 0 \ No newline at end of file --- dvd.py DELETED --- --- gui.py DELETED --- --- guimain.py DELETED --- --- guiprogressdialogue.py DELETED --- --- guisettings.py DELETED --- |