Module: Narou

Extended by:
Memoist, Mixin::Locker
Defined in:
lib/version.rb,
lib/input.rb,
lib/narou.rb,
lib/worker.rb,
lib/narou/api.rb,
lib/backtracer.rb,
lib/mixin/locker.rb,
lib/narou_logger.rb,
lib/web/pushserver.rb,
lib/web/web_worker.rb,
lib/mixin/output_error.rb,
lib/web/streaminginput.rb,
lib/web/settingmessages.rb,
lib/web/streaminglogger.rb,
lib/extensions/monkey_patches/pathname.rb

Overview

標準出力をフックする

$stdout を介した出力は echo イベントとして全てのクライアントへ送信される

Defined Under Namespace

Modules: Backtracer, Eventable, Input, LoggerModule, Mixin, MonkyPatches, ServerHelpers Classes: API, AppServer, ConcurrencyConvertLogger, ConcurrencyDefaultLogger, Logger, LoggerError, NullIO, PushServer, StreamingLogger, WebWorker, Worker

Constant Summary collapse

LOCAL_SETTING_DIR_NAME =
".narou"
GLOBAL_SETTING_DIR_NAME =
".narousetting"
AOZORAEPUB3_JAR_NAME =
"AozoraEpub3.jar"
AOZORAEPUB3_DIR =
"AozoraEpub3"
PRESET_DIR =
"preset"
MISC_DIR =
"misc"
LOG_DIR =
"log"
GLOBAL_REPLACE_NAME =
"replace.txt"
EXIT_SUCCESS =
0
EXIT_ERROR_CODE =
127
EXIT_INTERRUPT =
126
EXIT_REQUEST_REBOOT =
125
MODIFIED_TAG =
"modified"
LINE_HEIGHT_DEFAULT =

単位em

1.6
UPDATE_SORT_KEYS =
{
  "id" => "ID", "last_update" => "更新日", "title" => "タイトル", "author" => "作者名",
  "new_arrivals_date" => "新着日", "general_lastup" => "最新話掲載日"
}
VERSION =
"3.9.1"
SETTING_VARIABLES_WEBUI_MESSAGES =

WEB UI > 環境設定画面で表示する各項目の説明 ここになければ元々の説明が表示される

{
  "convert.multi-device" => "複数の端末用に同時に変換する。deviceよりも優先される。\nただのEPUBを出力したい場合はepubを指定",
  "device" => "変換、送信対象の端末",
  "difftool" => "%%ORIG%%。※WEB UIでは使われません",
  "update.sort-by" => "アップデートを指定した項目順で行う",
  "default.title_date_align" => "enable_add_date_to_title で付与する日付の位置",
  "force.title_date_align" => "enable_add_date_to_title で付与する日付の位置",
  "difftool.arg" => "difftoolで使う引数(指定しなければ単純に新旧ファイルを引数に呼び出す)\n" \
                    "特殊な変数\n" \
                    "<b>%NEW</b> : 最新データの差分用ファイルパス\n" \
                    "<b>%OLD</b> : 古い方の差分用ファイルパス",
  "no-color" => "コンソールのカラー表示を無効にする\n※要サーバ再起動",
  "economy" => "容量節約に関する設定",
  "send.without-freeze" => "一括送信時に凍結された小説は対象外にする。(個別送信時は凍結済みでも送信可能)",
  "server-digest-auth.enable" => "%%ORIG%%\n※digest-auth関連の設定を変更した場合サーバの再起動が必要",
  "server-digest-auth.hashed-password" => "サーバのDigest認証のパスワードを、Realmを\"narou.rb\"としてハッシュにしたもの。\n" \
                                          "https://tgws.plus/app/digest/ などで生成できる",
  "concurrency" => "%%ORIG%% ※要サーバ再起動",
  "logging" => "%%ORIG%%\n※要サーバ再起動",
  "logging.format-filename" => "%%ORIG%%\n※要サーバ再起動",
  "logging.format-timestamp" => "%%ORIG%%\n※要サーバ再起動",
}
@@is_web =
false

Class Method Summary collapse

Methods included from Mixin::Locker

lock, locked?, unlock

Class Method Details

.alias_to_id(target) ⇒ Object

target が alias だった場合はIDに変換する

全てのtarget照合系はこのメソッドを通過するので、ここで小文字にしてしまう



116
117
118
119
120
121
122
# File 'lib/narou.rb', line 116

def alias_to_id(target)
  aliases = Inventory.load("alias")
  if aliases[target]
    return aliases[target]
  end
  target.is_a?(Numeric) ? target : target.downcase
end

.already_init?Boolean

Returns:

  • (Boolean)


100
101
102
# File 'lib/narou.rb', line 100

def already_init?
  root_dir.present?
end

.aozoraepub3_directory?(path) ⇒ Boolean

Returns:

  • (Boolean)


133
134
135
# File 'lib/narou.rb', line 133

def aozoraepub3_directory?(path)
  create_aozoraepub3_jar_path(path).exist?
end

.aozoraepub3_pathObject

AozoraEpub3 の実行ファイル(.jar)のフルパス取得 検索順序

  1. グローバルセッティング (global_setting aozoraepub3dir)

  2. 小説保存ディレクトリ(Narou.root_dir) 直下の AozoraEpub3

  3. スクリプト保存ディレクトリ(Narou.script_dir) 直下の AozoraEpub3



185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/narou.rb', line 185

def aozoraepub3_path
  global_setting_aozora_path = Inventory.load("global_setting", :global)["aozoraepub3dir"]
  if global_setting_aozora_path
    aozora_jar_path = create_aozoraepub3_jar_path(global_setting_aozora_path)
    if aozora_jar_path.exist?
      return aozora_jar_path
    end
  end
  [Narou.root_dir, Narou.script_dir].each do |dir|
    aozora_jar_path = create_aozoraepub3_jar_path(dir, AOZORAEPUB3_DIR)
    return aozora_jar_path if aozora_jar_path.exist?
  end
  nil
end

.commit_versionObject



341
342
343
344
# File 'lib/narou.rb', line 341

def commit_version
  cv_path = File.expand_path("commitversion", script_dir)
  File.read(cv_path) if File.exist?(cv_path)
end

.concurrency_call(&block) ⇒ Object

同時実行が有効ならキューに積んで、無効なら普通に実行する



364
365
366
367
368
369
370
371
# File 'lib/narou.rb', line 364

def concurrency_call(&block)
  if concurrency_enabled?
    Worker.push(&block)
    EXIT_SUCCESS
  else
    block.call
  end
end

.concurrency_enabled?Boolean

Returns:

  • (Boolean)


358
359
360
# File 'lib/narou.rb', line 358

def concurrency_enabled?
  $stdout != $stdout2
end

.create_aozoraepub3_jar_path(*paths) ⇒ Object



129
130
131
# File 'lib/narou.rb', line 129

def create_aozoraepub3_jar_path(*paths)
  Pathname(File.expand_path(File.join(*paths, AOZORAEPUB3_JAR_NAME)))
end

.create_novel_filename(novel_data, ext = "") ⇒ Object

書籍ファイル名を生成する convert.filename-to-ncode を設定している場合に novel_data に ncode、domain を 設定しない場合は id カラムが必須

rubocop:disable Metrics/CyclomaticComplexity rubocop:disable Metrics/PerceivedComplexity



208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/narou.rb', line 208

def create_novel_filename(novel_data, ext = "")
  filename_to_ncode = Inventory.load("local_setting")["convert.filename-to-ncode"]
  novel_setting =
    if novel_data["id"]
      NovelSetting.load(novel_data["id"])
    else
      OpenStruct.new
    end
  if novel_setting.output_filename.present?
    %!#{novel_setting.output_filename}#{ext}!
  elsif filename_to_ncode
    ncode = novel_data["ncode"]
    domain = novel_data["domain"]
    if !ncode || !domain
      id = novel_data["id"]
      unless id
        raise ArgumentError, %!novel_data["id"] を設定して下さい!
      end
      site_setting = Downloader.get_sitesetting_by_target(id)
      ncode = site_setting["ncode"]
      domain = site_setting["domain"]
    end
    serialized_domain = domain.to_s.gsub(".", "_")
    %!#{serialized_domain}_#{ncode}#{ext}!
  else
    author = Helper.replace_filename_special_chars(
      novel_setting.novel_author.presence || novel_data["author"],
      true
    )
    title = Helper.replace_filename_special_chars(
      novel_setting.novel_title.presence || novel_data["title"],
      true
    )
    filename = "[#{author}] #{title}#{ext}"
    length_limit = Inventory.load["ebook-filename-length-limit"]
    length_limit ? Helper.truncate_path(filename, length_limit, extname: ext) : filename
  end
