narou 2.8.3.1 → 2.9.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/ChangeLog.md +77 -0
- data/Gemfile +2 -1
- data/README.md +69 -86
- data/lib/command/convert.rb +10 -11
- data/lib/command/init.rb +2 -2
- data/lib/command/list.rb +12 -3
- data/lib/command/setting.rb +20 -5
- data/lib/command/update.rb +0 -3
- data/lib/command/version.rb +2 -3
- data/lib/command/web.rb +58 -7
- data/lib/converterbase.rb +7 -24
- data/lib/database.rb +7 -6
- data/lib/device.rb +15 -1
- data/lib/device/ibunko.rb +1 -2
- data/lib/device/library/mac.rb +7 -0
- data/lib/downloader.rb +44 -21
- data/lib/extension.rb +25 -0
- data/lib/helper.rb +4 -3
- data/lib/html.rb +18 -2
- data/lib/narou.rb +20 -0
- data/lib/novelconverter.rb +11 -35
- data/lib/novelinfo.rb +19 -8
- data/lib/novelsetting.rb +0 -6
- data/lib/version.rb +1 -1
- data/lib/web/appserver.rb +134 -15
- data/lib/web/public/favicon.ico +0 -0
- data/lib/web/public/resources/common.ui.js +2 -2
- data/lib/web/public/resources/narou.library.js +657 -91
- data/lib/web/public/resources/narou.queue.js +1 -1
- data/lib/web/public/resources/narou.ui.js +253 -102
- data/lib/web/public/resources/sprintf.js +245 -0
- data/lib/web/pushserver.rb +14 -3
- data/lib/web/views/_about.haml +188 -0
- data/lib/web/views/{diff_list.haml → _diff_list.haml} +0 -0
- data/lib/web/views/{edit_replace_txt.haml → _edit_replace_txt.haml} +4 -4
- data/lib/web/views/_rebooting.haml +18 -0
- data/lib/web/views/bookmarklet/download.js.erb +2 -2
- data/lib/web/views/bookmarklet/insert_button.js.erb +1 -1
- data/lib/web/views/edit_menu.haml +223 -0
- data/lib/web/views/help.haml +29 -12
- data/lib/web/views/index.haml +99 -88
- data/lib/web/views/layout.haml +5 -3
- data/lib/web/views/notepad.haml +39 -0
- data/lib/web/views/novels/setting.haml +15 -5
- data/lib/web/views/settings.haml +2 -2
- data/lib/web/views/style.scss +72 -21
- data/lib/web/views/widget/download.haml +3 -2
- data/lib/web/views/widget/drag_and_drop.haml +3 -2
- data/lib/web/views/widget/notepad.haml +44 -0
- data/narou.gemspec +75 -6
- data/narou.rb +8 -14
- data/preset/ncode.syosetu.com/n5115cq/converter.rb +30 -0
- data/preset/ncode.syosetu.com/n7594ct/converter.rb +37 -0
- data/preset/ncode.syosetu.com/n8725k/converter.rb +1 -1
- data/preset/vertical_font.css +0 -10
- data/spec/generator/convert_spec_gen.rb +2 -0
- data/template/novel.txt.erb +3 -0
- data/webnovel/https.syosetu.org.yaml +1 -1
- data/webnovel/kakuyomu.jp.yaml +82 -0
- data/webnovel/ncode.syosetu.com.yaml +1 -0
- data/webnovel/syosetu.org.yaml +1 -1
- metadata +89 -12
- data/lib/web/views/about.haml +0 -85
- data/preset/DMincho.ttf +0 -0
data/lib/command/version.rb
CHANGED
@@ -14,9 +14,8 @@ module Command
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def self.create_version_string
|
17
|
-
|
18
|
-
|
19
|
-
"#{::Version} build #{commitversion}"
|
17
|
+
postfix = (Narou.commit_version ? "" : " (develop)")
|
18
|
+
"#{::Version}#{postfix}"
|
20
19
|
end
|
21
20
|
end
|
22
21
|
end
|
data/lib/command/web.rb
CHANGED
@@ -69,7 +69,35 @@ module Command
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def execute(argv)
|
72
|
-
|
72
|
+
if argv.delete("--boot")
|
73
|
+
@rebooted = !!argv.delete("--reboot")
|
74
|
+
super
|
75
|
+
boot
|
76
|
+
else
|
77
|
+
argv << "--backtrace" if $display_backtrace
|
78
|
+
argv << "--no-color" if $disable_color
|
79
|
+
argv << "--boot"
|
80
|
+
argv_copy = argv.dup
|
81
|
+
begin
|
82
|
+
loop do
|
83
|
+
if $development
|
84
|
+
system(RbConfig.ruby, "-x", $0, "web", *argv)
|
85
|
+
else
|
86
|
+
system("narou", "web", *argv)
|
87
|
+
end
|
88
|
+
break unless $?.exitstatus == Narou::EXIT_REQUEST_REBOOT
|
89
|
+
argv = argv_copy.dup
|
90
|
+
argv.push("--no-browser", "--reboot")
|
91
|
+
end
|
92
|
+
rescue Interrupt => e
|
93
|
+
# 中断されてコンソールへの入力が可能になってから、WEBrick が終了するまで
|
94
|
+
# タイムラグがあって表示がごちゃまぜになるので、終わるのを少し待つ
|
95
|
+
sleep 3
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def boot
|
73
101
|
require_relative "../web/all"
|
74
102
|
confirm_of_first
|
75
103
|
params = Narou::AppServer.create_address(@options["port"])
|
@@ -83,18 +111,18 @@ module Command
|
|
83
111
|
puts
|
84
112
|
|
85
113
|
push_server.run
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
Helper.open_browser(address)
|
90
|
-
end
|
91
|
-
end
|
114
|
+
open_browser_when_server_boot(address)
|
115
|
+
send_rebooted_event_when_connection_recover(push_server)
|
116
|
+
|
92
117
|
$stdout = Narou::StreamingLogger.new(push_server)
|
93
118
|
ProgressBar.push_server = push_server
|
94
119
|
Narou::AppServer.push_server = push_server
|
95
120
|
Narou::Worker.instance.start
|
96
121
|
Narou::AppServer.run!
|
97
122
|
push_server.quit
|
123
|
+
if Narou::AppServer.request_reboot?
|
124
|
+
exit Narou::EXIT_REQUEST_REBOOT
|
125
|
+
end
|
98
126
|
rescue Errno::EADDRINUSE => e
|
99
127
|
Helper.open_browser(address) unless @options["no-browser"]
|
100
128
|
STDOUT.puts <<-EOS
|
@@ -107,6 +135,29 @@ module Command
|
|
107
135
|
EOS
|
108
136
|
exit Narou::EXIT_ERROR_CODE
|
109
137
|
end
|
138
|
+
|
139
|
+
def open_browser_when_server_boot(address)
|
140
|
+
return if @options["no-browser"]
|
141
|
+
Thread.new do
|
142
|
+
sleep 0.2 until Narou::AppServer.running?
|
143
|
+
Helper.open_browser(address)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def send_rebooted_event_when_connection_recover(push_server)
|
148
|
+
return unless @rebooted
|
149
|
+
Thread.new do |th|
|
150
|
+
timeout = Time.now + 20
|
151
|
+
# WebSocketのコネクションが回復するまで待つ
|
152
|
+
until push_server.connections.count != 0
|
153
|
+
sleep 0.2
|
154
|
+
th.kill if Time.now > timeout
|
155
|
+
end
|
156
|
+
puts "<yellow>再起動が完了しました。</yellow>".termcolor
|
157
|
+
push_server.send_all(:"server.rebooted")
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
110
161
|
end
|
111
162
|
end
|
112
163
|
|
data/lib/converterbase.rb
CHANGED
@@ -17,8 +17,7 @@ class ConverterBase
|
|
17
17
|
ENGLISH_SENTENCES_CHARACTERS = /[\w.,!?'" &:;_-]+/
|
18
18
|
ENGLISH_SENTENCES_MIN_LENGTH = 8 # この文字数以上アルファベットが続くと半角のまま
|
19
19
|
|
20
|
-
|
21
|
-
attr_accessor :output_text_dir, :subtitles
|
20
|
+
attr_accessor :output_text_dir, :subtitles, :data_type
|
22
21
|
attr_accessor :current_index # 現在処理してる subtitles 内でのインデックス
|
23
22
|
|
24
23
|
def before(io, text_type)
|
@@ -39,9 +38,9 @@ class ConverterBase
|
|
39
38
|
@setting = setting
|
40
39
|
@inspector = inspector
|
41
40
|
@illustration = illustration
|
42
|
-
@use_dakuten_font = false
|
43
41
|
@output_text_dir = nil
|
44
42
|
@subtitles = nil
|
43
|
+
@data_type = "text"
|
45
44
|
@current_index = 0
|
46
45
|
reset_member_values
|
47
46
|
end
|
@@ -452,23 +451,6 @@ class ConverterBase
|
|
452
451
|
data.gsub!("※※", "※[#米印、1-2-8]")
|
453
452
|
end
|
454
453
|
|
455
|
-
#
|
456
|
-
# 濁点のついてない文字に濁点をつける表現を対応
|
457
|
-
#
|
458
|
-
# 濁点つきフォントに部分的に切り替える
|
459
|
-
#
|
460
|
-
def convert_dakuten_char_to_font(data)
|
461
|
-
data.gsub!(/(.)[゛゙]/) do
|
462
|
-
m1 = $1
|
463
|
-
if m1 =~ /[ぁ-んァ-ヶι]/ && @setting.enable_dakuten_font
|
464
|
-
@use_dakuten_font = true
|
465
|
-
"[#濁点]#{m1}[#濁点終わり]"
|
466
|
-
else
|
467
|
-
tcy(m1 + "゛")
|
468
|
-
end
|
469
|
-
end
|
470
|
-
end
|
471
|
-
|
472
454
|
#
|
473
455
|
# 小説のルールに沿うように変換
|
474
456
|
#
|
@@ -869,9 +851,11 @@ class ConverterBase
|
|
869
851
|
data.gsub!(/(.+?)≪([^≪]+?)≫/) do |match|
|
870
852
|
to_ruby(match, $1, $2, ["≪", "≫"])
|
871
853
|
end
|
872
|
-
|
873
|
-
|
874
|
-
|
854
|
+
if @data_type == "text"
|
855
|
+
# ()なルビの対処
|
856
|
+
data.gsub!(/(.+?)(#{AUTO_RUBY_CHARACTERS})/) do |match|
|
857
|
+
to_ruby(match, $1, $2, ["(", ")"])
|
858
|
+
end
|
875
859
|
end
|
876
860
|
end
|
877
861
|
data.replace(replace_tatesen(data))
|
@@ -1147,7 +1131,6 @@ class ConverterBase
|
|
1147
1131
|
convert_special_characters(data)
|
1148
1132
|
convert_fraction_and_date(data)
|
1149
1133
|
modify_kana_ni_to_kanji_ni(data)
|
1150
|
-
convert_dakuten_char_to_font(data)
|
1151
1134
|
end
|
1152
1135
|
|
1153
1136
|
def before_convert(io)
|
data/lib/database.rb
CHANGED
@@ -106,11 +106,12 @@ class Database
|
|
106
106
|
id
|
107
107
|
end
|
108
108
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
109
|
+
def sort_by(key, reverse: true)
|
110
|
+
values = @database.values.sort_by { |v| v[key] }
|
111
|
+
if reverse
|
112
|
+
values.reverse
|
113
|
+
else
|
114
|
+
values
|
115
|
+
end
|
115
116
|
end
|
116
117
|
end
|
data/lib/device.rb
CHANGED
@@ -85,7 +85,21 @@ class Device
|
|
85
85
|
end
|
86
86
|
|
87
87
|
def connecting?
|
88
|
-
!!get_documents_path
|
88
|
+
physical_support? && !!get_documents_path
|
89
|
+
end
|
90
|
+
|
91
|
+
def eject
|
92
|
+
if ejectable?
|
93
|
+
Device.eject(@device_module::VOLUME_NAME)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.support_eject?
|
98
|
+
respond_to?(:eject)
|
99
|
+
end
|
100
|
+
|
101
|
+
def ejectable?
|
102
|
+
Device.support_eject? && connecting?
|
89
103
|
end
|
90
104
|
|
91
105
|
def find_documents_directory(device_root_dir)
|
data/lib/device/ibunko.rb
CHANGED
data/lib/device/library/mac.rb
CHANGED
data/lib/downloader.rb
CHANGED
@@ -695,6 +695,8 @@ class Downloader
|
|
695
695
|
end
|
696
696
|
end
|
697
697
|
|
698
|
+
class DownloaderForceRedirect < StandardError; end
|
699
|
+
|
698
700
|
#
|
699
701
|
# HTMLの中から小説が削除されたか非公開なことを示すメッセージを検出する
|
700
702
|
#
|
@@ -707,22 +709,38 @@ class Downloader
|
|
707
709
|
def get_toc_source
|
708
710
|
toc_url = @setting["toc_url"]
|
709
711
|
return nil unless toc_url
|
712
|
+
max_retry = 5
|
710
713
|
toc_source = ""
|
711
714
|
cookie = @setting["cookie"] || ""
|
712
715
|
open_uri_options = make_open_uri_options("Cookie" => cookie, allow_redirections: :safe)
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
716
|
+
begin
|
717
|
+
open(toc_url, open_uri_options) do |toc_fp|
|
718
|
+
if toc_fp.base_uri.to_s != toc_url
|
719
|
+
# リダイレクトされた場合。
|
720
|
+
# ノクターン・ムーンライトのNコードを ncode.syosetu.com に渡すと、年齢認証のクッションページに飛ばされる
|
721
|
+
# 転送先を取得し再度ページを取得し直す
|
722
|
+
uri = URI.parse(toc_fp.base_uri.to_s)
|
723
|
+
if uri.host == "nl.syosetu.com"
|
724
|
+
decode = Hash[URI.decode_www_form(uri.query)]
|
725
|
+
toc_url = decode["url"] # 年齢認証確認ページからの転送先
|
726
|
+
raise DownloaderForceRedirect
|
727
|
+
end
|
728
|
+
s = Downloader.get_sitesetting_by_target(toc_fp.base_uri.to_s)
|
729
|
+
raise DownloaderNotFoundError unless s # 非公開や削除等でトップページへリダイレクトされる場合がある
|
730
|
+
@setting.clear # 今まで使っていたのは一旦クリア
|
731
|
+
@setting = s
|
732
|
+
toc_url = @setting["toc_url"]
|
733
|
+
end
|
734
|
+
toc_source = Helper.restor_entity(Helper.pretreatment_source(toc_fp.read, @setting["encoding"]))
|
735
|
+
raise DownloaderNotFoundError if Downloader.detect_error_message(@setting, toc_source)
|
736
|
+
end
|
737
|
+
rescue DownloaderForceRedirect
|
738
|
+
max_retry -= 1
|
739
|
+
if max_retry >= 0
|
740
|
+
retry
|
741
|
+
else
|
742
|
+
raise
|
723
743
|
end
|
724
|
-
toc_source = Helper.restor_entity(Helper.pretreatment_source(toc_fp.read, @setting["encoding"]))
|
725
|
-
raise DownloaderNotFoundError if Downloader.detect_error_message(@setting, toc_source)
|
726
744
|
end
|
727
745
|
toc_source
|
728
746
|
end
|
@@ -739,7 +757,7 @@ class Downloader
|
|
739
757
|
# なろうAPIの出力がおかしいので直るまで使用中止
|
740
758
|
info = Narou::API.new(@setting, "t-s-gf-gl-nu-w")
|
741
759
|
else
|
742
|
-
info = NovelInfo.load(@setting)
|
760
|
+
info = NovelInfo.load(@setting, toc_source)
|
743
761
|
end
|
744
762
|
if info
|
745
763
|
raise DownloaderNotFoundError unless info["title"]
|
@@ -782,7 +800,7 @@ class Downloader
|
|
782
800
|
$stdout.silence do
|
783
801
|
Command::Tag.execute!(%W(#{@id} --add 404 --color white --no-overwrite-color))
|
784
802
|
end
|
785
|
-
Command::Freeze.execute!([@id])
|
803
|
+
Command::Freeze.execute!([@id, "--on"])
|
786
804
|
end
|
787
805
|
else
|
788
806
|
@stream.error "何らかの理由により目次が取得できませんでした(#{e.message})"
|
@@ -920,7 +938,8 @@ class Downloader
|
|
920
938
|
subtitles << {
|
921
939
|
"index" => @setting["index"],
|
922
940
|
"href" => @setting["href"],
|
923
|
-
"chapter" => @setting["chapter"],
|
941
|
+
"chapter" => @setting["chapter"].to_s,
|
942
|
+
"subchapter" => @setting["subchapter"].to_s,
|
924
943
|
"subtitle" => @setting["subtitle"].gsub("\n", ""),
|
925
944
|
"file_subtitle" => title_to_filename(@setting["subtitle"]),
|
926
945
|
"subdate" => subdate,
|
@@ -957,15 +976,16 @@ class Downloader
|
|
957
976
|
@stream.puts "<bold><green>#{"ID:#{@id} #{get_title}".escape} のDL開始</green></bold>".termcolor
|
958
977
|
save_least_one = false
|
959
978
|
subtitles.each_with_index do |subtitle_info, i|
|
960
|
-
index, subtitle, file_subtitle, chapter =
|
961
|
-
|
962
|
-
|
979
|
+
index, subtitle, file_subtitle, chapter, subchapter =
|
980
|
+
%w(index subtitle file_subtitle chapter subchapter).map { |k|
|
981
|
+
subtitle_info[k]
|
982
|
+
}
|
963
983
|
info = subtitle_info.dup
|
964
984
|
info["element"] = a_section_download(subtitle_info)
|
965
985
|
|
966
|
-
unless chapter.empty?
|
967
|
-
|
968
|
-
|
986
|
+
@stream.puts "#{chapter}" unless chapter.to_s.empty?
|
987
|
+
@stream.puts "#{subchapter}" unless subchapter.to_s.empty?
|
988
|
+
|
969
989
|
if get_novel_type == NOVEL_TYPE_SERIES
|
970
990
|
if index.to_s.length <= DISPLAY_LIMIT_DIGITS
|
971
991
|
# indexの数字がでかいと見た目がみっともないので特定の桁以内だけ表示する
|
@@ -1112,6 +1132,9 @@ class Downloader
|
|
1112
1132
|
end
|
1113
1133
|
sleep(WAITING_TIME_FOR_503)
|
1114
1134
|
retry
|
1135
|
+
elsif e.message =~ /^404/
|
1136
|
+
@stream.error "#{url} がダウンロード出来ませんでした。時間をおいて再度試してみてください"
|
1137
|
+
exit Narou::EXIT_ERROR_CODE
|
1115
1138
|
else
|
1116
1139
|
raise
|
1117
1140
|
end
|
data/lib/extension.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
require "open-uri"
|
3
|
+
|
4
|
+
# open-uri で http → https へのリダイレクトを有効にする
|
5
|
+
require "open_uri_redirections"
|
6
|
+
|
7
|
+
# open-uri に渡すオプションを生成(必要に応じて extensions/*.rb でオーバーライドする)
|
8
|
+
def make_open_uri_options(add)
|
9
|
+
add
|
10
|
+
end
|
11
|
+
|
12
|
+
#
|
13
|
+
# 安全なファイルの書き込み
|
14
|
+
#
|
15
|
+
# ファイルに直接上書きしないで、一旦別名で作成してからファイル名変更をすることで、
|
16
|
+
# ファイル書き込み中のPCクラッシュ等でデータが飛ばない様にする
|
17
|
+
#
|
18
|
+
require "securerandom"
|
19
|
+
|
20
|
+
def File.write(path, string, *options)
|
21
|
+
dirname = File.dirname(path)
|
22
|
+
temp_path = File.join(dirname, SecureRandom.hex(15))
|
23
|
+
super(temp_path, string, *options)
|
24
|
+
File.rename(temp_path, path)
|
25
|
+
end
|
data/lib/helper.rb
CHANGED
@@ -108,11 +108,12 @@ module Helper
|
|
108
108
|
end
|
109
109
|
|
110
110
|
#
|
111
|
-
#
|
111
|
+
# ダウンロードした文字列をエンコード及び不正な文字列除去、改行コード統一
|
112
112
|
#
|
113
113
|
def pretreatment_source(src, encoding = Encoding::UTF_8)
|
114
|
-
src.force_encoding(encoding)
|
115
|
-
.
|
114
|
+
src.force_encoding(encoding)
|
115
|
+
.scrub("?")
|
116
|
+
.gsub("\r", "")
|
116
117
|
end
|
117
118
|
|
118
119
|
ENTITIES = { quot: '"', amp: "&", nbsp: " ", lt: "<", gt: ">", copy: "(c)", "#39" => "'" }
|
data/lib/html.rb
CHANGED
@@ -7,15 +7,20 @@ require "uri"
|
|
7
7
|
require_relative "helper"
|
8
8
|
|
9
9
|
class HTML
|
10
|
-
|
10
|
+
attr_reader :string
|
11
|
+
attr_accessor :strip_decoration_tag
|
11
12
|
|
12
13
|
def initialize(string = "")
|
13
|
-
|
14
|
+
self.string = string
|
14
15
|
@illust_current_url = nil
|
15
16
|
@illust_grep_pattern = /<img.+?src=\"(?<src>.+?)\".*?>/i
|
16
17
|
@strip_decoration_tag = false
|
17
18
|
end
|
18
19
|
|
20
|
+
def string=(str)
|
21
|
+
@string = str.to_s
|
22
|
+
end
|
23
|
+
|
19
24
|
#
|
20
25
|
# 挿絵を置換するための設定を変更する
|
21
26
|
#
|
@@ -34,6 +39,7 @@ class HTML
|
|
34
39
|
#
|
35
40
|
def to_aozora
|
36
41
|
@string = br_to_aozora
|
42
|
+
@string = p_to_aozora
|
37
43
|
@string = ruby_to_aozora
|
38
44
|
unless @strip_decoration_tag
|
39
45
|
@string = b_to_aozora
|
@@ -41,6 +47,7 @@ class HTML
|
|
41
47
|
@string = s_to_aozora
|
42
48
|
end
|
43
49
|
@string = img_to_aozora
|
50
|
+
@string = em_to_sesame
|
44
51
|
@string = delete_tag
|
45
52
|
@string = Helper.restor_entity(@string)
|
46
53
|
@string
|
@@ -54,6 +61,11 @@ class HTML
|
|
54
61
|
text.gsub(/[\r\n]+/, "").gsub(/<br.*?>/i, "\n")
|
55
62
|
end
|
56
63
|
|
64
|
+
# p タグで段落を作ってる場合(brタグが無い場合)に改行されるように
|
65
|
+
def p_to_aozora(text = @string)
|
66
|
+
text.gsub(/\n?<\/p>/i, "\n")
|
67
|
+
end
|
68
|
+
|
57
69
|
def ruby_to_aozora(text = @string)
|
58
70
|
text.tr("《》", "≪≫")
|
59
71
|
.gsub(/<ruby>(.+?)<\/ruby>/i) do
|
@@ -87,4 +99,8 @@ class HTML
|
|
87
99
|
text
|
88
100
|
end
|
89
101
|
end
|
102
|
+
|
103
|
+
def em_to_sesame(text = @string)
|
104
|
+
text.gsub(%r!<em class="emphasisDots">(.+?)</em>!, "[#傍点]\\1[#傍点終わり]")
|
105
|
+
end
|
90
106
|
end
|