Skip to content
7 changes: 6 additions & 1 deletion lib/command/backup.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,12 @@ def create_backup(data)
backup_dir = novel_dir.join(BACKUP_DIR_NAME)
backup_dir.mkdir unless backup_dir.exist?
Zip.unicode_names = true unless Helper.os_windows?
Zip::File.open(backup_dir.join(zipfilename), Zip::File::CREATE) do |zip|

# Windowsでのスレッド内ファイル操作対策: GCを強制実行してファイルハンドルを解放
GC.start
sleep 0.1

Zip::File.open(backup_dir.join(zipfilename), create: true) do |zip|
paths.each do |path|
relative_path = path_to_relative(novel_dir, path).to_s
zipped_filename =
Expand Down
12 changes: 12 additions & 0 deletions lib/command/update.rb
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,19 @@ def execute(argv)
end
convert_argv = [target]
convert_argv << "--no-open" if @options["no-open"]

# WebUI: 変換メッセージを変換コンソールに出力するため、一時的に$stdout2を切り替え
original_stdout2 = $stdout2
if $stdout2.respond_to?(:push_server) && $stdout2.respond_to?(:target_console)
# WebUIの場合、変換用の別コンソールに出力
$stdout2 = Narou::StreamingLogger.new($stdout2.push_server, $stdout2, target_console: "convert")
end

convert_status = Convert.execute!(convert_argv)

# $stdout2を元に戻す
$stdout2 = original_stdout2 if original_stdout2

if convert_status > 0
# 変換が失敗したか、中断された
data["_convert_failure"] = true
Expand Down
4 changes: 2 additions & 2 deletions lib/command/web.rb
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,12 @@ def open_browser_when_server_boot(address)

def send_rebooted_event_when_connection_recover(push_server)
return unless @rebooted
Thread.new do |th|
Thread.new do
timeout = Time.now + 20
# WebSocketのコネクションが回復するまで待つ
until push_server.connections.count != 0
sleep 0.2
th.kill if Time.now > timeout
Thread.current.kill if Time.now > timeout
end
puts "<yellow>再起動が完了しました。</yellow>".termcolor
push_server.send_all(:"server.rebooted")
Expand Down
18 changes: 14 additions & 4 deletions lib/device/ibunko.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,12 @@ def create_pure_aozora_zip

zipfile_path = @converted_txt_path.sub(/.txt$/, @device.ebook_file_ext)
File.delete(zipfile_path) if File.exist?(zipfile_path)
Zip::File.open(zipfile_path, Zip::File::CREATE) do |zip|

# Windowsでのスレッド内ファイル操作対策: GCを強制実行してファイルハンドルを解放
GC.start
sleep 0.1

Zip::File.open(zipfile_path, create: true) do |zip|
# テキスト本体(整形済み)
zip.add(File.basename(@converted_txt_path), sanitized_txt_path) { true }
# 挿絵(Aozora注記のまま。画像ファイルは同梱)
Expand All @@ -72,7 +77,7 @@ def create_pure_aozora_zip
end
end
FileUtils.rm_f(sanitized_txt_path)
output_io = (respond_to?(:stream_io) ? stream_io : nil) || $stdout
output_io = $stdout2
output_io.puts File.basename(zipfile_path) + " を出力しました"
output_io.puts "<bold><green>#{@device.display_name}用ファイルを出力しました</green></bold>".termcolor
if Narou.economy?("cleanup_temp") && @argument_target_type == :novel
Expand All @@ -98,7 +103,12 @@ def hook_convert_txt_to_ebook_file(&original_func)
translate_illust_chuki_to_img_tag
zipfile_path = @converted_txt_path.sub(/.txt$/, @device.ebook_file_ext)
File.delete(zipfile_path) if File.exist?(zipfile_path)
Zip::File.open(zipfile_path, Zip::File::CREATE) do |zip|

# Windowsでのスレッド内ファイル操作対策: GCを強制実行してファイルハンドルを解放
GC.start
sleep 0.1