end

.economy?(mode) ⇒ Boolean

Returns:

  • (Boolean)


319
320
321
322
# File 'lib/narou.rb', line 319

def economy?(mode)
  eco_modes = Inventory.load("local_setting")["economy"].to_s.split(",").map(&:strip)
  eco_modes.include?(mode)
end

.get_device(device_name = nil) ⇒ Object



279
280
281
282
283
284
285
# File 'lib/narou.rb', line 279

def get_device(device_name = nil)
  device_name ||= Inventory.load("local_setting")["device"]
  if device_name && Device.exists?(device_name)
    return Device.create(device_name)
  end
  nil
end

.get_ebook_file_paths(target, ext) ⇒ Object



253
254
255
256
257
258
259
260
261
# File 'lib/narou.rb', line 253

def get_ebook_file_paths(target, ext)
  data = Downloader.get_data_by_target(target)
  return nil unless data

  dir = Downloader.get_novel_data_dir_by_target(target)
  fname = create_novel_filename(data, ext)
  base = File.basename(fname, ext)
  get_ebook_file_paths_from_components(dir, base, ext)
end

.get_ebook_file_paths_from_components(dir, base, ext) ⇒ Object



263
264
265
266
267
268
269
270
271
# File 'lib/narou.rb', line 263

def get_ebook_file_paths_from_components(dir, base, ext)
  paths = [File.join(dir, "#{base}#{ext}")]
  index = 2
  while File.exist?(path = File.join(dir, "#{base}_#{index}#{ext}"))
    paths.push(Pathname(path))
    index += 1
  end
  paths
end

.get_mobi_paths(target) ⇒ Object

rubocop:enable Metrics/CyclomaticComplexity rubocop:enable Metrics/PerceivedComplexity



249
250
251
# File 'lib/narou.rb', line 249

def get_mobi_paths(target)
  get_ebook_file_paths(target, ".mobi")
end

.get_theme_dir(name = nil) ⇒ Object



307
308
309
# File 'lib/narou.rb', line 307

def get_theme_dir(name = nil)
  Pathname(File.join([script_dir, "lib/web/public/theme", name].compact))
end

.global_replace_patternObject



169
170
171
# File 'lib/narou.rb', line 169

def global_replace_pattern
  @@global_replace_pattern_pairs ||= load_global_replace_pattern
end

.global_setting_dirObject



75
76
77
78
79
80
81
82
83
# File 'lib/narou.rb', line 75

def global_setting_dir
  if root_dir
    dir = root_dir.join(GLOBAL_SETTING_DIR_NAME)
    return dir if dir.directory?
  end
  dir = Pathname(GLOBAL_SETTING_DIR_NAME).expand_path("~")
  dir.mkdir unless dir.exist?
  dir
end

.initObject



104
105
106
107
108
109
# File 'lib/narou.rb', line 104

def init
  return nil if already_init?
  FileUtils.mkdir(LOCAL_SETTING_DIR_NAME)
  puts "#{LOCAL_SETTING_DIR_NAME}/ を作成しました"
  Database.init
end

.kindlegen_pathObject



347
348
349
350
# File 'lib/narou.rb', line 347

def kindlegen_path
  postfix = Helper.os_windows? ? ".exe" : ""
  aozoraepub3_path.dirname.join("kindlegen#{postfix}")
end

.last_commit_yearObject



46
47
48
# File 'lib/narou.rb', line 46

def last_commit_year
  2024
end

.latest_versionObject

Narou.rb gem の最新バージョン番号を取得する

rubygems公式APIによる取得は、WindowsでのSSL証明書問題で取得出来ない 環境があるため、gemコマンド経由で取得する



334
335
336
337
338
339
# File 'lib/narou.rb', line 334

def latest_version
  response = `gem search ^narou$`.split("\n")
  if response.last =~ /\Anarou \(([0-9.]+).*?\)\z/
    $1
  end
end

.line_height(default: LINE_HEIGHT_DEFAULT) ⇒ Object



353
354
355
356
# File 'lib/narou.rb', line 353

def line_height(default: LINE_HEIGHT_DEFAULT)
  global_setting = Inventory.load("global_setting", :global)
  global_setting["line-height"] || default
