kindler 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|