600円で手に入れたオライリー本をKindle向けに全自動変換するWindowsスクリプトを作った

Kindle手に入れて興奮気味です。
そんな中、こんなエントリーが。
たった600円でオライリー本をiPadやKindleで読む。すてき。 - このブログは証明できない。
おー、あの高くて分厚いオライリー本を600円で手に入れてKindleで読める。
id:shunsukさん、詳しく説明されていて、素晴らしい。
読めるオライリー本は英語だけど。あー。
さらに、あのdankogaiさんがスクリプトを作っていたと。
404 Blog Not Found:perl - O'ReillyのiPhoneアプリ本からepubをぶっこぬく
あーこれは自分でもできるかも。
Windows Scripting Host、JScriptで書いてみようかなぁと思って書いたら何とかなった。やった。
Windows XP向けです。Macでは動きません。

導入手順

  1. http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000234621 から "KindleGen for Windows"をダウンロードして、展開。("I agree to terms of use"のチェック入れてね)
  2. App2EpubAndMobi.jsをダウンロード(右クリックして「リンク先を保存」で保存してね)、KindleGenを展開したフォルダに放り込みます。

以上。

活用手順

  1. iTunesオライリー本を購入。
  2. iTunes - ライブラリ - App に購入したオライリー本があるはずなので、このアイコンを、先に導入した App2EpubAndMobi.js にドラッグ。
  3. しばし待つ。
  4. App2EpubAndMobi.jsを格納したフォルダに .epub ファイルと .mobi ファイルができているので、.mobi ファイルをKindleのdocumentsフォルダにドラッグする。

以上。
http://www.d2.dion.ne.jp/~ujiie/kindle/how2use.jpg

こんな感じでどうでしょう。

今のところ、うちではうまくいっていますが、どうなんでしょう。

あと、.epubファイルをiTunesドラッグアンドドロップすると、ブックライブラリに登録されたりしますね。

うーん。初代iPod touchユーザーだから.epubファイルは使えないけど。
※600円のオライリー本を買うのをためらってしまい、350円のJavsScriptReferenceでテストしました。

ご注意

目次データは作成されません・・・
こいつはいつかどうにかせねばなぁ。

以下、スクリプトさらし。

プログラム組んでないとすごく時間かかる。
久しぶりであった。

if (WScript.Arguments.Length <= 0) {
	WScript.Echo('ファイルを指定してください。');
	WScript.Quit();
}

// extractZipHere(Zipファイル名)
var extractZipHere = function(fileInput) {
	var fso = new ActiveXObject('Scripting.FileSystemObject');
	var folderParent = fso.GetParentFolderName(fileInput);
	var folderExtract = fso.BuildPath(folderParent, fso.GetTempName());
	if (!fso.FolderExists(folderExtract)) {
		fso.CreateFolder(folderExtract);
	}
	var shell = new ActiveXObject('Shell.Application');
	shell.NameSpace(folderExtract).CopyHere(shell.NameSpace(fileInput).Items());
	return folderExtract;
}

// createZip(作成先のZipファイル名、元のフォルダ名)
var createZip = function(fileZip, folderSrc) {
	var fso = new ActiveXObject('Scripting.FileSystemObject');
	var shell = new ActiveXObject('Shell.Application');
	fso.CreateTextFile(fileZip);
	var ofileZip = fso.OpenTextFile(fileZip, 2); // 2 : for Write
	ofileZip.Write('PK' + String.fromCharCode(5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0));
	ofileZip.Close();
	var ofolderZip = shell.NameSpace(fileZip);
	var ofolderSrc = shell.NameSpace(folderSrc);
	var scount = ofolderSrc.Items().count;
	ofolderZip.CopyHere(ofolderSrc.Items());
	while (ofolderZip.items().count != scount ) {
		WScript.Sleep(100);
	}
}

//必要なオブジェクトの生成
var shell = new ActiveXObject('Shell.Application');
var wshell = new ActiveXObject('WScript.Shell');
var fso = new ActiveXObject('Scripting.FileSystemObject');

//指定されたファイルが.ipaファイルか確認
var fileOriginal = WScript.Arguments.Item(0);
if (fso.GetExtensionName(fileOriginal) != 'ipa') {
	WScript.Echo('iPhoneアプリをドラッグ&ドロップしてください。');
	WScript.Quit();
}

//スクリプトフォルダを取得
var folderScript = String(WScript.ScriptFullName).replace(WScript.ScriptName, '');

//オリジナルファイルを一時フォルダにコピー
var folderTmp = fso.GetSpecialFolder(2);
var fileInput = fso.BuildPath(folderTmp, fso.GetTempName() + '.zip' );
fso.CopyFile(fileOriginal, fileInput);

//zipファイルを展開
var folderExtract = extractZipHere(fileInput);

//appフォルダなどの取得
var folderPayload = fso.BuildPath(folderExtract, 'Payload');

//PayLoadフォルダ内の.appファイルのファイル名を取得する(1個しかないはず…)
var fileApp = shell.NameSpace(folderPayload).Items().Item(0).Name;
var folderApp = fso.BuildPath(folderPayload, fileApp);
var folderBook = fso.BuildPath(folderApp, 'book');
var folderOEBPS = fso.BuildPath(folderBook, 'OEBPS');
var fileTOCNCX = fso.BuildPath(folderOEBPS, 'toc.ncx');

//toc.ncxファイルを修正
var ofileTOCNCX = fso.OpenTextFile(fileTOCNCX, 1); // 1 : for Read
var strTOCNCX = ofileTOCNCX.ReadAll();
ofileTOCNCX.Close();
//ncx:を削除
strTOCNCX = strTOCNCX.replace(/<ncx:/g, '<');
strTOCNCX = strTOCNCX.replace(/<\/ncx:/g, '<\/');
//一時ファイルを作成
var fileOut = fso.BuildPath(folderOEBPS, fso.GetTempName());
fso.CreateTextFile(fileOut);
var ofileOut = fso.OpenTextFile(fileOut, 2); // 2 : for Write
ofileOut.Write(strTOCNCX);
ofileOut.Close();
//オリジナルファイルを削除し一時ファイルに置き換える
fso.DeleteFile(fileTOCNCX);
fso.MoveFile(fileOut, fileTOCNCX);

//.appフォルダ内をZipに固めて.epubファイルにリネーム
var fileZip = fso.BuildPath(folderScript, fso.GetBaseName(fileApp) + '.zip');

createZip(fileZip, folderBook);

var fileEpub = fso.BuildPath(folderScript, fso.GetBaseName(fileApp) + '.epub');
if (fso.FileExists(fileEpub)) {
	fso.DeleteFile(fileEpub);
}
fso.MoveFile(fileZip, fileEpub);

//アトシマツ 一時ファイルを削除
this.CollectGarbage(); //Shell.Applicationがフォルダを離さないことがあるので念のためGC
fso.DeleteFile(fileInput);
fso.DeleteFolder(folderExtract);

//kindlegen.exe に処理を渡す
var fileKindlegen = fso.BuildPath(folderScript, 'kindlegen.exe');
wshell.Run(fileKindlegen + ' "' + fileEpub +'"', 1 ,true);

WScript.Echo('終了しました。');