kindlerb 0.1.1 → 1.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.
- checksums.yaml +7 -0
- data/.gitignore +1 -1
- data/Gemfile +3 -3
- data/MIT-LICENSE.txt +21 -21
- data/{README.markdown → README.md} +139 -111
- data/Rakefile +71 -71
- data/_document.yml.sample +7 -7
- data/bin/setupkindlerb +6 -0
- data/kindlerb.gemspec +25 -26
- data/lib/kindlerb.rb +232 -146
- data/templates/contents.mustache +19 -19
- data/templates/ncx.mustache +37 -37
- data/templates/opf.mustache +42 -42
- data/templates/section.mustache +15 -15
- metadata +18 -22
- data/bin/kindlerb +0 -9
data/_document.yml.sample
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
doc_uuid:
|
2
|
-
title:
|
3
|
-
author:
|
4
|
-
publisher:
|
5
|
-
subject: News
|
6
|
-
date: 2012-01-04
|
7
|
-
|
1
|
+
doc_uuid:
|
2
|
+
title:
|
3
|
+
author:
|
4
|
+
publisher:
|
5
|
+
subject: News
|
6
|
+
date: 2012-01-04
|
7
|
+
|
data/bin/setupkindlerb
ADDED
data/kindlerb.gemspec
CHANGED
@@ -1,26 +1,25 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
$:.push File.expand_path("../lib", __FILE__)
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
s.
|
7
|
-
s.
|
8
|
-
s.
|
9
|
-
|
10
|
-
|
11
|
-
s.
|
12
|
-
s.
|
13
|
-
s.
|
14
|
-
s.
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
s.
|
20
|
-
s.
|
21
|
-
s.
|
22
|
-
|
23
|
-
|
24
|
-
s.add_dependency '
|
25
|
-
|
26
|
-
end
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "kindlerb"
|
6
|
+
s.version = '1.0.1'
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.required_ruby_version = '>= 2.0.0'
|
9
|
+
|
10
|
+
s.authors = ["Daniel Choi", "Emir Aydin"]
|
11
|
+
s.email = ["dhchoi@gmail.com", "emir@emiraydin.com"]
|
12
|
+
s.homepage = "http://github.com/danchoi/kindlerb"
|
13
|
+
s.summary = %q{Kindle eperiodical generator}
|
14
|
+
s.description = %q{Kindle eperiodical generator}
|
15
|
+
|
16
|
+
s.rubyforge_project = "kindlerb"
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
|
23
|
+
s.add_dependency 'nokogiri'
|
24
|
+
s.add_dependency 'mustache'
|
25
|
+
end
|
data/lib/kindlerb.rb
CHANGED
@@ -1,146 +1,232 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
require '
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# extract nav structure
|
4
|
+
|
5
|
+
require 'pathname'
|
6
|
+
require 'yaml'
|
7
|
+
require 'nokogiri'
|
8
|
+
require 'mustache'
|
9
|
+
require 'fileutils'
|
10
|
+
|
11
|
+
# monkeypatch
|
12
|
+
class String
|
13
|
+
def shorten(max)
|
14
|
+
length > max ? Array(self[0,max].split(/\s+/)[0..-2]).join(' ') + '...' : self
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module Kindlerb
|
19
|
+
|
20
|
+
def self.download
|
21
|
+
|
22
|
+
gem_path = Gem::Specification.find_by_name('kindlerb').gem_dir
|
23
|
+
ext_dir = gem_path + '/ext/'
|
24
|
+
bin_dir = gem_path + '/bin/'
|
25
|
+
|
26
|
+
# Define Kindlegen download files for different OS's
|
27
|
+
executable = 'kindlegen'
|
28
|
+
windows = false
|
29
|
+
compressed_file = case RbConfig::CONFIG['host_os']
|
30
|
+
when /mac|darwin/i
|
31
|
+
extract = 'unzip '
|
32
|
+
"KindleGen_Mac_i386_v2_9.zip"
|
33
|
+
when /linux|cygwin/i
|
34
|
+
extract = 'tar zxf '
|
35
|
+
"kindlegen_linux_2.6_i386_v2_9.tar.gz"
|
36
|
+
when /mingw32/i
|
37
|
+
windows = true
|
38
|
+
extract = 'unzip '
|
39
|
+
executable = 'kindlegen.exe'
|
40
|
+
"kindlegen_win32_v2_9.zip"
|
41
|
+
else
|
42
|
+
STDERR.puts "Host OS is not supported!"
|
43
|
+
exit(1)
|
44
|
+
end
|
45
|
+
|
46
|
+
url = 'http://kindlegen.s3.amazonaws.com/' + compressed_file
|
47
|
+
|
48
|
+
# Download and extract the Kindlegen file into gem's /etc folder
|
49
|
+
unless File.directory?(ext_dir)
|
50
|
+
FileUtils.mkdir_p(ext_dir)
|
51
|
+
end
|
52
|
+
system 'curl ' + url + ' -o ' + ext_dir + compressed_file
|
53
|
+
puts "Kindlegen downloaded: " + ext_dir + compressed_file
|
54
|
+
FileUtils.cd(ext_dir)
|
55
|
+
system extract + compressed_file
|
56
|
+
|
57
|
+
# Move the executable into gem's /bin folder
|
58
|
+
unless File.directory?(bin_dir)
|
59
|
+
FileUtils.mkdir_p(bin_dir)
|
60
|
+
end
|
61
|
+
moved = FileUtils.mv(ext_dir + executable, bin_dir)
|
62
|
+
puts "Kindlegen extracted to: " + bin_dir
|
63
|
+
# Clean up ext folder
|
64
|
+
if moved
|
65
|
+
FileUtils.rm_rf(ext_dir)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Give exec permissions to Kindlegen file
|
69
|
+
exec_file = bin_dir + executable
|
70
|
+
if windows
|
71
|
+
cmd = "icacls #{exec_file} /T /C /grant Everyone:(f)"
|
72
|
+
system cmd
|
73
|
+
else
|
74
|
+
FileUtils.chmod 0754, exec_file
|
75
|
+
end
|
76
|
+
puts "Execution permissions granted to the user for Kindlegen executable"
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns the full path to executable Kindlegen file
|
81
|
+
def self.executable
|
82
|
+
|
83
|
+
gem_path = Gem::Specification.find_by_name('kindlerb').gem_dir
|
84
|
+
|
85
|
+
# Different extensions based on OS
|
86
|
+
kindlegen = case RbConfig::CONFIG['host_os']
|
87
|
+
when /mac|darwin/i
|
88
|
+
"kindlegen"
|
89
|
+
when /linux|cygwin/i
|
90
|
+
"kindlegen"
|
91
|
+
when /mingw32/i
|
92
|
+
"kindlegen.exe"
|
93
|
+
else
|
94
|
+
STDERR.puts "Kindlegen is not installed because host OS is not supported!"
|
95
|
+
exit(1)
|
96
|
+
end
|
97
|
+
|
98
|
+
exec_path = gem_path + '/bin/' + kindlegen
|
99
|
+
|
100
|
+
return exec_path
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.run(target_dir, verbose = false, compression_method = 'c2')
|
105
|
+
|
106
|
+
opf_template = File.read(File.join(File.dirname(__FILE__), '..', "templates/opf.mustache"))
|
107
|
+
ncx_template = File.read(File.join(File.dirname(__FILE__), '..', "templates/ncx.mustache"))
|
108
|
+
contents_template = File.read(File.join(File.dirname(__FILE__), '..', "templates/contents.mustache"))
|
109
|
+
section_template = File.read(File.join(File.dirname(__FILE__), '..', "templates/section.mustache"))
|
110
|
+
masthead_gif = File.join(File.dirname(__FILE__), '..', "templates/masthead.gif")
|
111
|
+
cover_gif = File.join(File.dirname(__FILE__), '..', "templates/cover-image.gif")
|
112
|
+
|
113
|
+
Dir.chdir target_dir do
|
114
|
+
playorder = 1
|
115
|
+
|
116
|
+
images = []
|
117
|
+
manifest_items = []
|
118
|
+
|
119
|
+
unless File.exist?("_document.yml")
|
120
|
+
|
121
|
+
puts "Usage: kindlerb [target file directory]"
|
122
|
+
|
123
|
+
abort "Missing _document.yml. Your input file tree is not structured correctly. Please read the README."
|
124
|
+
end
|
125
|
+
|
126
|
+
document = YAML::load_file("_document.yml")
|
127
|
+
|
128
|
+
document[:spine_items] = []
|
129
|
+
section_html_files = []
|
130
|
+
|
131
|
+
sections = Dir['sections/*'].entries.sort.map.with_index {|section_dir|
|
132
|
+
c = File.read(Pathname.new(section_dir) + '_section.txt')
|
133
|
+
c.force_encoding("UTF-8")
|
134
|
+
section_title = c.strip
|
135
|
+
articles = Dir[Pathname.new(section_dir) + '*'].entries.select {|x| File.basename(x) !~ /section/}.sort
|
136
|
+
section_html_files << (section_html_file = (Pathname.new(section_dir) + 'section.html').to_s)
|
137
|
+
idref = "item-#{section_dir.gsub(/\D/, '')}"
|
138
|
+
|
139
|
+
document[:spine_items] << {:idref => idref}
|
140
|
+
manifest_items << {
|
141
|
+
:href => section_html_file,
|
142
|
+
:media => "application/xhtml+xml",
|
143
|
+
:idref => idref
|
144
|
+
}
|
145
|
+
|
146
|
+
s = {
|
147
|
+
:path => section_dir,
|
148
|
+
:title => section_title.shorten(40),
|
149
|
+
:playorder => (playorder += 1),
|
150
|
+
:idref => idref,
|
151
|
+
:href => Pathname.new(section_dir) + 'section.html',
|
152
|
+
:articles => articles.map {|article_file|
|
153
|
+
doc = Nokogiri::HTML(File.read(article_file, :encoding => 'UTF-8'))
|
154
|
+
article_images = doc.search("img").map {|img|
|
155
|
+
mimetype = img[:src] ? "image/#{File.extname(img[:src]).sub('.', '')}" : nil
|
156
|
+
{:href => img[:src], :mimetype => mimetype}
|
157
|
+
}
|
158
|
+
images.push *article_images
|
159
|
+
title = doc.search("html/head/title").map(&:inner_text).first || "no title"
|
160
|
+
idref = "item-#{article_file.gsub(/\D/, '')}"
|
161
|
+
document[:spine_items] << {:idref => idref}
|
162
|
+
article = {
|
163
|
+
:file => article_file,
|
164
|
+
:href => article_file,
|
165
|
+
:title => title,
|
166
|
+
:short_title => title.shorten(60),
|
167
|
+
:author => doc.search("html/head/meta[@name=author]").map{|n|n[:content]}.first,
|
168
|
+
:description => doc.search("html/head/meta[@name=description]").map{|n|n[:content]}.first,
|
169
|
+
:playorder => (playorder += 1),
|
170
|
+
:idref => idref
|
171
|
+
}
|
172
|
+
manifest_items << {
|
173
|
+
:href => article[:file],
|
174
|
+
:media => "application/xhtml+xml",
|
175
|
+
:idref => article[:idref]
|
176
|
+
}
|
177
|
+
article
|
178
|
+
}
|
179
|
+
}
|
180
|
+
|
181
|
+
# Generate the section html
|
182
|
+
out = Mustache.render section_template, s
|
183
|
+
File.open(section_html_file, "w") {|f| f.puts out}
|
184
|
+
s
|
185
|
+
|
186
|
+
}
|
187
|
+
|
188
|
+
document[:first_article] = sections[0][:articles][0]
|
189
|
+
document['sections'] = sections
|
190
|
+
|
191
|
+
|
192
|
+
document[:manifest_items] = manifest_items + images.map.with_index {|img, idx|
|
193
|
+
{
|
194
|
+
:href => img[:href],
|
195
|
+
:media => img[:mimetype],
|
196
|
+
:idref => "img-%03d" % idx
|
197
|
+
}
|
198
|
+
}
|
199
|
+
document[:cover_mimetype] ||= "image/gif"
|
200
|
+
|
201
|
+
opf = Mustache.render opf_template, document
|
202
|
+
File.open("kindlerb.opf", "w") {|f| f.puts opf}
|
203
|
+
puts "Wrote #{target_dir}/kindlerb.opf"
|
204
|
+
|
205
|
+
# NCX
|
206
|
+
ncx = Mustache.render ncx_template, document
|
207
|
+
File.open("nav-contents.ncx", "w") {|f| f.puts ncx}
|
208
|
+
puts "Wrote #{target_dir}/nav-contents.ncx"
|
209
|
+
|
210
|
+
# contents
|
211
|
+
contents = Mustache.render contents_template, document
|
212
|
+
File.open("contents.html", "w") {|f| f.puts contents}
|
213
|
+
puts "Wrote #{target_dir}/contents.html"
|
214
|
+
|
215
|
+
outfile = document['mobi_outfile']
|
216
|
+
puts "Writing #{outfile}"
|
217
|
+
cmd = self.executable + "#{' -verbose' if verbose} -#{compression_method} -o #{outfile} kindlerb.opf && echo 'Wrote MOBI to #{outfile}'"
|
218
|
+
puts cmd
|
219
|
+
system cmd
|
220
|
+
|
221
|
+
# If the system call returns anything other than nil, the call was successful
|
222
|
+
# Because Kindlegen completes build successfully with warnings
|
223
|
+
successful = $?.exitstatus.nil? ? false : true
|
224
|
+
if successful
|
225
|
+
return true
|
226
|
+
else
|
227
|
+
return false
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|