]> xmof Git - DeDRM.git/commitdiff
Front-ends for the mobi tools, and tools updated to work as libraries to the front...
authorsome_updates <some_updates@gmail.com>
Wed, 4 Feb 2015 11:18:20 +0000 (11:18 +0000)
committerApprentice Alf <apprenticealf@gmail.com>
Sat, 28 Feb 2015 11:19:18 +0000 (11:19 +0000)
Kindle_Mobi_Tools/KindlePID.pyw [new file with mode: 0644]
Kindle_Mobi_Tools/Kindleizer.pyw [new file with mode: 0644]
Kindle_Mobi_Tools/MobiDeDRM.pyw [new file with mode: 0644]
Kindle_Mobi_Tools/lib/kindlefix.py
Kindle_Mobi_Tools/lib/kindlepid.py
Kindle_Mobi_Tools/lib/mobidedrm.py
Kindle_Mobi_Tools/lib/mobihuff.py
Kindle_Mobi_Tools/lib/scrolltextwidget.py [new file with mode: 0644]
Kindle_Mobi_Tools/lib/subasyncio.py [new file with mode: 0644]

diff --git a/Kindle_Mobi_Tools/KindlePID.pyw b/Kindle_Mobi_Tools/KindlePID.pyw
new file mode 100644 (file)
index 0000000..719d025
--- /dev/null
@@ -0,0 +1,138 @@
+#!/usr/bin/env python
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+import sys
+sys.path.append('lib')
+import os, os.path, urllib
+import subprocess
+from subprocess import Popen, PIPE, STDOUT
+import subasyncio
+from subasyncio import Process
+import Tkinter
+import Tkconstants
+import tkFileDialog
+import tkMessageBox
+from scrolltextwidget import ScrolledText
+
+class MainDialog(Tkinter.Frame):
+    def __init__(self, root):
+        Tkinter.Frame.__init__(self, root, border=5)
+        self.root = root
+        self.interval = 2000
+        self.p2 = None
+        self.status = Tkinter.Label(self, text='Find your Kindle PID')
+        self.status.pack(fill=Tkconstants.X, expand=1)
+        body = Tkinter.Frame(self)
+        body.pack(fill=Tkconstants.X, expand=1)
+        sticky = Tkconstants.E + Tkconstants.W
+        body.grid_columnconfigure(1, weight=2)
+
+        Tkinter.Label(body, text='Kindle Serial # or iPhone UDID').grid(row=1, sticky=Tkconstants.E)
+        self.serialnum = Tkinter.StringVar()
+        self.serialinfo = Tkinter.Entry(body, width=45, textvariable=self.serialnum)
+        self.serialinfo.grid(row=1, column=1, sticky=sticky)
+
+        msg1 = 'Conversion Log \n\n'
+        self.stext = ScrolledText(body, bd=5, relief=Tkconstants.RIDGE, height=15, width=60, wrap=Tkconstants.WORD)
+        self.stext.grid(row=3, column=0, columnspan=2,sticky=sticky)
+        self.stext.insert(Tkconstants.END,msg1)
+
+        buttons = Tkinter.Frame(self)
+        buttons.pack()
+        self.sbotton = Tkinter.Button(
+            buttons, text="Start", width=10, command=self.convertit)
+        self.sbotton.pack(side=Tkconstants.LEFT)
+
+        Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
+        self.qbutton = Tkinter.Button(
+            buttons, text="Quit", width=10, command=self.quitting)
+        self.qbutton.pack(side=Tkconstants.RIGHT)
+
+    # read from subprocess pipe without blocking
+    # invoked every interval via the widget "after"
+    # option being used, so need to reset it for the next time
+    def processPipe(self):
+        poll = self.p2.wait('nowait')
+        if poll != None: 
+            text = self.p2.readerr()
+            text += self.p2.read()
+            msg = text + '\n\n' + 'Kindle PID Successfully Determined\n'
+            if poll != 0:
+                msg = text + '\n\n' + 'Error: Kindle PID Failed\n'
+            self.showCmdOutput(msg)
+            self.p2 = None
+            self.sbotton.configure(state='normal')
+            return
+        text = self.p2.readerr()
+        text += self.p2.read()
+        self.showCmdOutput(text)
+        # make sure we get invoked again by event loop after interval 
+        self.stext.after(self.interval,self.processPipe)
+        return
+
+    # post output from subprocess in scrolled text widget
+    def showCmdOutput(self, msg):
+        if msg and msg !='':
+            self.stext.insert(Tkconstants.END,msg)
+            self.stext.yview_pickplace(Tkconstants.END)
+        return
+
+    # run as a subprocess via pipes and collect stdout
+    def pidrdr(self, serial):
+        # os.putenv('PYTHONUNBUFFERED', '1')
+        cmdline = 'python ./lib/kindlepid.py "' + serial + '"'
+        if sys.platform[0:3] == 'win':
+            search_path = os.environ['PATH']
+            search_path = search_path.lower()
+            if search_path.find('python') >= 0: 
+                cmdline = 'python lib\kindlepid.py "' + serial + '"'
+            else :
+                cmdline = 'lib\kindlepid.py "' + serial + '"'
+
+        p2 = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=PIPE, stderr=PIPE, close_fds=False)
+        return p2
+
+    def quitting(self):
+        # kill any still running subprocess
+        if self.p2 != None:
+            if (self.p2.wait('nowait') == None):
+                self.p2.terminate()
+        self.root.destroy()
+
+    # actually ready to run the subprocess and get its output
+    def convertit(self):
+        # now disable the button to prevent multiple launches
+        self.sbotton.configure(state='disabled')
+        serial = self.serialinfo.get()
+        if not serial or serial == '':
+            self.status['text'] = 'No Kindle Serial Number or iPhone UDID specified'
+            self.sbotton.configure(state='normal')
+            return
+
+        log = 'Command = "python kindlepid.py"\n'
+        log += 'Serial = "' + serial + '"\n'
+        log += '\n\n'
+        log += 'Please Wait ...\n\n'
+        self.stext.insert(Tkconstants.END,log)
+        self.p2 = self.pidrdr(serial)
+
+        # python does not seem to allow you to create
+        # your own eventloop which every other gui does - strange 
+        # so need to use the widget "after" command to force
+        # event loop to run non-gui events every interval
+        self.stext.after(self.interval,self.processPipe)
+        return
+
+
+def main(argv=None):
+    root = Tkinter.Tk()
+    root.title('Kindle and iPhone PID Calculator')
+    root.resizable(True, False)
+    root.minsize(300, 0)
+    MainDialog(root).pack(fill=Tkconstants.X, expand=1)
+    root.mainloop()
+    return 0
+    
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/Kindle_Mobi_Tools/Kindleizer.pyw b/Kindle_Mobi_Tools/Kindleizer.pyw
new file mode 100644 (file)
index 0000000..7efec6e
--- /dev/null
@@ -0,0 +1,163 @@
+#!/usr/bin/env python
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+import sys
+sys.path.append('lib')
+import os, os.path, urllib
+import subprocess
+from subprocess import Popen, PIPE, STDOUT
+import subasyncio
+from subasyncio import Process
+import Tkinter
+import Tkconstants
+import tkFileDialog
+import tkMessageBox
+from scrolltextwidget import ScrolledText
+
+class MainDialog(Tkinter.Frame):
+    def __init__(self, root):
+        Tkinter.Frame.__init__(self, root, border=5)
+        self.root = root
+        self.interval = 2000
+        self.p2 = None
+        self.status = Tkinter.Label(self, text='Fix Encrypted Mobi eBooks so the Kindle can read them')
+        self.status.pack(fill=Tkconstants.X, expand=1)
+        body = Tkinter.Frame(self)
+        body.pack(fill=Tkconstants.X, expand=1)
+        sticky = Tkconstants.E + Tkconstants.W
+        body.grid_columnconfigure(1, weight=2)
+
+        Tkinter.Label(body, text='Mobi eBook input file').grid(row=0, sticky=Tkconstants.E)
+        self.mobipath = Tkinter.Entry(body, width=50)
+        self.mobipath.grid(row=0, column=1, sticky=sticky)
+        self.mobipath.insert(0, os.getcwd())
+        button = Tkinter.Button(body, text="...", command=self.get_mobipath)
+        button.grid(row=0, column=2)
+
+        Tkinter.Label(body, text='10 Character PID').grid(row=1, sticky=Tkconstants.E)
+        self.pidnum = Tkinter.StringVar()
+        self.pidinfo = Tkinter.Entry(body, width=12, textvariable=self.pidnum)
+        self.pidinfo.grid(row=1, column=1, sticky=sticky)
+
+        msg1 = 'Conversion Log \n\n'
+        self.stext = ScrolledText(body, bd=5, relief=Tkconstants.RIDGE, height=15, width=60, wrap=Tkconstants.WORD)
+        self.stext.grid(row=2, column=0, columnspan=2,sticky=sticky)
+        self.stext.insert(Tkconstants.END,msg1)
+
+        buttons = Tkinter.Frame(self)
+        buttons.pack()
+        self.sbotton = Tkinter.Button(
+            buttons, text="Start", width=10, command=self.convertit)
+        self.sbotton.pack(side=Tkconstants.LEFT)
+
+        Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
+        self.qbutton = Tkinter.Button(
+            buttons, text="Quit", width=10, command=self.quitting)
+        self.qbutton.pack(side=Tkconstants.RIGHT)
+
+    # read from subprocess pipe without blocking
+    # invoked every interval via the widget "after"
+    # option being used, so need to reset it for the next time
+    def processPipe(self):
+        poll = self.p2.wait('nowait')
+        if poll != None: 
+            text = self.p2.readerr()
+            text += self.p2.read()
+            msg = text + '\n\n' + 'Fix for Kindle successful\n'
+            if poll != 0:
+                msg = text + '\n\n' + 'Error: Fix for Kindle Failed\n'
+            self.showCmdOutput(msg)
+            self.p2 = None
+            self.sbotton.configure(state='normal')
+            return
+        text = self.p2.readerr()
+        text += self.p2.read()
+        self.showCmdOutput(text)
+        # make sure we get invoked again by event loop after interval 
+        self.stext.after(self.interval,self.processPipe)
+        return
+
+    # post output from subprocess in scrolled text widget
+    def showCmdOutput(self, msg):
+        if msg and msg !='':
+            self.stext.insert(Tkconstants.END,msg)
+            self.stext.yview_pickplace(Tkconstants.END)
+        return
+
+    # run as a subprocess via pipes and collect stdout
+    def krdr(self, infile, pidnum):
+        # os.putenv('PYTHONUNBUFFERED', '1')
+        cmdline = 'python ./lib/kindlefix.py "' + infile + '" "' + pidnum + '"'
+        if sys.platform[0:3] == 'win':
+            search_path = os.environ['PATH']
+            search_path = search_path.lower()
+            if search_path.find('python') >= 0: 
+                cmdline = 'python lib\kindlefix.py "' + infile + '" "' + pidnum + '"'
+            else :
+                cmdline = 'lib\kindlefix.py "' + infile + '" "' + pidnum + '"'
+
+        p2 = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=PIPE, stderr=PIPE, close_fds=False)
+        return p2
+
+
+    def get_mobipath(self):
+        mobipath = tkFileDialog.askopenfilename(
+            parent=None, title='Select Mobi eBook File',
+            defaultextension='.prc', filetypes=[('Mobi eBook File', '.prc'), ('Mobi eBook File', '.mobi'),
+                                                ('All Files', '.*')])
+        if mobipath:
+            mobipath = os.path.normpath(mobipath)
+            self.mobipath.delete(0, Tkconstants.END)
+            self.mobipath.insert(0, mobipath)
+        return
+
+    def quitting(self):
+        # kill any still running subprocess
+        if self.p2 != None:
+            if (self.p2.wait('nowait') == None):
+                self.p2.terminate()
+        self.root.destroy()
+
+    # actually ready to run the subprocess and get its output
+    def convertit(self):
+        # now disable the button to prevent multiple launches
+        self.sbotton.configure(state='disabled')
+        mobipath = self.mobipath.get()
+        pidnum = self.pidinfo.get()
+        if not mobipath or not os.path.exists(mobipath):
+            self.status['text'] = 'Specified Mobi eBook file does not exist'
+            self.sbotton.configure(state='normal')
+            return
+        if not pidnum or pidnum == '':
+            self.status['text'] = 'No PID specified'
+            self.sbotton.configure(state='normal')
+            return
+
+        log = 'Command = "python kindlefix.py"\n'
+        log += 'Mobi Path = "'+ mobipath + '"\n'
+        log += 'PID = "' + pidnum + '"\n'
+        log += '\n\n'
+        log += 'Please Wait ...\n\n'
+        self.stext.insert(Tkconstants.END,log)
+        self.p2 = self.krdr(mobipath, pidnum)
+
+        # python does not seem to allow you to create
+        # your own eventloop which every other gui does - strange 
+        # so need to use the widget "after" command to force
+        # event loop to run non-gui events every interval
+        self.stext.after(self.interval,self.processPipe)
+        return
+
+
+def main(argv=None):
+    root = Tkinter.Tk()
+    root.title('Fix Encrypted Mobi eBooks to work with the Kindle')
+    root.resizable(True, False)
+    root.minsize(300, 0)
+    MainDialog(root).pack(fill=Tkconstants.X, expand=1)
+    root.mainloop()
+    return 0
+    
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/Kindle_Mobi_Tools/MobiDeDRM.pyw b/Kindle_Mobi_Tools/MobiDeDRM.pyw
new file mode 100644 (file)
index 0000000..533223f
--- /dev/null
@@ -0,0 +1,192 @@
+#!/usr/bin/env python
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+import sys
+sys.path.append('lib')
+import os, os.path, urllib
+import subprocess
+from subprocess import Popen, PIPE, STDOUT
+import subasyncio
+from subasyncio import Process
+import Tkinter
+import Tkconstants
+import tkFileDialog
+import tkMessageBox
+from scrolltextwidget import ScrolledText
+
+class MainDialog(Tkinter.Frame):
+    def __init__(self, root):
+        Tkinter.Frame.__init__(self, root, border=5)
+        self.root = root
+        self.interval = 2000
+        self.p2 = None
+        self.status = Tkinter.Label(self, text='Remove Encryption from a Mobi eBook')
+        self.status.pack(fill=Tkconstants.X, expand=1)
+        body = Tkinter.Frame(self)
+        body.pack(fill=Tkconstants.X, expand=1)
+        sticky = Tkconstants.E + Tkconstants.W
+        body.grid_columnconfigure(1, weight=2)
+
+        Tkinter.Label(body, text='Mobi eBook input file').grid(row=0, sticky=Tkconstants.E)
+        self.mobipath = Tkinter.Entry(body, width=50)
+        self.mobipath.grid(row=0, column=1, sticky=sticky)
+        self.mobipath.insert(0, os.getcwd())
+        button = Tkinter.Button(body, text="...", command=self.get_mobipath)
+        button.grid(row=0, column=2)
+
+        Tkinter.Label(body, text='Name for Unencrypted Output File').grid(row=1, sticky=Tkconstants.E)
+        self.outpath = Tkinter.Entry(body, width=50)
+        self.outpath.grid(row=1, column=1, sticky=sticky)
+        self.outpath.insert(0, '')
+        button = Tkinter.Button(body, text="...", command=self.get_outpath)
+        button.grid(row=1, column=2)
+
+        Tkinter.Label(body, text='10 Character PID').grid(row=2, sticky=Tkconstants.E)
+        self.pidnum = Tkinter.StringVar()
+        self.pidinfo = Tkinter.Entry(body, width=12, textvariable=self.pidnum)
+        self.pidinfo.grid(row=2, column=1, sticky=sticky)
+
+        msg1 = 'Conversion Log \n\n'
+        self.stext = ScrolledText(body, bd=5, relief=Tkconstants.RIDGE, height=15, width=60, wrap=Tkconstants.WORD)
+        self.stext.grid(row=3, column=0, columnspan=2,sticky=sticky)
+        self.stext.insert(Tkconstants.END,msg1)
+
+        buttons = Tkinter.Frame(self)
+        buttons.pack()
+        self.sbotton = Tkinter.Button(
+            buttons, text="Start", width=10, command=self.convertit)
+        self.sbotton.pack(side=Tkconstants.LEFT)
+
+        Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
+        self.qbutton = Tkinter.Button(
+            buttons, text="Quit", width=10, command=self.quitting)
+        self.qbutton.pack(side=Tkconstants.RIGHT)
+
+    # read from subprocess pipe without blocking
+    # invoked every interval via the widget "after"
+    # option being used, so need to reset it for the next time
+    def processPipe(self):
+        poll = self.p2.wait('nowait')
+        if poll != None: 
+            text = self.p2.readerr()
+            text += self.p2.read()
+            msg = text + '\n\n' + 'Encryption successfully removed\n'
+            if poll != 0:
+                msg = text + '\n\n' + 'Error: Encryption Removal Failed\n'
+            self.showCmdOutput(msg)
+            self.p2 = None
+            self.sbotton.configure(state='normal')
+            return
+        text = self.p2.readerr()
+        text += self.p2.read()
+        self.showCmdOutput(text)
+        # make sure we get invoked again by event loop after interval 
+        self.stext.after(self.interval,self.processPipe)
+        return
+
+    # post output from subprocess in scrolled text widget
+    def showCmdOutput(self, msg):
+        if msg and msg !='':
+            self.stext.insert(Tkconstants.END,msg)
+            self.stext.yview_pickplace(Tkconstants.END)
+        return
+
+    # run as a subprocess via pipes and collect stdout
+    def mobirdr(self, infile, outfile, pidnum):
+        # os.putenv('PYTHONUNBUFFERED', '1')
+        cmdline = 'python ./lib/mobidedrm.py "' + infile + '" "' + outfile + '" "' + pidnum + '"'
+        if sys.platform[0:3] == 'win':
+            search_path = os.environ['PATH']
+            search_path = search_path.lower()
+            if search_path.find('python') >= 0: 
+                cmdline = 'python lib\mobidedrm.py "' + infile + '" "' + outfile + '" "' + pidnum + '"'
+            else :
+                cmdline = 'lib\mobidedrm.py "' + infile + '" "' + outfile + '" "' + pidnum + '"'
+
+        p2 = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=PIPE, stderr=PIPE, close_fds=False)
+        return p2
+
+
+    def get_mobipath(self):
+        mobipath = tkFileDialog.askopenfilename(
+            parent=None, title='Select Mobi eBook File',
+            defaultextension='.prc', filetypes=[('Mobi eBook File', '.prc'), ('Mobi eBook File', '.mobi'),
+                                                ('All Files', '.*')])
+        if mobipath:
+            mobipath = os.path.normpath(mobipath)
+            self.mobipath.delete(0, Tkconstants.END)
+            self.mobipath.insert(0, mobipath)
+        return
+
+    def get_outpath(self):
+        mobipath = self.mobipath.get()
+        initname = os.path.basename(mobipath)
+        p = initname.find('.')
+        if p >= 0: initname = initname[0:p]
+        initname += '_nodrm.mobi' 
+        outpath = tkFileDialog.asksaveasfilename(
+            parent=None, title='Select Unencrypted Mobi File to produce',
+            defaultextension='.mobi', initialfile=initname,
+            filetypes=[('Mobi files', '.mobi'), ('All files', '.*')])
+        if outpath:
+            outpath = os.path.normpath(outpath)
+            self.outpath.delete(0, Tkconstants.END)
+            self.outpath.insert(0, outpath)
+        return
+
+    def quitting(self):
+        # kill any still running subprocess
+        if self.p2 != None:
+            if (self.p2.wait('nowait') == None):
+                self.p2.terminate()
+        self.root.destroy()
+
+    # actually ready to run the subprocess and get its output
+    def convertit(self):
+        # now disable the button to prevent multiple launches
+        self.sbotton.configure(state='disabled')
+        mobipath = self.mobipath.get()
+        outpath = self.outpath.get()
+        pidnum = self.pidinfo.get()
+        if not mobipath or not os.path.exists(mobipath):
+            self.status['text'] = 'Specified Mobi eBook file does not exist'
+            self.sbotton.configure(state='normal')
+            return
+        if not outpath:
+            self.status['text'] = 'No output file specified'
+            self.sbotton.configure(state='normal')
+            return
+        if not pidnum or pidnum == '':
+            self.status['text'] = 'No PID specified'
+            self.sbotton.configure(state='normal')
+            return
+
+        log = 'Command = "python mobidedrm.py"\n'
+        log += 'Mobi Path = "'+ mobipath + '"\n'
+        log += 'Output File = "' + outpath + '"\n'
+        log += 'PID = "' + pidnum + '"\n'
+        log += '\n\n'
+        log += 'Please Wait ...\n\n'
+        self.stext.insert(Tkconstants.END,log)
+        self.p2 = self.mobirdr(mobipath, outpath, pidnum)
+
+        # python does not seem to allow you to create
+        # your own eventloop which every other gui does - strange 
+        # so need to use the widget "after" command to force
+        # event loop to run non-gui events every interval
+        self.stext.after(self.interval,self.processPipe)
+        return
+
+
+def main(argv=None):
+    root = Tkinter.Tk()
+    root.title('Mobi eBook Encryption Removal')
+    root.resizable(True, False)
+    root.minsize(300, 0)
+    MainDialog(root).pack(fill=Tkconstants.X, expand=1)
+    root.mainloop()
+    return 0
+    
+
+if __name__ == "__main__":
+    sys.exit(main())
index 492faceda3ddaca40580e97d813da19fff5395a3..6a0b57d78d2aab831efff7b0ed972779180f4091 100644 (file)
@@ -1,16 +1,17 @@
-#!/usr/bin/python
-# The Kindleizer v0.2. Copyright (c) 2007, 2009 Igor Skochinsky <skochinsky@mail.ru>
-# This script enables encrypted Mobipocket books to be readable by Kindle
-# History: 
-#  0.1 initial release
-#  0.2 fixed corrupted metadata issue (thanks to Mark Peek)
+class Unbuffered:
+    def __init__(self, stream):
+        self.stream = stream
+    def write(self, data):
+        self.stream.write(data)
+        self.stream.flush()
+    def __getattr__(self, attr):
+        return getattr(self.stream, attr)
 