end

.load_global_replace_patternObject



155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/narou.rb', line 155

def load_global_replace_pattern
  path = root_dir.join(GLOBAL_REPLACE_NAME)
  pairs =
    if path.exist?
      Helper::CacheLoader.memo(path) do |text|
        parse_replace_txt(text)
      end
    else
      []
    end
  @@global_replace_pattern_pairs = pairs
  pairs
end

.local_setting_dirObject



70
71
72
# File 'lib/narou.rb', line 70

def local_setting_dir
  root_dir&.join(LOCAL_SETTING_DIR_NAME)
end

.log_dirObject



91
92
93
# File 'lib/narou.rb', line 91

def log_dir
  root_dir&.join(LOG_DIR)
end

.misc_dirObject



273
274
275
# File 'lib/narou.rb', line 273

def misc_dir
  root_dir.join(MISC_DIR)
end

.novel_frozen?(target) ⇒ Boolean

Returns:

  • (Boolean)


124
125
126
127
# File 'lib/narou.rb', line 124

def novel_frozen?(target)
  id = Downloader.get_id_by_target(target) or return false
  Inventory.load("freeze").include?(id)
end

.novel_type_text(type) ⇒ Object



324
325
326
# File 'lib/narou.rb', line 324

def novel_type_text(type)
  type == 2 ? "短編" : "連載"
end

.parse_replace_txt(text) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/narou.rb', line 137

def parse_replace_txt(text)
  pattern = []
  text.each_line do |line|
    line.sub!(/[\r\n]+\z/, "")
    next if line[0] == ";" # コメント記号
    pair = line.split("\t", 2)
    if pair.length == 2 && pair[0]
      pattern << pair
    end
  end
  pattern
end

.preset_dirObject



95
96
97
# File 'lib/narou.rb', line 95

def preset_dir
  script_dir&.join(PRESET_DIR)
end

.root_dirObject



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/narou.rb', line 50

def root_dir
  root = nil
  path = Dir.pwd
  drive_letter = ""
  if Helper.os_windows?
    path.encode!(Encoding::UTF_8)
    path.gsub!(/^[a-z]:/i, "")
    drive_letter = $&
  end
  while path != ""
    if File.directory?("#{drive_letter}#{path}/#{LOCAL_SETTING_DIR_NAME}")
      root = drive_letter + path
      break
    end
    path.gsub!(%r!/[^/]*$!, "")
  end
  Pathname(root) if root
end

.save_global_replace_patternObject



173
174
175
176
# File 'lib/narou.rb', line 173

def save_global_replace_pattern
  path = root_dir.join(GLOBAL_REPLACE_NAME)
  write_replace_txt(path, @@global_replace_pattern_pairs)
end

.script_dirObject



86
87
88
# File 'lib/narou.rb', line 86

def script_dir
  Pathname(__dir__).join("..").expand_path
end

.themeObject



303
304
305
# File 'lib/narou.rb', line 303

def theme
  Inventory.load("local_setting")["webui.theme"]
end

.theme_namesObject



311
312
313
314
315
316
# File 'lib/narou.rb', line 311

def theme_names
  Dir.glob(get_theme_dir("*")).map do |path|
    name = File.basename(path)
    name == "fonts" ? nil : name
  end.compact
end

.update_sort_key_summaries(left_space = 28) ⇒ Object



295
296
297
298
299
300
301
# File 'lib/narou.rb', line 295

def update_sort_key_summaries(left_space = 28)
  summaries = { "KEY" => "   対象" }.merge(UPDATE_SORT_KEYS)
  key_max_width = summaries.keys.max_by(&:length).length
  summaries.map do |(key, summary)|
    "#{" " * left_space}| #{key.center(key_max_width)} | #{summary}"
  end.join("\n")
end

.web=(bool) ⇒ Object



287
288
289
# File 'lib/narou.rb', line 287

def web=(bool)
  @@is_web = bool
end

.web?Boolean

Returns:

  • (Boolean)


291
292
293
# File 'lib/narou.rb', line 291

def web?
  @@is_web
end

.write_replace_txt(path, pairs) ⇒ Object



150
151
152
153
# File 'lib/narou.rb', line 150

def write_replace_txt(path, pairs)
  buffer = pairs.each_with_object("\t").map(&:join).join("\n")
  File.write(path, buffer)
end