lknovel 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1 +1,3 @@
1
1
  0.0.1: Initial release.
2
+ 0.0.2: Move block all out of Chapter/Volume class, use more helper function and
3
+ mixin to generalize code.
data/README.md CHANGED
@@ -16,9 +16,37 @@ And then execute:
16
16
 
17
17
  $ bundle
18
18
 
19
+ ## Dependencies
20
+
21
+ * Ruby 1.9
22
+ * [nokogiri](http://nokogiri.org/)
23
+ * [gepub](https://github.com/skoji/gepub)
24
+ * [parallel](https://github.com/grosser/parallel)
25
+
26
+ Rubygems will solve it for you.
27
+
28
+ If you encounter compiling/installing problem about nokogiri, install it
29
+ manually using the package manager. For Debian/Ubuntu:
30
+
31
+ # apt-get install ruby-nokogiri
32
+
19
33
  ## Usage
20
34
 
21
- $ lknovel http://lknovel.lightnovel.cn/main/book/2136.html
35
+ $ lknovel -h
36
+ Usage: lknovel [options] url
37
+
38
+ Specific options:
39
+ -k, --[no-]keep Keep temporary files
40
+ -q, --[no-]quiet Run quietly
41
+
42
+ Common options:
43
+ -h, --help Show this message
44
+ -V, --version Show version
45
+
46
+ $ lknovel http://lknovel.lightnovel.cn/main/book/2123.html
47
+ Chapters: 5/5 第3章 魔王和勇者,在笹塚相会。
48
+ Images: 13/13 20120817193746_95414.jpg
49
+ Finish: 打工吧!魔王大人 - 第01卷 - 第1卷.epub
22
50
 
23
51
  ## Contributing
24
52
 
data/lib/lknovel/app.rb CHANGED
@@ -22,8 +22,8 @@ module Lknovel
22
22
  options.keep = k
23
23
  end
24
24
 
25
- opts.on('-v', '--[no-]verbose', 'Run verbosely') do |v|
26
- options.verbose = v
25
+ opts.on('-q', '--[no-]quiet', 'Run quietly') do |q|
26
+ options.verbose = !q
27
27
  end
28
28
 
29
29
  opts.separator "\nCommon options:"
@@ -51,11 +51,6 @@ module Lknovel
51
51
 
52
52
  def initialize(args)
53
53
  @options = self.class.parse(args)
54
- begin
55
- @console_width = `tput cols`.to_i
56
- rescue Exception
57
- @console_width = 80
58
- end
59
54
  end
60
55
 
61
56
  def run
@@ -65,60 +60,49 @@ module Lknovel
65
60
  end
66
61
 
67
62
  def process_volume(url)
68
- if @options.verbose
69
- STDERR.write "Parsing chapters: ..."
70
- end
71
- volume = Volume.new(url) do |chapter|
72
- if @options.verbose
73
- STDERR.write "\r#{' ' * @console_width}"
74
- STDERR.write "\rParsing chapters: #{chapter.title}"
75
- end
76
- end
77
- if @options.verbose
78
- puts
63
+ volume = Volume.new(url)
64
+
65
+ parallel_verbose(volume.chapters, title: 'Chapters',
66
+ verbose: @options.verbose) do |chapter|
67
+ chapter.parse
68
+ chapter.title
79
69
  end
80
70
 
81
71
  FileUtils.mkdir_p(volume.path)
82
72
  Dir.chdir(volume.path) do
73
+ # generate html
74
+ FileUtils.mkdir_p(HTML_DIR)
75
+ Dir.chdir(HTML_DIR) do
76
+ volume.render(File.join(TEMPLATE_PATH, 'front.html.erb'), 'front.html')
77
+ erb = File.read(File.join(TEMPLATE_PATH, 'chapter.html.erb'))
78
+ template = ERB.new(erb, nil, '-')
79
+ volume.chapters.each_with_index do |chapter, index|
80
+ chapter.render(template, HTML_FILE_FORMAT % index)
81
+ end
82
+ end
83
+
84
+ # download images
83
85
  FileUtils.mkdir_p(IMAGE_DIR)
84
86
  Dir.chdir(IMAGE_DIR) do
85
87
  images = []
86
88
  volume.chapters.each do |chapter|
87
89
  chapter.content.each do |content|
88
- images.push content if content.is_a?(Image)
90
+ images << content if content.is_a?(Image)
89
91
  end
90
92
  end
91
93
 
92
- if @options.verbose
93
- progress = 1
94
- end
95
- STDERR.write "\rDownload images: 0/#{images.length}\t..."
96
- Parallel.each(images, :in_threads => 5) do |image|
94
+ parallel_verbose(images, title: 'Images',
95
+ verbose: @options.verbose) do |image|
97
96
  image.download
98
- if @options.verbose
99
- STDERR.write "\r#{' ' * @console_width}"
100
- STDERR.write \
101
- "\rDownload images: #{progress}/#{images.length}\t#{image.file}"
102
- progress = progress + 1
103
- end
104
- end
105
- if @options.verbose
106
- puts
97
+ image.file
107
98
  end
108
99
 
109
- # crop cover image if width > height * 1.5
110
- cropped = volume.cover_image.crop('cover.jpg',
111
- '52%x100%+0+0') { |w, h| w > h * 1.5 }
112
- volume.cover_image_cropped = 'cover.jpg' if cropped
113
- end
114
-
115
- FileUtils.mkdir_p(HTML_DIR)
116
- Dir.chdir(HTML_DIR) do
117
- volume.html(File.join(TEMPLATE_PATH, 'front.html.erb'), 'front.html')
118
- erb = File.read(File.join(TEMPLATE_PATH, 'chapter.html.erb'))
119
- template = ERB.new(erb, nil, '-')
120
- volume.chapters.each_with_index do |chapter, index|
121
- chapter.html(template, HTML_FILE_FORMAT % index)
100
+ if images.size > 0
101
+ cover_image = images[0]
102
+ # crop cover image if width > height * 1.4
103
+ cropped = cover_image.crop('cover.jpg',
104
+ '52%x100%+0+0') { |w, h| w > h * 1.4 }
105
+ volume.cover_image = cropped ? 'cover.jpg' : cover_image.file
122
106
  end
123
107
  end
124
108
  end
@@ -136,7 +120,7 @@ module Lknovel
136
120
  contributor volume.illustrator, 'ill'
137
121
 
138
122
  resources(:workdir => volume.path) {
139
- cover_image File.join(IMAGE_DIR, volume.cover_image_cropped)
123
+ cover_image File.join(IMAGE_DIR, volume.cover_image)
140
124
  file \
141
125
  "#{STYLESHEET_DIR}/default.css" => "#{STYLESHEET_PATH}/default.css"
142
126
  glob "#{IMAGE_DIR}/*"
@@ -9,12 +9,14 @@ require 'open-uri'
9
9
 
10
10
  module Lknovel
11
11
  class Chapter
12
+ include ERBRender
12
13
 
13
14
  attr_reader :url, :title, :content
14
15
 
15
- def initialize(url)
16
+ def initialize(url, options = {})
17
+ @options = {:title => nil}.merge(options)
16
18
  @url = url
17
- parse
19
+ @title = @options[:title]
18
20
  end
19
21
 
20
22
  def parse
@@ -39,19 +41,5 @@ module Lknovel
39
41
  end
40
42
  end
41
43
 
42
- def html(erb, path = nil)
43
- template = erb.is_a?(ERB) ? erb : ERB.new(File.read(erb), nil, '-')
44
- html_content = template.result(binding)
45
- if path.nil?
46
- html_content
47
- else
48
- if !File.exists?(path)
49
- File.open(path, 'w') do |file|
50
- file.puts html_content
51
- end
52
- end
53
- end
54
- end
55
-
56
44
  end
57
45
  end
data/lib/lknovel/meta.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Lknovel
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  MODULE_PATH = File.expand_path('..', __FILE__)
4
4
  TEMPLATE_PATH = File.join(MODULE_PATH, 'templates')
5
5
  STYLESHEET_PATH = File.join(MODULE_PATH, 'assets', 'stylesheets')
@@ -3,14 +3,14 @@
3
3
  <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh">
4
4
  <head>
5
5
  <meta http-equiv="content-type" content="text/html;charset=UTF-8">
6
- <link href="<%= "../#{STYLESHEET_DIR}/default.css" %>" rel="stylesheet" type="text/css"/>
6
+ <link href="<%= "../#{Lknovel::STYLESHEET_DIR}/default.css" %>" rel="stylesheet" type="text/css"/>
7
7
  <title><%= @title %></title>
8
8
  </head>
9
9
  <body>
10
10
  <h1><%= @title %></h1>
11
11
  <% @content.each do |item| -%>
12
- <% if item.is_a?(Image) -%>
13
- <img src="<%= "../#{IMAGE_DIR}/#{item.file}" %>">
12
+ <% if item.is_a?(Lknovel::Image) -%>
13
+ <img src="<%= "../#{Lknovel::IMAGE_DIR}/#{item.file}" %>">
14
14
  <% else -%>
15
15
  <p><%= item %></p>
16
16
  <% end -%>
@@ -2,16 +2,16 @@
2
2
  <html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops">
3
3
  <head>
4
4
  <meta http-equiv="content-type" content="text/html;charset=UTF-8">
5
- <link href="<%= "../#{STYLESHEET_DIR}/default.css" %>" rel="stylesheet" type="text/css"/>
5
+ <link href="<%= "../#{Lknovel::STYLESHEET_DIR}/default.css" %>" rel="stylesheet" type="text/css"/>
6
6
  </head>
7
7
  <body>
8
- <img class="page-break" src="<%= "../#{IMAGE_DIR}/#{@cover_image_cropped}" %>">
8
+ <img class="page-break" src="<%= "../#{Lknovel::IMAGE_DIR}/#{@cover_image}" %>">
9
9
  <h1>简介</h1>
10
10
  <p><%= @intro %></p>
11
11
  <nav epub:type="toc" id="toc">
12
12
  <ul class="nav">
13
13
  <% @chapters.each_with_index do |chapter, index| -%>
14
- <li><a href="<%= HTML_FILE_FORMAT % index %>"><%= chapter.title %></a></li>
14
+ <li><a href="<%= Lknovel::HTML_FILE_FORMAT % index %>"><%= chapter.title %></a></li>
15
15
  <% end -%>
16
16
  </ul>
17
17
  </nav>
data/lib/lknovel/utils.rb CHANGED
@@ -1,3 +1,6 @@
1
+ # encoding: utf-8
2
+ require 'parallel'
3
+
1
4
  # http://blog.codefront.net/2008/01/14/retrying-code-blocks-in-ruby-on-exceptions-whatever/
2
5
  # Options:
3
6
  # * :tries - Number of retries to perform. Defaults to 3.
@@ -22,3 +25,43 @@ def retryable(options = {}, &block)
22
25
 
23
26
  yield
24
27
  end
28
+
29
+ def parallel_verbose(items, options = {}, &block)
30
+ opts = {:title => 'Working', :verbose => true, :threads => 5}.merge(options)
31
+ if opts[:verbose]
32
+ progress = 1
33
+ msg = "\r#{opts[:title]}: 0/#{items.size} ..."
34
+ STDERR.write msg
35
+ end
36
+ Parallel.each_with_index(items, :in_threads => 5) do |item, index|
37
+ extra_msg = block.call(item, index)
38
+ if opts[:verbose]
39
+ # get previous console output length, aware of wide chars
40
+ size =
41
+ msg.gsub(/\p{Han}|\p{Katakana}|\p{Hiragana}|\p{Hangul}/, ' ').size - 1
42
+ STDERR.write "\r#{' ' * size}"
43
+ msg = "\r#{opts[:title]}: #{progress}/#{items.size} #{extra_msg}"
44
+ STDERR.write msg
45
+ progress = progress + 1
46
+ end
47
+ end
48
+ if opts[:verbose]
49
+ puts
50
+ end
51
+ end
52
+
53
+ module ERBRender
54
+ def render(erb, path = nil)
55
+ template = erb.is_a?(ERB) ? erb : ERB.new(File.read(erb), nil, '-')
56
+ html_content = template.result(binding)
57
+ if path.nil?
58
+ html_content
59
+ else
60
+ if !File.exists?(path)
61
+ File.open(path, 'w') do |file|
62
+ file.puts html_content
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -4,23 +4,22 @@ require 'lknovel/meta'
4
4
  require 'lknovel/utils'
5
5
  require 'nokogiri'
6
6
  require 'open-uri'
7
- require 'parallel'
8
7
 
9
8
  module Lknovel
10
9
  class Volume
10
+ include ERBRender
11
11
 
12
12
  attr_reader :url, :series, :author, :title, :number_s, :number, :date,
13
- :illustrator, :publisher, :intro, :chapters, :path, :cover_image
13
+ :illustrator, :publisher, :intro, :chapters, :path
14
14
 
15
- attr_accessor :cover_image_cropped
15
+ attr_accessor :cover_image
16
16
 
17
- def initialize(url, options = {}, &block)
17
+ def initialize(url)
18
18
  @url = url
19
- @options = {:threads => 4}.merge(options)
20
- parse(&block)
19
+ parse
21
20
  end
22
21
 
23
- def parse(&block)
22
+ def parse
24
23
  page = retryable do
25
24
  Nokogiri::HTML(open(@url))
26
25
  end
@@ -51,31 +50,10 @@ module Lknovel
51
50
 
52
51
  @path = "#{@series} - #{@number_s} - #{@title}"
53
52
 
54
- @chapters = Parallel.map(
55
- page.css('ul.lk-chapter-list li.span3'),
56
- :in_threads => @threads) do |x|
53
+ @chapters = page.css('ul.lk-chapter-list li.span3').map do |x|
57
54
  chapter_title = x.text.strip.sub(/\s+/, ' ')
58
55
  chapter_url = URI.join(url, x.css('a')[0]['href']).to_s
59
- chapter = Chapter.new(chapter_url)
60
- block.call(chapter)
61
- chapter
62
- end
63
-
64
- @cover_image = @chapters[0].content.find { |x| x.is_a?(Lknovel::Image) }
65
- @cover_image_cropped = @cover_image.file
66
- end
67
-
68
- def html(erb, path = nil)
69
- template = erb.is_a?(ERB) ? erb : ERB.new(File.read(erb), nil, '-')
70
- html_content = template.result(binding)
71
- if path.nil?
72
- html_content
73
- else
74
- if !File.exists?(path)
75
- File.open(path, 'w') do |file|
76
- file.puts html_content
77
- end
78
- end
56
+ Chapter.new(chapter_url, title: chapter_title)
79
57
  end
80
58
  end
81
59
 
@@ -5,6 +5,7 @@ require 'lknovel/chapter'
5
5
  describe Lknovel::Chapter do
6
6
 
7
7
  chapter = Lknovel::Chapter.new('http://lknovel.lightnovel.cn/main/view/1486.html')
8
+ chapter.parse
8
9
 
9
10
  it 'get chapter title' do
10
11
  chapter.title.must_equal '第0章 序章'
@@ -19,7 +20,7 @@ describe Lknovel::Chapter do
19
20
  end
20
21
 
21
22
  it 'generate html' do
22
- html = chapter.html(File.join(Lknovel::TEMPLATE_PATH, 'chapter.html.erb'))
23
+ html = chapter.render(File.join(Lknovel::TEMPLATE_PATH, 'chapter.html.erb'))
23
24
  html.must_match '"../stylesheets/default.css"'
24
25
  html.must_match '"../images/20120808202639_66681.jpg"'
25
26
  end
@@ -5,6 +5,10 @@ require 'lknovel/volume'
5
5
  describe Lknovel::Volume do
6
6
 
7
7
  volume = Lknovel::Volume.new('http://lknovel.lightnovel.cn/main/book/2136.html')
8
+ volume.parse
9
+ volume.chapters[0].parse
10
+ cover_image = volume.chapters[0].content.find { |x| x.is_a?(Lknovel::Image) }
11
+ volume.cover_image = cover_image
8
12
 
9
13
  it 'get path' do
10
14
  volume.path.must_equal '打工吧!魔王大人 - 短篇01 - 短篇01'
@@ -35,7 +39,7 @@ describe Lknovel::Volume do
35
39
  end
36
40
 
37
41
  it 'generate front html' do
38
- html = volume.html(File.join(Lknovel::TEMPLATE_PATH, 'front.html.erb'))
42
+ html = volume.render(File.join(Lknovel::TEMPLATE_PATH, 'front.html.erb'))
39
43
  html.must_match '<li><a href="000.html">第0章 序章</a></li>'
40
44
  end
41
45
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lknovel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-03-17 00:00:00.000000000 Z
12
+ date: 2014-03-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler