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.
- checksums.yaml +4 -4
- data/.gitignore +2 -5
- data/.haml-lint.yml +7 -0
- data/.rubocop.yml +23 -5
- data/.scss-lint.yml +9 -0
- data/ChangeLog.md +86 -0
- data/Gemfile.lock +35 -35
- data/README.md +80 -64
- data/lib/backtracer.rb +2 -2
- data/lib/color.rb +5 -1
- data/lib/command.rb +7 -2
- data/lib/command/alias.rb +3 -5
- data/lib/command/backup.rb +3 -5
- data/lib/command/browser.rb +3 -5
- data/lib/command/clean.rb +5 -1
- data/lib/command/console.rb +33 -0
- data/lib/command/convert.rb +143 -117
- data/lib/command/csv.rb +2 -1
- data/lib/command/diff.rb +20 -18
- data/lib/command/download.rb +25 -14
- data/lib/command/folder.rb +3 -5
- data/lib/command/freeze.rb +3 -5
- data/lib/command/help.rb +20 -18
- data/lib/command/init.rb +4 -3
- data/lib/command/inspect.rb +2 -1
- data/lib/command/list.rb +10 -8
- data/lib/command/list/novel_decorator.rb +2 -1
- data/lib/command/log.rb +100 -0
- data/lib/command/log/tail.rb +76 -0
- data/lib/command/mail.rb +20 -17
- data/lib/command/remove.rb +7 -6
- data/lib/command/send.rb +23 -20
- data/lib/command/setting.rb +74 -40
- data/lib/command/tag.rb +16 -15
- data/lib/command/trace.rb +2 -2
- data/lib/command/update.rb +78 -128
- data/lib/command/update/general_lastup_updater.rb +3 -2
- data/lib/command/update/hotentry_manager.rb +2 -1
- data/lib/command/update/interval.rb +2 -1
- data/lib/command/version.rb +2 -1
- data/lib/command/web.rb +17 -3
- data/lib/commandbase.rb +34 -7
- data/lib/commandline.rb +54 -35
- data/lib/converterbase.rb +21 -15
- data/lib/database.rb +3 -2
- data/lib/device.rb +5 -4
- data/lib/device/epub.rb +2 -1
- data/lib/device/ibooks.rb +2 -1
- data/lib/device/ibunko.rb +2 -1
- data/lib/device/kindle.rb +2 -1
- data/lib/device/kobo.rb +2 -1
- data/lib/device/library/cygwin.rb +2 -1
- data/lib/device/library/linux.rb +2 -1
- data/lib/device/library/mac.rb +2 -1
- data/lib/device/library/windows.rb +2 -1
- data/lib/device/library/windows/eject.rb +2 -1
- data/lib/device/reader.rb +2 -1
- data/lib/diffviewer.rb +8 -11
- data/lib/downloader.rb +159 -151
- data/lib/eventable.rb +2 -1
- data/lib/extension.rb +16 -14
- data/lib/extensions/jruby.rb +2 -1
- data/lib/extensions/monkey_patches.rb +7 -0
- data/lib/extensions/monkey_patches/pathname.rb +22 -0
- data/lib/extensions/windows.rb +2 -1
- data/lib/extensions/windows_write_color.rb +2 -1
- data/lib/helper.rb +35 -20
- data/lib/html.rb +2 -1
- data/lib/illustration.rb +2 -1
- data/lib/ini.rb +2 -1
- data/lib/input.rb +2 -1
- data/lib/inspector.rb +3 -2
- data/lib/inventory.rb +3 -3
- data/lib/mailer.rb +3 -2
- data/lib/mixin/all.rb +8 -0
- data/lib/mixin/locker.rb +40 -0
- data/lib/mixin/output_error.rb +28 -0
- data/lib/narou.rb +69 -51
- data/lib/narou/api.rb +2 -4
- data/lib/narou_logger.rb +236 -108
- data/lib/novelconverter.rb +77 -69
- data/lib/novelinfo.rb +4 -2
- data/lib/novelsetting.rb +15 -12
- data/lib/progressbar.rb +13 -9
- data/lib/sitesetting.rb +39 -18
- data/lib/template.rb +5 -4
- data/lib/version.rb +3 -2
- data/lib/web/all.rb +2 -1
- data/lib/web/appserver.rb +83 -65
- data/lib/web/helper4web.rb +10 -5
- data/lib/web/progressbar4web.rb +8 -4
- data/lib/web/public/resources/default-style.css +2 -3
- data/lib/web/public/resources/narou.library.js +86 -60
- data/lib/web/public/resources/narou.queue.js +24 -30
- data/lib/web/public/resources/narou.ui.js +22 -3
- data/lib/web/public/theme/Cerulean/style.css +5 -5
- data/lib/web/public/theme/Darkly/style.css +5 -5
- data/lib/web/public/theme/Readable/style.css +5 -5
- data/lib/web/public/theme/Slate/style.css +2 -3
- data/lib/web/public/theme/Superhero/style.css +2 -3
- data/lib/web/public/theme/United/style.css +5 -5
- data/lib/web/pushserver.rb +10 -7
- data/lib/web/server_helpers.rb +16 -1
- data/lib/web/settingmessages.rb +10 -7
- data/lib/web/streaminginput.rb +2 -1
- data/lib/web/streaminglogger.rb +45 -32
- data/lib/web/views/_about.haml +6 -3
- data/lib/web/views/_header.haml +2 -3
- data/lib/web/views/_move_to_top.haml +2 -0
- data/lib/web/views/_queue.haml +7 -0
- data/lib/web/views/bookmarklet/insert_button.js.erb +1 -1
- data/lib/web/views/index.haml +30 -27
- data/lib/web/views/layout.haml +2 -0
- data/lib/web/views/novels/setting.haml +3 -4
- data/lib/web/views/settings.haml +22 -8
- data/lib/web/views/style.scss +54 -3
- data/lib/web/views/widget/download.haml +9 -3
- data/lib/web/views/widget/drag_and_drop.haml +10 -4
- data/lib/web/web_worker.rb +132 -0
- data/lib/worker.rb +142 -0
- data/narou.gemspec +80 -45
- data/narou.rb +6 -4
- data/template/novel.txt.erb +1 -0
- data/webnovel/kakuyomu.jp.yaml +9 -13
- data/webnovel/ncode.syosetu.com.yaml +3 -1
- data/webnovel/novel18.syosetu.com.yaml +8 -1
- data/webnovel/syosetu.org.yaml +3 -1
- data/webnovel/www.akatsuki-novels.com.yaml +4 -2
- data/webnovel/www.mai-net.net.yaml +3 -1
- metadata +109 -48
- data/lib/web/worker.rb +0 -126
data/lib/narou/api.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
#
|
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|
|
data/lib/narou_logger.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
|
-
#
|
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
|
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
|
-
|
21
|
-
|
27
|
+
def self.included(klass)
|
28
|
+
klass.class_eval do
|
29
|
+
alias_method :original_write, :write
|
30
|
+
end
|
31
|
+
end
|
22
32
|
|
23
|
-
|
24
|
-
|
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
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
36
|
+
def initialize
|
37
|
+
super
|
38
|
+
self.silent = false
|
39
|
+
@capturing = false
|
40
|
+
init_logs
|
41
|
+
end
|
34
42
|
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
40
|
-
|
41
|
-
|
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
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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
|
-
|
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
|
-
|
95
|
-
|
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
|
-
|
103
|
-
|
104
|
-
|
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
|
-
|
107
|
-
|
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
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|
-
|
125
|
-
|
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
|
-
|
129
|
-
|
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
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
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
|
-
|
142
|
-
|
231
|
+
class NullIO < StringIO
|
232
|
+
include LoggerModule
|
233
|
+
|
234
|
+
def write(_str); end
|
143
235
|
end
|
144
|
-
end
|
145
236
|
|
146
|
-
|
147
|
-
|
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
|
-
|
150
|
-
|
151
|
-
|
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
|
-
|
155
|
-
|
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
|
-
|
169
|
-
$
|
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
|
data/lib/novelconverter.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
#
|
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.
|
93
|
-
aozora_dir = File.dirname(Narou.
|
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.
|
109
|
-
aozora_dir = File.dirname(Narou.
|
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.
|
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 "
|
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
|
-
|
204
|
-
|
205
|
-
error "Javaの実行エラーが発生しました。EPUBを作成出来ませんでした\n" \
|
206
|
-
|
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 =
|
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
|
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 =
|
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
|
-
|
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
|
|