Books: read DRM-free MOBI, AZW and AZW3; refuse Kindle files with DRM

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UaC9HmtHMdRP8dAbN4wAKj
This commit is contained in:
Claude committed 2026-10-24 02:54:34 +00:00
1 parent 07c16ddc25
commit 2e99893c8b
7 files changed
+138 -21

No files matched your search

+30
View File
@@ -22,3 +22,33 @@ data = '%PDF-1.4\n'; offs = []
for i, o in enumerate(objs): offs.append(len(data)); data += f'{i+1} 0 obj\n{o}\nendobj\n'
x = len(data); data += f'xref\n0 {len(objs)+1}\n0000000000 65535 f \n' + ''.join(f'{o:010d} 00000 n \n' for o in offs) + f'trailer\n<< /Size {len(objs)+1} /Root 1 0 R >>\nstartxref\n{x}\n%%EOF\n'
open(os.path.join(out, 'test.pdf'), 'w').write(data)
# minimal MOBI 6 (uncompressed text, no images), enough to open in a reader. Set DRM=1 in the environment to mark it encrypted.
import struct
def mkmobi(path, drm=False):
html = '<html><head><title>Mobi Rabbits</title></head><body><h1>Chapter One</h1>' + ''.join('<p>The rabbit hops through the clover field number %d, nibbling as it goes.</p>' % i for i in range(30)) + '<mbp:pagebreak/><h1>Chapter Two</h1>' + ''.join('<p>Carrots are the second chapter topic, paragraph %d.</p>' % i for i in range(30)) + '</body></html>'
text = html.encode('utf8'); recs = [text[i:i+4096] for i in range(0, len(text), 4096)]
title = b'Mobi Rabbits'; hdrlen = 232
exth = b''
def item(t, v): return struct.pack('>II', t, 8 + len(v)) + v
body = item(100, b'M. Author') + item(503, title); exth = b'EXTH' + struct.pack('>II', 12 + len(body), 2) + body
exth += b'\0' * (-len(exth) % 4)
r0 = struct.pack('>HHIHHHH', 1, 0, len(text), len(recs), 4096, 2 if drm else 0, 0)
mobi = bytearray(hdrlen)
mobi[0:4] = b'MOBI'; struct.pack_into('>I', mobi, 4, hdrlen); struct.pack_into('>I', mobi, 8, 2); struct.pack_into('>I', mobi, 12, 65001); struct.pack_into('>I', mobi, 16, 1234); struct.pack_into('>I', mobi, 20, 6)
for off in range(24, 64, 4): struct.pack_into('>I', mobi, off, 0xFFFFFFFF)
name_off = 16 + hdrlen + len(exth)
struct.pack_into('>II', mobi, 68 - 16, name_off, len(title)); struct.pack_into('>I', mobi, 72 - 16 + 4, 9)
struct.pack_into('>I', mobi, 0x5C - 16 + 0, 0)
struct.pack_into('>I', mobi, 0x6C - 16, 0xFFFFFFFF); struct.pack_into('>I', mobi, 0x70 - 16, 0xFFFFFFFF)
struct.pack_into('>I', mobi, 0x80 - 16, 0x40)
for off in (0x90 - 16, 0x94 - 16, 0x98 - 16, 0xA0 - 16): struct.pack_into('>I', mobi, off, 0xFFFFFFFF)
struct.pack_into('>I', mobi, 0xE0 - 16, 0xFFFFFFFF); struct.pack_into('>H', mobi, 0xF2 - 16, 0)
rec0 = r0 + bytes(mobi) + exth + title + b'\0\0'
allrecs = [rec0] + recs; n = len(allrecs); off = 78 + 8 * n + 2; offs = []
for r in allrecs: offs.append(off); off += len(r)
hdr = b'MobiRabbits'.ljust(32, b'\0') + struct.pack('>HHIIIIII', 0, 0, 0, 0, 0, 0, 0, 0) + b'BOOKMOBI' + struct.pack('>IIH', 1234, 0, n)
hdr = hdr[:78 - 0] if len(hdr) >= 78 else hdr.ljust(78, b'\0')
lst = b''.join(struct.pack('>IBBBB', o, 0, 0, 0, i & 255) for i, o in enumerate(offs))
open(path, 'wb').write(hdr + lst + b'\0\0' + b''.join(allrecs))
mkmobi(os.path.join(out, 'test.mobi')); mkmobi(os.path.join(out, 'drm.mobi'), True)