lknovel 0.0.1 → 0.0.2

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/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