kindlerb 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []