kindlerb 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ testtree/
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+
2
+ gem 'nokogiri'
3
+ gem 'mustache'
data/MIT-LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2010 Daniel Choi, http://danielchoi.com/software/
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
data/README.markdown ADDED
@@ -0,0 +1,99 @@
1
+ # kindlerb
2
+
3
+ kindlerb is a Ruby Kindle periodical-format ebook generator.
4
+
5
+ kindlerb converts a file tree of sections, articles, images, and metadata into
6
+ a MOBI periodical-formatted document for the Kindle. It is a wrapper around the
7
+ `kindlegen` program from Amazon that hides the details for templating OPF and NCX
8
+ files.
9
+
10
+ ## Requirements
11
+
12
+ * Ruby 1.9.x.
13
+ * Make sure kindlegen is on your PATH.
14
+
15
+ You can get kindlegen [here][kindlegen].
16
+
17
+ [kindlegen]:http://www.amazon.com/gp/feature.html?docId=1000234621
18
+
19
+ ## Install
20
+
21
+ gem install kindlerb
22
+
23
+ ## How to use it
24
+
25
+ Run the program at the root of the file tree:
26
+
27
+ kindlerb [filetree dir]
28
+
29
+ The output will be a mobi document.
30
+
31
+ The file tree input structure is
32
+
33
+ _document.yml
34
+ sections/
35
+ 000/
36
+ _section.txt # contains section title
37
+ 000.html # an article
38
+ 001.html
39
+ 001/
40
+ _section.txt
41
+ 000.html
42
+ 001.html
43
+ 003.html
44
+
45
+ kindlerb will extract article titles from the `<title>` (in `<head>`) tag in
46
+ the *.html files .
47
+
48
+ The _document.yml is a YAML document. It should look like something like this:
49
+
50
+ ---
51
+ doc_uuid: kindlefeeder.21395-2011-12-19
52
+ title: kindlefeeder
53
+ author: kindlefeeder
54
+ publisher: kindlefeeder.com
55
+ subject: News
56
+ date: "2011-12-19"
57
+ masthead: /home/choi/Desktop/masthead.gif
58
+ cover: /home/choi/Desktop/cover.gif
59
+
60
+ kindlerb uses the the file tree and _document.yml to construct these additional
61
+ resource required by Amazon's `kindlegen` program:
62
+
63
+ * nav-contents.ncx
64
+ * contents.html
65
+ * kindlerb.opf
66
+
67
+ After that, kindlerb will exec the `kindlegen` program to generate your mobi document.
68
+ The document will be named after `title` in your _document.yml.
69
+
70
+ ## Images
71
+
72
+ kindlerb will incorporate images into the generated ebook by parsing all the
73
+ `src` attributes of all the `<img>` tags in your *.html files.
74
+
75
+ The `src` attributes must point to image files on the local filesystem. If the
76
+ paths are relative, they should be relative to the target file tree root.
77
+
78
+
79
+ ## Author
80
+
81
+ Daniel Choi
82
+
83
+ * email: dhchoi@gmail.com
84
+ * github: [danchoi][github]
85
+ * twitter: @danchoi
86
+
87
+ [github]:http://github.com/danchoi
88
+
89
+
90
+ I'm indebted to [mhl][mhl] for writing the
91
+ [guardian-for-kindle][guardian-for-kindle] MOBI generator in Python. kindlerb
92
+ ported a bunch of ideas from that project over to Ruby.
93
+
94
+ [mhl]:https://github.com/mhl
95
+ [guardian-for-kindle]:https://github.com/mhl/guardian-for-kindle
96
+
97
+
98
+
99
+
data/Rakefile ADDED
@@ -0,0 +1,71 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'bundler'
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), 'lib')
7
+
8
+ desc "release and build and push new website"
9
+ task :push => [:release, :web]
10
+
11
+ desc "Bumps version number up one and git commits"
12
+ task :bump do
13
+ basefile = "lib/vmail/version.rb"
14
+ file = File.read(basefile)
15
+ oldver = file[/VERSION = '(\d.\d.\d)'/, 1]
16
+ newver_i = oldver.gsub(".", '').to_i + 1
17
+ newver = ("%.3d" % newver_i).split(//).join('.')
18
+ puts oldver
19
+ puts newver
20
+ puts "Bumping version: #{oldver} => #{newver}"
21
+ newfile = file.gsub("VERSION = '#{oldver}'", "VERSION = '#{newver}'")
22
+ File.open(basefile, 'w') {|f| f.write newfile}
23
+ `git commit -am 'Bump'`
24
+ end
25
+
26
+
27
+ desc "build and push website"
28
+ task :web => :build_webpage do
29
+ puts "Building and pushing website"
30
+ Dir.chdir "../project-webpages" do
31
+ `scp out/vmail.html zoe2@instantwatcher.com:~/danielchoi.com/public/software/`
32
+ `rsync -avz out/images-vmail zoe2@instantwatcher.com:~/danielchoi.com/public/software/`
33
+ `rsync -avz out/stylesheets zoe2@instantwatcher.com:~/danielchoi.com/public/software/`
34
+ `rsync -avz out/lightbox2 zoe2@instantwatcher.com:~/danielchoi.com/public/software/`
35
+ end
36
+ `open http://danielchoi.com/software/vmail.html`
37
+ end
38
+
39
+ desc "build webpage"
40
+ task :build_webpage do
41
+ `cp README.markdown ../project-webpages/src/vmail.README.markdown`
42
+ `cp coverage.markdown ../project-webpages/src/vmail.coverage.markdown`
43
+ Dir.chdir "../project-webpages" do
44
+ puts `ruby gen.rb vmail #{Vmail::VERSION}`
45
+ #`open out/vmail.html`
46
+ end
47
+ end
48
+
49
+
50
+ desc "git push and rake release bumped version"
51
+ task :bumped do
52
+ puts `git push && rake release`
53
+ Rake::Task["web"].execute
54
+ end
55
+
56
+ desc "Run tests"
57
+ task :test do
58
+ $:.unshift File.expand_path("test")
59
+ require 'test_helper'
60
+ Dir.chdir("test") do
61
+ Dir['*_test.rb'].each do |x|
62
+ puts "requiring #{x}"
63
+ require x
64
+ end
65
+ end
66
+
67
+ MiniTest::Unit.autorun
68
+ end
69
+
70
+ task :default => :test
71
+
@@ -0,0 +1,7 @@
1
+ doc_uuid:
2
+ title:
3
+ author:
4
+ publisher:
5
+ subject: News
6
+ date: 2012-01-04
7
+
data/bin/kindlerb ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ require 'kindlerb'
3
+
4
+ Kindlerb.run
5
+
data/kindlerb.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "kindlerb"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "kindlerb"
7
+ s.version = Kindlerb::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.required_ruby_version = '>= 1.9.0'
10
+
11
+ s.authors = ["Daniel Choi"]
12
+ s.email = ["dhchoi@gmail.com"]
13
+ s.homepage = "http://github.com/danchoi/kindlerb"
14
+ s.summary = %q{Kindle eperiodical generator}
15
+ s.description = %q{Kindle eperiodical generator}
16
+
17
+ s.rubyforge_project = "kindlerb"
18
+
19
+ s.files = `git ls-files`.split("\n")
20
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
22
+ s.require_paths = ["lib"]
23
+
24
+ s.add_dependency 'nokogiri'
25
+ s.add_dependency 'mustache'
26
+ end
data/lib/kindlerb.rb ADDED
@@ -0,0 +1,144 @@
1
+ unless `which kindlegen` =~ /kindlegen/
2
+ abort "Please install kindlegen on your path"
3
+ end
4
+
5
+ # extract nav structure
6
+
7
+ require 'pathname'
8
+ require 'yaml'
9
+ require 'nokogiri'
10
+ require 'mustache'
11
+ require 'fileutils'
12
+
13
+ # monkeypatch
14
+ class String
15
+ def shorten(max)
16
+ length > max ? Array(self[0,max].split(/\s+/)[0..-2]).join(' ') + '...' : self
17
+ end
18
+ end
19
+
20
+
21
+ module Kindlerb
22
+ VERSION = '0.0.1'
23
+
24
+ def self.run
25
+
26
+ target_dir = Pathname.new(ARGV.first || '.')
27
+
28
+ opf_template = File.read(File.join(File.dirname(__FILE__), '..', "templates/opf.mustache"))
29
+ ncx_template = File.read(File.join(File.dirname(__FILE__), '..', "templates/ncx.mustache"))
30
+ contents_template = File.read(File.join(File.dirname(__FILE__), '..', "templates/contents.mustache"))
31
+ section_template = File.read(File.join(File.dirname(__FILE__), '..', "templates/section.mustache"))
32
+ masthead_gif = File.join(File.dirname(__FILE__), '..', "templates/masthead.gif")
33
+ cover_gif = File.join(File.dirname(__FILE__), '..', "templates/cover-image.gif")
34
+
35
+ `cp #{masthead_gif} #{target_dir}/masthead.gif`
36
+ `cp #{cover_gif} #{target_dir}/cover-image.gif`
37
+
38
+ Dir.chdir target_dir do
39
+ playorder = 1
40
+
41
+ images = []
42
+ manifest_items = []
43
+
44
+ unless File.exist?("_document.yml")
45
+
46
+ puts "Usage: kindlerb [target file directory]"
47
+
48
+ abort "Missing _document.yml. Your input file tree is not structured correctly. Please read the README."
49
+ end
50
+
51
+ document = YAML::load_file("_document.yml")
52
+
53
+ document[:spine_items] = []
54
+ section_html_files = []
55
+
56
+ sections = Dir['sections/*'].entries.sort.map.with_index {|section_dir|
57
+ section_title = File.read((Pathname.new(section_dir) + '_section.txt')).strip
58
+ articles = Dir[Pathname.new(section_dir) + '*'].entries.select {|x| File.basename(x) !~ /section/}.sort
59
+ section_html_files << (section_html_file = (Pathname.new(section_dir) + 'section.html').to_s)
60
+ idref = "item-#{section_dir.gsub(/\D/, '')}"
61
+
62
+ document[:spine_items] << {:idref => idref}
63
+ manifest_items << {
64
+ :href => section_html_file,
65
+ :media => "application/xhtml+xml",
66
+ :idref => idref
67
+ }
68
+
69
+ s = {
70
+ :path => section_dir,
71
+ :title => section_title.shorten(40),
72
+ :playorder => (playorder += 1),
73
+ :idref => idref,
74
+ :href => Pathname.new(section_dir) + 'section.html',
75
+ :articles => articles.map {|article_file|
76
+ doc = Nokogiri::HTML(File.read(article_file))
77
+ article_images = doc.search("img").map {|img|
78
+ mimetype = img[:src] ? "image/#{File.extname(img[:src]).sub('.', '')}" : nil
79
+ {:href => img[:src], :mimetype => mimetype}
80
+ }
81
+ images.push *article_images
82
+ title = doc.search("html/head/title").map(&:inner_text).first || "no title"
83
+ idref = "item-#{article_file.gsub(/\D/, '')}"
84
+ document[:spine_items] << {:idref => idref}
85
+ article = {
86
+ :file => article_file,
87
+ :href => article_file,
88
+ :title => title,
89
+ :short_title => title.shorten(40),
90
+ :author => doc.search("html/head/meta[@name=author]").map{|n|n[:name]}.first,
91
+ :description => doc.search("html/head/meta[@name=description]").map{|n|n[:content]}.first,
92
+ :playorder => (playorder += 1),
93
+ :idref => idref
94
+ }
95
+ manifest_items << {
96
+ :href => article[:file],
97
+ :media => "application/xhtml+xml",
98
+ :idref => article[:idref]
99
+ }
100
+ article
101
+ }
102
+ }
103
+
104
+ # Generate the section html
105
+ out = Mustache.render section_template, s
106
+ File.open(section_html_file, "w") {|f| f.puts out}
107
+ s
108
+
109
+ }
110
+
111
+ document['masthead'] ||= "masthead.gif"
112
+ document['cover'] ||= "cover.gif"
113
+ document[:first_article] = sections[0][:articles][0]
114
+ document[:sections] = sections
115
+
116
+
117
+ document[:manifest_items] = manifest_items + images.map.with_index {|img, idx|
118
+ {
119
+ :href => img[:href],
120
+ :media => img[:mimetype],
121
+ :idref => "img-%03d" % idx
122
+ }
123
+ }
124
+
125
+ opf = Mustache.render opf_template, document
126
+ File.open("kindlerb.opf", "w") {|f| f.puts opf}
127
+ puts "Wrote #{target_dir}/kindlerb.opf"
128
+
129
+ # NCX
130
+ ncx = Mustache.render ncx_template, document
131
+ File.open("nav-contents.ncx", "w") {|f| f.puts ncx}
132
+ puts "Wrote #{target_dir}/nav-contents.ncx"
133
+
134
+ # contents
135
+ contents = Mustache.render contents_template, document
136
+ File.open("contents.html", "w") {|f| f.puts contents}
137
+ puts "Wrote #{target_dir}/contents.html"
138
+
139
+ outfile = "%s.mobi" % document['title'].gsub(/\W/, '_')
140
+
141
+ exec "kindlegen -verbose -c2 -o #{outfile} kindlerb.opf && echo 'Wrote MOBI to #{outfile}'"
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,19 @@
1
+ <html>
2
+ <head>
3
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
4
+ <title>Table of Contents</title>
5
+ </head>
6
+ <body>
7
+ <h1>Contents</h1>
8
+ {{#sections}}
9
+ <h4>{{{title}}}</h4>
10
+ <ul>
11
+ {{#articles}}
12
+ <li>
13
+ <a href="{{file}}">{{{title}}}</a>
14
+ </li>
15
+ {{/articles}}
16
+ </ul>
17
+ {{/sections}}
18
+ </body>
19
+ </html>
@@ -0,0 +1,37 @@
1
+ <?xml version='1.0' encoding='utf-8'?>
2
+ <!DOCTYPE ncx PUBLIC "-//NISO//DTD ncx 2005-1//EN" "http://www.daisy.org/z3986/2005/ncx-2005-1.dtd">
3
+ <ncx xmlns:mbp="http://mobipocket.com/ns/mbp" xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1" xml:lang="en-US">
4
+ <head>
5
+ <meta content="{{doc_uuid}}" name="dtb:uid"/>
6
+ <meta content="2" name="dtb:depth"/>
7
+ <meta content="0" name="dtb:totalPageCount"/>
8
+ <meta content="0" name="dtb:maxPageNumber"/>
9
+ </head>
10
+ <docTitle>
11
+ <text>{{title}}</text>
12
+ </docTitle>
13
+ <docAuthor>
14
+ <text>{{author}}</text>
15
+ </docAuthor>
16
+ <navMap>
17
+ <navPoint playOrder="1" class="periodical" id="periodical">
18
+ <mbp:meta-img src="{{masthead}}" name="mastheadImage"/>
19
+ <navLabel><text>Table of Contents</text></navLabel>
20
+ <content src="contents.html"/>
21
+ {{#sections}}
22
+ <navPoint playOrder="{{playorder}}" class="section" id="{{idref}}">
23
+ <navLabel><text>{{title}}</text></navLabel>
24
+ <content src="{{href}}"/>
25
+ {{#articles}}
26
+ <navPoint playOrder="{{playorder}}" class="article" id="{{idref}}">
27
+ <navLabel><text>{{short_title}}</text></navLabel>
28
+ <content src="{{href}}"/>
29
+ <mbp:meta name="description">{{description}}</mbp:meta>
30
+ <mbp:meta name="author">{{author}}</mbp:meta>
31
+ </navPoint>
32
+ {{/articles}}
33
+ </navPoint>
34
+ {{/sections}}
35
+ </navPoint>
36
+ </navMap>
37
+ </ncx>
@@ -0,0 +1,42 @@
1
+ <?xml version='1.0' encoding='utf-8'?>
2
+ <package xmlns="http://www.idpf.org/2007/opf" version="2.0" unique-identifier="{{doc_uuid}}">
3
+ <metadata>
4
+ <dc-metadata xmlns:dc="http://purl.org/dc/elements/1.1/">
5
+ <dc:title>{{title}}</dc:title>
6
+ <dc:language>en-gb</dc:language>
7
+ <meta content="cover-image" name="cover"/>
8
+ <dc:creator>{{author}}</dc:creator>
9
+ <dc:publisher>{{publisher}}</dc:publisher>
10
+ <dc:subject>{{subject}}</dc:subject>
11
+ <dc:date>{{date}}</dc:date>
12
+ <dc:description>{{description}}</dc:description>
13
+ </dc-metadata>
14
+
15
+ <x-metadata>
16
+ <output content-type="application/x-mobipocket-subscription-magazine" encoding="utf-8"/>
17
+ </x-metadata>
18
+
19
+
20
+ </metadata>
21
+ <manifest>
22
+ <item href="contents.html" media-type="application/xhtml+xml" id="contents"/>
23
+ <item href="nav-contents.ncx" media-type="application/x-dtbncx+xml" id="nav-contents"/>
24
+ <item href="{{cover}}" media-type="image/gif" id="cover-image"/>
25
+ <item href="{{masthead}}" media-type="image/gif" id="masthead"/>
26
+ {{#manifest_items}}
27
+ <item href="{{href}}" media-type="{{media}}" id="{{idref}}"/>
28
+ {{/manifest_items}}
29
+ </manifest>
30
+ <spine toc="nav-contents">
31
+ <itemref idref="contents"/>
32
+ {{#spine_items}}
33
+ <itemref idref="{{idref}}"/>
34
+ {{/spine_items}}
35
+ </spine>
36
+ <guide>
37
+ <reference href="contents.html" type="toc" title="Table of Contents"/>
38
+ {{#first_article}}
39
+ <reference href="{{href}}" type="text" title="Beginning"/>
40
+ {{/first_article}}
41
+ </guide>
42
+ </package>
@@ -0,0 +1,15 @@
1
+ <html lang="en" xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
2
+ <head>
3
+ <meta content="http://www.w3.org/1999/xhtml; charset=utf-8" http-equiv="Content-Type"/>
4
+ <title>{{title}}</title>
5
+ <meta content="kindlerb" name="author"/>
6
+ <meta content="kindlerb" name="description"/>
7
+ </head>
8
+ <body>
9
+ <p>&nbsp;</p>
10
+ <p>&nbsp;</p>
11
+ <p>&nbsp;</p>
12
+ <p>&nbsp;</p>
13
+ <h1>{{title}}</h1>
14
+ </body>
15
+ </html>
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kindlerb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Daniel Choi
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-01-10 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: nokogiri
16
+ requirement: &16020420 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *16020420
25
+ - !ruby/object:Gem::Dependency
26
+ name: mustache
27
+ requirement: &16019460 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *16019460
36
+ description: Kindle eperiodical generator
37
+ email:
38
+ - dhchoi@gmail.com
39
+ executables:
40
+ - kindlerb
41
+ extensions: []
42
+ extra_rdoc_files: []
43
+ files:
44
+ - .gitignore
45
+ - Gemfile
46
+ - MIT-LICENSE.txt
47
+ - README.markdown
48
+ - Rakefile
49
+ - _document.yml.sample
50
+ - bin/kindlerb
51
+ - kindlerb.gemspec
52
+ - lib/kindlerb.rb
53
+ - templates/contents.mustache
54
+ - templates/ncx.mustache
55
+ - templates/opf.mustache
56
+ - templates/section.mustache
57
+ homepage: http://github.com/danchoi/kindlerb
58
+ licenses: []
59
+ post_install_message:
60
+ rdoc_options: []
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: 1.9.0
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ! '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ requirements: []
76
+ rubyforge_project: kindlerb
77
+ rubygems_version: 1.8.10
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: Kindle eperiodical generator
81
+ test_files: []