2010年

9月

12日

O'Reilly の iPhone アプリ本から Amazon Kindle の MOBI ファイルを作る Python スクリプト

作りました。[2011/03/03追記 | 不具合があったのでコードを修正しました。][2011/03/12 | 修正したコードに不具合がありました(^^;; ので直しました。(コメント欄で指摘いただきました)]

ええと。以下のサイトを参考にしました。

 

たった600円でオライリー本をKindleで読む。自動化。

 

参考というより、そのままです。はい。

 

先のサイトのスクリプト実行したら libXML だかのモジュールがない、と言われてカッとなりまして。

それではさっそくコードです。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
ipa2mobi.py
    A transformer from an ipa file to a MOBI file in Python.
"""

import codecs
import os
import re
import subprocess
import shutil
import sys
from glob import glob
from tempfile import mkdtemp
from xml.etree import ElementTree


if len(sys.argv) != 2:
    print("Usage: python %s IPA_FILE" % sys.argv[0])
    quit()
src_file_path = os.path.abspath(sys.argv[1])

print("Create a temporary directory.")
work_dir_path = mkdtemp()

try:
    os.chdir(work_dir_path)
    print
    
    print("Extract files from the .ipa file.")
    subprocess.check_call(["unzip", src_file_path])
    print
    
    print("Go into a book data directory.")
    os.chdir(glob("Payload/*.app/book/OEBPS")[0])
    print
    
    print("Modify toc.ncx.")
    shutil.move("toc.ncx", "toc.tmp")
    in_file = open("toc.tmp", "r")
    out_file = open("toc.ncx", "w")
    ncx_ns_re = re.compile(r"<(/?)ncx:")
    nsdef_ncx_re = re.compile("<ncx xmlns=")
    for line in in_file:
        line = ncx_ns_re.sub(r"<\1", line)
        # Default namespace makes find("navMap/navPoint") fail.
        line = nsdef_ncx_re.sub("<ncx xmlns:ncx=", line)
        out_file.write(line)
    out_file.flush()
    out_file.close()
    in_file.close()
    os.remove("toc.tmp")
    print
    
    if os.path.isfile("toc.html"):
        print("Skip generate toc.html since it already exists.")
        print
    else:
        print("Generate toc.html.")
        out_file = codecs.lookup("utf-8")[-1](open("toc.html", "w"))
        out_file.write('<?xml version="1.0" encoding="utf-8"?>\n')
        out_file.write("<html><head><title>Table of Contents</title></head><body>")
        top_elem = ElementTree.parse("toc.ncx").find("navMap/navPoint")
        top_title = top_elem.find("navLabel/text").text
        top_ref = top_elem.find("content").get("src")
        out_file.write('<h1><a href="%s">%s</a></h1>' % (top_ref, top_title))
        def print_nav_recur(elems):
            out_file.write("<ul>")
            for e in elems:
                title = e.find("navLabel/text").text
                ref = e.find("content").get("src")
                out_file.write('<li><a href="%s">%s</a></li>' % (ref, title))
                children = e.findall("navPoint")
                if children:
                    print_nav_recur(children)
            out_file.write("</ul>")
        children = top_elem.findall("navPoint")
        if children:
            print_nav_recur(children)
        out_file.write("</body></html>")
        out_file.flush()
        out_file.close()
        print
        
        print("Modify content.opf.")
        shutil.move("content.opf", "content.tmp")
        in_file = open("content.tmp", "r")
        out_file = open("content.opf", "w")
        item_re = re.compile(r'<item id="cover"')
        itemref_re = re.compile(r'<itemref idref="cover"')
        reference_re = re.compile(r'<reference href="cover.html"')
        for line in in_file:
            out_file.write(line)
            if item_re.search(line):
                out_file.write('<item id="toc" href="toc.html" media-type="text/html"/>\n')
            elif itemref_re.search(line):
                out_file.write('<itemref idref="toc" linear="no"/>\n')
            elif reference_re.search(line):
                out_file.write('<reference href="toc.html" type="toc" title="Table of Contents"/>\n')
        out_file.flush()
        out_file.close()
        in_file.close()
        os.remove("content.tmp")
        print
    # if os.path.isfile("toc.html")
    
    print("Make an ePub file.")
    os.chdir("..")
    epub_file_path = re.sub(r"(\.ipa)?$", ".epub", src_file_path)
    if os.path.isfile(epub_file_path):
        os.remove(epub_file_path)
    subprocess.check_call(["zip", "-q0X", epub_file_path, "mimetype"])
    subprocess.check_call(["zip", "-qXr9D", epub_file_path, "META-INF", "OEBPS"])
    print
    
    print("Make a MOBI file.")
    (epub_dir_path, epub_file_name) = os.path.split(epub_file_path)
    os.chdir(epub_dir_path)
    subprocess.check_call(["kindlegen", epub_file_name])
    print
    
finally:
    print("Cleanup the working directory.")
    shutil.rmtree(work_dir_path)
    print

print("OK")

一応、Python の標準モジュールだけを使って作ったつもりです。自信はありません。

 

試したのは一冊だけです。他の本でも運が良ければ使えるかもしれません。

 

[2011/03/03追記 | あれから2冊試しましたが、"JavaScript Patterns" を変換しようとしたところ失敗したので、コードを修正しました。]

ちなみに大元となったのは、こちらのサイト。すてき。

 

たった600円でオライリー本をiPadやKindleで読む。すてき。

 

[2011/03/03追記 | 最近800円に値上がりしちゃいましたね。それでも十分安いですが]

コメントをお書きください

コメント: 3
  • #1

    aaa (土曜日, 12 3月 2011 10:01)

    s/undefined/%s/g

  • #2

    pekodeco (土曜日, 12 3月 2011 22:01)

    >aaa
    ご指摘ありがとうございます! 修正しました。

  • #3

    Todd (日曜日, 22 7月 2012 03:26)

    I was very pleased to find this website. I wanted to thank you for your time for this wonderful post!! I definitely enjoy reading it and I have you bookmarked to check out new stuff you blog post.