narou 2.5.2 → 2.6.0

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.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/ChangeLog.md +54 -0
  4. data/LICENSE.txt +26 -0
  5. data/README.md +70 -84
  6. data/lib/command/alias.rb +7 -0
  7. data/lib/command/convert.rb +40 -61
  8. data/lib/command/download.rb +9 -1
  9. data/lib/command/mail.rb +24 -13
  10. data/lib/command/send.rb +18 -2
  11. data/lib/command/setting.rb +152 -34
  12. data/lib/command/tag.rb +5 -0
  13. data/lib/command/update.rb +137 -4
  14. data/lib/converterbase.rb +12 -6
  15. data/lib/database.rb +1 -1
  16. data/lib/device/ibooks.rb +6 -5
  17. data/lib/downloader.rb +85 -83
  18. data/lib/helper.rb +3 -2
  19. data/lib/html.rb +3 -0
  20. data/lib/logger.rb +20 -12
  21. data/lib/narou.rb +16 -0
  22. data/lib/novelconverter.rb +209 -75
  23. data/lib/novelinfo.rb +1 -1
  24. data/lib/novelsetting.rb +53 -29
  25. data/lib/template.rb +4 -3
  26. data/lib/version.rb +1 -1
  27. data/lib/web/appserver.rb +46 -32
  28. data/lib/web/public/resources/common.ui.js +2 -4
  29. data/lib/web/public/resources/default-style.css +37 -0
  30. data/lib/web/public/resources/narou.library.js +48 -57
  31. data/lib/web/public/resources/narou.ui.js +71 -39
  32. data/lib/web/public/theme/Cerulean/bootstrap.min.css +11 -0
  33. data/lib/web/public/theme/Cerulean/style.css +49 -0
  34. data/lib/web/public/theme/Darkly/bootstrap.min.css +11 -0
  35. data/lib/web/public/theme/Darkly/style.css +53 -0
  36. data/lib/web/public/theme/Readable/bootstrap.min.css +11 -0
  37. data/lib/web/public/theme/Readable/style.css +53 -0
  38. data/lib/web/public/theme/Slate/bootstrap.min.css +11 -0
  39. data/lib/web/public/theme/Slate/style.css +40 -0
  40. data/lib/web/public/theme/Superhero/bootstrap.min.css +11 -0
  41. data/lib/web/public/theme/Superhero/style.css +40 -0
  42. data/lib/web/public/theme/United/bootstrap.min.css +11 -0
  43. data/lib/web/public/theme/United/style.css +47 -0
  44. data/lib/web/public/theme/fonts/glyphicons-halflings-regular.eot +0 -0
  45. data/lib/web/public/theme/fonts/glyphicons-halflings-regular.svg +288 -0
  46. data/lib/web/public/theme/fonts/glyphicons-halflings-regular.ttf +0 -0
  47. data/lib/web/public/theme/fonts/glyphicons-halflings-regular.woff +0 -0
  48. data/lib/web/public/theme/fonts/glyphicons-halflings-regular.woff2 +0 -0
  49. data/lib/web/settingmessages.rb +5 -0
  50. data/lib/web/views/index.haml +15 -19
  51. data/lib/web/views/layout.haml +6 -1
  52. data/lib/web/views/novels/setting.haml +76 -75
  53. data/lib/web/views/settings.haml +111 -107
  54. data/lib/web/views/style.scss +6 -21
  55. data/narou.gemspec +49 -6
  56. data/narou.rb +3 -1
  57. data/preset/ncode.syosetu.com/n7975cr/setting.ini +7 -0
  58. data/spec/README.txt +4 -0
  59. data/spec/data/convert_test/ruby/correct_test_ruby.txt +4 -4
  60. data/template/hotentry.txt.erb +28 -0
  61. data/template/ibunko_novel.txt.erb +4 -2
  62. data/template/novel.txt.erb +6 -3
  63. data/template/setting.ini.erb +18 -2
  64. metadata +73 -8
data/lib/command/tag.rb CHANGED
@@ -12,6 +12,8 @@ module Command
12
12
  COLORS = %w(green yellow blue magenta cyan red white)
13
13
  # 禁止文字
14
14
  BAN_CHAR = /[:;"'><$@&^\\\|%'\/`]/
15
+ # 禁止ワード
16
+ BAN_WORD = %w(hotentry)
15
17
 
16
18
  def self.oneline_help
17
19
  "各小説にタグを設定及び閲覧が出来ます"
@@ -57,6 +59,9 @@ module Command
57
59
  if tag =~ BAN_CHAR
58
60
  error "#{tag} に使用禁止記号が含まれています"
59
61
  exit Narou::EXIT_ERROR_CODE
62
+ elsif BAN_WORD.include?(tag)
63
+ error "#{tag} は使用禁止ワードです"
64
+ exit Narou::EXIT_ERROR_CODE
60
65
  end
61
66
  end
62
67
  }
@@ -3,9 +3,12 @@
3
3
  # Copyright 2013 whiteleaf. All rights reserved.
4
4
  #
5
5
 
6
+ require "fileutils"
6
7
  require "memoist"
7
8
  require_relative "../database"
8
9
  require_relative "../downloader"
10
+ require_relative "../template"
11
+ require_relative "../novelconverter"
9
12
 
10
13
  module Command
11
14
  class Update < CommandBase
@@ -15,6 +18,11 @@ module Command
15
18
  LOG_NUM_LIMIT = 30 # ログの保存する上限数
16
19
  LOG_FILENAME_FORMAT = "update_log_%s.txt"
17
20
 
21
+ HOTENTRY_DIR_NAME = "hotentry"
22
+ HOTENTRY_TEMPLATE_NAME = "hotentry.txt"
23
+ HOTENTRY_TITLE_PATTERN = "hotentry %y/%m/%d %H:%M"
24
+ HOTENTRY_FILE_PATTERN = "hotentry_%y-%m-%d_%H%M.txt"
25
+
18
26
  def self.oneline_help
19
27
  "小説を更新します"
20
28
  end
@@ -56,6 +64,9 @@ module Command
56
64
  update_general_lastup
57
65
  exit 0
58
66
  }
67
+ @opt.on("-f", "--force", "凍結済みも更新する") {
68
+ @options["force"] = true
69
+ }
59
70
  @opt.on("-s", "--sort-by KEY", "アップデートする順番を変更する\n#{Narou.update_sort_key_summaries}") { |key|
60
71
  @options["sort-by"] = key
61
72
  }
@@ -123,13 +134,18 @@ module Command
123
134
  end
124
135
  flush_cache # memoist のキャッシュ削除
125
136
 
137
+ inv = Inventory.load("local_setting", :local)
138
+ @options["hotentry"] = inv["hotentry"]
139
+ @options["hotentry.auto-mail"] = inv["hotentry.auto-mail"]
140
+ hotentry = {}
141
+
126
142
  update_log = $stdout.capture(quiet: false) do
127
143
  sort_by_key(sort_key, update_target_list).each_with_index do |target, i|
128
144
  display_message = nil
129
145
  data = Downloader.get_data_by_target(target)
130
146
  if !data
131
147
  display_message = "<bold><red>[ERROR]</red></bold> #{target} は管理小説の中に存在しません".termcolor
132
- elsif Narou.novel_frozen?(target)
148
+ elsif Narou.novel_frozen?(target) && !@options["force"]
133
149
  if argv.length > 0
134
150
  display_message = "ID:#{data["id"]} #{data["title"]} は凍結中です"
135
151
  else
@@ -142,7 +158,16 @@ module Command
142
158
  mistook_count += 1
143
159
  next
144
160
  end
145
- result = Downloader.start(target)
161
+ downloader = Downloader.new(target)
162
+
163
+ if @options["hotentry"]
164
+ downloader.on(:newarrival) do |hash|
165
+ entry = hotentry[hash[:id]] ||= []
166
+ entry << hash[:subtitle_info]
167
+ end
168
+ end
169
+
170
+ result = downloader.start_download
146
171
  case result.status
147
172
  when :ok
148
173
  unless @options["no-convert"] ||
@@ -162,7 +187,10 @@ module Command
162
187
  end
163
188
  end
164
189
  end
190
+
191
+ process_hotentry(hotentry)
165
192
  save_log(update_log)
193
+
166
194
  exit mistook_count if mistook_count > 0
167
195
  rescue Interrupt
168
196
  puts "アップデートを中断しました"
@@ -253,8 +281,8 @@ module Command
253
281
 
254
282
  # オンラインの目次からgeneral_lastupを取得する
255
283
  # ただし、toc.yaml に最新話が存在し、かつsubdateが設定されていたらそれを使う
256
- def get_latest_dates(setting)
257
- downloader = Downloader.new(setting)
284
+ def get_latest_dates(target)
285
+ downloader = Downloader.new(target)
258
286
  old_toc = downloader.load_toc_file
259
287
  latest_toc = downloader.get_latest_table_of_contents(old_toc, through_error: true)
260
288
  {
@@ -262,5 +290,110 @@ module Command
262
290
  "general_lastup" => downloader.get_general_lastup
263
291
  }
