mirror of
				https://github.com/noDRM/DeDRM_tools.git
				synced 2025-10-23 23:07:47 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			262 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			262 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #!/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 Tkinter
 | |
| import Tkconstants
 | |
| import tkFileDialog
 | |
| import tkMessageBox
 | |
| from scrolltextwidget import ScrolledText
 | |
| import subprocess
 | |
| from subprocess import Popen, PIPE, STDOUT
 | |
| import subasyncio
 | |
| from subasyncio import Process
 | |
| 
 | |
| class MainDialog(Tkinter.Frame):
 | |
|     def __init__(self, root):
 | |
|         Tkinter.Frame.__init__(self, root, border=5)
 | |
|         self.root = root
 | |
|         self.interval = 1000
 | |
|         self.p2 = None
 | |
|         self.status = Tkinter.Label(self, text='Remove Encryption from a Kindle/Mobi/Topaz 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='Kindle/Mobi/Topaz 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)
 | |
|         cwd = os.getcwdu()
 | |
|         cwd = cwd.encode('utf-8')
 | |
|         self.mobipath.insert(0, cwd)
 | |
|         button = Tkinter.Button(body, text="...", command=self.get_mobipath)
 | |
|         button.grid(row=0, column=2)
 | |
| 
 | |
|         Tkinter.Label(body, text='Directory for the Unencrypted Output File(s)').grid(row=1, sticky=Tkconstants.E)
 | |
|         self.outpath = Tkinter.Entry(body, width=50)
 | |
|         self.outpath.grid(row=1, column=1, sticky=sticky)
 | |
|         cwd = os.getcwdu()
 | |
|         cwd = cwd.encode('utf-8')
 | |
|         outname = cwd
 | |
|         self.outpath.insert(0, outname)
 | |
|         button = Tkinter.Button(body, text="...", command=self.get_outpath)
 | |
|         button.grid(row=1, column=2)
 | |
|         
 | |
|         Tkinter.Label(body, text='Optional Alternative Kindle.info file').grid(row=2, sticky=Tkconstants.E)
 | |
|         self.altinfopath = Tkinter.Entry(body, width=50)
 | |
|         self.altinfopath.grid(row=2, column=1, sticky=sticky)
 | |
|         #cwd = os.getcwdu()
 | |
|         #cwd = cwd.encode('utf-8')
 | |
|         #self.altinfopath.insert(0, cwd)
 | |
|         button = Tkinter.Button(body, text="...", command=self.get_altinfopath)
 | |
|         button.grid(row=2, column=2)
 | |
| 
 | |
|         Tkinter.Label(body, text='Optional Comma Separated List of 10 Character PIDs (no spaces)').grid(row=3, sticky=Tkconstants.E)
 | |
|         self.pidnums = Tkinter.StringVar()
 | |
|         self.pidinfo = Tkinter.Entry(body, width=50, textvariable=self.pidnums)
 | |
|         self.pidinfo.grid(row=3, column=1, sticky=sticky)
 | |
| 
 | |
|         Tkinter.Label(body, text='Optional Comma Separated List of 16 Character Kindle Serial Numbers (no spaces)').grid(row=4, sticky=Tkconstants.E)
 | |
|         self.sernums = Tkinter.StringVar()
 | |
|         self.serinfo = Tkinter.Entry(body, width=50, textvariable=self.sernums)
 | |
|         self.serinfo.grid(row=4, 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=6, 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 == 1:
 | |
|                 msg = text + '\n\n' + 'Error: Encryption Removal Failed\n'
 | |
|             if poll == 2:
 | |
|                 msg = text + '\n\n' + 'Input File was Not Encrypted - No Output File Needed\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 !='':
 | |
|             msg = msg.encode('utf-8')
 | |
|             if sys.platform.startswith('win'):
 | |
|                 msg = msg.replace('\r\n','\n')
 | |
|             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, altinfopath, pidnums, sernums):
 | |
|         # os.putenv('PYTHONUNBUFFERED', '1')
 | |
|         tool = 'k4mobidedrm.py'
 | |
|         pidoption = ''
 | |
|         if pidnums and pidnums != '':
 | |
|             pidoption = ' -p "' + pidnums + '" '
 | |
|         seroption = ''
 | |
|         if sernums and sernums != '':
 | |
|             seroption = ' -s "' + sernums + '" '
 | |
|         infooption = ''
 | |
|         if altinfopath and altinfopath != '':
 | |
|             infooption = ' -k "' + altinfopath + '" '
 | |
|         cmdline = 'python ./lib/' + tool + ' ' + pidoption + seroption + infooption + '"' + infile + '" "' + outfile + '"'
 | |
|         print cmdline
 | |
|         if sys.platform.startswith('win'):
 | |
|             search_path = os.environ['PATH']
 | |
|             search_path = search_path.lower()
 | |
|             if search_path.find('python') >= 0: 
 | |
|                 cmdline = 'python lib\\' + tool + ' ' + pidoption + seroption + infooption + '"' + infile + '" "' + outfile + '"'
 | |
