mirror of
https://github.com/noDRM/DeDRM_tools.git
synced 2026-03-20 04:58:56 +00:00
Compare commits
6 Commits
3b0b4c91d4
...
8228e38d5a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8228e38d5a | ||
|
|
10b6caf9f5 | ||
|
|
53996cf49c | ||
|
|
d388ae72fd | ||
|
|
bc089ee46d | ||
|
|
ad33aea18d |
71
.github/workflows/main.yml
vendored
71
.github/workflows/main.yml
vendored
@@ -20,46 +20,33 @@ jobs:
|
||||
path: |
|
||||
DeDRM_tools_*.zip
|
||||
DeDRM_tools.zip
|
||||
|
||||
- name: Prepare release
|
||||
run: cp DeDRM_tools.zip DeDRM_alpha_${{ github.sha }}.zip
|
||||
|
||||
# - name: Delete old release
|
||||
# uses: cb80/delrel@latest
|
||||
# with:
|
||||
# tag: autorelease
|
||||
# token: ${{ github.token }}
|
||||
#
|
||||
# - name: Delete old tag
|
||||
# uses: dev-drprasad/delete-tag-and-release@v1.0
|
||||
# with:
|
||||
# tag_name: autorelease
|
||||
# github_token: ${{ github.token }}
|
||||
# delete_release: true
|
||||
#
|
||||
# - name: Prepare release
|
||||
# run: cp DeDRM_tools.zip DeDRM_alpha_${{ github.sha }}.zip
|
||||
#
|
||||
# - name: Auto-release
|
||||
# id: autorelease
|
||||
# uses: softprops/action-gh-release@v1
|
||||
# with:
|
||||
# tag_name: autorelease
|
||||
# token: ${{ github.token }}
|
||||
# name: Automatic alpha release with latest changes
|
||||
# body: |
|
||||
# This release is automatically generated by Github for each commit.
|
||||
#
|
||||
# This means, every time a change is made to this repo, this release will be updated to contain an untested copy of the plugin at that stage. This will contain the most up-to-date code, but it's not tested at all and may be broken.
|
||||
#
|
||||
# Last update based on Git commit ${{ github.sha }}.
|
||||
# prerelease: true
|
||||
# draft: true
|
||||
# files: DeDRM_alpha_${{ github.sha }}.zip
|
||||
#
|
||||
# - name: Make release public
|
||||
# uses: irongut/EditRelease@v1.2.0
|
||||
# with:
|
||||
# token: ${{ github.token }}
|
||||
# id: ${{ steps.autorelease.outputs.id }}
|
||||
# draft: false
|
||||
# prerelease: true
|
||||
#
|
||||
#
|
||||
|
||||
- uses: dev-drprasad/delete-older-releases@v0.2.1
|
||||
with:
|
||||
repo: noDRM/DeDRM_tools_autorelease
|
||||
keep_latest: 0
|
||||
delete_tags: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.AUTORELEASE_KEY }}
|
||||
|
||||
- name: Auto-release
|
||||
id: autorelease
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
tag_name: autorelease_${{ github.sha }}
|
||||
repository: noDRM/DeDRM_tools_autorelease
|
||||
token: ${{ secrets.AUTORELEASE_KEY }}
|
||||
name: Automatic alpha release with latest changes
|
||||
body: |
|
||||
This release is automatically generated by Github for each commit.
|
||||
|
||||
This means, every time a change is made to the repo, a release with an untested copy of the plugin at that stage will be created. This will contain the most up-to-date code, but it's not tested at all and may be broken.
|
||||
|
||||
Last update based on Git commit [${{ github.sha }}](https://github.com/noDRM/DeDRM_tools/commit/${{ github.sha }}).
|
||||
prerelease: true
|
||||
draft: false
|
||||
files: DeDRM_alpha_${{ github.sha }}.zip
|
||||
|
||||
@@ -101,4 +101,5 @@ This is v10.0.9, a release candidate for v10.1.0. I don't expect there to be maj
|
||||
|
||||
- Fix a bug where decrypting a 40-bit RC4 pdf with R=2 didn't work.
|
||||
- Fix a bug where decrypting a 256-bit AES pdf with V=5 didn't work.
|
||||
- Fix bugs in kgenpids.py and kindlekey.py that caused it to fail on Python 2 (#380).
|
||||
- Fix bugs in kgenpids.py, alfcrypto.py, mobidedrm.py and kindlekey.py that caused it to fail on Python 2 (#380).
|
||||
- Fix some bugs (Python 2 and Python 3) in erdr2pml.py (untested).
|
||||
|
||||
@@ -15,6 +15,6 @@ if "calibre" in sys.modules and sys.version_info[0] == 2:
|
||||
sys.path.insert(0, os.path.join(config_dir, "plugins", "DeDRM.zip"))
|
||||
|
||||
# Explicitly set the package identifier so we are allowed to import stuff ...
|
||||
#__package__ = "DeDRM_plugin"
|
||||
__package__ = "calibre_plugins.dedrm"
|
||||
|
||||
#@@CALIBRE_COMPAT_CODE_END@@
|
||||
|
||||
@@ -96,7 +96,10 @@ import traceback
|
||||
#@@CALIBRE_COMPAT_CODE@@
|
||||
|
||||
try:
|
||||
import __version
|
||||
try:
|
||||
from . import __version
|
||||
except:
|
||||
import __version
|
||||
except:
|
||||
print("#############################")
|
||||
print("Failed to load the DeDRM plugin")
|
||||
@@ -134,8 +137,10 @@ try:
|
||||
except:
|
||||
config_dir = ""
|
||||
|
||||
|
||||
import utilities
|
||||
try:
|
||||
from . import utilities
|
||||
except:
|
||||
import utilities
|
||||
|
||||
|
||||
PLUGIN_NAME = __version.PLUGIN_NAME
|
||||
@@ -915,6 +920,9 @@ class DeDRM(FileTypePlugin):
|
||||
# perhaps we need to get a new default Kindle for Mac/PC key
|
||||
defaultkeys = []
|
||||
print("{0} v{1}: Failed to decrypt with error: {2}".format(PLUGIN_NAME, PLUGIN_VERSION,e.args[0]))
|
||||
|
||||
traceback.print_exc()
|
||||
|
||||
print("{0} v{1}: Looking for new default Kindle Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
|
||||
|
||||
try:
|
||||
|
||||
@@ -44,10 +44,11 @@ __version__ = '7.4'
|
||||
import sys, os, struct, getopt
|
||||
from base64 import b64decode
|
||||
|
||||
#@@CALIBRE_COMPAT_CODE@@
|
||||
|
||||
|
||||
from utilities import SafeUnbuffered
|
||||
from argv_utils import unicode_argv
|
||||
from .utilities import SafeUnbuffered
|
||||
from .argv_utils import unicode_argv
|
||||
|
||||
|
||||
try:
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
# pbkdf2.py Copyright © 2009 Daniel Holth <dholth@fastmail.fm>
|
||||
# pbkdf2.py This code may be freely used and modified for any purpose.
|
||||
|
||||
import sys
|
||||
import hmac
|
||||
from struct import pack
|
||||
import hashlib
|
||||
@@ -25,7 +26,10 @@ class Pukall_Cipher(object):
|
||||
raise Exception("PC1: Bad key length")
|
||||
wkey = []
|
||||
for i in range(8):
|
||||
wkey.append(key[i*2]<<8 | key[i*2+1])
|
||||
if sys.version_info[0] == 2:
|
||||
wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1]))
|
||||
else:
|
||||
wkey.append(key[i*2]<<8 | key[i*2+1])
|
||||
dst = bytearray(len(src))
|
||||
for i in range(len(src)):
|
||||
temp1 = 0;
|
||||
@@ -37,7 +41,12 @@ class Pukall_Cipher(object):
|
||||
sum2 = (sum2+sum1)&0xFFFF
|
||||
temp1 = (temp1*20021+1)&0xFFFF
|
||||
byteXorVal ^= temp1 ^ sum2
|
||||
curByte = src[i]
|
||||
|
||||
if sys.version_info[0] == 2:
|
||||
curByte = ord(src[i])
|
||||
else:
|
||||
curByte = src[i]
|
||||
|
||||
if not decryption:
|
||||
keyXorVal = curByte * 257;
|
||||
curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF
|
||||
@@ -45,7 +54,12 @@ class Pukall_Cipher(object):
|
||||
keyXorVal = curByte * 257;
|
||||
for j in range(8):
|
||||
wkey[j] ^= keyXorVal;
|
||||
dst[i] = curByte
|
||||
|
||||
if sys.version_info[0] == 2:
|
||||
dst[i] = chr(curByte)
|
||||
else:
|
||||
dst[i] = curByte
|
||||
|
||||
return bytes(dst)
|
||||
|
||||
class Topaz_Cipher(object):
|
||||
@@ -103,7 +117,7 @@ class KeyIVGen(object):
|
||||
def xorbytes( a, b ):
|
||||
if len(a) != len(b):
|
||||
raise Exception("xorbytes(): lengths differ")
|
||||
return bytes([x ^ y for x, y in zip(a, b)])
|
||||
return bytes(bytearray([x ^ y for x, y in zip(a, b)]))
|
||||
|
||||
def prf( h, data ):
|
||||
hm = h.copy()
|
||||
|
||||
@@ -29,7 +29,7 @@ from calibre.constants import iswindows, isosx
|
||||
|
||||
from __init__ import PLUGIN_NAME, PLUGIN_VERSION
|
||||
from __version import RESOURCE_NAME as help_file_name
|
||||
from utilities import uStrCmp
|
||||
from .utilities import uStrCmp
|
||||
|
||||
import prefs
|
||||
import androidkindlekey
|
||||
|
||||
@@ -53,12 +53,12 @@ import sys, struct, os, traceback
|
||||
import zlib
|
||||
import zipfile
|
||||
import xml.etree.ElementTree as etree
|
||||
from argv_utils import unicode_argv
|
||||
from .argv_utils import unicode_argv
|
||||
|
||||
NSMAP = {'adept': 'http://ns.adobe.com/adept',
|
||||
'enc': 'http://www.w3.org/2001/04/xmlenc#'}
|
||||
|
||||
from utilities import SafeUnbuffered
|
||||
from .utilities import SafeUnbuffered
|
||||
|
||||
|
||||
_FILENAME_LEN_OFFSET = 26
|
||||
|
||||
@@ -79,12 +79,13 @@ except ImportError:
|
||||
|
||||
#@@CALIBRE_COMPAT_CODE@@
|
||||
|
||||
from utilities import SafeUnbuffered
|
||||
from .utilities import SafeUnbuffered
|
||||
from .argv_utils import unicode_argv
|
||||
|
||||
iswindows = sys.platform.startswith('win')
|
||||
isosx = sys.platform.startswith('darwin')
|
||||
|
||||
from argv_utils import unicode_argv
|
||||
|
||||
|
||||
import cgi
|
||||
import logging
|
||||
@@ -141,14 +142,20 @@ def sanitizeFileName(name):
|
||||
|
||||
def fixKey(key):
|
||||
def fixByte(b):
|
||||
if sys.version_info[0] == 2:
|
||||
b = ord(b)
|
||||
|
||||
return b ^ ((b ^ (b<<1) ^ (b<<2) ^ (b<<3) ^ (b<<4) ^ (b<<5) ^ (b<<6) ^ (b<<7) ^ 0x80) & 0x80)
|
||||
return bytes([fixByte(a) for a in key])
|
||||
return bytes(bytearray([fixByte(a) for a in key]))
|
||||
|
||||
def deXOR(text, sp, table):
|
||||
r=''
|
||||
r=b''
|
||||
j = sp
|
||||
for i in range(len(text)):
|
||||
r += chr(ord(table[j]) ^ ord(text[i]))
|
||||
if sys.version_info[0] == 2:
|
||||
r += chr(ord(table[j]) ^ ord(text[i]))
|
||||
else:
|
||||
r += bytes(bytearray([table[j] ^ text[i]]))
|
||||
j = j + 1
|
||||
if j == len(table):
|
||||
j = 0
|
||||
|
||||
@@ -50,9 +50,9 @@ try:
|
||||
except ImportError:
|
||||
from Crypto.Cipher import AES
|
||||
|
||||
from utilities import SafeUnbuffered
|
||||
from .utilities import SafeUnbuffered
|
||||
|
||||
from argv_utils import unicode_argv
|
||||
from .argv_utils import unicode_argv
|
||||
|
||||
class IGNOBLEError(Exception):
|
||||
pass
|
||||
|
||||
@@ -27,14 +27,14 @@ import hashlib
|
||||
import getopt
|
||||
import re
|
||||
|
||||
from utilities import SafeUnbuffered
|
||||
from .utilities import SafeUnbuffered
|
||||
|
||||
try:
|
||||
from calibre.constants import iswindows
|
||||
except:
|
||||
iswindows = sys.platform.startswith('win')
|
||||
|
||||
from argv_utils import unicode_argv
|
||||
from .argv_utils import unicode_argv
|
||||
|
||||
class DrmException(Exception):
|
||||
pass
|
||||
|
||||
@@ -70,9 +70,8 @@ def unpad(data, padding=16):
|
||||
|
||||
return data[:-pad_len]
|
||||
|
||||
from utilities import SafeUnbuffered
|
||||
|
||||
from argv_utils import unicode_argv
|
||||
from .utilities import SafeUnbuffered
|
||||
from .argv_utils import unicode_argv
|
||||
|
||||
|
||||
class ADEPTError(Exception):
|
||||
|
||||
@@ -93,12 +93,12 @@ def unpad(data, padding=16):
|
||||
return data[:-pad_len]
|
||||
|
||||
|
||||
from utilities import SafeUnbuffered
|
||||
from .utilities import SafeUnbuffered
|
||||
|
||||
iswindows = sys.platform.startswith('win')
|
||||
isosx = sys.platform.startswith('darwin')
|
||||
|
||||
from argv_utils import unicode_argv
|
||||
from .argv_utils import unicode_argv
|
||||
|
||||
class ADEPTError(Exception):
|
||||
pass
|
||||
|
||||
@@ -88,9 +88,9 @@ import kgenpids
|
||||
import androidkindlekey
|
||||
import kfxdedrm
|
||||
|
||||
from utilities import SafeUnbuffered
|
||||
from .utilities import SafeUnbuffered
|
||||
|
||||
from argv_utils import unicode_argv
|
||||
from .argv_utils import unicode_argv
|
||||
|
||||
|
||||
# cleanup unicode filenames
|
||||
|
||||
@@ -190,6 +190,10 @@ def getKindlePids(rec209, token, serialnum):
|
||||
if isinstance(serialnum,str):
|
||||
serialnum = serialnum.encode('utf-8')
|
||||
|
||||
if sys.version_info[0] == 2:
|
||||
if isinstance(serialnum,unicode):
|
||||
serialnum = serialnum.encode('utf-8')
|
||||
|
||||
if rec209 is None:
|
||||
return [serialnum]
|
||||
|
||||
|
||||
@@ -62,7 +62,9 @@ except NameError:
|
||||
|
||||
# Routines common to Mac and PC
|
||||
|
||||
from utilities import SafeUnbuffered
|
||||
from .utilities import SafeUnbuffered
|
||||
from .argv_utils import unicode_argv
|
||||
|
||||
|
||||
try:
|
||||
from calibre.constants import iswindows, isosx
|
||||
@@ -70,7 +72,7 @@ except:
|
||||
iswindows = sys.platform.startswith('win')
|
||||
isosx = sys.platform.startswith('darwin')
|
||||
|
||||
from argv_utils import unicode_argv
|
||||
|
||||
|
||||
class DrmException(Exception):
|
||||
pass
|
||||
@@ -238,9 +240,14 @@ if iswindows:
|
||||
|
||||
# replace any non-ASCII values with 0xfffd
|
||||
for i in range(0,len(buffer)):
|
||||
if buffer[i]>"\u007f":
|
||||
#print "swapping char "+str(i)+" ("+buffer[i]+")"
|
||||
buffer[i] = "\ufffd"
|
||||
if sys.version_info[0] == 2:
|
||||
if buffer[i]>u"\u007f":
|
||||
#print "swapping char "+str(i)+" ("+buffer[i]+")"
|
||||
buffer[i] = u"\ufffd"
|
||||
else:
|
||||
if buffer[i]>"\u007f":
|
||||
#print "swapping char "+str(i)+" ("+buffer[i]+")"
|
||||
buffer[i] = "\ufffd"
|
||||
# return utf-8 encoding of modified username
|
||||
#print "modified username:"+buffer.value
|
||||
return buffer.value.encode('utf-8')
|
||||
|
||||
@@ -16,24 +16,24 @@
|
||||
import sys
|
||||
import binascii
|
||||
|
||||
from utilities import SafeUnbuffered
|
||||
from .utilities import SafeUnbuffered
|
||||
|
||||
from argv_utils import unicode_argv
|
||||
from .argv_utils import unicode_argv
|
||||
|
||||
letters = 'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789'
|
||||
letters = b'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789'
|
||||
|
||||
def crc32(s):
|
||||
return (~binascii.crc32(s,-1))&0xFFFFFFFF
|
||||
|
||||
def checksumPid(s):
|
||||
crc = crc32(s.encode('ascii'))
|
||||
crc = crc32(s)
|
||||
crc = crc ^ (crc >> 16)
|
||||
res = s
|
||||
l = len(letters)
|
||||
for i in (0,1):
|
||||
b = crc & 0xff
|
||||
pos = (b // l) ^ (b % l)
|
||||
res += letters[pos%l]
|
||||
res += bytes(bytearray([letters[pos%l]]))
|
||||
crc >>= 8
|
||||
|
||||
return res
|
||||
@@ -43,16 +43,19 @@ def pidFromSerial(s, l):
|
||||
|
||||
arr1 = [0]*l
|
||||
for i in range(len(s)):
|
||||
arr1[i%l] ^= s[i]
|
||||
if sys.version_info[0] == 2:
|
||||
arr1[i%l] ^= ord(s[i])
|
||||
else:
|
||||
arr1[i%l] ^= s[i]
|
||||
|
||||
crc_bytes = [crc >> 24 & 0xff, crc >> 16 & 0xff, crc >> 8 & 0xff, crc & 0xff]
|
||||
for i in range(l):
|
||||
arr1[i] ^= crc_bytes[i&3]
|
||||
|
||||
pid = ''
|
||||
pid = b""
|
||||
for i in range(l):
|
||||
b = arr1[i] & 0xff
|
||||
pid+=letters[(b >> 7) + ((b >> 5 & 3) ^ (b & 0x1f))]
|
||||
pid+=bytes(bytearray([letters[(b >> 7) + ((b >> 5 & 3) ^ (b & 0x1f))]]))
|
||||
|
||||
return pid
|
||||
|
||||
|
||||
@@ -80,11 +80,14 @@ import sys
|
||||
import os
|
||||
import struct
|
||||
import binascii
|
||||
from alfcrypto import Pukall_Cipher
|
||||
|
||||
from utilities import SafeUnbuffered
|
||||
|
||||
from argv_utils import unicode_argv
|
||||
#@@CALIBRE_COMPAT_CODE@@
|
||||
|
||||
|
||||
from .alfcrypto import Pukall_Cipher
|
||||
from .utilities import SafeUnbuffered
|
||||
from .argv_utils import unicode_argv
|
||||
|
||||
|
||||
class DrmException(Exception):
|
||||
@@ -103,19 +106,26 @@ def PC1(key, src, decryption=True):
|
||||
except:
|
||||
raise
|
||||
|
||||
# accepts unicode returns unicode
|
||||
letters = b'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789'
|
||||
|
||||
def crc32(s):
|
||||
return (~binascii.crc32(s,-1))&0xFFFFFFFF
|
||||
|
||||
def checksumPid(s):
|
||||
letters = 'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789'
|
||||
crc = (~binascii.crc32(s.encode('utf-8'),-1))&0xFFFFFFFF
|
||||
|
||||
s = s.encode()
|
||||
|
||||
|
||||
crc = crc32(s)
|
||||
crc = crc ^ (crc >> 16)
|
||||
res = s
|
||||
l = len(letters)
|
||||
for i in (0,1):
|
||||
b = crc & 0xff
|
||||
pos = (b // l) ^ (b % l)
|
||||
res += letters[pos%l]
|
||||
res += bytes(bytearray([letters[pos%l]]))
|
||||
crc >>= 8
|
||||
return res
|
||||
return res.decode()
|
||||
|
||||
# expects bytearray
|
||||
def getSizeOfTrailingDataEntries(ptr, size, flags):
|
||||
@@ -124,7 +134,11 @@ def getSizeOfTrailingDataEntries(ptr, size, flags):
|
||||
if size <= 0:
|
||||
return result
|
||||
while True:
|
||||
v = ptr[size-1]
|
||||
if sys.version_info[0] == 2:
|
||||
v = ord(ptr[size-1])
|
||||
else:
|
||||
v = ptr[size-1]
|
||||
|
||||
result |= (v & 0x7F) << bitpos
|
||||
bitpos += 7
|
||||
size -= 1
|
||||
@@ -140,7 +154,10 @@ def getSizeOfTrailingDataEntries(ptr, size, flags):
|
||||
# if multibyte data is included in the encryped data, we'll
|
||||
# have already cleared this flag.
|
||||
if flags & 1:
|
||||
num += (ptr[size - num - 1] & 0x3) + 1
|
||||
if sys.version_info[0] == 2:
|
||||
num += (ord(ptr[size - num - 1]) & 0x3) + 1
|
||||
else:
|
||||
num += (ptr[size - num - 1] & 0x3) + 1
|
||||
return num
|
||||
|
||||
|
||||
@@ -299,7 +316,10 @@ class MobiBook:
|
||||
for pid in pidlist:
|
||||
bigpid = pid.encode('utf-8').ljust(16,b'\0')
|
||||
temp_key = PC1(keyvec1, bigpid, False)
|
||||
temp_key_sum = sum(temp_key) & 0xff
|
||||
if sys.version_info[0] == 2:
|
||||
temp_key_sum = sum(map(ord,temp_key)) & 0xff
|
||||
else:
|
||||
temp_key_sum = sum(temp_key) & 0xff
|
||||
found_key = None
|
||||
for i in range(count):
|
||||
verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30])
|
||||
@@ -315,7 +335,11 @@ class MobiBook:
|
||||
# Then try the default encoding that doesn't require a PID
|
||||
pid = '00000000'
|
||||
temp_key = keyvec1
|
||||
temp_key_sum = sum(temp_key) & 0xff
|
||||
if sys.version_info[0] == 2:
|
||||
temp_key_sum = sum(map(ord,temp_key)) & 0xff
|
||||
else:
|
||||
temp_key_sum = sum(temp_key) & 0xff
|
||||
|
||||
for i in range(count):
|
||||
verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30])
|
||||
if cksum == temp_key_sum:
|
||||
|
||||
@@ -24,10 +24,10 @@ import traceback
|
||||
from struct import pack
|
||||
from struct import unpack
|
||||
|
||||
from alfcrypto import Topaz_Cipher
|
||||
from utilities import SafeUnbuffered
|
||||
from .alfcrypto import Topaz_Cipher
|
||||
from .utilities import SafeUnbuffered
|
||||
|
||||
from argv_utils import unicode_argv
|
||||
from .argv_utils import unicode_argv
|
||||
|
||||
|
||||
#global switch
|
||||
|
||||
@@ -327,36 +327,50 @@ class KoboLibrary(object):
|
||||
elif sys.platform.startswith('darwin'):
|
||||
self.kobodir = os.path.join(os.environ['HOME'], "Library", "Application Support", "Kobo", "Kobo Desktop Edition")
|
||||
elif sys.platform.startswith('linux'):
|
||||
# Since on Linux, you have to run Kobo Desktop within Wine,
|
||||
# there is no guarantee where Kobo.sqlite (and the rest of
|
||||
# the kobo directory) might be.
|
||||
#
|
||||
# It should also be noted that Kobo Desktop will store all
|
||||
# of it files in the current directory where you run it,
|
||||
# meaning that a user might accidentally create several
|
||||
# Kobo.sqlite files which are all separate and then be
|
||||
# confused why Obok can't find any of the new books. Sadly
|
||||
# there isn't a trivial way to deal with this.
|
||||
|
||||
#sets ~/.config/calibre as the location to store the kobodir location info file and creates this directory if necessary
|
||||
kobodir_cache_dir = os.path.join(os.environ['HOME'], ".config", "calibre")
|
||||
# We cache the kobodir we find in ~/.config/calibre.
|
||||
kobodir_cache_dir = os.path.join(os.environ['HOME'], ".config", "calibre", "plugins", "obok")
|
||||
if not os.path.isdir(kobodir_cache_dir):
|
||||
os.mkdir(kobodir_cache_dir)
|
||||
|
||||
#appends the name of the file we're storing the kobodir location info to the above path
|
||||
kobodir_cache_file = str(kobodir_cache_dir) + "/" + "kobo location"
|
||||
|
||||
"""if the above file does not exist, recursively searches from the root
|
||||
of the filesystem until kobodir is found and stores the location of kobodir
|
||||
in that file so this loop can be skipped in the future"""
|
||||
original_stdout = sys.stdout
|
||||
if not os.path.isfile(kobodir_cache_file):
|
||||
for root, dirs, files in os.walk('/'):
|
||||
for file in files:
|
||||
if file == 'Kobo.sqlite':
|
||||
kobo_linux_path = str(root)
|
||||
with open(kobodir_cache_file, 'w') as f:
|
||||
sys.stdout = f
|
||||
print(kobo_linux_path, end='')
|
||||
sys.stdout = original_stdout
|
||||
kobodir_cache_file = os.path.join(kobodir_cache_dir, "kobo-location")
|
||||
|
||||
f = open(kobodir_cache_file, 'r' )
|
||||
self.kobodir = f.read()
|
||||
try:
|
||||
# If the cached version exists and the path does really
|
||||
# contain Kobo.sqlite, use that.
|
||||
with open(kobodir_cache_file, "r") as f:
|
||||
cached_kobodir = f.read().strip()
|
||||
assert os.path.isfile(os.path.join(cached_kobodir, "Kobo.sqlite"))
|
||||
self.kobodir = cached_kobodir
|
||||
except (AssertionError, FileNotFoundError):
|
||||
# If there was no cached version, search the entire
|
||||
# filesystem tree for a directory containing
|
||||
# "Kobo.sqlite".
|
||||
#
|
||||
# We first search $HOME to avoid picking another user's
|
||||
# Kobo.sqlite file, but then fallback to / if there was
|
||||
# nothing in $HOME.
|
||||
for candidate_root in (os.environ["HOME"], "/"):
|
||||
for root, _, files in os.walk(candidate_root):
|
||||
if "Kobo.sqlite" in files:
|
||||
with open(kobodir_cache_file, "w") as f:
|
||||
f.write("%s/\n" % (root,))
|
||||
self.kobodir = root
|
||||
break
|
||||
|
||||
# desktop versions use Kobo.sqlite
|
||||
# Desktop versions use Kobo.sqlite.
|
||||
kobodb = os.path.join(self.kobodir, "Kobo.sqlite")
|
||||
# check for existence of file
|
||||
if (not(os.path.isfile(kobodb))):
|
||||
if not os.path.isfile(kobodb):
|
||||
# give up here, we haven't found anything useful
|
||||
self.kobodir = u""
|
||||
kobodb = u""
|
||||
@@ -430,7 +444,7 @@ class KoboLibrary(object):
|
||||
macaddrs = []
|
||||
if sys.platform.startswith('win'):
|
||||
c = re.compile('\s?(' + '[0-9a-f]{2}[:\-]' * 5 + '[0-9a-f]{2})(\s|$)', re.IGNORECASE)
|
||||
try:
|
||||
try:
|
||||
output = subprocess.Popen('ipconfig /all', shell=True, stdout=subprocess.PIPE, text=True).stdout
|
||||
for line in output:
|
||||
m = c.search(line)
|
||||
|
||||
@@ -3,11 +3,13 @@ DeDRM tools for ebooks
|
||||
|
||||
This is a fork of Apprentice Harper's version of the DeDRM tools. Apprentice Harper said that the original version of the plugin [is no longer maintained](https://github.com/apprenticeharper/DeDRM_tools#no-longer-maintained), so I've taken over, merged a bunch of open PRs, and added a ton more features and bugfixes.
|
||||
|
||||
The latest stable (released) version is v10.0.3 which [can be downloaded here](https://github.com/noDRM/DeDRM_tools/releases/tag/v10.0.3).
|
||||
The latest stable (released) version is v10.0.3 which [can be downloaded here](https://github.com/noDRM/DeDRM_tools/releases/tag/v10.0.3). The latest beta is v10.0.9, as a release candidate for v10.1.0. It [can be downloaded here](https://github.com/noDRM/DeDRM_tools/releases/tag/v10.0.9).
|
||||
|
||||
Take a look at [the CHANGELOG](https://github.com/noDRM/DeDRM_tools/blob/master/CHANGELOG.md) to see a list of changes since the last version by Apprentice Harper (v7.2.1). This plugin will start with version v10.0.0.
|
||||
The latest alpha version is available [at this link](https://github.com/noDRM/DeDRM_tools_autorelease/releases). This version is completely untested and will contain the latest code changes in this repository. With each commit in this repository, a new automatic alpha version will be uploaded there. If you want the most up-to-date code to test things and are okay with the plugin occasionally breaking, you can download this version.
|
||||
|
||||
The v10.0.0 versions of this plugin should both work with Calibre 5.x (Python 3) as well as Calibre 4.x and lower (Python 2). If you encounter issues with this plugin in Calibre 4.x or lower, please open a bug report.
|
||||
Take a look at [the CHANGELOG](https://github.com/noDRM/DeDRM_tools/blob/master/CHANGELOG.md) to see a list of changes since the last version by Apprentice Harper (v7.2.1).
|
||||
|
||||
My version of the plugin should both work with Calibre 5.x/6.x (Python 3) as well as Calibre 4.x and lower (Python 2). If you encounter issues with this plugin in Calibre 4.x or lower, please open a bug report.
|
||||
|
||||
# Original README from Apprentice Harper
|
||||
|
||||
|
||||
Reference in New Issue
Block a user