narou 3.2.5.1 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of narou might be problematic. Click here for more details.

Files changed (131) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -5
  3. data/.haml-lint.yml +7 -0
  4. data/.rubocop.yml +23 -5
  5. data/.scss-lint.yml +9 -0
  6. data/ChangeLog.md +86 -0
  7. data/Gemfile.lock +35 -35
  8. data/README.md +80 -64
  9. data/lib/backtracer.rb +2 -2
  10. data/lib/color.rb +5 -1
  11. data/lib/command.rb +7 -2
  12. data/lib/command/alias.rb +3 -5
  13. data/lib/command/backup.rb +3 -5
  14. data/lib/command/browser.rb +3 -5
  15. data/lib/command/clean.rb +5 -1
  16. data/lib/command/console.rb +33 -0
  17. data/lib/command/convert.rb +143 -117
  18. data/lib/command/csv.rb +2 -1
  19. data/lib/command/diff.rb +20 -18
  20. data/lib/command/download.rb +25 -14
  21. data/lib/command/folder.rb +3 -5
  22. data/lib/command/freeze.rb +3 -5
  23. data/lib/command/help.rb +20 -18
  24. data/lib/command/init.rb +4 -3
  25. data/lib/command/inspect.rb +2 -1
  26. data/lib/command/list.rb +10 -8
  27. data/lib/command/list/novel_decorator.rb +2 -1
  28. data/lib/command/log.rb +100 -0
  29. data/lib/command/log/tail.rb +76 -0
  30. data/lib/command/mail.rb +20 -17
  31. data/lib/command/remove.rb +7 -6
  32. data/lib/command/send.rb +23 -20
  33. data/lib/command/setting.rb +74 -40
  34. data/lib/command/tag.rb +16 -15
  35. data/lib/command/trace.rb +2 -2
  36. data/lib/command/update.rb +78 -128
  37. data/lib/command/update/general_lastup_updater.rb +3 -2
  38. data/lib/command/update/hotentry_manager.rb +2 -1
  39. data/lib/command/update/interval.rb +2 -1
  40. data/lib/command/version.rb +2 -1
  41. data/lib/command/web.rb +17 -3
  42. data/lib/commandbase.rb +34 -7
  43. data/lib/commandline.rb +54 -35
  44. data/lib/converterbase.rb +21 -15
  45. data/lib/database.rb +3 -2
  46. data/lib/device.rb +5 -4
  47. data/lib/device/epub.rb +2 -1
  48. data/lib/device/ibooks.rb +2 -1
  49. data/lib/device/ibunko.rb +2 -1
  50. data/lib/device/kindle.rb +2 -1
  51. data/lib/device/kobo.rb +2 -1
  52. data/lib/device/library/cygwin.rb +2 -1
  53. data/lib/device/library/linux.rb +2 -1
  54. data/lib/device/library/mac.rb +2 -1
  55. data/lib/device/library/windows.rb +2 -1
  56. data/lib/device/library/windows/eject.rb +2 -1
  57. data/lib/device/reader.rb +2 -1
  58. data/lib/diffviewer.rb +8 -11
  59. data/lib/downloader.rb +159 -151
  60. data/lib/eventable.rb +2 -1
  61. data/lib/extension.rb +16 -14
  62. data/lib/extensions/jruby.rb +2 -1
  63. data/lib/extensions/monkey_patches.rb +7 -0
  64. data/lib/extensions/monkey_patches/pathname.rb +22 -0
  65. data/lib/extensions/windows.rb +2 -1
  66. data/lib/extensions/windows_write_color.rb +2 -1
  67. data/lib/helper.rb +35 -20
  68. data/lib/html.rb +2 -1
  69. data/lib/illustration.rb +2 -1
  70. data/lib/ini.rb +2 -1
  71. data/lib/input.rb +2 -1
  72. data/lib/inspector.rb +3 -2
  73. data/lib/inventory.rb +3 -3
  74. data/lib/mailer.rb +3 -2
  75. data/lib/mixin/all.rb +8 -0
  76. data/lib/mixin/locker.rb +40 -0
  77. data/lib/mixin/output_error.rb +28 -0
  78. data/lib/narou.rb +69 -51
  79. data/lib/narou/api.rb +2 -4
  80. data/lib/narou_logger.rb +236 -108
  81. data/lib/novelconverter.rb +77 -69
  82. data/lib/novelinfo.rb +4 -2
  83. data/lib/novelsetting.rb +15 -12
  84. data/lib/progressbar.rb +13 -9
  85. data/lib/sitesetting.rb +39 -18
  86. data/lib/template.rb +5 -4
  87. data/lib/version.rb +3 -2
  88. data/lib/web/all.rb +2 -1
  89. data/lib/web/appserver.rb +83 -65
  90. data/lib/web/helper4web.rb +10 -5
  91. data/lib/web/progressbar4web.rb +8 -4
  92. data/lib/web/public/resources/default-style.css +2 -3
  93. data/lib/web/public/resources/narou.library.js +86 -60
  94. data/lib/web/public/resources/narou.queue.js +24 -30
  95. data/lib/web/public/resources/narou.ui.js +22 -3
  96. data/lib/web/public/theme/Cerulean/style.css +5 -5
  97. data/lib/web/public/theme/Darkly/style.css +5 -5
  98. data/lib/web/public/theme/Readable/style.css +5 -5
  99. data/lib/web/public/theme/Slate/style.css +2 -3
  100. data/lib/web/public/theme/Superhero/style.css +2 -3
  101. data/lib/web/public/theme/United/style.css +5 -5
  102. data/lib/web/pushserver.rb +10 -7
  103. data/lib/web/server_helpers.rb +16 -1
  104. data/lib/web/settingmessages.rb +10 -7
  105. data/lib/web/streaminginput.rb +2 -1
  106. data/lib/web/streaminglogger.rb +45 -32
  107. data/lib/web/views/_about.haml +6 -3
  108. data/lib/web/views/_header.haml +2 -3
  109. data/lib/web/views/_move_to_top.haml +2 -0
  110. data/lib/web/views/_queue.haml +7 -0
  111. data/lib/web/views/bookmarklet/insert_button.js.erb +1 -1
  112. data/lib/web/views/index.haml +30 -27
  113. data/lib/web/views/layout.haml +2 -0
  114. data/lib/web/views/novels/setting.haml +3 -4
  115. data/lib/web/views/settings.haml +22 -8
  116. data/lib/web/views/style.scss +54 -3
  117. data/lib/web/views/widget/download.haml +9 -3
  118. data/lib/web/views/widget/drag_and_drop.haml +10 -4
  119. data/lib/web/web_worker.rb +132 -0
  120. data/lib/worker.rb +142 -0
  121. data/narou.gemspec +80 -45
  122. data/narou.rb +6 -4
  123. data/template/novel.txt.erb +1 -0
  124. data/webnovel/kakuyomu.jp.yaml +9 -13
  125. data/webnovel/ncode.syosetu.com.yaml +3 -1
  126. data/webnovel/novel18.syosetu.com.yaml +8 -1
  127. data/webnovel/syosetu.org.yaml +3 -1
  128. data/webnovel/www.akatsuki-novels.com.yaml +4 -2
  129. data/webnovel/www.mai-net.net.yaml +3 -1
  130. metadata +109 -48
  131. data/lib/web/worker.rb +0 -126
@@ -1,4 +1,5 @@
1
- # -*- coding: utf-8 -*-
1
+ # frozen_string_literal: true
2
+
2
3
  #
3
4
  # Copyright 2013 whiteleaf. All rights reserved.
4
5
  #
@@ -15,8 +16,6 @@ module Narou
15
16
  # 小説家になろうデベロッパーAPI操作クラス
16
17
  #
17
18
  class API
18
- extend Memoist
19
-
20
19
  # 一度に問い合わせする件数
21
20
  BATCH_LIMIT = 300
22
21
 
@@ -82,7 +81,6 @@ module Narou
82
81
  def has_of?(type)
83
82
  @splited_of.include?(type)
84
83
  end
85
- memoize :has_of?
86
84
 
87
85
  def private_novels
88
86
  (@ncodes - @stores.map { |st| st["ncode"] }).map do |ncode|
@@ -1,11 +1,15 @@
1
- # -*- coding: utf-8 -*-
1
+ # frozen_string_literal: true
2
+
2
3
  #
3
4
  # Copyright 2013 whiteleaf. All rights reserved.
4
5
  #
5
6
 
6
7
  require "stringio"
7
8
  require "termcolorlight"
9
+ require "io/console/size"
10
+ require "unicode/display_width"
8
11
  require_relative "color"
12
+ require_relative "inventory"
9
13
 
10
14
  if $disable_color
11
15
  class String
@@ -15,144 +19,263 @@ if $disable_color
15
19
  end
16
20
  end
17
21
 
18
- module Narou end unless defined?(Narou)
22
+ module Narou
23
+ module LoggerModule
24
+ attr_accessor :capturing, :stream, :log_postfix
25
+ attr_reader :logging_enabled, :format_filename, :format_timestamp, :format_timestamp_disabled
19
26
 
20
- module Narou::LoggerModule
21
- attr_accessor :capturing
27
+ def self.included(klass)
28
+ klass.class_eval do
29
+ alias_method :original_write, :write
30
+ end
31
+ end
22
32
 
23
- def initialize
24
- super
25
- @is_silent = false
26
- @capturing = false
27
- end
33
+ LOG_FORMAT_FILENAME = "%Y%m%d.txt"
34
+ LOG_FORMAT_TIMESTAMP = "[%H:%M:%S]"
28
35
 
29
- def copy_instance
30
- self.class.new.tap { |obj|
31
- obj.silent = silent
32
- }
33
- end
36
+ def initialize
37
+ super
38
+ self.silent = false
39
+ @capturing = false
40
+ init_logs
41
+ end
34
42
 