264
292
  end
293
+
294
+ #
295
+ # 新着話をまとめたデータの作成に関する処理
296
+ #
297
+ def process_hotentry(hotentry)
298
+ return if hotentry.empty?
299
+ cmd_convert = Command::Convert.new
300
+ cmd_convert.load_local_settings
301
+ cmd_convert.device = Narou.get_device
302
+
303
+ ebook_path = convert_hotentry(hotentry, cmd_convert)
304
+ copy_hotentry(ebook_path, cmd_convert)
305
+ send_hotentry(ebook_path, cmd_convert)
306
+ mail_hotentry
307
+ end
308
+
309
+ def convert_hotentry(hotentry, cmd_convert)
310
+ output_filename = nil
311
+ display_inspector = false
312
+ ignore_force = false
313
+ ignore_default = false
314
+
315
+ converted_text_array = []
316
+ use_dakuten_font = false
317
+
318
+ Helper.print_horizontal_rule
319
+ puts "hotentry の変換を開始"
320
+
321
+ subtitles_size = hotentry.inject(0) { |sum, (_, subtitles)| subtitles.size + sum }
322
+ progressbar = ProgressBar.new(subtitles_size)
323
+ total_progress = 0
324
+
325
+ hotentry.each do |id, subtitles|
326
+ setting = NovelSetting.load(id, ignore_force, ignore_default)
327
+ novel_converter = NovelConverter.new(setting, output_filename, display_inspector)
328
+ last_num = 0
329
+ novel_converter.on(:"convert_main.loop") do |i|
330
+ progressbar.output(total_progress + i)
331
+ last_num = i
332
+ end
333
+ converted_text_array << {
334
+ setting: setting,
335
+ text: novel_converter.convert_main_for_novel(subtitles, true)
336
+ }
337
+ use_dakuten_font |= novel_converter.use_dakuten_font
338
+
339
+ total_progress += last_num + 1
340
+ end
341
+ progressbar.clear
342
+ puts "縦書用の変換が終了しました"
343
+
344
+ device = Narou.get_device
345
+ now = Time.now
346
+ # テキストの生成
347
+ hotentry_title = now.strftime(HOTENTRY_TITLE_PATTERN)
348
+ hotentry_text = Template.get(HOTENTRY_TEMPLATE_NAME, binding, 1.0)
349
+ # 生成したテキストファイルの保存
350
+ txt_output_path = File.join(Update.hotentry_dirname, now.strftime(HOTENTRY_FILE_PATTERN))
351
+ create_inclusive_directory(txt_output_path)
352
+ File.write(txt_output_path, hotentry_text)
353
+ # テキストを書籍データに変換
354
+ relay_proc = -> {
355
+ NovelConverter.convert_txt_to_ebook_file(txt_output_path, {
356
+ use_dakuten_font: use_dakuten_font,
357
+ device: device
358
+ })
359
+ }
360
+ if device
361
+ cmd_convert.extend(device.get_hook_module)
362
+ cmd_convert.converted_txt_path = txt_output_path
363
+ cmd_convert.hook_call(:change_settings)
364
+ end
365
+ if cmd_convert.respond_to?(:hook_convert_txt_to_ebook_file)
366
+ ebook_path = cmd_convert.hook_convert_txt_to_ebook_file(&relay_proc)
367
+ else
368
+ ebook_path = relay_proc.call
369
+ end
370
+ ebook_path
371
+ end
372
+
373
+ def create_inclusive_directory(path)
374
+ FileUtils.mkdir_p(File.dirname(path))
375
+ end
376
+
377
+ def copy_hotentry(ebook_path, cmd_convert)
378
+ cmd_convert.copy_to_converted_file(ebook_path)
379
+ end
380
+
381
+ def send_hotentry(ebook_path, cmd_convert)
382
+ cmd_convert.send_file_to_device(ebook_path)
383
+ end
384
+
385
+ def mail_hotentry
386
+ return unless @options["hotentry.auto-mail"]
387
+ Mail.execute!(["hotentry"])
388
+ end
389
+
390
+ def self.hotentry_dirname
391
+ @@__hotentry_dirname ||= File.join(Narou.get_root_dir, HOTENTRY_DIR_NAME)
392
+ end
393
+
394
+ def self.get_newest_hotentry_file_path(device)
395
+ pattern = File.join(Update.hotentry_dirname, "hotentry_*#{device.ebook_file_ext}")
396
+ Dir.glob(pattern).sort.last
397
+ end
265
398
  end
266
399
  end
data/lib/converterbase.rb CHANGED
@@ -284,7 +284,7 @@ class ConverterBase
284
284
  data.gsub!(/([!?!?]+)([^!?!?])/) do
285
285
  m1, m2 = $1, $2
286
286
  m2 = " " if m2 == " "
287
- if m2 =~ /[^」]\]』】〉》〕>>≫))"”’〟 ☆★♪[―]/
287
+ if m2 =~ /[^」]}\]\}』】〉》〕>>≫))"”’〟 ☆★♪[―]/
288
288
  "#{m1} #{m2}"
289
289
  else
290
290
  "#{m1}#{m2}"
@@ -344,7 +344,7 @@ class ConverterBase
344
344
  data.gsub!(/[#{SINGLE_MINUTE_FAMILY}]([^"\n]+?)[#{SINGLE_MINUTE_FAMILY}]/, "〝\\1〟")
345
345
  # MEMO: シングルミュートを表示出来るフォントはほとんど無いためダブルにする
346
346
  data.gsub!(/[#{DOUBLE_MINUTE_FAMILY}]([^"\n]+?)[#{DOUBLE_MINUTE_FAMILY}]/, "〝\\1〟")
347
- data.tr!("-=+/*《》'\"%$#&!?<><>()|‐,._;:[]",
347
+ data.tr!("-=+/*《》'\"%$#&!?<><>()|‐,._;:\[\]{}",
348
348
  "-=+/*≪≫’”%$#&!?〈〉〈〉()|-,._;:[]")
349
349
  data.gsub!("\\", "¥")
350
350
  data
@@ -798,7 +798,7 @@ class ConverterBase
798
798
  # かぎ括弧内自動連結
799
799
  #
800
800
  def auto_join_in_brackets(data)
801
- if !@setting.enable_auto_join_in_brackets && !@setting.enable_inspect_invalid_openclose_brackets
801
+ if !@setting.enable_auto_join_in_brackets && !@setting.enable_inspect
802
802
  return
803
803
  end
804
804
  OPENCLOSE_REGEXPS.each_with_index do |openclose, i|
@@ -813,7 +813,7 @@ class ConverterBase
813
813
  end
814
814
  "[#かぎ括弧=#{j}]"
815
815
  end
816
- if @setting.enable_inspect_invalid_openclose_brackets
816
+ if @setting.enable_inspect
817
817
  # 正しく閉じてないかぎ括弧だけが data に残ってる
818
818
  @inspector.inspect_invalid_openclose_brackets(data, BRACKETS[i], stack)
819
819
  end
@@ -836,7 +836,7 @@ class ConverterBase
836
836
  end
837
837
 
838
838
  CHARACTER_OF_RUBY = "一-龠A-Za-zA-Za-z"
839
- AUTO_RUBY_CHARACTERS = "([ぁ-んァ-ヶーゝゞ・A-Za-zA-Za-z  ]{,20})"
839
+ AUTO_RUBY_CHARACTERS = "([ぁ-んァ-ヶーゝゞ・  ]{,20})"
840
840
 
841
841
  #
842
842
  # 小説家になろうのルビ対策
@@ -1225,7 +1225,13 @@ class ConverterBase
1225
1225
  symbol = false
1226
1226
  case char
1227
1227
  when "|"
1228
- ss.scan(/.+?》/)
1228
+ buffer << char
1229
+ if ss.scan(/.+?》/)
1230
+ buffer << "#{ss.matched}"
1231
+ else
1232
+ before_symbol = false
1233
+ end
1234
+ next
1229
1235
  when "["
1230
1236
  buffer << char
1231
1237
  if ss.scan(/^#.+?]/)
data/lib/database.rb CHANGED
@@ -100,7 +100,7 @@ class Database
100
100
  nil
101
101
  end
102
102
 
103
- def get_new_id
103
+ def create_new_id
104
104
  max_id = @database.keys.max
105
105
  id = max_id ? max_id + 1 : 0
106
106
  id
data/lib/device/ibooks.rb CHANGED
@@ -36,19 +36,20 @@ module Device::Ibooks
36
36
  ebook_file_path = original_func.call
37
37
  return ebook_file_path unless ebook_file_path
38
38
  return ebook_file_path unless @@__ibooks_container_dir
39
+ epubdir_path = nil
39
40
  if @argument_target_type == :file
40
- warn "テキストファイル変換時はiBooksへの登録は行われません"
41
- return ebook_file_path
41
+ @toc_url = nil
42
+ else
43
+ @toc_url = @novel_data["toc_url"]
44
+ epubdir_path = get_epubdir_path_in_ibooks_container
42
45
  end
43
- @toc_url = @novel_data["toc_url"]
44
- epubdir_path = get_epubdir_path_in_ibooks_container
45
46
  if epubdir_path && File.exist?(epubdir_path)
46
47
  extract_epub(ebook_file_path, epubdir_path)
47
48
  puts "iBooksに登録してあるEPUBを更新しました"
48
49
  else
49
50
  epubdir_path = watch_ibooks_container(ebook_file_path)
50
51
  if epubdir_path
51
- regist_epubdir_path_to_setting(epubdir_path)
52
+ regist_epubdir_path_to_setting(epubdir_path) if @toc_url
52
53
  puts "iBooksへの登録を確認しました"
53
54
  else
54
55
  error "EPUBの展開後のフォルダが見つかりませんでした。" \
data/lib/downloader.rb CHANGED
@@ -12,6 +12,7 @@ require_relative "sitesetting"
12
12
  require_relative "template"
13
13
  require_relative "database"
14
14
  require_relative "inventory"
15
+ require_relative "eventable"
15
16
  require_relative "narou/api"
16
17
  require_relative "html"
17
18
  require_relative "input"
@@ -20,6 +21,8 @@ require_relative "input"
20
21
  # 小説サイトからのダウンロード
21
22
  #
22
23
  class Downloader
24
+ include Narou::Eventable
25
+
23
26
  NOVEL_SITE_SETTING_DIR = "webnovel/"
24
27
  SECTION_SAVE_DIR_NAME = "本文" # 本文を保存するディレクトリ名
25
28
  CACHE_SAVE_DIR_NAME = "cache" # 差分用キャッシュ保存用ディレクトリ名
@@ -34,44 +37,28 @@ class Downloader
34
37
 
35
38
  attr_reader :id
36
39
 
37
- #
38
- # ターゲット(ID、URL、Nコード、小説名)を指定して小説データのダウンロードを開始する
39
- #
40
- # force が true なら全話強制ダウンロード
41
- #
42
- def self.start(target, force = false, from_download = false)
43
- setting = nil
44
- target = Narou.alias_to_id(target)
45
- fail_status = OpenStruct.new(:id => nil, :new_arrivals => nil,
46
- :status => false).freeze
47
- case type = get_target_type(target)
48
- when :url, :ncode
49
- setting = get_sitesetting_by_target(target)
50
- unless setting
51
- error "対応外の#{type}です(#{target})"
52
- return fail_status
53
- end
54
- when :id
55
- data = @@database[target.to_i]
56
- unless data
57
- error "指定のID(#{target})は存在しません"
58
- return fail_status
59
- end
60
- setting = get_sitesetting_by_sitename(data["sitename"])
61
- setting.multi_match(data["toc_url"], "url")
62
- when :other
63
- data = @@database.get_data("title", target)
64
- if data
65
- setting = get_sitesetting_by_sitename(data["sitename"])
66
- setting.multi_match(data["toc_url"], "url")
67
- else
68
- error "指定の小説(#{target})は存在しません"
69
- return fail_status
40
+ class InvalidTarget < StandardError; end
41
+
42
+ def initialize(target, options = {})
43
+ id = Downloader.get_id_by_target(target)
44
+ options = {
45
+ force: false, from_download: false,
46
+ stream: $stdout
47
+ }.merge(options)
48
+ setting = Downloader.get_sitesetting_by_target(target)
49
+
50
+ unless setting
51
+ case type = Downloader.get_target_type(target)
52
+ when :url, :ncode
53
+ raise InvalidTarget, "対応外の#{type}です(#{target})"
54
+ when :id
55
+ raise InvalidTarget, "指定のID(#{target})は存在しません"
56
+ when :other
57
+ raise InvalidTarget, "指定の小説(#{target})は存在しません"
70
58
  end
71
59
  end
72
- downloader = Downloader.new(setting, force, from_download)
73
- result = downloader.start_download
74
- result
60
+
61
+ initialize_variables(id, setting, options)
75
62
  end
76
63
 
77
64
  #
@@ -139,8 +126,8 @@ class Downloader
139
126
  else
140
127
  @@database.delete(id)
141
128
  @@database.save_database
142
- error "#{path} が見つかりません。"
143
- warn "保存フォルダが消去されていたため、データベースのインデックスを削除しました。"
129
+ error "#{path} が見つかりません。\n" \
130
+ "保存フォルダが消去されていたため、データベースのインデックスを削除しました。"
144
131
  return nil
145
132
  end
146
133
  end
@@ -286,37 +273,43 @@ class Downloader
286
273
  end
287
274
 
288
275
  #
289
- # コンストラクタ
276
+ # 変数初期化
290
277
  #
291
- def initialize(site_setting, force = false, from_download = false)
278
+ def initialize_variables(id, setting, options)
292
279
  @title = nil
293
280
  @file_title = nil
294
- @setting = site_setting
295
- @force = force
296
- @from_download = from_download
281
+ @setting = setting
282
+ @force = options[:force]
283
+ @from_download = options[:from_download]
284
+ @stream = options[:stream]
297
285
  @cache_dir = nil
298
286
  @new_arrivals = false
299
287
  @novel_data_dir = nil
300
288
  @novel_status = nil
301
- @id = @@database.get_id("toc_url", @setting["toc_url"]) || @@database.get_new_id
289
+ @id = id || @@database.create_new_id
302
290
  @new_novel = @@database[@id].!
303
291
  @section_download_cache = {}
292
+ @download_wait_steps = Inventory.load("local_setting", :local)["download.wait-steps"] || 0
293
+ @download_use_subdirectory = use_subdirectory?
294
+ if @setting["is_narou"] && (@download_wait_steps > 10 || @download_wait_steps == 0)
295
+ @download_wait_steps = 10
296
+ end
297
+ initialize_wait_counter
298
+ end
304
299
 
305
- # ウェイト管理関係初期化
300
+ #
301
+ # ウェイト関係初期化
302
+ #
303
+ def initialize_wait_counter
306
304
  @@__run_once ||= false
307
305
  unless @@__run_once
308
306
  @@__run_once = true
309
307
  @@__wait_counter = 0
310
308
  @@__last_download_time = Time.now - 20
311
- @@interval_sleep_time = Inventory.load("local_setting", :local)["download.interval"] || 0
312
- @@interval_sleep_time = 0 if @@interval_sleep_time < 0
313
- @@max_steps_wait_time = [STEPS_WAIT_TIME, @@interval_sleep_time].max
314
- end
315
- @download_wait_steps = Inventory.load("local_setting", :local)["download.wait-steps"] || 0
316
- @download_use_subdirectory = use_subdirectory?
317
- if @setting["is_narou"] && (@download_wait_steps > 10 || @download_wait_steps == 0)
318
- @download_wait_steps = 10
319
309
  end
310
+ @@interval_sleep_time = Inventory.load("local_setting", :local)["download.interval"] || 0
311
+ @@interval_sleep_time = 0 if @@interval_sleep_time < 0
312
+ @@max_steps_wait_time = [STEPS_WAIT_TIME, @@interval_sleep_time].max
320
313
  end
321
314
 
322
315
  #
@@ -372,12 +365,12 @@ class Downloader
372
365
  old_toc = @new_novel ? nil : load_toc_file
373
366
  latest_toc = get_latest_table_of_contents(old_toc)
374
367
  unless latest_toc
375
- error @setting["toc_url"] + " の目次データが取得出来ませんでした"
368
+ @stream.error @setting["toc_url"] + " の目次データが取得出来ませんでした"
376
369
  return :failed
377
370
  end
378
371
  if @setting["confirm_over18"]
379
372
  unless confirm_over18?
380
- puts "18歳以上のみ閲覧出来る小説です。ダウンロードを中止しました"
373
+ @stream.puts "18歳以上のみ閲覧出来る小説です。ダウンロードを中止しました"
381
374
  return :canceled
382
375
  end
383
376
  end
@@ -394,7 +387,7 @@ class Downloader
394
387
  end
395
388
 
396
389
  if old_toc.empty? && update_subtitles.count == 0
397
- error "#{@setting['title']} の目次がありません"
390
+ @stream.error "#{@setting['title']} の目次がありません"
398
391
  return :failed
399
392
  end
400
393
 
@@ -417,23 +410,23 @@ class Downloader
417
410
  end
418
411
  rescue Interrupt
419
412
  remove_cache_dir
420
- puts "ダウンロードを中断しました"
413
+ @stream.puts "ダウンロードを中断しました"
421
414
  exit Narou::EXIT_ERROR_CODE
422
415
  end
423
416
  update_database
424
417
  :ok
425
418
  when old_toc["subtitles"].size > latest_toc["subtitles"].size
426
419
  # 削除された節がある(かつ更新がない)場合
427
- puts "#{id_and_title} は一部の話が削除されています"
420
+ @stream.puts "#{id_and_title} は一部の話が削除されています"
428
421
  :ok
429
422
  when old_toc["title"] != latest_toc["title"]
430
423
  # タイトルが更新されている場合
431
- puts "#{id_and_title} のタイトルが更新されています"
424
+ @stream.puts "#{id_and_title} のタイトルが更新されています"
432
425
  update_database
433
426
  :ok
434
427
  when old_toc["story"] != latest_toc["story"]
435
428
  # あらすじが更新されている場合
436
- puts "#{id_and_title} のあらすじが更新されています"
429
+ @stream.puts "#{id_and_title} のあらすじが更新されています"
437
430
  :ok
438
431
  else
439
432
  :none
@@ -447,7 +440,7 @@ class Downloader
447
440
  Command::Tag.execute!([@id, "--add", "end", "--color", "white"])
448
441
  end
449
442
  msg = old_toc.empty? ? "完結しているようです" : "完結したようです"
450
- puts "<cyan>#{id_and_title.escape} は#{msg}</cyan>".termcolor
443
+ @stream.puts "<cyan>#{id_and_title.escape} は#{msg}</cyan>".termcolor
451
444
  return_status = :ok
452
445
  end
453
446
  else
@@ -456,7 +449,7 @@ class Downloader
456
449
  $stdout.silence do
457
450
  Command::Tag.execute!([@id, "--delete", "end"])
458
451
  end
459
- puts "<cyan>#{id_and_title.escape} は連載を再開したようです</cyan>".termcolor
452
+ @stream.puts "<cyan>#{id_and_title.escape} は連載を再開したようです</cyan>".termcolor
460
453
  return_status = :ok
461
454
  end
462
455
  end
@@ -708,7 +701,7 @@ class Downloader
708
701
  @setting = s
709
702
  toc_url = @setting["toc_url"]
710
703
  end
711
- toc_source = Helper.pretreatment_source(toc_fp.read, @setting["encoding"])
704
+ toc_source = Helper.restor_entity(Helper.pretreatment_source(toc_fp.read, @setting["encoding"]))
712
705
  raise DownloaderNotFoundError if Downloader.detect_error_message(@setting, toc_source)
713
706
  end
714
707
  toc_source
@@ -762,7 +755,7 @@ class Downloader
762
755
  rescue OpenURI::HTTPError, Errno::ECONNRESET => e
763
756
  raise if through_error # エラー処理はしなくていいからそのまま例外を受け取りたい時用
764
757
  if e.message.include?("404")
765
- error "小説が削除されているか非公開な可能性があります"
758
+ @stream.error "小説が削除されているか非公開な可能性があります"
766
759
  if @@database.novel_exists?(@id)
767
760
  $stdout.silence do
768
761
  Command::Tag.execute!(%W(#{@id} --add 404 --color white))
@@ -770,7 +763,7 @@ class Downloader
770
763
  Command::Freeze.execute!([@id])
771
764
  end
772
765
  else
773
- error "何らかの理由により目次が取得できませんでした(#{e.message})"
766
+ @stream.error "何らかの理由により目次が取得できませんでした(#{e.message})"
774
767
  end
775
768
  false
776
769
  end
@@ -930,7 +923,7 @@ class Downloader
930
923
  def sections_download_and_save(subtitles)
931
924
  max = subtitles.size
932
925
  return if max == 0
933
- puts "<bold><green>#{"ID:#{@id} #{get_title}".escape} のDL開始</green></bold>".termcolor
926
+ @stream.puts "<bold><green>#{"ID:#{@id} #{get_title}".escape} のDL開始</green></bold>".termcolor
934
927
  save_least_one = false
935
928
  subtitles.each_with_index do |subtitle_info, i|
936
929
  index, subtitle, file_subtitle, chapter = %w(index subtitle file_subtitle chapter).map { |k|
@@ -940,24 +933,25 @@ class Downloader
940
933
  info["element"] = a_section_download(subtitle_info)
941
934
 
942
935
  unless chapter.empty?
943
- puts "#{chapter}"
936
+ @stream.puts "#{chapter}"
944
937
  end
945
938
  if get_novel_type == NOVEL_TYPE_SERIES
946
939
  if index.to_s.length <= DISPLAY_LIMIT_DIGITS
947
940
  # indexの数字がでかいと見た目がみっともないので特定の桁以内だけ表示する
948
- print "第#{index}部分 "
941
+ @stream.print "第#{index}部分 "
949
942
  end
950
943
  else
951
- print "短編 "
944
+ @stream.print "短編 "
952
945
  end
953
- print "#{subtitle} (#{i+1}/#{max})"
946
+ @stream.print "#{subtitle} (#{i+1}/#{max})"
954
947
 
955
948
  section_file_name = "#{index} #{file_subtitle}.yaml"
956
949
  section_file_relative_path = File.join(SECTION_SAVE_DIR_NAME, section_file_name)
957
- if File.exist?(File.join(get_novel_data_dir, section_file_relative_path))
950
+ section_file_full_path = File.join(get_novel_data_dir, section_file_relative_path)
951
+ if File.exist?(section_file_full_path)
958
952
  if @force
959
953
  if different_section?(section_file_relative_path, info)
960
- print " (更新あり)"
954
+ @stream.print " (更新あり)"
961
955
  move_to_cache_dir(section_file_relative_path)
962
956
  end
963
957
  else
@@ -965,13 +959,17 @@ class Downloader
965
959
  end
966
960
  else
967
961
  if !@from_download || (@from_download && @force)
968
- print " <bold><magenta>(新着)</magenta></bold>".termcolor
962
+ @stream.print " <bold><magenta>(新着)</magenta></bold>".termcolor
963
+ trigger(:newarrival, {
964
+ id: @id,
965
+ subtitle_info: subtitle_info
966
+ })
969
967
  end
970
968
  @new_arrivals = true
971
969
  end
972
970
  save_novel_data(section_file_relative_path, info)
973
971
  save_least_one = true
974
- puts
972
+ @stream.puts
975
973
  end
976
974
  remove_cache_dir unless save_least_one
977
975
  end
@@ -1033,6 +1031,7 @@ class Downloader
1033
1031
  end
1034
1032
  raw = download_raw_data(subtitle_url)
1035
1033
  if @setting["is_narou"]
1034
+ raw = Helper.restor_entity(raw)
1036
1035
  save_raw_data(raw, subtitle_info)
1037
1036
  element = extract_elements_in_section(raw, subtitle_info["subtitle"])
1038
1037
  element["data_type"] = "text"
@@ -1055,24 +1054,25 @@ class Downloader
1055
1054
  def download_raw_data(url)
1056
1055
  raw = nil
1057
1056
  retry_count = RETRY_MAX_FOR_503
1057
+ cookie = @setting["cookie"] || ""
1058
1058
  begin
1059
- open(url) do |fp|
1059
+ open(url, "Cookie" => cookie) do |fp|
1060
1060
  raw = Helper.pretreatment_source(fp.read, @setting["encoding"])
1061
1061
  end
1062
1062
  rescue OpenURI::HTTPError => e
1063
1063
  if e.message =~ /^503/
1064
1064
  if retry_count == 0
1065
- error "上限までリトライしましたがファイルがダウンロード出来ませんでした"
1065
+ @stream.error "上限までリトライしましたがファイルがダウンロード出来ませんでした"
1066
1066
  exit Narou::EXIT_ERROR_CODE
1067
1067
  end
1068
1068
  retry_count -= 1
1069
- puts
1070
- warn "server message: #{e.message}"
1071
- warn "リトライ待機中……"
1069
+ @stream.puts
1070
+ @stream.warn "server message: #{e.message}"
1071
+ @stream.warn "リトライ待機中……"
1072
1072
  @@display_hint_once ||= false
1073
1073
  unless @@display_hint_once
1074
- warn "ヒント: narou s download.wait-steps=10 とすることで、" \
1075
- "10話ごとにウェイトをいれられます"
1074
+ @stream.warn "ヒント: narou s download.wait-steps=10 とすることで、" \
1075
+ "10話ごとにウェイトをいれられます"
1076
1076
  @@display_hint_once = true
1077
1077
  end
1078
1078
  sleep(WAITING_TIME_FOR_503)
@@ -1193,11 +1193,13 @@ class Downloader
1193
1193
  novel_dir_path = get_novel_data_dir
1194
1194
  file_title = File.basename(novel_dir_path)
1195
1195
  FileUtils.mkdir_p(novel_dir_path) unless File.exist?(novel_dir_path)
1196
- original_settings = NovelSetting::ORIGINAL_SETTINGS
1196
+ original_settings = NovelSetting.get_original_settings
1197
+ default_settings = NovelSetting.load_default_settings
1198
+ novel_setting = NovelSetting.new(@id, true, true)
1197
1199
  special_preset_dir = File.join(Narou.get_preset_dir, @setting["domain"], @setting["ncode"])
1198
1200
  exists_special_preset_dir = File.exist?(special_preset_dir)
1199
1201
  templates = [
1200
- [NovelSetting::INI_NAME, 1.1],
1202
+ [NovelSetting::INI_NAME, NovelSetting::INI_ERB_BINARY_VERSION],
1201
1203
  ["converter.rb", 1.0],
1202
1204
  [NovelSetting::REPLACE_NAME, 1.0]
1203
1205
  ]