-import prc, sys, struct
+import sys
+sys.stdout=Unbuffered(sys.stdout)
 
-if sys.hexversion >= 0x3000000:
-  print "This script is incompatible with Python 3.x. Please install Python 2.6.x from python.org"
-  sys.exit(2)
 
+import prc, struct
 from binascii import hexlify
 
 def strByte(s,off=0):
@@ -104,7 +105,7 @@ def find_key(rec0, pid):
             drmInfo = strPutDWord(drmInfo,4,(dw4|0x800))
             dw0, dw4, dw18, dw1c = struct.unpack(">II16xII", drmInfo)
             #print "Updated drmInfo:", "%08X, %08X, %s, %08X, %08X"%(dw0, dw4, hexlify(drmInfo[0x8:0x18]), dw18, dw1c)
-            return rec0[:iOff+0x10] + PC1(temp_key, drmInfo, False) + rec0[iOff+0x30:]
+            return rec0[:iOff+0x10] + PC1(temp_key, drmInfo, False) + rec0[:iOff+0x30]
       iOff += dwSize
     return None
 
@@ -115,7 +116,14 @@ def replaceext(filename, newext):
   else:
     return nameparts[0]+newext
 
-def main(fname, pid):
+def main(argv=sys.argv):
+  print "The Kindleizer v0.2. Copyright (c) 2007 Igor Skochinsky"
+  if len(sys.argv) != 3:
+      print "Fixes encrypted Mobipocket books to be readable by Kindle"
+      print "Usage: kindlefix.py file.mobi PID"
+      return 1
+  fname = sys.argv[1]
+  pid = sys.argv[2]
   if len(pid)==10 and pid[-3]=='*':
     pid = pid[:-2]
   if len(pid)!=8 or pid[-1]!='*':