35
- def silent=(enable)
36
- @is_silent = !!enable
37
- end
43
+ def init_logs
44
+ inv = Inventory.load("local_setting")
45
+ inv_logging = inv.group("logging")
46
+ @logging_enabled = inv["logging"]
47
+ @format_filename = inv_logging.format_filename || LOG_FORMAT_FILENAME
48
+ @format_timestamp = inv_logging.format_timestamp || LOG_FORMAT_TIMESTAMP
49
+ @format_timestamp_disabled = format_timestamp.blank? || format_timestamp.strip == "$none"
50
+ create_log_dir
51
+ end
38
52
 
39
- def silent
40
- if block_given?
41
- if /^(.+?):(\d+)/ =~ caller.first
42
- file = $1
43
- line = $2.to_i
44
- error_msg = "Did you mean: silence\n"
45
- str = File.read(file).split("\n")[line-1]
46
- error_msg += "in #{file}:#{line}\n"
47
- error_msg += str + "\n"
48
- error_msg += " " * str.index("silent") + "~~~~~~"
49
- raise error_msg
53
+ def copy_instance
54
+ self.class.new.tap do |obj|
55
+ obj.silent = silent?
50
56
  end
51
57
  end
52
- @is_silent
53
- end
54
58
 
55
- def silence(&block)
56
- raise "need a block" unless block
57
- tmp = self.silent
58
- self.silent = true
59
- block.call
60
- self.silent = tmp
61
- end
59
+ def dup_with_disabled_logging
60
+ obj = dup
61
+ obj.disable_logging
62
+ obj
63
+ end
62
64
 
63
- #
64
- # 標準出力($stdout)のバッファリング+取得
65
- #
66
- # キャプチャー用途なので標準エラーはキャプチャーしない
67
- # quiet :: 標準出力に出力をしないかどうか
68
- # ansicolor_strip :: エスケープシーケンスを除去するか
69
- #
70
- def capture(options = {}, &block)
71
- options = {
72
- quiet: true, ansicolor_strip: true
73
- }.merge(options)
74
- raise "#capture block given" unless block
75
- temp_stream = $stdout
76
- $stdout = (self == $stdout ? copy_instance : self)
77
- $stdout.capturing = true
78
- if options[:quiet]
79
- $stdout.silence { block.call }
80
- else
81
- block.call
82
- end
83
- $stdout.capturing = false
84
- buffer = $stdout.string
85
- $stdout = temp_stream
86
- result = options[:ansicolor_strip] ? strip_color(buffer) : buffer
87
- if $stdout.capturing && !options[:quiet]
88
- # 多段キャプチャ中かつ quite: false の場合は外側にも伝播する
89
- $stdout.string << result
90
- end
91
- result
92
- end
65
+ def silent=(enable)
66
+ @silent = enable.present?
67
+ end
93
68
 