Zip::File.open(zipfile_path, create: true) do |zip|
# テキスト本体
zip.add(File.basename(@converted_txt_path), @converted_txt_path) { true }
# 挿絵
Expand All @@ -116,7 +126,7 @@ def hook_convert_txt_to_ebook_file(&original_func)
zip.add(cover_name, File.join(dirpath, cover_name)) { true }
end
end
output_io = (respond_to?(:stream_io) ? stream_io : nil) || $stdout
output_io = $stdout2
output_io.puts File.basename(zipfile_path) + " を出力しました"
output_io.puts "<bold><green>#{@device.display_name}用ファイルを出力しました</green></bold>".termcolor
if Narou.economy?("cleanup_temp") && @argument_target_type == :novel
Expand Down
27 changes: 25 additions & 2 deletions lib/downloader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,7 @@ def get_toc_source
toc_url = @setting["toc_url"]
return nil unless toc_url
max_retry = 5
retry_count = LIMIT_TO_RETRY_NETWORK
toc_source = ""
cookie = @setting["cookie"] || ""
open_uri_options = make_open_uri_options("Cookie" => cookie, allow_redirections: :safe)
Expand Down Expand Up @@ -787,6 +788,28 @@ def get_toc_source
else
raise
end
rescue OpenURI::HTTPError, Errno::ECONNRESET, Errno::ECONNABORTED, Errno::ETIMEDOUT, Net::OpenTimeout, IO::TimeoutError, SocketError => e
case e.message
when /^503/
@stream&.error "server message: #{e.message}"
display_hint if @stream
raise SuspendDownload
when /^404/
# 404は上位のget_latest_table_of_contentsで処理させるため、そのまま再raise
raise e
else
if retry_count == 0
@stream&.error "上限までリトライしましたが目次がダウンロード出来ませんでした"
raise SuspendDownload
end
retry_count -= 1
@stream&.puts <<~MSG
server message: #{e.message}
リトライ待機中...
MSG
sleep(WAIT_TIME_TO_RETRY_NETWORK)
retry
end
end
toc_source
end
Expand Down Expand Up @@ -834,7 +857,7 @@ def get_latest_table_of_contents(old_toc, through_error: false)
"subtitles" => subtitles
}
toc_objects
rescue OpenURI::HTTPError, Errno::ECONNRESET, Errno::ETIMEDOUT, Net::OpenTimeout, IO::TimeoutError => e
rescue OpenURI::HTTPError, Errno::ECONNRESET, Errno::ECONNABORTED, Errno::ETIMEDOUT, Net::OpenTimeout, IO::TimeoutError, SocketError => e
raise if through_error # エラー処理はしなくていいからそのまま例外を受け取りたい時用
if e.message.include?("404")
@stream.error "小説が削除されているか非公開な可能性があります"
Expand Down Expand Up @@ -1235,7 +1258,7 @@ def download_raw_data(url)
URI.open(url, "r:#{@setting["encoding"]}", open_uri_options) do |fp|
raw = Helper.pretreatment_source(fp.read, @setting["encoding"])
end
rescue OpenURI::HTTPError, Errno::ECONNRESET, Errno::ETIMEDOUT, Net::OpenTimeout, IO::TimeoutError => e
rescue OpenURI::HTTPError, Errno::ECONNRESET, Errno::ECONNABORTED, Errno::ETIMEDOUT, Net::OpenTimeout, IO::TimeoutError, SocketError => e
case e.message
when /^503/
# 503 はアクセス規制やメンテ等でリトライしてもほぼ意味がないことが多いため一度で諦める
Expand Down
12 changes: 8 additions & 4 deletions lib/novelconverter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -280,17 +280,21 @@ def self.add_dc_subject_to_epub(epub_path, subjects, stream_io: $stdout2)
return :error
end

content = opf_body.dup
content = opf_body.dup.force_encoding("UTF-8")
content.gsub!(/<dc:subject>.*?<\/dc:subject>\s*\n?\s*/m, "")
dc_subject_lines = subjects.map(&:strip).reject(&:empty?).map { |s|
esc = s.gsub("&","&amp;").gsub("<","&lt;").gsub(">","&gt;").gsub("\"","&quot;").gsub("'","&apos;")
" <dc:subject>#{esc}</dc:subject>"
"\t\t<dc:subject>#{esc}</dc:subject>"
}
if dc_subject_lines.any?
dc_subjects_xml = dc_subject_lines.join("\n") + "\n"
content.sub!(/(\s*)<\/metadata>/, "\n#{dc_subjects_xml}\\1</metadata>")
end
entries[opf_name] = content
entries[opf_name] = content.b

# Windowsでのスレッド内ファイル操作対策: GCを強制実行してファイルハンドルを解放
GC.start
sleep 0.1