@@ -159,10 +167,6 @@ def main(fname, pid):
   print "Output written to "+outfname
   return 0
 
-print "The Kindleizer v0.2. Copyright (c) 2007, 2009 Igor Skochinsky"
-if len(sys.argv)<3:
-  print "Fixes encrypted Mobipocket books to be readable by Kindle"
-  print "Usage: kindlefix.py file.mobi PID"
-else:  
-  fname = sys.argv[1]
-  sys.exit(main(fname, sys.argv[2]))
+
+if __name__ == "__main__":
+    sys.exit(main())
index 37903c254bf5569a387d13209a500c58de18fa38..29e4b30a6f02bde44df9c6a0ef27f0738a640caf 100644 (file)
@@ -4,9 +4,20 @@
 # History:
 #  0.1 Initial release
 #  0.2 Added support for generating PID for iPhone (thanks to mbp)
-#  Unofficial: Added support for Kindle DX and Kindle 2 International
+#  0.3 changed to autoflush stdout, fixed return code usage
+class Unbuffered:
+    def __init__(self, stream):
+        self.stream = stream
+    def write(self, data):
+        self.stream.write(data)
+        self.stream.flush()
+    def __getattr__(self, attr):
+        return getattr(self.stream, attr)
 
-import sys, binascii
+import sys
+sys.stdout=Unbuffered(sys.stdout)
+
+import binascii
 
 if sys.hexversion >= 0x3000000:
   print "This script is incompatible with Python 3.x. Please install Python 2.6.x from python.org"