|             else :
 | |
|                 cmdline = 'lib\\' + tool + ' ' + pidoption + seroption + infooption + '"' + infile + '" "' + outfile + '"'
 | |
| 
 | |
|         cmdline = cmdline.encode(sys.getfilesystemencoding())
 | |
|         p2 = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=PIPE, stderr=PIPE, close_fds=False)
 | |
|         return p2
 | |
| 
 | |
| 
 | |
|     def get_mobipath(self):
 | |
|         cpath = self.mobipath.get()
 | |
|         mobipath = tkFileDialog.askopenfilename(
 | |
|             initialdir = cpath,
 | |
|             parent=None, title='Select Kindle/Mobi/Topaz  eBook File',
 | |
|             defaultextension='.prc', filetypes=[('Mobi eBook File', '.prc'), ('Mobi eBook File', '.azw'),('Mobi eBook File', '.mobi'),('Mobi eBook File', '.tpz'),('Mobi eBook File', '.azw1'),('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):
 | |
|         cwd = os.getcwdu()
 | |
|         cwd = cwd.encode('utf-8')
 | |
|         outpath = tkFileDialog.askdirectory(
 | |
|             parent=None, title='Directory to Store Unencrypted file(s) into',
 | |
|             initialdir=cwd, initialfile=None)
 | |
|         if outpath:
 | |
|             outpath = os.path.normpath(outpath)
 | |
|             self.outpath.delete(0, Tkconstants.END)
 | |
|             self.outpath.insert(0, outpath)
 | |
|         return
 | |
| 
 | |
|     def get_altinfopath(self):
 | |
|         cwd = os.getcwdu()
 | |
|         cwd = cwd.encode('utf-8')
 | |
|         altinfopath = tkFileDialog.askopenfilename(
 | |
|             parent=None, title='Select Alternative kindle.info File',
 | |
|             defaultextension='.prc', filetypes=[('Kindle Info', '.info'),
 | |
|                                                 ('All Files', '.*')],
 | |
|             initialdir=cwd)
 | |
|         if altinfopath:
 | |
|             altinfopath = os.path.normpath(altinfopath)
 | |
|             self.altinfopath.delete(0, Tkconstants.END)
 | |
|             self.altinfopath.insert(0, altinfopath)
 | |
|         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):
 | |
|         self.status['text'] = ''
 | |
|         # now disable the button to prevent multiple launches
 | |
|         self.sbotton.configure(state='disabled')
 | |
|         mobipath = self.mobipath.get()
 | |
|         outpath = self.outpath.get()
 | |
|         altinfopath = self.altinfopath.get()
 | |
|         pidnums = self.pidinfo.get()
 | |
|         sernums = self.serinfo.get()
 | |
| 
 | |
|         if not mobipath or not os.path.exists(mobipath) or not os.path.isfile(mobipath):
 | |
|             self.status['text'] = 'Specified Kindle Mobi eBook file does not exist'
 | |
|             self.sbotton.configure(state='normal')
 | |
|             return
 | |
| 
 | |
|         tpz = False
 | |
|         # Identify any Topaz Files
 | |
|         with open(mobipath, 'rb') as f:
 | |
|             raw = f.read(3)
 | |
|             if raw.startswith('TPZ'):
 | |
|                 tpz = True
 | |
|             f.close()
 | |
|         if not outpath:
 | |
|             self.status['text'] = 'No output directory specified'
 | |
|             self.sbotton.configure(state='normal')
 | |
|             return
 | |
|         if not os.path.isdir(outpath):
 | |
|             self.status['text'] = 'Error specified output directory does not exist'
 | |
|             self.sbotton.configure(state='normal')
 | |
|             return
 | |
|         if altinfopath and not os.path.exists(altinfopath):
 | |
|             self.status['text'] = 'Specified kindle.info file does not exist'
 | |
|             self.sbotton.configure(state='normal')
 | |
|             return
 | |
| 
 | |
|         log = 'Command = "python k4mobidedrm.py"\n'
 | |
|         if not tpz:
 | |
|             log += 'Kindle/Mobi Path = "'+ mobipath + '"\n'
 | |
|         else:
 | |
|             log += 'Topaz Path = "'+ mobipath + '"\n'
 | |
|         log += 'Output Directory = "' + outpath + '"\n'
 | |
|         log += 'Kindle.info file = "' + altinfopath + '"\n'
 | |
|         log += 'PID list = "' + pidnums + '"\n'
 | |
|         log += 'Serial Number list = "' + sernums + '"\n'
 | |
|         log += '\n\n'
 | |
|         log += 'Please Wait ...\n\n'
 | |
|         log = log.encode('utf-8')
 | |
|         self.stext.insert(Tkconstants.END,log)
 | |
|         self.p2 = self.mobirdr(mobipath, outpath, altinfopath, pidnums, sernums)
 | |
| 
 | |
|         # 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/Mobi/Topaz 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())
 | 