# 再Zip化 (mimetypeは無圧縮で先頭)
File.delete(epub_path)
Expand All @@ -315,7 +319,7 @@ def self.add_dc_subject_to_epub(epub_path, subjects, stream_io: $stdout2)
stream_io.puts "dc:subjectを追加しました: #{subjects.join(', ')}"
:success
rescue => e
stream_io.error "dc:subject追加中にエラーが発生しました: #{e.message}"
stream_io.error "dc:subject追加中にエラーが発生しました: #{e.class} - #{e.message}"
:error
end
end
Expand Down
30 changes: 19 additions & 11 deletions lib/web/public/resources/js/narou.library.js
Original file line number Diff line number Diff line change
Expand Up @@ -1902,6 +1902,23 @@ var Narou = (function () {
return matched;
});

// 通常単語を追加するためのイベント(テーブル tbody に直接登録)
// サーバーサイド/クライアントサイド両方で必要
$("#novel-list tbody").on("click", "[data-add-filter]", function (e) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
self.appendWordToFilter($(this).data("addFilter"));
return false;
});
// 追加イベントが発生した時に行選択イベントが起こらないように
$("#novel-list tbody").on("mousedown", "[data-add-filter]", function (e) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
return false;
});

// サーバーサイド処理の場合はカスタムフィルタボックスのイベントを登録しない
// (narou.ui.jsで登録されるハンドラを使用)
if (this.table.settings()[0].oFeatures.bServerSide) {
Expand Down Expand Up @@ -1931,17 +1948,6 @@ var Narou = (function () {
self.search();
}
});

this.table
// 通常単語を追加するためのイベント
.on("click", "[data-add-filter]", function (e) {
e.stopPropagation();
self.appendWordToFilter($(this).data("addFilter"));
})
// 追加イベントが発生した時に行選択イベントが起こらないように
.on("mousedown", "[data-add-filter]", function (e) {
e.stopPropagation();
});
},

// フィルタにタグを追加
Expand Down Expand Up @@ -2107,13 +2113,15 @@ var Narou = (function () {
$target
.on("click", ".tag", args, function (e) {
if (e.data.stop_bubbling) e.stopPropagation();
e.preventDefault(); // テキスト選択を防ぐ
var tag_name = String($(this).data("tag"));
self.search.appendTagToFilter(tag_name, e.altKey);
self.table.$("[data-toggle=tooltip]").tooltip("hide");
})
.on("mousedown", ".tag", args, function (e) {
// 範囲選択モードでもクリック出来るように
if (e.data.stop_bubbling) e.stopPropagation();
e.preventDefault(); // テキスト選択を防ぐ
});
},

Expand Down
12 changes: 10 additions & 2 deletions lib/web/public/resources/js/narou.ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,6 @@ $(function() {
var isLightweightMode = false;
var table, t;
var action; // アクション管理インスタンス
window.action = action;
var isInitialLoad = true; // 初回ロードフラグ

/*
Expand Down Expand Up @@ -1285,7 +1284,12 @@ $(function() {
// ここだけはスマフォ系でも touchstart ではなく click で。
// touchstart だと画面スクロールのためのスワイプでも反応してしまう
table
.on("click", "tr", function () {
.on("click", "tr", function (e) {
// data-add-filter要素がクリックされた場合は何もしない
if ($(e.target).closest('[data-add-filter]').length > 0) {
return;
}

// DOM の選択状態を変更
$(this).toggleClass("selected");

Expand All @@ -1311,6 +1315,10 @@ $(function() {
// リンクをクリックした時は選択処理は行わない
e.stopPropagation();
})
.on("click", "[data-add-filter]", function(e) {
// フィルタ追加要素をクリックした時は選択処理は行わない
e.stopPropagation();
})
$("#novel-list tbody").css("cursor", "pointer");
}

Expand Down
13 changes: 10 additions & 3 deletions lib/web/pushserver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,16 @@ def run
end

thread = Thread.new do
while true
data = que.pop
ws.send(data)
begin
while true
data = que.pop
ws.send(data)
end
rescue Errno::ECONNRESET, Errno::EPIPE, IOError => e
# 接続が切れた場合、スレッドを終了
rescue => e
# その他のエラーもログに出力してスレッド終了
puts "[ERROR] WebSocket send thread error: #{e.class}: #{e.message}" if $DEBUG
end
end

Expand Down