@@ -49,29 +60,38 @@ def pidFromSerial(s, l):
 
   return pid
 
-print "Mobipocket PID calculator for Amazon Kindle. Copyright (c) 2007, 2009 Igor Skochinsky"
-if len(sys.argv)>1:
-  serial = sys.argv[1]
+def main(argv=sys.argv):
+  print "Mobipocket PID calculator for Amazon Kindle. Copyright (c) 2007, 2009 Igor Skochinsky"
+  if len(sys.argv)==2:
+      serial = sys.argv[1]
+  else:
+      print "Usage: kindlepid.py <Kindle Serial Number>/<iPhone/iPod Touch UDID>"
+      return 1
   if len(serial)==16:
-    if serial.startswith("B001"):
-      print "Kindle 1 serial number detected"
-    elif serial.startswith("B002"):
-      print "Kindle 2 serial number detected"
-    elif serial.startswith("B003"):
-      print "Kindle 2i serial number detected"
-    elif serial.startswith("B004"):
-      print "Kindle DX serial number detected"
-    else:
-      print "Warning: unrecognized serial number. Please recheck input."
-      sys.exit(1)
-    pid = pidFromSerial(serial,7)+"*"
-    print "Mobipocked PID for Kindle serial# "+serial+" is "+checksumPid(pid)
+      if serial.startswith("B001"):
+          print "Kindle 1 serial number detected"
+      elif serial.startswith("B002"):
+          print "Kindle 2 serial number detected"
+      elif serial.startswith("B003"):
+          print "Kindle 2 Global serial number detected"
+      elif serial.startswith("B004"):
+          print "Kindle DX serial number detected"
+      else:
+          print "Warning: unrecognized serial number. Please recheck input."
+          return 1
+      pid = pidFromSerial(serial,7)+"*"
+      print "Mobipocked PID for Kindle serial# "+serial+" is "+checksumPid(pid)
+      return 0
   elif len(serial)==40:
-    print "iPhone serial number (UDID) detected"
-    pid = pidFromSerial(serial,8)
-    print "Mobipocked PID for iPhone serial# "+serial+" is "+checksumPid(pid)
+      print "iPhone serial number (UDID) detected"
+      pid = pidFromSerial(serial,8)
+      print "Mobipocked PID for iPhone serial# "+serial+" is "+checksumPid(pid)
+      return 0
   else:
-    print "Warning: unrecognized serial number. Please recheck input."
-    sys.exit(1)
-else:
-  print "Usage: kindlepid.py <Kindle Serial Number>/<iPhone/iPod Touch UDID>"
+      print "Warning: unrecognized serial number. Please recheck input."
+      return 1
+  return 0
+
+
+if __name__ == "__main__":
+  sys.exit(main())
index 1f02cf93f8fad24d8b269e5cb8e6288cf412ce90..4ec5b75981ade8c7ff26abf24e7fbbe476bada5a 100644 (file)
 #  0.07 - The extra data flags aren't present in MOBI header < 0xE8 in size
 #  0.08 - ...and also not in Mobi header version < 6
 #  0.09 - ...but they are there with Mobi header version 6, header size 0xE4!
+#  0.10 - use autoflushed stdout and proper return values
 
-import sys,struct,binascii
+class Unbuffered:
+    def __init__(self, stream):
+        self.stream = stream
+    def write(self, data):
+        self.stream.write(data)
+        self.stream.flush()
+    def __getattr__(self, attr):
+        return getattr(self.stream, attr)
+
+import sys
+sys.stdout=Unbuffered(sys.stdout)
+
+import struct,binascii
 
 class DrmException(Exception):
        pass
