kindler 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 +19 -0
- data/Gemfile +20 -0
- data/Guardfile +8 -0
- data/Rakefile +2 -0
- data/Readme.md +26 -0
- data/kindler.gemspec +21 -0
- data/lib/kindler/html_generator.rb +7 -0
- data/lib/kindler/railtie.rb +7 -0
- data/lib/kindler/toc_generator.rb +7 -0
- data/lib/kindler/version.rb +3 -0
- data/lib/kindler.rb +254 -0
- data/spec/cases/generator_spec.rb +43 -0
- data/spec/spec_helper.rb +7 -0
- metadata +83 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
source 'http://rubygems.org'
|
2
|
+
|
3
|
+
group :development, :test do
|
4
|
+
gem 'rspec'
|
5
|
+
# Testing infrastructure
|
6
|
+
gem 'guard'
|
7
|
+
gem 'guard-rspec'
|
8
|
+
|
9
|
+
if RUBY_PLATFORM =~ /darwin/
|
10
|
+
# OS X integration
|
11
|
+
gem "ruby_gntp"
|
12
|
+
gem "rb-fsevent", "~> 0.4.3.1"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
gem "ruby-readability",:git=>'https://github.com/iterationlabs/ruby-readability.git', :require => 'readability'
|
17
|
+
gem 'mini_magick'
|
18
|
+
|
19
|
+
# Specify your gem's dependencies in kindler.gemspec
|
20
|
+
gemspec
|
data/Guardfile
ADDED
data/Rakefile
ADDED
data/Readme.md
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
### Todo
|
2
|
+
1.show images within page
|
3
|
+
2.support magzine like format
|
4
|
+
|
5
|
+
### A kindle mobi book generator
|
6
|
+
which receive a couple of urls then output one mobi file
|
7
|
+
|
8
|
+
### Command Line Use [Todo]
|
9
|
+
kindler url1 url2 url3 url4 -o test.mobi
|
10
|
+
|
11
|
+
outputs : test.mobi
|
12
|
+
|
13
|
+
### Api use
|
14
|
+
```ruby
|
15
|
+
# generate my book
|
16
|
+
book = Kindler::Book.new ({:urls=>["http://blog.farmostwood.net/643.html",
|
17
|
+
"http://www.ifanr.com/69878","http://www.oneplus.info/archives/455"],
|
18
|
+
:title=>'Test_book',:author=>'mike'})
|
19
|
+
# you will get my_book.mobi file
|
20
|
+
book.generate 'my_book'
|
21
|
+
```
|
22
|
+
|
23
|
+
|
24
|
+
Hope you love it !
|
25
|
+
|
26
|
+
|
data/kindler.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/kindler/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["dongbin.li"]
|
6
|
+
gem.email = ["mike.d.1984@gmail.com"]
|
7
|
+
gem.description = %q{a simple gem to generate kindle mobi book}
|
8
|
+
gem.summary = %q{a simple gem to generate kindle mobi book}
|
9
|
+
gem.homepage = "https://github.com/29decibel/kindler"
|
10
|
+
|
11
|
+
gem.rubyforge_project = "kindler"
|
12
|
+
gem.add_dependency 'mini_magick'
|
13
|
+
gem.add_dependency 'ruby-readability'
|
14
|
+
|
15
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
16
|
+
gem.files = `git ls-files`.split("\n")
|
17
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
gem.name = "kindler"
|
19
|
+
gem.require_paths = ["lib"]
|
20
|
+
gem.version = Kindler::VERSION
|
21
|
+
end
|
data/lib/kindler.rb
ADDED
@@ -0,0 +1,254 @@
|
|
1
|
+
require "kindler/version"
|
2
|
+
require "readability"
|
3
|
+
require "open-uri"
|
4
|
+
# require 'mini_magick'
|
5
|
+
require 'kindler/railtie' if defined?(Rails)
|
6
|
+
|
7
|
+
module Kindler
|
8
|
+
class Book
|
9
|
+
class KindlerError < StandardError;end
|
10
|
+
attr_accessor :urls,:title,:author
|
11
|
+
TMP_DIR = 'kindler_generated_mobi'
|
12
|
+
|
13
|
+
# availabel options
|
14
|
+
# @param options [Hash]
|
15
|
+
# @option urls [Array] urls to generate
|
16
|
+
# @option title [String] book title
|
17
|
+
# @option output_dir [String] directory want to generate
|
18
|
+
def initialize(options={})
|
19
|
+
@urls = options[:urls] || {}
|
20
|
+
@title = options[:title] || ''
|
21
|
+
@output_dir = options[:output_dir] || './'
|
22
|
+
raise KindlerError.new("urls option could not be empty") if @urls.empty?
|
23
|
+
@author = options[:author] || ''
|
24
|
+
@doc_infos = {}
|
25
|
+
# init doc infos by url
|
26
|
+
@urls.each {|url| @doc_infos[url]= {} }
|
27
|
+
end
|
28
|
+
|
29
|
+
# add url to book
|
30
|
+
# @param url [String] url to add to book
|
31
|
+
# @param options [Hash]
|
32
|
+
# @option section [Symbol] indicate which section the url belongs to,if not empty the book will be generated with magzine style
|
33
|
+
def add_url(url,options={})
|
34
|
+
return if @doc_infos[url]
|
35
|
+
@urls << url
|
36
|
+
@doc_infos[url] = {}
|
37
|
+
end
|
38
|
+
|
39
|
+
# generate books by given urls
|
40
|
+
def generate(title='')
|
41
|
+
make_generated_dirs
|
42
|
+
# generate
|
43
|
+
generate_html
|
44
|
+
generate_toc
|
45
|
+
generate_opf
|
46
|
+
generate_ncx
|
47
|
+
kindlegen
|
48
|
+
# clear
|
49
|
+
end
|
50
|
+
|
51
|
+
# check mobi file is generated already
|
52
|
+
def mobi_generated?
|
53
|
+
File.exist? "#{tmp_dir}/#{@title}.mobi"
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
# make sure kindlegen is installed
|
58
|
+
# you can use "sudo brew install " to install it
|
59
|
+
def kindlegen
|
60
|
+
puts 'begin generate mobi'
|
61
|
+
system("kindlegen #{tmp_dir}/#{@title}.opf ")
|
62
|
+
end
|
63
|
+
|
64
|
+
def generate_toc
|
65
|
+
contents = <<-CODE
|
66
|
+
<html>
|
67
|
+
<head>
|
68
|
+
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
|
69
|
+
<title>Table of Contents</title>
|
70
|
+
</head>
|
71
|
+
<body>
|
72
|
+
<h1>Contents</h1>
|
73
|
+
<h4>Main section</h4>
|
74
|
+
<ul>
|
75
|
+
CODE
|
76
|
+
files_count = 1
|
77
|
+
@doc_infos.each do |url,infos|
|
78
|
+
contents << "<li><a href='#{files_count.to_s.rjust(3,'0')}.html'>#{infos[:title]}</a></li>"
|
79
|
+
files_count += 1
|
80
|
+
end
|
81
|
+
# append footer
|
82
|
+
contents << "</ul></body></html>"
|
83
|
+
File.open(file_path('contents'),'w') do |f|
|
84
|
+
f.puts contents
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# generate ncx , which is navigation
|
89
|
+
def generate_ncx
|
90
|
+
contents = <<-NCX
|
91
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
92
|
+
<!DOCTYPE ncx PUBLIC "-//NISO//DTD ncx 2005-1//EN" "http://www.daisy.org/z3986/2005/ncx-2005-1.dtd">
|
93
|
+
<ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1" xml:lang="en-US">
|
94
|
+
<head>
|
95
|
+
<meta name="dtb:uid" content="#{@title}"/>
|
96
|
+
<meta name="dtb:depth" content="1"/>
|
97
|
+
<meta name="dtb:totalPageCount" content="0"/>
|
98
|
+
<meta name="dtb:maxPageNumber" content="0"/>
|
99
|
+
</head>
|
100
|
+
<docTitle>
|
101
|
+
<text>#{@title}</text>
|
102
|
+
</docTitle>
|
103
|
+
<docAuthor>
|
104
|
+
<text>#{@author}</text>
|
105
|
+
</docAuthor>
|
106
|
+
<navMap>
|
107
|
+
NCX
|
108
|
+
# this navPoint seems not useful
|
109
|
+
# contents << <<-NAV
|
110
|
+
# <navPoint id="navpoint-1" playOrder="1">
|
111
|
+
# <navLabel><text>Table Of Contents</text></navLabel>
|
112
|
+
# <content src="contents.html"/>
|
113
|
+
# </navPoint>
|
114
|
+
# NAV
|
115
|
+
####################### periodocal , magzine like format #########################
|
116
|
+
# <navPoint playOrder="0" class="periodical" id="periodical">
|
117
|
+
# <mbp:meta-img src="masthead.gif" name="mastheadImage"/>
|
118
|
+
# <navLabel>
|
119
|
+
# <text>Table of Contents</text>
|
120
|
+
# </navLabel>
|
121
|
+
# <content src="contents.html"/>
|
122
|
+
# <navPoint playOrder="1" class="section" id="Main-section">
|
123
|
+
# <navLabel>
|
124
|
+
# <text>Main section</text>
|
125
|
+
# </navLabel>
|
126
|
+
# <content src="001.html"/>
|
127
|
+
# <navPoint playOrder="2" class="article" id="item-001">
|
128
|
+
# <navLabel>
|
129
|
+
# <text>Nick Clegg and David Cameron agree key changes on NHS plans</text>
|
130
|
+
# </navLabel>
|
131
|
+
# <content src="001.html"/>
|
132
|
+
# <mbp:meta name="description">Deputy PM tells Andrew Marr show that GPs should not be forced to sign up to new commissioning consortiums</mbp:meta>
|
133
|
+
# <mbp:meta name="author">Nicholas Watt and Denis Campbell</mbp:meta>
|
134
|
+
# </navPoint>
|
135
|
+
# ####################################################################################
|
136
|
+
files_count = 2
|
137
|
+
@doc_infos.each do |url,infos|
|
138
|
+
nav_point = <<-NAV
|
139
|
+
<navPoint id="navpoint-#{files_count}" playOrder="#{files_count}">
|
140
|
+
<navLabel><text>#{infos[:title]}</text></navLabel>
|
141
|
+
<content src="#{(files_count-1).to_s.rjust(3,'0')}.html"/>
|
142
|
+
</navPoint>
|
143
|
+
NAV
|
144
|
+
contents << nav_point
|
145
|
+
files_count += 1
|
146
|
+
end
|
147
|
+
contents << "</navMap></ncx>"
|
148
|
+
File.open("#{tmp_dir}/nav-contents.ncx",'w') { |f| f.puts contents }
|
149
|
+
end
|
150
|
+
|
151
|
+
# generate the opf, manifest of book,including all articles and images and css
|
152
|
+
def generate_opf
|
153
|
+
# mark mobi as magzine format
|
154
|
+
# <x-metadata>
|
155
|
+
# <output content-type="application/x-mobipocket-subscription-magazine" encoding="utf-8"/>
|
156
|
+
# </x-metadata>
|
157
|
+
contents = <<-HTML
|
158
|
+
<?xml version='1.0' encoding='utf-8'?>
|
159
|
+
<package xmlns="http://www.idpf.org/2007/opf" version="2.0" unique-identifier="#{@title}">
|
160
|
+
<metadata>
|
161
|
+
<dc-metadata xmlns:dc="http://purl.org/dc/elements/1.1/">
|
162
|
+
<dc:title>#{@title}</dc:title>
|
163
|
+
<dc:language>en-gb</dc:language>
|
164
|
+
<meta content="cover-image" name="cover"/>
|
165
|
+
<dc:creator>Kindler- 29decibel</dc:creator>
|
166
|
+
<dc:publisher>Kindler- 29decibel</dc:publisher>
|
167
|
+
<dc:subject>News</dc:subject>
|
168
|
+
<dc:identifier id="#{@title}">#{@title}</dc:identifier>
|
169
|
+
<dc:date>#{Time.now.to_date}/dc:date>
|
170
|
+
<dc:description>Kindler generated book</dc:description>
|
171
|
+
</dc-metadata>
|
172
|
+
</metadata>
|
173
|
+
<manifest>
|
174
|
+
HTML
|
175
|
+
files_count = 1
|
176
|
+
@doc_infos.each do |url,infos|
|
177
|
+
doc_id = files_count.to_s.rjust(3,'0')
|
178
|
+
contents << "<item href='#{doc_id}.html' media-type='application/xhtml+xml' id='#{doc_id}'/>"
|
179
|
+
files_count += 1
|
180
|
+
end
|
181
|
+
contents << "<item href='contents.html' media-type='application/xhtml+xml' id='contents'/>"
|
182
|
+
contents << "<item href='nav-contents.ncx' media-type='application/x-dtbncx+xml' id='nav-contents'/>"
|
183
|
+
contents << "</manifest>"
|
184
|
+
contents << "<spine toc='nav-contents'>"
|
185
|
+
contents << "<itemref idref='contents'/>"
|
186
|
+
files_count = 1
|
187
|
+
@doc_infos.each do |url,infos|
|
188
|
+
contents << "<itemref idref='#{files_count.to_s.rjust(3,'0')}'/>"
|
189
|
+
files_count += 1
|
190
|
+
end
|
191
|
+
contents << "</spine><guide><reference href='contents.html' type='toc' title='Table of Contents'/></guide></package>"
|
192
|
+
File.open("#{tmp_dir}/#{@title}.opf",'w') {|f| f.puts contents}
|
193
|
+
end
|
194
|
+
|
195
|
+
# generate every url to article in readable format
|
196
|
+
def generate_html
|
197
|
+
@doc_infos.each do |url,infos|
|
198
|
+
article = readable_article(url)
|
199
|
+
# puts article.images
|
200
|
+
infos[:content] = html_wrap(article.title,article.content)
|
201
|
+
infos[:title] = article.title
|
202
|
+
end
|
203
|
+
# make html files
|
204
|
+
files_count = 1
|
205
|
+
@doc_infos.each do |url,infos|
|
206
|
+
File.open(file_path(files_count.to_s.rjust(3,'0')),'w') do |f|
|
207
|
+
f.puts infos[:content]
|
208
|
+
end
|
209
|
+
files_count += 1
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# html file path
|
214
|
+
def file_path(file_name)
|
215
|
+
"#{tmp_dir}/#{file_name}.html"
|
216
|
+
end
|
217
|
+
|
218
|
+
# wrap readable contents with in html format
|
219
|
+
def html_wrap(title,content)
|
220
|
+
result = ''
|
221
|
+
result << '<html><head>'
|
222
|
+
result << "<meta content='text/html; charset=utf-8' http-equiv='Content-Type'/>"
|
223
|
+
result << '</head><body>'
|
224
|
+
result << "<h3>#{title}</h3>"
|
225
|
+
result << content
|
226
|
+
result << '</body></html>'
|
227
|
+
end
|
228
|
+
|
229
|
+
# get readable document by url, using ruby-readability here
|
230
|
+
def readable_article(url)
|
231
|
+
puts "begin fetch url : #{url}"
|
232
|
+
source = open(url).read
|
233
|
+
Readability::Document.new(source)
|
234
|
+
end
|
235
|
+
|
236
|
+
# the dir path to generated files
|
237
|
+
def tmp_dir
|
238
|
+
File.join @output_dir,"#{TMP_DIR}_#{@title.gsub(' ','_')}"
|
239
|
+
end
|
240
|
+
|
241
|
+
# create dirs of generated files
|
242
|
+
def make_generated_dirs
|
243
|
+
FileUtils.rm_rf tmp_dir if File.exist?(tmp_dir)
|
244
|
+
FileUtils.mkdir_p tmp_dir unless File.exist?(tmp_dir)
|
245
|
+
end
|
246
|
+
|
247
|
+
# exist to clear tmp files such as ncx,opf or html other than mobi file
|
248
|
+
# keep them right now
|
249
|
+
def clear_tmp_dirs
|
250
|
+
|
251
|
+
end
|
252
|
+
|
253
|
+
end
|
254
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
describe "Mobi html file generator" do
|
3
|
+
|
4
|
+
it "should generate html files by urls" do
|
5
|
+
title = "Test_book"
|
6
|
+
book = Kindler::Book.new ({:urls=>["http://blog.farmostwood.net/643.html",
|
7
|
+
"http://www.ifanr.com/69878","http://www.oneplus.info/archives/455"],
|
8
|
+
:title=>title,:author=>'mike'})
|
9
|
+
book.generate
|
10
|
+
File.exist?(mobi_book_path(title)).should == true
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
it "should generate hacker news book" do
|
15
|
+
title = 'haker_news'
|
16
|
+
urls = []
|
17
|
+
urls << "http://jseliger.com/2010/09/26/how-universities-work-or-what-i-wish-i%E2%80%99d-known-freshman-year-a-guide-to-american-university-life-for-the-uninitiated/"
|
18
|
+
urls << "http://randykepple.com/photoblog/2010/10/8-bad-habits-that-crush-your-creativity-and-stifle-your-success/"
|
19
|
+
urls << "http://nathanmarz.com/blog/how-to-get-a-job-at-a-kick-ass-startup-for-programmers.html"
|
20
|
+
urls << "http://tumblr.intranation.com/post/766290565/how-set-up-your-own-private-git-server-linux"
|
21
|
+
urls << "http://antirez.com/post/what-is-wrong-with-2006-programming.html"
|
22
|
+
urls << "http://fak3r.com/2009/09/14/howto-build-your-own-open-source-dropbox-clone/"
|
23
|
+
book = Kindler::Book.new :urls=>urls,:title=>title,:author=>'mike'
|
24
|
+
book.generate
|
25
|
+
File.exist?(mobi_book_path(title)).should == true
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should generate book and infos on output_dir" do
|
29
|
+
title = 'my_dir_book'
|
30
|
+
urls = []
|
31
|
+
urls << "http://www.wowsai.com/home/space.php?uid=1&do=blog&id=4362&classid=2"
|
32
|
+
urls << "http://www.honeykennedy.com/2012/01/miss-moss-love-letters/"
|
33
|
+
urls << "http://www.mysenz.com/?p=3692"
|
34
|
+
book = Kindler::Book.new :urls=>urls,:title=>title,:author=>'mike',:output_dir=>'/Users/lidongbin/projects'
|
35
|
+
book.generate
|
36
|
+
File.exist?(mobi_book_path(title,'/Users/lidongbin/projects')).should == true
|
37
|
+
end
|
38
|
+
|
39
|
+
def mobi_book_path(title,output_dir='.')
|
40
|
+
File.join(output_dir,"kindler_generated_mobi_#{title}/#{title}.mobi")
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kindler
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- dongbin.li
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-01-30 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: mini_magick
|
16
|
+
requirement: &2162549440 !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: *2162549440
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: ruby-readability
|
27
|
+
requirement: &2162549020 !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: *2162549020
|
36
|
+
description: a simple gem to generate kindle mobi book
|
37
|
+
email:
|
38
|
+
- mike.d.1984@gmail.com
|
39
|
+
executables: []
|
40
|
+
extensions: []
|
41
|
+
extra_rdoc_files: []
|
42
|
+
files:
|
43
|
+
- .gitignore
|
44
|
+
- Gemfile
|
45
|
+
- Guardfile
|
46
|
+
- Rakefile
|
47
|
+
- Readme.md
|
48
|
+
- kindler.gemspec
|
49
|
+
- lib/kindler.rb
|
50
|
+
- lib/kindler/html_generator.rb
|
51
|
+
- lib/kindler/railtie.rb
|
52
|
+
- lib/kindler/toc_generator.rb
|
53
|
+
- lib/kindler/version.rb
|
54
|
+
- spec/cases/generator_spec.rb
|
55
|
+
- spec/spec_helper.rb
|
56
|
+
homepage: https://github.com/29decibel/kindler
|
57
|
+
licenses: []
|
58
|
+
post_install_message:
|
59
|
+
rdoc_options: []
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ! '>='
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ! '>='
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
requirements: []
|
75
|
+
rubyforge_project: kindler
|
76
|
+
rubygems_version: 1.8.11
|
77
|
+
signing_key:
|
78
|
+
specification_version: 3
|
79
|
+
summary: a simple gem to generate kindle mobi book
|
80
|
+
test_files:
|
81
|
+
- spec/cases/generator_spec.rb
|
82
|
+
- spec/spec_helper.rb
|
83
|
+
has_rdoc:
|