94
- def strip_color(str)
95
- if $disable_color
96
- str
97
- else
98
- str.gsub(/(?:\e\[\d*[a-zA-Z])+/, "")
69
+ def silent?
70
+ @silent
99
71
  end
100
- end
101
72
 
102
- def save(path)
103
- File.write(path, strip_color(string))
104
- end
73
+ def silence(&block)
74
+ raise "need a block" unless block
75
+ tmp = self.silent?
76
+ self.silent = true
77
+ yield
78
+ self.silent = tmp
79
+ end
105
80
 
106
- def write_console(str, target)
107
- unless @is_silent
81
+ #
82
+ # 標準出力($stdout)のバッファリング+取得
83
+ #
84
+ # キャプチャー用途なので標準エラーはキャプチャーしない
85
+ # quiet :: 標準出力に出力をしないかどうか
86
+ # ansicolor_strip :: エスケープシーケンスを除去するか
87
+ #
88
+ def capture(options = {}, &block)
89
+ options = {
90
+ quiet: true, ansicolor_strip: true
91
+ }.merge(options)
92
+ raise "#capture block given" unless block
93
+ temp_stream = $stdout
94
+ $stdout = (self == $stdout ? copy_instance : self)
95
+ $stdout.capturing = true
96
+ if options[:quiet]
97
+ $stdout.silence { yield }
98
+ else
99
+ yield
100
+ end
101
+ $stdout.capturing = false
102
+ buffer = $stdout.string
103
+ $stdout = temp_stream
104
+ result = options[:ansicolor_strip] ? strip_color(buffer) : buffer
105
+ if $stdout.capturing && !options[:quiet]
106
+ # 多段キャプチャ中かつ quiet: false の場合は外側にも伝播する
107
+ $stdout.string << result
108
+ end
109
+ result
110
+ end
111
+
112
+ def strip_color(str)
113
+ if $disable_color
114
+ str
115
+ else
116
+ str.gsub(/(?:\e\[\d*[a-zA-Z])+/, "")
117
+ end
118
+ end
119
+
120
+ def save(path)
121
+ File.write(path, strip_color(string))
122
+ end
123
+
124
+ def write_console(str, target)
125
+ return if silent?
108
126
  if $disable_color
109
127
  target.write(str)
110
128
  else
111
129
  write_color(str, target)
112
130
  end
113
131
  end
114
- end
115
132
 
116
- def write_base(str, stream)
117
- str = str.to_s
118
- if str.encoding == Encoding::ASCII_8BIT
119
- str.force_encoding(Encoding::UTF_8)
133
+ def write_base(str, stream, force_disable_logging = false)
134
+ str = str.to_s
135
+ if str.encoding == Encoding::ASCII_8BIT
136
+ str.force_encoding(Encoding::UTF_8)
137
+ end
138
+ write_console(str, stream)
139
+ append_log(str) unless force_disable_logging
140
+ end
141
+
142
+ def warn(str)
143
+ self.puts str
144
+ end
145
+
146
+ def error(str)
147
+ self.puts "<bold><red>[ERROR]</red></bold> ".termcolor + str
148
+ end
149
+
150
+ def logging?
151
+ logging_enabled && ENV["NAROU_ENV"] != "test"
120
152
  end
121
- write_console(str, stream)
122
- end
123
153
 
124
- def warn(str)
125
- self.puts str
154
+ def create_log_dir
155
+ return unless logging?
156
+ dir = Narou.log_dir
157
+ dir.mkdir unless dir.exist?
158
+ end
159
+
160
+ def disable_logging
161
+ @logging_enabled = false
162
+ end
163
+
164
+ def append_log(str)
165
+ return unless logging?
166
+ File.write(log_filepath, strip_color(embed_timestamp(str)), mode: "a")
167
+ end
168
+
169
+ def log_filepath
170
+ Narou.log_dir.join(log_filename)
171
+ end
172
+
173
+ def log_filename
174
+ name = Time.now.strftime(format_filename)
175
+ return name unless log_postfix
176
+ ext = File.extname(name)
177
+ basename = File.basename(name, ext)
178
+ "#{basename}#{log_postfix}#{ext}"
179
+ end
180
+
181
+ def embed_timestamp(str)
182
+ unless @before_head_ln
183
+ str = "\n#{str}"
184
+ @before_head_ln = true
185
+ end
186
+ if str.end_with?("\n")
187
+ str = str.sub(/\n\z/, "")
188
+ @before_head_ln = false
189
+ end
190
+ return str if format_timestamp_disabled
191
+ str.gsub("\n", "\n#{Time.now.strftime(format_timestamp)} ")
192
+ end
126
193
  end
127
194
 
128
- def error(str)
129
- self.puts "<bold><red>[ERROR]</red></bold> ".termcolor + str
195
+ class Logger < StringIO
196
+ include LoggerModule
197
+
198
+ def initialize
199
+ super
200
+ self.stream = STDOUT
201
+ end
202
+
203
+ def write(str)
204
+ write_base(str, stream)
205
+ super(str)
206
+ end
207
+
208
+ def tty?
209
+ STDOUT.tty?
210
+ end
130
211
  end
131
- end
132
212
 
133
- class Narou::Logger < StringIO
134
- include Narou::LoggerModule
135
-
136
- def write(str)
137
- write_base(str, STDOUT)
138
- super(str)
213
+ class LoggerError < StringIO
214
+ include LoggerModule
215
+
216
+ def initialize
217
+ super
218
+ self.stream = STDERR
219
+ end
220
+
221
+ def write(str)
222
+ write_base(str, stream, false)
223
+ super(str)
224
+ end
225
+
226
+ def tty?
227
+ STDERR.tty?
228
+ end
139
229
  end
140
230
 
141
- def tty?
142
- STDOUT.tty?
231
+ class NullIO < StringIO
232
+ include LoggerModule
233
+
234
+ def write(_str); end
143
235
  end
144
- end
145
236
 
146
- class Narou::LoggerError < StringIO
147
- include Narou::LoggerModule
237
+ # 同時実行時のデフォルト側ロガー
238
+ # 変換中のキューを表示出来るようにする(キュー表示部分はログに保存しない)
239
+ class ConcurrencyDefaultLogger < Logger
240
+ attr_accessor :end_with_new_line, :format_text, :format_style
241
+
242
+ FORMAT_TEXT = "変換中:%<size>d"
243
+ FORMAT_STYLE = "\e[%<left>dG<cyan>%<text>s</cyan>\e[1G"
148
244
 
149
- def write(str)
150
- write_base(str, STDERR)
151
- super(str)
245
+ def initialize
246
+ super
247
+ self.end_with_new_line = true
248
+ concurrency = Inventory.load.group("concurrency")
249
+ self.format_text = concurrency.format_queue_text || FORMAT_TEXT
250
+ self.format_style = concurrency.format_queue_style || FORMAT_STYLE
251
+ end
252
+
253
+ def write_console(str, target)
254
+ str.each_line do |line|
255
+ size = Worker.size
256
+ if size > 0 && end_with_new_line
257
+ text = format(format_text, size: size)
258
+ text_width = text.display_width
259
+ console_width = IO.console_size[1]
260
+ left = console_width - text_width - 1
261
+ stream.print format(format_style, left: left, text: text, space: " ").termcolor
262
+ end
263
+ super(line, target)
264
+ end
265
+ self.end_with_new_line = str.end_with?("\n")
266
+ end
152
267
  end
153
268
 
154
- def tty?
155
- STDERR.tty?
269
+ # 同時実行時の変換側ロガー
270
+ # デフォルトロガーが出力中の間は標準出力には表示しない
271
+ class ConcurrencyConvertLogger < Logger
272
+ attr_accessor :end_with_new_line
273
+
274
+ def initialize
275
+ super
276
+ self.silent = true
277
+ self.log_postfix = "_convert"
278
+ end
156
279
  end
157
280
  end
158
281
 
@@ -165,5 +288,10 @@ def error(str)
165
288
  $stdout.error str
166
289
  end
167
290
 
168
- $stdout = Narou::Logger.new
169
- $stderr = Narou::LoggerError.new
291
+ if Inventory.load["concurrency"]
292
+ $stdout = Narou::ConcurrencyDefaultLogger.new
293
+ $stdout2 = Narou::ConcurrencyConvertLogger.new
294
+ else
295
+ $stdout = Narou::Logger.new
296
+ $stdout2 = $stdout
297
+ end
@@ -1,4 +1,5 @@
1
- # -*- coding: utf-8 -*-
1
+ # frozen_string_literal: true
2
+
2
3
  #
3
4
  # Copyright 2013 whiteleaf. All rights reserved.
4
5
  #
@@ -23,7 +24,7 @@ class NovelConverter
23
24
  NOVEL_TEXT_TEMPLATE_NAME = "novel.txt"
24
25
  NOVEL_TEXT_TEMPLATE_NAME_FOR_IBUNKO = "ibunko_novel.txt"
25
26
 
26
- attr_reader :use_dakuten_font
27
+ attr_reader :use_dakuten_font, :stream_io
27
28
 
28
29
  def self.extensions_of_converted_files(device)
29
30
  exts = [".txt"]
@@ -89,8 +90,8 @@ class NovelConverter
89
90
  DAKUTEN_ERB = [true, false]
90
91
 
91
92
  def self.activate_dakuten_font_files
92
- preset_dir = Narou.get_preset_dir
93
- aozora_dir = File.dirname(Narou.get_aozoraepub3_path)
93
+ preset_dir = Narou.preset_dir
94
+ aozora_dir = File.dirname(Narou.aozoraepub3_path)
94
95
  line_height = Narou.line_height
95
96
 
96
97
  DAKUTEN_FROM.each_with_index do |name, i|
@@ -105,8 +106,8 @@ class NovelConverter
105
106
  end
106
107
 
107
108
  def self.inactivate_dakuten_font_files
108
- preset_dir = Narou.get_preset_dir
109
- aozora_dir = File.dirname(Narou.get_aozoraepub3_path)
109
+ preset_dir = Narou.preset_dir
110
+ aozora_dir = File.dirname(Narou.aozoraepub3_path)
110
111
  path_normal_vertical_css = File.join(preset_dir, "vertical_font.css")
111
112
  line_height = Narou.line_height
112
113
 
@@ -124,7 +125,7 @@ class NovelConverter
124
125
  #
125
126
  # 返り値:正常終了 :success、エラー終了 :error、AozoraEpub3が見つからなかった nil
126
127
  #
127
- def self.txt_to_epub(filename, dst_dir: nil, device: nil, verbose: false, yokogaki: false, use_dakuten_font: false)
128
+ def self.txt_to_epub(filename, dst_dir: nil, device: nil, verbose: false, yokogaki: false, use_dakuten_font: false, stream_io: $stdout2)
128
129
  abs_srcpath = File.expand_path(filename)
129
130
  src_dir = File.dirname(abs_srcpath)
130
131
 
@@ -155,7 +156,7 @@ class NovelConverter
155
156
 
156
157
  pwd = Dir.pwd
157
158
 
158
- aozoraepub3_path = Narou.get_aozoraepub3_path
159
+ aozoraepub3_path = Narou.aozoraepub3_path
159
160
  unless aozoraepub3_path
160
161
  error "AozoraEpub3が見つからなかったのでEPUBが出力出来ませんでした。" +
161
162
  "narou initでAozoraEpub3の設定を行なって下さい"
@@ -173,13 +174,13 @@ class NovelConverter
173
174
  command = %!java #{java_encoding} -cp #{aozoraepub3_basename} AozoraEpub3 -enc UTF-8 -of #{device_option} ! +
174
175
  %!#{cover_option} #{dst_option} #{ext_option} #{yokogaki_option} "#{abs_srcpath}"!
175
176
  if Helper.os_windows?
176
- command = "cmd /c " + command.encode(Encoding::Windows_31J)
177
+ command = "cmd /c #{command}".encode(Encoding::Windows_31J)
177
178
  end
178
179
  activate_dakuten_font_files if use_dakuten_font
179
- print "AozoraEpub3でEPUBに変換しています"
180
+ stream_io.print "AozoraEpub3でEPUBに変換しています"
180
181
  begin
181
182
  res = Helper::AsyncCommand.exec(command) do
182
- print "."
183
+ stream_io.print "."
183
184
  end
184
185
  ensure
185
186
  Dir.chdir(pwd)
@@ -189,9 +190,9 @@ class NovelConverter
189
190
  # AozoraEpub3はエラーだとしてもexitコードは0なので、
190
191
  # 失敗した場合はjavaが実行できない場合と確定できる
191
192
  unless res[2].success?
192
- puts
193
- puts res
194
- error "JavaがインストールされていないかAozoraEpub3実行時にエラーが発生しました。EPUBを作成出来ませんでした"
193
+ stream_io.puts
194
+ stream_io.puts res
195
+ stream_io.error "JavaがインストールされていないかAozoraEpub3実行時にエラーが発生しました。EPUBを作成出来ませんでした"
195
196
  return :error
196
197
  end
197
198
 
@@ -199,11 +200,11 @@ class NovelConverter
199
200
 
200
201
  # Javaの実行環境に由来するであろうエラー
201
202
  if stdout_capture =~ /Error occurred during initialization of VM/
202
- puts
203
- warn stdout_capture.strip
204
- warn "-" * 70
205
- error "Javaの実行エラーが発生しました。EPUBを作成出来ませんでした\n" \
206
- "Hint: 複数のJava環境が混じっていると起きやすいエラーのようです"
203
+ stream_io.puts
204
+ stream_io.puts stdout_capture.strip
205
+ stream_io.puts "-" * 70
206
+ stream_io.error "Javaの実行エラーが発生しました。EPUBを作成出来ませんでした\n" \
207
+ "Hint: 複数のJava環境が混じっていると起きやすいエラーのようです"
207
208
  return :error
208
209
  end
209
210
 
@@ -212,27 +213,27 @@ class NovelConverter
212
213
  info_list = stdout_capture.scan(/^\[INFO\].+$/)
213
214
 
214
215
  if verbose
215
- puts
216
- puts "==== AozoraEpub3 stdout capture " + "=" * 47
217
- puts stdout_capture.strip
218
- puts "=" * 79
216
+ stream_io.puts
217
+ stream_io.puts "==== AozoraEpub3 stdout capture " + "=" * 47
218
+ stream_io.puts stdout_capture.strip
219
+ stream_io.puts "=" * 79
219
220
  end
220
221
 
221
222
  if !error_list.empty? || !warn_list.empty?
222
223
  unless verbose
223
- puts
224
- puts error_list, warn_list
224
+ stream_io.puts
225
+ stream_io.puts error_list, warn_list
225
226
  end
226
227
  unless error_list.empty?
227
228
  # AozoraEpub3 のエラーにはEPUBが出力されないエラーとEPUBが出力されるエラーの2種類ある。
228
229
  # EPUBが出力される場合は「変換完了」という文字があるのでそれを検出する
229
230
  if stdout_capture !~ /^変換完了/
230
- error "AozoraEpub3実行中にエラーが発生したため、EPUBが出力出来ませんでした"
231
+ stream_io.error "AozoraEpub3実行中にエラーが発生したため、EPUBが出力出来ませんでした"
231
232
  return :error
232
233
  end
233
234
  end
234
235
  end
235
- puts "変換しました"
236
+ stream_io.puts "変換しました"
236
237
  :success
237
238
  end
238
239
 
@@ -242,49 +243,48 @@ class NovelConverter
242
243
  #
243
244
  # 返り値:正常終了 :success、エラー終了 :error、中断終了 :abort
244
245
  #
245
- def self.epub_to_mobi(epub_path, verbose = false)
246
+ def self.epub_to_mobi(epub_path, verbose = false, stream_io: $stdout2)
246
247
  kindlegen_path = Narou.kindlegen_path
247
248
  unless File.exist?(kindlegen_path)
248
- error "kindlegenが見つかりませんでした。AozoraEpub3と同じディレクトリにインストールして下さい"
249
+ stream_io.error "kindlegenが見つかりませんでした。AozoraEpub3と同じフォルダにインストールして下さい"
249
250
  return :error
250
251
  end
251
252
 
252
253
  if Helper.os_cygwin?
253
254
  epub_path = Helper.convert_to_windows_path(epub_path)
254
255
  end
255
- command = %!"#{kindlegen_path}" -locale ja "#{epub_path}"!
256
+ command = +%!"#{kindlegen_path}" -locale ja "#{epub_path}"!
256
257
  if Helper.os_windows?
257
258
  command.encode!(Encoding::Windows_31J)
258
259
  end
259
- print "kindlegen実行中"
260
+ stream_io.print "kindlegen実行中"
260
261
  res = Helper::AsyncCommand.exec(command) do
261
- print "."
262
+ stream_io.print "."
262
263
  end
263
264
  stdout_capture, _, proccess_status = res
264
265
  stdout_capture.force_encoding(Encoding::UTF_8)
265
266
 
266
267
  if verbose
267
- puts
268
- puts "==== kindlegen stdout capture " + "=" * 49
269
- puts stdout_capture.gsub("\n\n", "\n").strip
270
- puts "=" * 79
268
+ stream_io.puts
269
+ stream_io.puts "==== kindlegen stdout capture " + "=" * 49
270
+ stream_io.puts stdout_capture.gsub("\n\n", "\n").strip
271
+ stream_io.puts "=" * 79
271
272
  end
272
273
 
273
274
  if proccess_status.exited?
274
275
  if proccess_status.exitstatus == 2
275
- puts ""
276
- error "kindlegen実行中にエラーが発生したため、MOBIが出力出来ませんでした"
276
+ stream_io.puts
277
+ stream_io.error "kindlegen実行中にエラーが発生したため、MOBIが出力出来ませんでした"
277
278
  if stdout_capture.scan(/(エラー\(.+?\):\w+?:.+)$/)
278
- error $1
279
+ stream_io.error $1
279
280
  end
280
281
  return :error
281
282
  end
282
283
  else
283
- puts ""
284
- error "kindlegenが中断させられたぽいのでMOBIは出力出来ませんでした"
284
+ stream_io.puts
285
285
  return :abort
286
286
  end
287
- puts "変換しました"
287
+ stream_io.puts "変換しました"
288
288
  :success
289
289
  end
290
290
 
@@ -302,7 +302,9 @@ class NovelConverter
302
302
  no_cleanup_txt: false,
303
303
  yokogaki: false,
304
304
  use_dakuten_font: false,
305
+ stream_io: $stdout2
305
306
  }.merge(options)
307
+ stream_io = options[:stream_io]
306
308
 
307
309
  device = options[:device]
308
310
  clean_up_file_list = []
@@ -314,7 +316,8 @@ class NovelConverter
314
316
  txt_path,
315
317
  dst_dir: options[:dst_dir], device: device,
316
318
  verbose: options[:verbose], yokogaki: options[:yokogaki],
317
- use_dakuten_font: options[:use_dakuten_font]
319
+ use_dakuten_font: options[:use_dakuten_font],
320
+ stream_io: stream_io
318
321
  )