@@ -206,7 +219,7 @@ if not __name__ == "__main__":
                description         = 'Removes DRM from secure Mobi files'
                supported_platforms = ['linux', 'osx', 'windows'] # Platforms this plugin will run on
                author              = 'The Dark Reverser' # The author of this plugin
-               version             = (0, 0, 9)   # The version number of this plugin
+               version             = (0, 0, 10)   # The version number of this plugin
                file_types          = set(['prc','mobi','azw']) # The file types that this plugin will be applied to
                on_import           = True # Run this plugin during the import
 
@@ -232,12 +245,13 @@ if not __name__ == "__main__":
                def customization_help(self, gui=False):
                        return 'Enter PID (separate multiple PIDs with comma)'
 
-if __name__ == "__main__":
-       print "MobiDeDrm v0.09. Copyright (c) 2008 The Dark Reverser"
+def main(argv=sys.argv):
+       print "MobiDeDrm v0.10. Copyright (c) 2008 The Dark Reverser"
        if len(sys.argv)<4:
                print "Removes protection from Mobipocket books"
                print "Usage:"
                print "  mobidedrm infile.mobi outfile.mobi PID"
+                return 1
        else:  
                infile = sys.argv[1]
                outfile = sys.argv[2]
@@ -247,3 +261,9 @@ if __name__ == "__main__":
                        file(outfile, 'wb').write(DrmStripper(data_file, pid).getResult())
                except DrmException, e:
                        print "Error: %s" % e
