Move to new positions
This commit is contained in:
@@ -0,0 +1 @@
|
||||
chcp 65001 > nul && set PWD="%~dp0" && cd /d "%~dp0DeDRM_lib" && start /min python DeDRM_App.pyw %*
|
||||
@@ -0,0 +1,702 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# DeDRM.pyw
|
||||
# Copyright 2010-2016 some_updates, Apprentice Alf and Apprentice Harper
|
||||
|
||||
# Revision history:
|
||||
# 6.0.0 - Release along with unified plugin
|
||||
# 6.0.1 - Bug Fixes for Windows App
|
||||
# 6.0.2 - Fixed problem with spaces in paths and the bat file
|
||||
# 6.0.3 - Fix for Windows non-ascii user names
|
||||
# 6.0.4 - Fix for other potential unicode problems
|
||||
# 6.0.5 - Fix typo
|
||||
# 6.2.0 - Update to match plugin and AppleScript
|
||||
# 6.2.1 - Fix for non-ascii user names
|
||||
# 6.2.2 - Added URL method for B&N/nook books
|
||||
# 6.3.0 - Add in Android support
|
||||
# 6.3.1 - Version bump for clarity
|
||||
# 6.3.2 - Version bump to match plugin
|
||||
# 6.3.3 - Version bump to match plugin
|
||||
# 6.3.4 - Version bump to match plugin
|
||||
# 6.3.5 - Version bump to match plugin
|
||||
# 6.3.6 - Version bump to match plugin
|
||||
# 6.4.0 - Fix for Kindle for PC encryption change
|
||||
# 6.4.1 - Fix for new tags in Topaz ebooks
|
||||
# 6.4.2 - Fix for new tags in Topaz ebooks, and very small Topaz ebooks
|
||||
# 6.4.3 - Version bump to match plugin & Mac app
|
||||
# 6.5.0 - Fix for some new tags in Topaz ebooks
|
||||
# 6.5.1 - Version bump to match plugin & Mac app
|
||||
# 6.5.2 - Fix for a new tag in Topaz ebooks
|
||||
# 6.5.3 - Explicitly warn about KFX files
|
||||
# 6.5.4 - PDF float fix.
|
||||
# 6.5.5 - Kindle for PC/Accented characters in username fix.
|
||||
# 6.6.0 - Initial KFX support from TomThumb
|
||||
# 6.6.1 - Standalong app fix from wzyboy
|
||||
|
||||
__version__ = '6.6.1'
|
||||
|
||||
import sys
|
||||
import os, os.path
|
||||
sys.path.append(os.path.join(sys.path[0],"lib"))
|
||||
import sys, os
|
||||
import codecs
|
||||
|
||||
from argv_utils import add_cp65001_codec, set_utf8_default_encoding, unicode_argv
|
||||
add_cp65001_codec()
|
||||
set_utf8_default_encoding()
|
||||
|
||||
|
||||
import shutil
|
||||
import Tkinter
|
||||
from Tkinter import *
|
||||
import Tkconstants
|
||||
import tkFileDialog
|
||||
from scrolltextwidget import ScrolledText
|
||||
from activitybar import ActivityBar
|
||||
if sys.platform.startswith("win"):
|
||||
from askfolder_ed import AskFolder
|
||||
import re
|
||||
import simpleprefs
|
||||
import traceback
|
||||
|
||||
from Queue import Full
|
||||
from Queue import Empty
|
||||
from multiprocessing import Process, Queue
|
||||
|
||||
from scriptinterface import decryptepub, decryptpdb, decryptpdf, decryptk4mobi
|
||||
|
||||
|
||||
# Wrap a stream so that output gets flushed immediately
|
||||
# and appended to shared queue
|
||||
class QueuedUTF8Stream:
|
||||
def __init__(self, stream, q):
|
||||
self.stream = stream
|
||||
self.encoding = 'utf-8'
|
||||
self.q = q
|
||||
def write(self, data):
|
||||
if isinstance(data,unicode):
|
||||
data = data.encode('utf-8',"replace")
|
||||
self.q.put(data)
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.stream, attr)
|
||||
|
||||
class DrmException(Exception):
|
||||
pass
|
||||
|
||||
class MainApp(Tk):
|
||||
def __init__(self, apphome, dnd=False, filenames=[]):
|
||||
Tk.__init__(self)
|
||||
self.withdraw()
|
||||
self.dnd = dnd
|
||||
self.apphome = apphome
|
||||
|
||||
# preference settings
|
||||
# [dictionary key, file in preferences directory where info is stored]
|
||||
description = [ ['pids' , 'pidlist.txt' ],
|
||||
['serials', 'seriallist.txt'],
|
||||
['sdrms' , 'sdrmlist.txt' ],
|
||||
['outdir' , 'outdir.txt' ]]
|
||||
self.po = simpleprefs.SimplePrefs("DeDRM",description)
|
||||
if self.dnd:
|
||||
self.cd = ConvDialog(self)
|
||||
prefs = self.getPreferences()
|
||||
self.cd.doit(prefs, filenames)
|
||||
else:
|
||||
prefs = self.getPreferences()
|
||||
self.pd = PrefsDialog(self, prefs)
|
||||
self.cd = ConvDialog(self)
|
||||
self.pd.show()
|
||||
|
||||
def getPreferences(self):
|
||||
prefs = self.po.getPreferences()
|
||||
prefdir = prefs['dir']
|
||||
adeptkeyfile = os.path.join(prefdir,'adeptkey.der')
|
||||
if not os.path.exists(adeptkeyfile):
|
||||
import adobekey
|
||||
try:
|
||||
adobekey.getkey(adeptkeyfile)
|
||||
except:
|
||||
pass
|
||||
kindlekeyfile = os.path.join(prefdir,'kindlekey.k4i')
|
||||
if not os.path.exists(kindlekeyfile):
|
||||
import kindlekey
|
||||
try:
|
||||
kindlekey.getkey(kindlekeyfile)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
pass
|
||||
bnepubkeyfile = os.path.join(prefdir,'bnepubkey.b64')
|
||||
if not os.path.exists(bnepubkeyfile):
|
||||
import ignoblekey
|
||||
try:
|
||||
ignoblekey.getkey(bnepubkeyfile)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
pass
|
||||
return prefs
|
||||
|
||||
def setPreferences(self, newprefs):
|
||||
prefdir = self.po.prefdir
|
||||
if 'adkfile' in newprefs:
|
||||
dfile = newprefs['adkfile']
|
||||
fname = os.path.basename(dfile)
|
||||
nfile = os.path.join(prefdir,fname)
|
||||
if os.path.isfile(dfile):
|
||||
shutil.copyfile(dfile,nfile)
|
||||
if 'bnkfile' in newprefs:
|
||||
dfile = newprefs['bnkfile']
|
||||
fname = os.path.basename(dfile)
|
||||
nfile = os.path.join(prefdir,fname)
|
||||
if os.path.isfile(dfile):
|
||||
shutil.copyfile(dfile,nfile)
|
||||
if 'kindlefile' in newprefs:
|
||||
dfile = newprefs['kindlefile']
|
||||
fname = os.path.basename(dfile)
|
||||
nfile = os.path.join(prefdir,fname)
|
||||
if os.path.isfile(dfile):
|
||||
shutil.copyfile(dfile,nfile)
|
||||
if 'androidfile' in newprefs:
|
||||
dfile = newprefs['androidfile']
|
||||
fname = os.path.basename(dfile)
|
||||
nfile = os.path.join(prefdir,fname)
|
||||
if os.path.isfile(dfile):
|
||||
shutil.copyfile(dfile,nfile)
|
||||
self.po.setPreferences(newprefs)
|
||||
return
|
||||
|
||||
def alldone(self):
|
||||
if not self.dnd:
|
||||
self.pd.enablebuttons()
|
||||
else:
|
||||
self.destroy()
|
||||
|
||||
class PrefsDialog(Toplevel):
|
||||
def __init__(self, mainapp, prefs_array):
|
||||
Toplevel.__init__(self, mainapp)
|
||||
self.withdraw()
|
||||
self.protocol("WM_DELETE_WINDOW", self.withdraw)
|
||||
self.title("DeDRM " + __version__)
|
||||
self.prefs_array = prefs_array
|
||||
self.status = Tkinter.Label(self, text='Setting Preferences')
|
||||
self.status.pack(fill=Tkconstants.X, expand=1)
|
||||
body = Tkinter.Frame(self)
|
||||
self.body = body
|
||||
body.pack(fill=Tkconstants.X, expand=1)
|
||||
sticky = Tkconstants.E + Tkconstants.W
|
||||
body.grid_columnconfigure(1, weight=2)
|
||||
|
||||
cur_row = 0
|
||||
Tkinter.Label(body, text='Adobe Key file (adeptkey.der)').grid(row=cur_row, sticky=Tkconstants.E)
|
||||
self.adkpath = Tkinter.Entry(body, width=50)
|
||||
self.adkpath.grid(row=cur_row, column=1, sticky=sticky)
|
||||
prefdir = self.prefs_array['dir']
|
||||
keyfile = os.path.join(prefdir,'adeptkey.der')
|
||||
if os.path.isfile(keyfile):
|
||||
path = keyfile
|
||||
self.adkpath.insert(cur_row, path)
|
||||
button = Tkinter.Button(body, text="...", command=self.get_adkpath)
|
||||
button.grid(row=cur_row, column=2)
|
||||
|
||||
cur_row = cur_row + 1
|
||||
Tkinter.Label(body, text='Kindle Key file (kindlekey.k4i)').grid(row=cur_row, sticky=Tkconstants.E)
|
||||
self.kkpath = Tkinter.Entry(body, width=50)
|
||||
self.kkpath.grid(row=cur_row, column=1, sticky=sticky)
|
||||
prefdir = self.prefs_array['dir']
|
||||
keyfile = os.path.join(prefdir,'kindlekey.k4i')
|
||||
if os.path.isfile(keyfile):
|
||||
path = keyfile
|
||||
self.kkpath.insert(0, path)
|
||||
button = Tkinter.Button(body, text="...", command=self.get_kkpath)
|
||||
button.grid(row=cur_row, column=2)
|
||||
|
||||
cur_row = cur_row + 1
|
||||
Tkinter.Label(body, text='Android Kindle backup file (backup.ab)').grid(row=cur_row, sticky=Tkconstants.E)
|
||||
self.akkpath = Tkinter.Entry(body, width=50)
|
||||
self.akkpath.grid(row=cur_row, column=1, sticky=sticky)
|
||||
prefdir = self.prefs_array['dir']
|
||||
keyfile = os.path.join(prefdir,'backup.ab')
|
||||
if os.path.isfile(keyfile):
|
||||
path = keyfile
|
||||
self.akkpath.insert(0, path)
|
||||
button = Tkinter.Button(body, text="...", command=self.get_akkpath)
|
||||
button.grid(row=cur_row, column=2)
|
||||
|
||||
cur_row = cur_row + 1
|
||||
Tkinter.Label(body, text='Barnes and Noble Key file (bnepubkey.b64)').grid(row=cur_row, sticky=Tkconstants.E)
|
||||
self.bnkpath = Tkinter.Entry(body, width=50)
|
||||
self.bnkpath.grid(row=cur_row, column=1, sticky=sticky)
|
||||
prefdir = self.prefs_array['dir']
|
||||
keyfile = os.path.join(prefdir,'bnepubkey.b64')
|
||||
if os.path.isfile(keyfile):
|
||||
path = keyfile
|
||||
self.bnkpath.insert(0, path)
|
||||
button = Tkinter.Button(body, text="...", command=self.get_bnkpath)
|
||||
button.grid(row=cur_row, column=2)
|
||||
|
||||
cur_row = cur_row + 1
|
||||
Tkinter.Label(body, text='Mobipocket PID list\n(8 or 10 characters, comma separated)').grid(row=cur_row, sticky=Tkconstants.E)
|
||||
self.pidnums = Tkinter.StringVar()
|
||||
self.pidinfo = Tkinter.Entry(body, width=50, textvariable=self.pidnums)
|
||||
if 'pids' in self.prefs_array:
|
||||
self.pidnums.set(self.prefs_array['pids'])
|
||||
self.pidinfo.grid(row=cur_row, column=1, sticky=sticky)
|
||||
|
||||
cur_row = cur_row + 1
|
||||
Tkinter.Label(body, text='eInk Kindle Serial Number list\n(16 characters, comma separated)').grid(row=cur_row, sticky=Tkconstants.E)
|
||||
self.sernums = Tkinter.StringVar()
|
||||
self.serinfo = Tkinter.Entry(body, width=50, textvariable=self.sernums)
|
||||
if 'serials' in self.prefs_array:
|
||||
self.sernums.set(self.prefs_array['serials'])
|
||||
self.serinfo.grid(row=cur_row, column=1, sticky=sticky)
|
||||
|
||||
cur_row = cur_row + 1
|
||||
Tkinter.Label(body, text='eReader data list\n(name:last 8 digits on credit card, comma separated)').grid(row=cur_row, sticky=Tkconstants.E)
|
||||
self.sdrmnums = Tkinter.StringVar()
|
||||
self.sdrminfo = Tkinter.Entry(body, width=50, textvariable=self.sdrmnums)
|
||||
if 'sdrms' in self.prefs_array:
|
||||
self.sdrmnums.set(self.prefs_array['sdrms'])
|
||||
self.sdrminfo.grid(row=cur_row, column=1, sticky=sticky)
|
||||
|
||||
cur_row = cur_row + 1
|
||||
Tkinter.Label(body, text="Output Folder (if blank, use input ebook's folder)").grid(row=cur_row, sticky=Tkconstants.E)
|
||||
self.outpath = Tkinter.Entry(body, width=50)
|
||||
self.outpath.grid(row=cur_row, column=1, sticky=sticky)
|
||||
if 'outdir' in self.prefs_array:
|
||||
dpath = self.prefs_array['outdir']
|
||||
self.outpath.insert(0, dpath)
|
||||
button = Tkinter.Button(body, text="...", command=self.get_outpath)
|
||||
button.grid(row=cur_row, column=2)
|
||||
|
||||
cur_row = cur_row + 1
|
||||
Tkinter.Label(body, text='').grid(row=cur_row, column=0, columnspan=2, sticky=Tkconstants.N)
|
||||
|
||||
cur_row = cur_row + 1
|
||||
Tkinter.Label(body, text='Alternatively Process an eBook').grid(row=cur_row, column=0, columnspan=2, sticky=Tkconstants.N)
|
||||
|
||||
cur_row = cur_row + 1
|
||||
Tkinter.Label(body, text='Select an eBook to Process*').grid(row=cur_row, sticky=Tkconstants.E)
|
||||
self.bookpath = Tkinter.Entry(body, width=50)
|
||||
self.bookpath.grid(row=cur_row, column=1, sticky=sticky)
|
||||
button = Tkinter.Button(body, text="...", command=self.get_bookpath)
|
||||
button.grid(row=cur_row, column=2)
|
||||
|
||||
cur_row = cur_row + 1
|
||||
Tkinter.Label(body, font=("Helvetica", "10", "italic"), text='*To DeDRM multiple ebooks simultaneously, set your preferences and quit.\nThen drag and drop ebooks or folders onto the DeDRM_Drop_Target').grid(row=cur_row, column=1, sticky=Tkconstants.E)
|
||||
|
||||
cur_row = cur_row + 1
|
||||
Tkinter.Label(body, text='').grid(row=cur_row, column=0, columnspan=2, sticky=Tkconstants.E)
|
||||
|
||||
buttons = Tkinter.Frame(self)
|
||||
buttons.pack()
|
||||
self.sbotton = Tkinter.Button(buttons, text="Set Prefs", width=14, command=self.setprefs)
|
||||
self.sbotton.pack(side=Tkconstants.LEFT)
|
||||
|
||||
buttons.pack()
|
||||
self.pbotton = Tkinter.Button(buttons, text="Process eBook", width=14, command=self.doit)
|
||||
self.pbotton.pack(side=Tkconstants.LEFT)
|
||||
buttons.pack()
|
||||
self.qbotton = Tkinter.Button(buttons, text="Quit", width=14, command=self.quitting)
|
||||
self.qbotton.pack(side=Tkconstants.RIGHT)
|
||||
buttons.pack()
|
||||
|
||||
def disablebuttons(self):
|
||||
self.sbotton.configure(state='disabled')
|
||||
self.pbotton.configure(state='disabled')
|
||||
self.qbotton.configure(state='disabled')
|
||||
|
||||
def enablebuttons(self):
|
||||
self.sbotton.configure(state='normal')
|
||||
self.pbotton.configure(state='normal')
|
||||
self.qbotton.configure(state='normal')
|
||||
|
||||
def show(self):
|
||||
self.deiconify()
|
||||
self.tkraise()
|
||||
|
||||
def hide(self):
|
||||
self.withdraw()
|
||||
|
||||
def get_outpath(self):
|
||||
cpath = self.outpath.get()
|
||||
if sys.platform.startswith("win"):
|
||||
# tk_chooseDirectory is horribly broken for unicode paths
|
||||
# on windows - bug has been reported but not fixed for years
|
||||
# workaround by using our own unicode aware version
|
||||
outpath = AskFolder(message="Choose the folder for DRM-free ebooks",
|
||||
defaultLocation=cpath)
|
||||
else:
|
||||
outpath = tkFileDialog.askdirectory(
|
||||
parent=None, title='Choose the folder for DRM-free ebooks',
|
||||
initialdir=cpath, initialfile=None)
|
||||
if outpath:
|
||||
outpath = os.path.normpath(outpath)
|
||||
self.outpath.delete(0, Tkconstants.END)
|
||||
self.outpath.insert(0, outpath)
|
||||
return
|
||||
|
||||
def get_adkpath(self):
|
||||
cpath = self.adkpath.get()
|
||||
adkpath = tkFileDialog.askopenfilename(initialdir = cpath, parent=None, title='Select Adept Key file',
|
||||
defaultextension='.der', filetypes=[('Adept Key file', '.der'), ('All Files', '.*')])
|
||||
if adkpath:
|
||||
adkpath = os.path.normpath(adkpath)
|
||||
self.adkpath.delete(0, Tkconstants.END)
|
||||
self.adkpath.insert(0, adkpath)
|
||||
return
|
||||
|
||||
def get_kkpath(self):
|
||||
cpath = self.kkpath.get()
|
||||
kkpath = tkFileDialog.askopenfilename(initialdir = cpath, parent=None, title='Select Kindle Key file',
|
||||
defaultextension='.k4i', filetypes=[('Kindle Key file', '.k4i'), ('All Files', '.*')])
|
||||
if kkpath:
|
||||
kkpath = os.path.normpath(kkpath)
|
||||
self.kkpath.delete(0, Tkconstants.END)
|
||||
self.kkpath.insert(0, kkpath)
|
||||
return
|
||||
|
||||
def get_akkpath(self):
|
||||
cpath = self.akkpath.get()
|
||||
akkpath = tkFileDialog.askopenfilename(initialdir = cpath, parent=None, title='Select Android for Kindle backup file',
|
||||
defaultextension='.ab', filetypes=[('Kindle for Android backup file', '.ab'), ('All Files', '.*')])
|
||||
if akkpath:
|
||||
akkpath = os.path.normpath(akkpath)
|
||||
self.akkpath.delete(0, Tkconstants.END)
|
||||
self.akkpath.insert(0, akkpath)
|
||||
return
|
||||
|
||||
def get_bnkpath(self):
|
||||
cpath = self.bnkpath.get()
|
||||
bnkpath = tkFileDialog.askopenfilename(initialdir = cpath, parent=None, title='Select Barnes and Noble Key file',
|
||||
defaultextension='.b64', filetypes=[('Barnes and Noble Key file', '.b64'), ('All Files', '.*')])
|
||||
if bnkpath:
|
||||
bnkpath = os.path.normpath(bnkpath)
|
||||
self.bnkpath.delete(0, Tkconstants.END)
|
||||
self.bnkpath.insert(0, bnkpath)
|
||||
return
|
||||
|
||||
def get_bookpath(self):
|
||||
cpath = self.bookpath.get()
|
||||
bookpath = tkFileDialog.askopenfilename(parent=None, title='Select eBook for DRM Removal',
|
||||
filetypes=[('All Files', '.*'),
|
||||
('ePub Files','.epub'),
|
||||
('Kindle','.azw'),
|
||||
('Kindle','.azw1'),
|
||||
('Kindle','.azw3'),
|
||||
('Kindle','.azw4'),
|
||||
('Kindle','.tpz'),
|
||||
('Kindle','.azw8'),
|
||||
('Kindle','.kfx'),
|
||||
('Kindle','.kfx-zip'),
|
||||
('Kindle','.mobi'),
|
||||
('Kindle','.prc'),
|
||||
('eReader','.pdb'),
|
||||
('PDF','.pdf')],
|
||||
initialdir=cpath)
|
||||
if bookpath:
|
||||
bookpath = os.path.normpath(bookpath)
|
||||
self.bookpath.delete(0, Tkconstants.END)
|
||||
self.bookpath.insert(0, bookpath)
|
||||
return
|
||||
|
||||
def quitting(self):
|
||||
self.master.destroy()
|
||||
|
||||
def setprefs(self):
|
||||
# setting new prefereces
|
||||
new_prefs = {}
|
||||
prefdir = self.prefs_array['dir']
|
||||
new_prefs['dir'] = prefdir
|
||||
new_prefs['pids'] = self.pidinfo.get().replace(" ","")
|
||||
new_prefs['serials'] = self.serinfo.get().replace(" ","")
|
||||
new_prefs['sdrms'] = self.sdrminfo.get().strip().replace(", ",",")
|
||||
new_prefs['outdir'] = self.outpath.get().strip()
|
||||
adkpath = self.adkpath.get()
|
||||
if os.path.dirname(adkpath) != prefdir:
|
||||
new_prefs['adkfile'] = adkpath
|
||||
bnkpath = self.bnkpath.get()
|
||||
if os.path.dirname(bnkpath) != prefdir:
|
||||
new_prefs['bnkfile'] = bnkpath
|
||||
kkpath = self.kkpath.get()
|
||||
if os.path.dirname(kkpath) != prefdir:
|
||||
new_prefs['kindlefile'] = kkpath
|
||||
akkpath = self.akkpath.get()
|
||||
if os.path.dirname(akkpath) != prefdir:
|
||||
new_prefs['androidfile'] = akkpath
|
||||
self.master.setPreferences(new_prefs)
|
||||
# and update internal copies
|
||||
self.prefs_array['pids'] = new_prefs['pids']
|
||||
self.prefs_array['serials'] = new_prefs['serials']
|
||||
self.prefs_array['sdrms'] = new_prefs['sdrms']
|
||||
self.prefs_array['outdir'] = new_prefs['outdir']
|
||||
|
||||
def doit(self):
|
||||
self.disablebuttons()
|
||||
filenames=[]
|
||||
bookpath = self.bookpath.get()
|
||||
bookpath = os.path.abspath(bookpath)
|
||||
filenames.append(bookpath)
|
||||
self.master.cd.doit(self.prefs_array,filenames)
|
||||
|
||||
|
||||
|
||||
class ConvDialog(Toplevel):
|
||||
def __init__(self, master, prefs_array={}, filenames=[]):
|
||||
Toplevel.__init__(self, master)
|
||||
self.withdraw()
|
||||
self.protocol("WM_DELETE_WINDOW", self.withdraw)
|
||||
self.title("DeDRM Processing")
|
||||
self.master = master
|
||||
self.apphome = self.master.apphome
|
||||
self.prefs_array = prefs_array
|
||||
self.filenames = filenames
|
||||
self.interval = 50
|
||||
self.p2 = None
|
||||
self.q = Queue()
|
||||
self.running = 'inactive'
|
||||
self.numgood = 0
|
||||
self.numbad = 0
|
||||
self.status = Tkinter.Label(self, text='DeDRM processing...')
|
||||
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='Activity Bar').grid(row=0, sticky=Tkconstants.E)
|
||||
self.bar = ActivityBar(body, length=80, height=15, barwidth=5)
|
||||
self.bar.grid(row=0, column=1, sticky=sticky)
|
||||
|
||||
msg1 = ''
|
||||
self.stext = ScrolledText(body, bd=5, relief=Tkconstants.RIDGE, height=4, width=80, 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.qbutton = Tkinter.Button(buttons, text="Quit", width=14, command=self.quitting)
|
||||
self.qbutton.pack(side=Tkconstants.BOTTOM)
|
||||
self.status['text'] = ''
|
||||
|
||||
self.logfile = open(os.path.join(os.path.expanduser('~'),'DeDRM.log'),'w')
|
||||
|
||||
def show(self):
|
||||
self.deiconify()
|
||||
self.tkraise()
|
||||
|
||||
def hide(self):
|
||||
self.withdraw()
|
||||
|
||||
def doit(self, prefs, filenames):
|
||||
self.running = 'inactive'
|
||||
self.prefs_array = prefs
|
||||
self.filenames = filenames
|
||||
self.show()
|
||||
self.processBooks()
|
||||
|
||||
def conversion_done(self):
|
||||
self.hide()
|
||||
self.master.alldone()
|
||||
|
||||
def processBooks(self):
|
||||
while self.running == 'inactive':
|
||||
rscpath = self.prefs_array['dir']
|
||||
filename = None
|
||||
if len(self.filenames) > 0:
|
||||
filename = self.filenames.pop(0)
|
||||
if filename == None:
|
||||
msg = 'Complete: '
|
||||
msg += 'Successes: %d, ' % self.numgood
|
||||
msg += 'Failures: %d\n' % self.numbad
|
||||
self.showCmdOutput(msg)
|
||||
if self.numbad == 0:
|
||||
self.after(2000,self.conversion_done())
|
||||
self.logfile.write("DeDRM v{0}: {1}".format(__version__,msg))
|
||||
self.logfile.close()
|
||||
return
|
||||
infile = filename
|
||||
bname = os.path.basename(infile)
|
||||
msg = 'Processing: ' + bname + '...'
|
||||
self.logfile.write("DeDRM v{0}: {1}\n".format(__version__,msg))
|
||||
self.showCmdOutput(msg)
|
||||
outdir = os.path.dirname(filename)
|
||||
if 'outdir' in self.prefs_array:
|
||||
dpath = self.prefs_array['outdir']
|
||||
if dpath.strip() != '':
|
||||
outdir = dpath
|
||||
rv = self.decrypt_ebook(infile, outdir, rscpath)
|
||||
if rv == 0:
|
||||
self.bar.start()
|
||||
self.running = 'active'
|
||||
self.processQueue()
|
||||
else:
|
||||
msg = 'Unknown File: ' + bname + '\n'
|
||||
self.logfile.write("DeDRM v{0}: {1}".format(__version__,msg))
|
||||
self.showCmdOutput(msg)
|
||||
self.numbad += 1
|
||||
|
||||
def quitting(self):
|
||||
# kill any still running subprocess
|
||||
self.running = 'stopped'
|
||||
if self.p2 != None:
|
||||
if (self.p2.exitcode == None):
|
||||
self.p2.terminate()
|
||||
self.conversion_done()
|
||||
|
||||
# post output from subprocess in scrolled text widget
|
||||
def showCmdOutput(self, msg):
|
||||
if msg and msg !='':
|
||||
if sys.platform.startswith('win'):
|
||||
msg = msg.replace('\r\n','\n')
|
||||
self.stext.insert(Tkconstants.END,msg)
|
||||
self.stext.yview_pickplace(Tkconstants.END)
|
||||
return
|
||||
|
||||
# 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 processQueue(self):
|
||||
if self.p2 == None:
|
||||
# nothing to wait for so just return
|
||||
return
|
||||
poll = self.p2.exitcode
|
||||
#print "processing", poll
|
||||
done = False
|
||||
text = ''
|
||||
while not done:
|
||||
try:
|
||||
data = self.q.get_nowait()
|
||||
text += data
|
||||
except Empty:
|
||||
done = True
|
||||
if text != '':
|
||||
self.logfile.write(text)
|
||||
if poll != None:
|
||||
self.bar.stop()
|
||||
if poll == 0:
|
||||
msg = 'Success\n'
|
||||
self.numgood += 1
|
||||
else:
|
||||
msg = 'Failed\n'
|
||||
self.numbad += 1
|
||||
self.p2.join()
|
||||
self.logfile.write("DeDRM v{0}: {1}\n".format(__version__,msg))
|
||||
self.showCmdOutput(msg)
|
||||
self.p2 = None
|
||||
self.running = 'inactive'
|
||||
self.after(50,self.processBooks)
|
||||
return
|
||||
# make sure we get invoked again by event loop after interval
|
||||
self.stext.after(self.interval,self.processQueue)
|
||||
return
|
||||
|
||||
def decrypt_ebook(self, infile, outdir, rscpath):
|
||||
q = self.q
|
||||
rv = 1
|
||||
name, ext = os.path.splitext(os.path.basename(infile))
|
||||
ext = ext.lower()
|
||||
if ext == '.epub':
|
||||
self.p2 = Process(target=processEPUB, args=(q, infile, outdir, rscpath))
|
||||
self.p2.start()
|
||||
return 0
|
||||
if ext == '.pdb':
|
||||
self.p2 = Process(target=processPDB, args=(q, infile, outdir, rscpath))
|
||||
self.p2.start()
|
||||
return 0
|
||||
if ext in ['.azw', '.azw1', '.azw3', '.azw4', '.prc', '.mobi', '.pobi', '.tpz', '.azw8', '.kfx', '.kfx-zip']:
|
||||
self.p2 = Process(target=processK4MOBI,args=(q, infile, outdir, rscpath))
|
||||
self.p2.start()
|
||||
return 0
|
||||
if ext == '.pdf':
|
||||
self.p2 = Process(target=processPDF, args=(q, infile, outdir, rscpath))
|
||||
self.p2.start()
|
||||
return 0
|
||||
return rv
|
||||
|
||||
|
||||
# child process starts here
|
||||
def processK4MOBI(q, infile, outdir, rscpath):
|
||||
add_cp65001_codec()
|
||||
set_utf8_default_encoding()
|
||||
sys.stdout = QueuedUTF8Stream(sys.stdout, q)
|
||||
sys.stderr = QueuedUTF8Stream(sys.stderr, q)
|
||||
rv = decryptk4mobi(infile, outdir, rscpath)
|
||||
sys.exit(rv)
|
||||
|
||||
# child process starts here
|
||||
def processPDF(q, infile, outdir, rscpath):
|
||||
add_cp65001_codec()
|
||||
set_utf8_default_encoding()
|
||||
sys.stdout = QueuedUTF8Stream(sys.stdout, q)
|
||||
sys.stderr = QueuedUTF8Stream(sys.stderr, q)
|
||||
rv = decryptpdf(infile, outdir, rscpath)
|
||||
sys.exit(rv)
|
||||
|
||||
# child process starts here
|
||||
def processEPUB(q, infile, outdir, rscpath):
|
||||
add_cp65001_codec()
|
||||
set_utf8_default_encoding()
|
||||
sys.stdout = QueuedUTF8Stream(sys.stdout, q)
|
||||
sys.stderr = QueuedUTF8Stream(sys.stderr, q)
|
||||
rv = decryptepub(infile, outdir, rscpath)
|
||||
sys.exit(rv)
|
||||
|
||||
# child process starts here
|
||||
def processPDB(q, infile, outdir, rscpath):
|
||||
add_cp65001_codec()
|
||||
set_utf8_default_encoding()
|
||||
sys.stdout = QueuedUTF8Stream(sys.stdout, q)
|
||||
sys.stderr = QueuedUTF8Stream(sys.stderr, q)
|
||||
rv = decryptpdb(infile, outdir, rscpath)
|
||||
sys.exit(rv)
|
||||
|
||||
|
||||
def main():
|
||||
argv=unicode_argv()
|
||||
apphome = os.path.dirname(argv[0])
|
||||
apphome = os.path.abspath(apphome)
|
||||
|
||||
# windows may pass a spurious quoted null string as argv[1] from bat file
|
||||
# simply work around this until we can figure out a better way to handle things
|
||||
if sys.platform.startswith('win') and len(argv) == 2:
|
||||
temp = argv[1]
|
||||
temp = temp.strip('"')
|
||||
temp = temp.strip()
|
||||
if temp == '':
|
||||
argv.pop()
|
||||
|
||||
if len(argv) == 1:
|
||||
filenames = []
|
||||
dnd = False
|
||||
|
||||
else : # processing books via drag and drop
|
||||
dnd = True
|
||||
# build a list of the files to be processed
|
||||
# note all filenames and paths have been utf-8 encoded
|
||||
infilelst = argv[1:]
|
||||
filenames = []
|
||||
for infile in infilelst:
|
||||
infile = infile.replace('"','')
|
||||
infile = os.path.abspath(infile)
|
||||
if os.path.isdir(infile):
|
||||
bpath = infile
|
||||
filelst = os.listdir(infile)
|
||||
for afile in filelst:
|
||||
if not afile.startswith('.'):
|
||||
filepath = os.path.join(bpath,afile)
|
||||
if os.path.isfile(filepath):
|
||||
filenames.append(filepath)
|
||||
else :
|
||||
afile = os.path.basename(infile)
|
||||
if not afile.startswith('.'):
|
||||
if os.path.isfile(infile):
|
||||
filenames.append(infile)
|
||||
|
||||
# start up gui app
|
||||
app = MainApp(apphome, dnd, filenames)
|
||||
app.mainloop()
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
65
contrib/DeDRM_Windows_Application/DeDRM_App_ReadMe.txt
Normal file
65
contrib/DeDRM_Windows_Application/DeDRM_App_ReadMe.txt
Normal file
@@ -0,0 +1,65 @@
|
||||
DeDRM_App - DeDRM_App.pyw and DeDRM_Drop_Target.bat
|
||||
===================================================
|
||||
|
||||
DeDRM_App.pyw is a python drag and drop application that allows users to drag and drop ebooks or folders of ebooks onto the DeDRM_Drop_Target.bat to have the DRM removed. It repackages all the tools (except obok) in one easy to use program that remembers preferences and settings.
|
||||
|
||||
It will work without manual configuration for Kindle for PC ebooks, Adobe Digital Edition (2.0.1) epub and pdf ebooks and Barnes & Noble NOOK Study ePubs when Kindle for PC, Adobe Digital Editions and NOOK Study are installed on the same computer and user account.
|
||||
|
||||
To remove the DRM from eInk Kindle ebooks, Mobipocket ebooks and Fictionwise eReader ebooks requires the user to double-click the DeDRM_Drop_Target.bat file and set some additional Preferences including:
|
||||
|
||||
eInk Kindle: 16 digit Serial Number
|
||||
MobiPocket: 10 digit PID
|
||||
eReader Social DRM: Name:Last 8 digits of CC number
|
||||
|
||||
Once these preferences have been set, the user can simply drag and drop ebooks onto the DeDRM_Drop_Target to remove the DRM. Note that after setting preferences it is necessary to click on "Set Prefs" button and then quit the application for the change in preferences to fully take effect.
|
||||
|
||||
This program requires that Python 2.7 and PyCrypto 2.6 for Python 2.7 be installed on your computer before it will work. See below for how to get and install these programs for Windows.
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
0. If you don't already have a correct version of Python and PyCrypto installed, follow the "Installing Python on Windows" and "Installing PyCrypto on Windows" sections below before continuing.
|
||||
|
||||
1. Drag the DeDRM_App folder from DeDRM_Application_Windows to your "My Documents" folder.
|
||||
|
||||
2. Open the DeDRM_App folder you've just dragged, and make a short-cut of the DeDRM_Drop_Target.bat file (right-click/Create Shortcut). Drag the shortcut file onto your Desktop.
|
||||
|
||||
3. To set the preferences simply double-click on the short-cut you've just created.
|
||||
|
||||
|
||||
Credits
|
||||
-------
|
||||
The original inept and ignoble scripts were by i♥cabbages
|
||||
The original mobidedrm and erdr2pml scripts were by The Dark Reverser
|
||||
The original topaz DRM removal script was by CMBDTC
|
||||
The original topaz format conversion scripts were by some_updates, clarknova and Bart Simpson
|
||||
|
||||
The alfcrypto library is by some_updates
|
||||
The ePub encryption detection script is by Apprentice Alf, adapted from a script by Paul Durrant
|
||||
The ignoblekey script is by Apprentice Harper
|
||||
The DeDRM python GUI was by some_updates and is maintained by Apprentice Alf and Apprentice Harper
|
||||
|
||||
Many fixes, updates and enhancements to the scripts and applicatons have been made by many other people. For more details, see the commments in the individual scripts.
|
||||
|
||||
|
||||
Installing Python on Windows
|
||||
----------------------------
|
||||
I strongly recommend fully installing ActiveState’s Active Python, free Community Edition for Windows. This is a free, full version of the Python. It comes with some important additional modules that are not included in the bare-bones version from www.python.org unless you choose to install everything.
|
||||
|
||||
1. Download ActivePython 2.7.8 for Windows (or later 2.7.x version for Windows, but NOT 3.x) from http://www.activestate.com/activepython/downloads.
|
||||
|
||||
2. When it has finished downloading, run the installer. Accept the default options.
|
||||
|
||||
|
||||
Installing PyCrypto on Windows
|
||||
------------------------------
|
||||
PyCrypto is a set of encryption/decryption routines that work with Python. The sources are freely available, and compiled versions are available from several sources. You must install a version that is for Python 2.7. I recommend the installer linked from Michael Foord’s blog.
|
||||
|
||||
1. Download PyCrypto 2.6 (or later) for Windows and Python 2.7 from http://www.voidspace.org.uk/python/modules.shtml#pycrypto
|
||||
|
||||
2. When it has finished downloading, run the application. Accept the default options.
|
||||
|
||||
|
||||
Linux Users
|
||||
===========
|
||||
The DeDRM_app.pyw script, although not the .bat shortcut, should work under Linux. Drag & drop functionality is not available. Depending on your Linux installation, you may or may not need to install Python 2 and PyCrypto.
|
||||
Reference in New Issue
Block a user