319
322
  return nil if status != :success
320
323
  if device && device.kobo?
@@ -325,28 +328,28 @@ class NovelConverter
325
328
  epub_path = txt_path.sub(/\.txt$/, epub_ext)
326
329
 
327
330
  if !device || !device.kindle? || options[:no_mobi]
328
- puts File.basename(epub_path) + " を出力しました"
329
- puts "<bold><green>EPUBファイルを出力しました</green></bold>".termcolor
331
+ stream_io.puts File.basename(epub_path) + " を出力しました"
332
+ stream_io.puts "<bold><green>EPUBファイルを出力しました</green></bold>".termcolor
330
333
  return epub_path
331
334
  end
332
335
 
333
336
  clean_up_file_list << epub_path
334
337
  # mobi
335
- status = NovelConverter.epub_to_mobi(epub_path, options[:verbose])
338
+ status = NovelConverter.epub_to_mobi(epub_path, options[:verbose], stream_io: stream_io)
336
339
  return nil if status != :success
337
340
  mobi_path = epub_path.sub(/\.epub$/, device.ebook_file_ext)
338
341
 
339
342
  # strip
340
343
  unless options[:no_strip]
341
- puts "kindlestrip実行中"
344
+ stream_io.puts "kindlestrip実行中"
342
345
  begin