+                       return 1
+        return 0
+
+if __name__ == "__main__":
+    sys.exit(main())
+
index 84f0471c9de64706e8350892acd9009d1f4b4745..fe30719fd400c226d2436639a4463704d32087e6 100644 (file)
@@ -9,9 +9,22 @@
 #  0.01 - Initial version
 #  0.02 - Fix issue with size computing
 #  0.03 - Fix issue with some files
+#  0.04 - make stdout self flushing and fix return values
 
+class Unbuffered:
+    def __init__(self, stream):
+        self.stream = stream
+    def write(self, data):
+        self.stream.write(data)
+        self.stream.flush()
+    def __getattr__(self, attr):
+        return getattr(self.stream, attr)
 
-import struct, sys
+import sys
+sys.stdout=Unbuffered(sys.stdout)
+
+
+import struct
 
 class BitReader:
        def __init__(self, data):
@@ -146,16 +159,18 @@ def unpackBook(input_file):
                r += decompressSection(i)
        return r
 
-print "MobiHuff v0.03"
-print "  Copyright (c) 2008 The Dark Reverser <dark.reverser@googlemail.com>"
-if len(sys.argv)!=3:
-       print ""
+def main(argv=sys.argv):
+    print "MobiHuff v0.03"
+    print "  Copyright (c) 2008 The Dark Reverser <dark.reverser@googlemail.com>"
+    if len(sys.argv)!=3:
+        print ""
        print "Description:"
        print "  Unpacks the new mobipocket huffdic compression."
        print "  This program works with unencrypted files only."
        print "Usage:"
        print "  mobihuff.py infile.mobi outfile.html"
-else:  
+       return 1
+    else:  
        infile = sys.argv[1]
        outfile = sys.argv[2]
        try:
@@ -165,4 +180,10 @@ else:
                print "done"
        except ValueError, e:
                print 