343
346
  SectionStripper.strip(mobi_path, nil, false)
344
347
  rescue StripException => e
345
- error "#{e.message}"
348
+ stream_io.error e.message
346
349
  end
347
350
  end
348
- puts File.basename(mobi_path).encode(Encoding::UTF_8) + " を出力しました"
349
- puts "<bold><green>MOBIファイルを出力しました</green></bold>".termcolor
351
+ stream_io.puts File.basename(mobi_path).encode(Encoding::UTF_8) + " を出力しました"
352
+ stream_io.puts "<bold><green>MOBIファイルを出力しました</green></bold>".termcolor
350
353
 
351
354
  return mobi_path
352
355
  ensure
@@ -357,12 +360,13 @@ class NovelConverter
357
360
  end
358
361
 
359
362
  def self.clean_up_temp_files(path_list)
363
+ return unless path_list
360
364
  path_list.each do |path|
361
365
  FileUtils.rm_f(path)
362
366
  end
363
367
  end
364
368
 
365
- def initialize(setting, output_filename = nil, display_inspector = false, output_text_dir = nil)
369
+ def initialize(setting, output_filename = nil, display_inspector = false, output_text_dir = nil, stream_io: $stdout2)
366
370
  @setting = setting
367
371
  @novel_id = setting.id