-               print "Error: %s" % e
\ No newline at end of file
+               print "Error: %s" % e
+               return 1
+       return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/Kindle_Mobi_Tools/lib/scrolltextwidget.py b/Kindle_Mobi_Tools/lib/scrolltextwidget.py
new file mode 100644 (file)
index 0000000..98b4147
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+import Tkinter
+import Tkconstants
+
+# basic scrolled text widget
+class ScrolledText(Tkinter.Text):
+    def __init__(self, master=None, **kw):
+        self.frame = Tkinter.Frame(master)
+        self.vbar = Tkinter.Scrollbar(self.frame)
+        self.vbar.pack(side=Tkconstants.RIGHT, fill=Tkconstants.Y)
+        kw.update({'yscrollcommand': self.vbar.set})
+        Tkinter.Text.__init__(self, self.frame, **kw)
+        self.pack(side=Tkconstants.LEFT, fill=Tkconstants.BOTH, expand=True)
+        self.vbar['command'] = self.yview
+        # Copy geometry methods of self.frame without overriding Text
+        # methods = hack!
+        text_meths = vars(Tkinter.Text).keys()
+        methods = vars(Tkinter.Pack).keys() + vars(Tkinter.Grid).keys() + vars(Tkinter.Place).keys()
+        methods = set(methods).difference(text_meths)
+        for m in methods:
+            if m[0] != '_' and m != 'config' and m != 'configure':
+                setattr(self, m, getattr(self.frame, m))
+
+    def __str__(self):
+        return str(self.frame)
diff --git a/Kindle_Mobi_Tools/lib/subasyncio.py b/Kindle_Mobi_Tools/lib/subasyncio.py
new file mode 100644 (file)
index 0000000..ed13aa1
--- /dev/null
@@ -0,0 +1,149 @@
+#!/usr/bin/env python
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+import os, sys
+import signal
+import threading
+import subprocess
+from subprocess import Popen, PIPE, STDOUT
+
+# **heavily** chopped up and modfied version of asyncproc.py
+# to make it actually work on Windows as well as Mac/Linux
+# For the original see:
+# "http://www.lysator.liu.se/~bellman/download/"
+# author is  "Thomas Bellman <bellman@lysator.liu.se>"
+# available under GPL version 3 or Later
+
+# create an asynchronous subprocess whose output can be collected in
+# a non-blocking manner
+
+# What a mess!  Have to use threads just to get non-blocking io
+# in a cross-platform manner
+
+# luckily all thread use is hidden within this class
+
+class Process(object):
+    def __init__(self, *params, **kwparams):
+        if len(params) <= 3:
+            kwparams.setdefault('stdin', subprocess.PIPE)
+        if len(params) <= 4:
+            kwparams.setdefault('stdout', subprocess.PIPE)
+        if len(params) <= 5:
+            kwparams.setdefault('stderr', subprocess.PIPE)
+        self.__pending_input = []
+        self.__collected_outdata = []
+        self.__collected_errdata = []
+        self.__exitstatus = None
+        self.__lock = threading.Lock()
+        self.__inputsem = threading.Semaphore(0)
+        self.__quit = False
+
+        self.__process = subprocess.Popen(*params, **kwparams)
+
+        if self.__process.stdin:
+            self.__stdin_thread = threading.Thread(
+                name="stdin-thread",
+                target=self.__feeder, args=(self.__pending_input,
+                                            self.__process.stdin))
+            self.__stdin_thread.setDaemon(True)
+            self.__stdin_thread.start()
+
+        if self.__process.stdout:
+            self.__stdout_thread = threading.Thread(
+                name="stdout-thread",
+                target=self.__reader, args=(self.__collected_outdata,
+                                           self.__process.stdout))
+            self.__stdout_thread.setDaemon(True)
+            self.__stdout_thread.start()
+
+        if self.__process.stderr:
+            self.__stderr_thread = threading.Thread(
+                name="stderr-thread",
+                target=self.__reader, args=(self.__collected_errdata,
+                                           self.__process.stderr))
+            self.__stderr_thread.setDaemon(True)
+            self.__stderr_thread.start()
+
+    def pid(self):
+        return self.__process.pid
+
+    def kill(self, signal):
+        self.__process.send_signal(signal)
+
+    # check on subprocess (pass in 'nowait') to act like poll
+    def wait(self, flag):
+        if flag.lower() == 'nowait':
+            rc = self.__process.poll()
+        else:
+            rc = self.__process.wait()
+        if rc != None:
+            if self.__process.stdin:
+                self.closeinput()
+            if self.__process.stdout:
+                self.__stdout_thread.join()
+            if self.__process.stderr:
+                self.__stderr_thread.join()
+        return self.__process.returncode
+
+    def terminate(self):
+        if self.__process.stdin:
+            self.closeinput()
+        self.__process.terminate()
+
+    # thread gets data from subprocess stdout
+    def __reader(self, collector, source):
+        while True:
+            data = os.read(source.fileno(), 65536)
+            self.__lock.acquire()
+            collector.append(data)
+            self.__lock.release()
+            if data == "":
+                source.close()
+                break
+        return
+
+    # thread feeds data to subprocess stdin
+    def __feeder(self, pending, drain):
+        while True:
+            self.__inputsem.acquire()
+            self.__lock.acquire()
+            if not pending  and self.__quit:
+                drain.close()
+                self.__lock.release()
+                break
+            data = pending.pop(0)
+            self.__lock.release()
+            drain.write(data)
+
+    # non-blocking read of data from subprocess stdout
+    def read(self):
+        self.__lock.acquire()
+        outdata = "".join(self.__collected_outdata)
+        del self.__collected_outdata[:]
+        self.__lock.release()
+        return outdata
+
+    # non-blocking read of data from subprocess stderr
+    def readerr(self):
+        self.__lock.acquire()
+        errdata = "".join(self.__collected_errdata)
+        del self.__collected_errdata[:]
+        self.__lock.release()
+        return errdata
+
+    # non-blocking write to stdin of subprocess
+    def write(self, data):
+        if self.__process.stdin is None:
+            raise ValueError("Writing to process with stdin not a pipe")
+        self.__lock.acquire()
+        self.__pending_input.append(data)
+        self.__inputsem.release()
+        self.__lock.release()
+
+    # close stdinput of subprocess
+    def closeinput(self):
+        self.__lock.acquire()
+        self.__quit = True
+        self.__inputsem.release()
+        self.__lock.release()
+