368
372
  @novel_author = setting.novel_author.empty? ? setting.author : setting.novel_author
@@ -376,6 +380,7 @@ class NovelConverter
376
380
  @converter = create_converter
377
381
  @converter.output_text_dir = output_text_dir
378
382
  @data = @novel_id ? Database.instance.get_data("id", @novel_id) : {}
383
+ @stream_io = stream_io
379
384
  end
380
385
 
381
386
  #
@@ -409,7 +414,7 @@ class NovelConverter
409
414
  progressbar = nil
410
415
 
411
416
  on(:"convert_main.init") do |subtitles|
412
- progressbar = ProgressBar.new(subtitles.size)
417
+ progressbar = ProgressBar.new(subtitles.size, io: stream_io)
413
418
  end
414
419
  on(:"convert_main.loop") do |i|
415
420
  progressbar.output(i) if progressbar
@@ -420,21 +425,24 @@ class NovelConverter
420
425
  end
421
426
 
422
427
  def display_header
423
- print "ID:#{@novel_id} " if @novel_id
424
- puts "#{@novel_title} の変換を開始"
428
+ stream_io.print "ID:#{@novel_id} " if @novel_id
429
+ stream_io.puts "#{@novel_title} の変換を開始"
425
430
  end
426
431
 
427
432
  def display_footer
428
- puts "縦書用の変換が終了しました"
433
+ stream_io.puts "縦書用の変換が終了しました"
429
434
  end
430
435
 
431
436
  def load_novel_section(subtitle_info, section_save_dir)
432
437
  file_subtitle = subtitle_info["file_subtitle"] || subtitle_info["subtitle"] # 互換性維持のため
433
- path = File.join(section_save_dir, "#{subtitle_info["index"]} #{file_subtitle}.yaml")
438
+ path = section_save_dir.join("#{subtitle_info["index"]} #{file_subtitle}.yaml")
434
439
  YAML.load_file(path)
435
440
  rescue Errno::ENOENT => e
436
- error "#{path} を見つけることが出来ませんでした。narou update #{@novel_id} を実行することで、" \
437
- "削除されてしまったファイルを再ダウンロードすることが出来ます"
441
+ stream_io.puts
442
+ stream_io.error(<<~MSG.termcolor)
443
+ <yellow>"#{path.basename}"</yellow> を見つけることが出来ませんでした。
444
+ 対象の小説を一度 Update を実行することで、ファイルをダウンロード出来ます。
445
+ MSG
438
446
  exit Narou::EXIT_ERROR_CODE
439
447
  end
440
448
 
@@ -567,7 +575,7 @@ class NovelConverter
567
575
  # 最終的に出力するパスを生成
568
576
  #
569
577
  def create_output_path(is_text_file_mode, converted_text, index)
570
- output_path = ""
578
+ output_path = +""
571
579
  if @output_filename
572
580
  output_path = File.join(@setting.archive_path, File.basename(@output_filename))
573
581
  else
@@ -627,7 +635,7 @@ class NovelConverter
627
635
  subtitles = cut_subtitles(toc["subtitles"])
628
636
  end
629
637
  if is_hotentry == false && @setting.slice_size > 0 && subtitles.length > @setting.slice_size
630
- puts "#{@setting.slice_size}話ごとに分割して変換します"
638
+ stream_io.puts "#{@setting.slice_size}話ごとに分割して変換します"
631
639
  array_of_subtitles = subtitles.each_slice(@setting.slice_size).to_a
632
640
  else
633
641
  array_of_subtitles = [subtitles]
@@ -665,10 +673,10 @@ class NovelConverter
665
673
  when 0
666
674
  result = subtitles
667
675
  when 1...subtitles.size
668
- puts "#{cut_size}話分カットして変換します"
676
+ stream_io.puts "#{cut_size}話分カットして変換します"
669
677
  result = subtitles[cut_size..-1]
670
678
  else
671
- puts "最新話のみ変換します"
679
+ stream_io.puts "最新話のみ変換します"
672
680
  result = [subtitles[-1]]
673
681
  end
674
682
  result
@@ -728,21 +736,21 @@ class NovelConverter
728
736
 
729
737
  if !@display_inspector
730
738
  unless @inspector.empty?
731
- @inspector.display_summary
739
+ @inspector.display_summary(stream_io)
732
740
  end
733
741
  else
734
742
  # 小説の監視・検査状況を表示する
735
743
  if @inspector.error? || @inspector.warning?
736
- puts "<bold><yellow>―――― 小説にエラーもしくは警告が存在します ――――</yellow></bold>".termcolor
737
- puts ""
744
+ stream_io.puts "<bold><yellow>―――― 小説にエラーもしくは警告が存在します ――――</yellow></bold>".termcolor
745
+ stream_io.puts
738
746
  @inspector.display(Inspector::ERROR | Inspector::WARNING)
739
- puts ""
747
+ stream_io.puts
740
748
  end
741
749
  if @inspector.info?
742
- puts "<bold><yellow>―――― 小説の検査状況を表示します ――――</yellow></bold>".termcolor
743
- puts ""
750
+ stream_io.puts "<bold><yellow>―――― 小説の検査状況を表示します ――――</yellow></bold>".termcolor
751
+ stream_io.puts
744
752
  @inspector.display(Inspector::INFO)
745
- puts ""
753
+ stream_io.puts
746
754
  end
747
755
  end
748
756