narou 3.1.11 → 3.2.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.

@@ -9,7 +9,7 @@ require_relative "html"
9
9
 
10
10
  class NovelInfo
11
11
  REFRESH_INTERVAL = 10 # キャッシュを捨てて再取得するまでの時間(s)
12
- DEFAULT_OF = "t-nt-ga-s-gf-nu-gl-w"
12
+ DEFAULT_OF = "t-nt-ga-s-gf-nu-gl-w-l"
13
13
  @@novel_info_parameters = {}
14
14
 
15
15
  def self.load(setting, toc_source: nil, of: DEFAULT_OF)
@@ -70,6 +70,7 @@ class NovelInfo
70
70
  story_html.strip_decoration_tag = true
71
71
  result["story"] = story_html.to_aozora
72
72
  result["writer"] = @setting["writer"]
73
+ result["length"] = Helper.numeric_length(@setting["length"])
73
74
  %w(general_firstup novelupdated_at general_lastup).each do |elm|
74
75
  result[elm] = Helper.date_string_to_time(@setting[elm])
75
76
  end
@@ -193,14 +193,6 @@ class NovelSetting
193
193
  @settings[name] = value
194
194
  end
195
195
 
196
- def output_filename
197
- @settings.fetch("output_filename", nil)
198
- end
199
-
200
- def include?(key)
201
- @settings.include?(key)
202
- end
203
-
204
196
  #
205
197
  # replace.txt による置換定義を読み込む
206
198
  #
@@ -452,12 +444,6 @@ $ntag 小説のタグをカンマ区切りにしたもの
452
444
  value: false,
453
445
  help: "HTMLの装飾系タグを削除する(主にArcadiaの作品に影響)"
454
446
  },
455
- {
456
- name: "enable_double_dash_to_image",
457
- type: :boolean,
458
- value: false,
459
- help: "2倍ダッシュ(――)を画像に差し替える。Kindleのデフォルトフォントみたいにダッシュが太くて気になる人用"
460
- },
461
447
  {
462
448
  name: "enable_add_end_to_title",
463
449
  type: :boolean,
@@ -471,6 +457,13 @@ $ntag 小説のタグをカンマ区切りにしたもの
471
457
  help: "1話目から指定した話数分、変換の対象外にする。" \
472
458
  "全話数分以上の数値を指定した場合、最新話だけ変換する"
473
459
  },
460
+ {
461
+ name: "slice_size",
462
+ type: :integer,
463
+ value: 0,
464
+ help: "小説が指定した話数より多い場合、指定した話数ごとに分割する。" \
465
+ "cut_old_subtitlesで処理した後の話数を対象に処理する"
466
+ },
474
467
  {
475
468
  name: "author_comment_style",
476
469
  type: :select,
@@ -479,6 +472,24 @@ $ntag 小説のタグをカンマ区切りにしたもの
479
472
  select_keys: %w(css simple plain),
480
473
  select_summaries: %w(CSSで装飾 シンプルに段落 装飾しない)
481
474
  },
475
+ {
476
+ name: "novel_author",
477
+ type: :string,
478
+ value: "",
479
+ help: "小説の著者名を変更する。作品内著者名及び出力ファイル名に影響する"
480
+ },
481
+ {
482
+ name: "novel_title",
483
+ type: :string,
484
+ value: "",
485
+ help: "小説のタイトルを変更する。作品内タイトル及び出力ファイル名に影響する"
486
+ },
487
+ {
488
+ name: "output_filename",
489
+ type: :string,
490
+ value: "",
491
+ help: "出力ファイル名を任意の文字列に変更する。convert.filename-to-ncode の設定よりも優先される。※拡張子を含めないで下さい"
492
+ },
482
493
  ]
483
494
 
484
495
  ORIGINAL_SETTINGS_KEY_INDEXES = {}.tap { |hash|
@@ -4,5 +4,5 @@
4
4
  #
5
5
 
6
6
  module Narou
7
- VERSION = "3.1.11"
7
+ VERSION = "3.2.0"
8
8
  end
@@ -20,115 +20,7 @@ require_relative "../inventory"
20
20
  require_relative "worker"
21
21
  require_relative "pushserver"
22
22
  require_relative "settingmessages"
23
-
24
- module Narou::ServerHelpers
25
- #
26
- # タグをHTMLで装飾する
27
- #
28
- def decorate_tags(tags)
29
- tags.sort.map do |tag|
30
- %!<span class="tag label label-#{Command::Tag.get_color(tag)}" data-tag="#{escape_html(tag)}">#{escape_html(tag)}</span>!
31
- end.join(" ")
32
- end
33
-
34
- #
35
- # タグをHTMLで装飾する(除外タグ指定用)
36
- #
37
- def decorate_exclusion_tags(tags)
38
- tags.sort.map do |tag|
39
- %!<span class="tag label label-#{Command::Tag.get_color(tag)}" data-exclusion-tag="#{escape_html(tag)}">^tag:#{escape_html(tag)}</span>!
40
- end.join(" ")
41
- end
42
-
43
- #
44
- # Rubyバージョンを構築
45
- #
46
- def build_ruby_version
47
- begin
48
- `"#{RbConfig.ruby}" -v`.strip
49
- rescue
50
- config = RbConfig::CONFIG
51
- "ruby #{RUBY_VERSION}p#{config["PATCHLEVEL"]} [#{RUBY_PLATFORM}]"
52
- end
53
- end
54
-
55
- #
56
- # 有効な novel ID だけの配列を生成する
57
- # ID が指定されなかったか、1件も存在しない場合は nil を返す
58
- #
59
- def select_valid_novel_ids(ids)
60
- return nil unless ids.kind_of?(Array)
61
- result = ids.select do |id|
62
- id =~ /^\d+$/
63
- end
64
- result.empty? ? nil : result
65
- end
66
-
67
- #
68
- # フォーム情報の真偽値データを実際のデータに変換
69
- #
70
- def convert_on_off_to_boolean(str)
71
- case str
72
- when "on"
73
- true
74
- when "off"
75
- false
76
- else
77
- nil
78
- end
79
- end
80
-
81
- #
82
- # nil true false を nil on off という文字列に変換
83
- #
84
- def convert_boolean_to_on_off(bool)
85
- case bool
86
- when TrueClass
87
- "on"
88
- when FalseClass
89
- "off"
90
- else
91
- "nil"
92
- end
93
- end
94
-
95
- #
96
- # HTMLエスケープヘルパー
97
- #
98
- def h(text)
99
- Rack::Utils.escape_html(text)
100
- end
101
-
102
- #
103
- # 与えられたデータが真偽値だった場合、設定画面用に「はい」「いいえ」に変換する
104
- # 真偽値ではなかった場合、そのまま返す
105
- #
106
- def value_to_msg(value)
107
- case value
108
- when TrueClass
109
- "はい"
110
- when FalseClass
111
- "いいえ"
112
- else
113
- value
114
- end
115
- end
116
-
117
- def notepad_text_path
118
- File.join(Narou.local_setting_dir, "notepad.txt")
119
- end
120
-
121
- def query_to_boolean(value, default: false)
122
- case value
123
- when "1", 1, "true", true
124
- true
125
- when "0", 0, "false", false
126
- false
127
- else
128
- default
129
- end
130
- end
131
- end
23
+ require_relative "server_helpers"
132
24
 
133
25
  class Narou::AppServer < Sinatra::Base
134
26
  register Sinatra::Reloader if $development
@@ -273,12 +165,13 @@ class Narou::AppServer < Sinatra::Base
273
165
  # とりあえずDigest認証のみ
274
166
  def setup_server_authentication
275
167
  auth = Inventory.load("global_setting", :global).group("server-digest-auth")
276
- return unless auth.enable
277
-
278
168
  user = auth.user
279
169
  hashed = auth.hashed_password
280
170
  passwd = hashed || auth.password
281
171
 
172
+ # enableかつユーザー名とパスワードが設定されている時のみ認証を有効にする
173
+ return unless auth.enable && user && passwd
174
+
282
175
  self.class.class_exec do
283
176
  use Rack::Auth::Digest::MD5, { realm: "narou.rb", opaque: "", passwords_hashed: hashed } do |username|
284
177
  passwd if username == user
@@ -292,13 +185,13 @@ class Narou::AppServer < Sinatra::Base
292
185
 
293
186
  before do
294
187
  headers "Cache-Control" => "no-cache" if $development
295
- @bootstrap_theme = case params["theme"]
188
+ @bootstrap_theme = case params["webui.theme"]
296
189
  when nil
297
190
  Narou.get_theme
298
191
  when "" # 環境設定画面で未設定が選択された時
299
192
  nil
300
193
  else
301
- params["theme"]
194
+ params["webui.theme"]
302
195
  end
303
196
  Narou::Worker.push_as_system_worker do
304
197
  Inventory.clear
@@ -518,9 +411,9 @@ class Narou::AppServer < Sinatra::Base
518
411
  get "/novels/:id/download" do
519
412
  device = Narou.get_device
520
413
  ext = device ? device.ebook_file_ext : ".epub"
521
- path = Narou.get_ebook_file_path(@id, ext)
522
- if File.exist?(path)
523
- send_file(path, filename: File.basename(path), type: "application/octet-stream")
414
+ paths = Narou.get_ebook_file_paths(@id, ext)
415
+ if !paths.empty? && File.exist?(paths[0])
416
+ send_file(paths[0], filename: File.basename(paths[0]), type: "application/octet-stream")
524
417
  else
525
418
  not_found
526
419
  end
@@ -566,7 +459,12 @@ class Narou::AppServer < Sinatra::Base
566
459
  sitename: data["sitename"],
567
460
  toc_url: data["toc_url"],
568
461
  novel_type: data["novel_type"] == 2 ? "短編" : "連載",
569
- tags: (tags.empty? ? "" : decorate_tags(tags) + '&nbsp;<span class="tag label label-white" data-tag="" data-toggle="tooltip" title="タグ検索を解除">&nbsp;</span>'),
462
+ tags: if tags.empty?
463
+ ""
464
+ else
465
+ %!#{decorate_tags(tags)}&nbsp;<span class="tag tag-reset label label-white"! +
466
+ %!data-tag="" data-toggle="tooltip" title="タグ検索を解除">&nbsp;</span>!
467
+ end,
570
468
  status: [
571
469
  is_frozen ? "凍結" : nil,
572
470
  tags.include?("end") ? "完結" : nil,
@@ -579,6 +477,7 @@ class Narou::AppServer < Sinatra::Base
579
477
  # 掲載話数
580
478
  general_all_no: data["general_all_no"],
581
479
  last_check_date: data["last_check_date"].tap { |m| break m.to_i if m },
480
+ length: data["length"],
582
481
  }
583
482
  end.compact
584
483
  json_objects[:recordsTotal] = json_objects[:data].size
@@ -631,8 +530,10 @@ class Narou::AppServer < Sinatra::Base
631
530
  end
632
531
  Narou::Worker.push do
633
532
  cmd = Command::Update.new
634
- cmd.on(:success) do
635
- @@push_server.send_all(:"table.reload")
533
+ if table_reload_timing == "every"
534
+ cmd.on(:success) do
535
+ @@push_server.send_all(:"table.reload")
536
+ end
636
537
  end
637
538
  cmd.execute!([ids, opt_arguments].flatten)
638
539
  @@push_server.send_all(:"table.reload")
@@ -651,8 +552,10 @@ class Narou::AppServer < Sinatra::Base
651
552
  pass if tag_params.empty?
652
553
  Narou::Worker.push do
653
554
  cmd = Command::Update.new
654
- cmd.on(:success) do
655
- @@push_server.send_all(:"table.reload")
555
+ if table_reload_timing == "every"
556
+ cmd.on(:success) do
557
+ @@push_server.send_all(:"table.reload")
558
+ end
656
559
  end
657
560
  cmd.execute!(tag_params)
658
561
  @@push_server.send_all(:"table.reload")
@@ -763,7 +666,7 @@ class Narou::AppServer < Sinatra::Base
763
666
 
764
667
  get "/api/tag_list" do
765
668
  result =
766
- '<div><span class="tag label label-default" data-tag="">タグ検索を解除</span></div>' \
669
+ '<div><span class="tag tag-reset label label-default" data-tag="">タグ検索を解除</span></div>' \
767
670
  '<div class="text-muted" style="font-size:10px">Altキーを押しながらで除外検索</div>'
768
671
  tagname_list = Command::Tag.get_tag_list.keys
769
672
  tagname_list.sort.each do |tagname|
@@ -55,7 +55,7 @@ var Narou = (function() {
55
55
  storage = new Storage();
56
56
 
57
57
  /*************************************************************************
58
- * ユーティリティ
58
+ * ユーティリティ / Utility
59
59
  *************************************************************************/
60
60
  $.extend(Narou, {
61
61
  registerCloseHandler: function(callback) {
@@ -115,6 +115,19 @@ var Narou = (function() {
115
115
  allowScroll: function() {
116
116
  $("html, body").css("overflow", "");
117
117
  },
118
+
119
+ // 桁数の多い数字を、千や万単位に変換する
120
+ unitizeNumeric: function(num) {
121
+ var man = num / 10000 | 0;
122
+ if (man) {
123
+ return man + "万";
124
+ }
125
+ var sen = num / 1000 | 0;
126
+ if (sen) {
127
+ return sen + "千";
128
+ }
129
+ return num;
130
+ },
118
131
  });
119
132
 
120
133
  /*************************************************************************
@@ -1145,8 +1158,20 @@ var Narou = (function() {
1145
1158
  self.search();
1146
1159
  self.myFilterClearToggleVisiblity();
1147
1160
  });
1161
+
1162
+ this.table
1163
+ // 通常単語を追加するためのイベント
1164
+ .on("click", "[data-add-filter]", function(e) {
1165
+ e.stopPropagation();
1166
+ self.appendWordToFilter($(this).data("addFilter"));
1167
+ })
1168
+ // 追加イベントが発生した時に行選択イベントが起こらないように
1169
+ .on("mousedown", "[data-add-filter]", function(e) {
1170
+ e.stopPropagation();
1171
+ });
1148
1172
  },
1149
1173
 
1174
+ // フィルタにタグを追加
1150
1175
  appendTagToFilter: function(tag_name, exclude) {
1151
1176
  if (_.includes(this.filter_tags, tag_name) ||
1152
1177
  _.includes(this.exclusion_tags, tag_name)) {
@@ -1171,6 +1196,14 @@ var Narou = (function() {
1171
1196
  this.myFilterClearToggleVisiblity();
1172
1197
  },
1173
1198
 
1199
+ // フィルタに通常の単語を追加
1200
+ appendWordToFilter: function(word) {
1201
+ var str = (this.myfilter.val() + " " + word).trim() + " ";
1202
+ this.myfilter.val(str);
1203
+ this.search()
1204
+ this.myFilterClearToggleVisiblity();
1205
+ },
1206
+
1174
1207
  removeAllTagsByFilter: function() {
1175
1208
  var normal_words = this.splitFilter().normal;
1176
1209
  this.myfilter.val(normal_words.join(" "));
@@ -24,14 +24,14 @@ $(document).ready(function() {
24
24
  id: false, last_update: false, title: true, author: false, sitename: false,
25
25
  toc_url: false, novel_type: false, tags: false, status: false, menu: true,
26
26
  download: true, folder: false, update_button: false, general_lastup: false,
27
- general_all_no: false, last_check_date: false,
27
+ general_all_no: false, last_check_date: false, length: false, average_length: false,
28
28
  }
29
29
  // PC
30
30
  : {
31
31
  id: true, last_update: true, title: true, author: true, sitename: true,
32
32
  toc_url: true, novel_type: false, tags: true, status: true, menu: true,
33
33
  download: false, folder: true, update_button: true, general_lastup: true,
34
- general_all_no: false, last_check_date: false,
34
+ general_all_no: false, last_check_date: false, length: false, average_length: false,
35
35
  });
36
36
 
37
37
  $.fn.dataTable.Api.register("fireChangeSelect()", function() {
@@ -258,10 +258,30 @@ $(document).ready(function() {
258
258
  data: "title", visible: cell_visible.title },
259
259
  // 作者名
260
260
  { title: "作者名",
261
- data: "author", visible: cell_visible.author },
261
+ data: "author", visible: cell_visible.author,
262
+ render: function(author, type, row) {
263
+ if (author && type === "display") {
264
+ return sprintf(
265
+ '<span class="add-filter" data-add-filter="%(author)s">%(author)s</span>',
266
+ { author: author }
267
+ );
268
+ }
269
+ return author;
270
+ }
271
+ },
262
272
  // 掲載サイト
263
273
  { title: "掲載",
264
- data: "sitename", visible: cell_visible.sitename },
274
+ data: "sitename", visible: cell_visible.sitename,
275
+ render: function(sitename, type, row) {
276
+ if (sitename && type === "display") {
277
+ return sprintf(
278
+ '<span class="add-filter" data-add-filter="%(sitename)s">%(sitename)s</span>',
279
+ { sitename: sitename }
280
+ );
281
+ }
282
+ return sitename;
283
+ }
284
+ },
265
285
  // 小説種別
266
286
  { title: "種別",
267
287
  data: "novel_type", className: "text-center", width: "25px",
@@ -282,6 +302,31 @@ $(document).ready(function() {
282
302
  return data;
283
303
  }
284
304
  },
305
+ // 文字数
306
+ {
307
+ title: "文字数",
308
+ data: "length", visible: cell_visible.length,
309
+ className: "text-center", searchable: false, orderSequence: [ "desc", "asc" ],
310
+ render: function(length, type, row) {
311
+ if (length && type === "display") {
312
+ return Narou.unitizeNumeric(length) + "字";
313
+ }
314
+ return length;
315
+ }
316
+ },
317
+ // 1話あたりの平均文字数
318
+ {
319
+ title: "平均<br>文字数",
320
+ data: "average_length", visible: cell_visible.average_length,
321
+ className: "text-center", searchable: false, orderSequence: [ "desc", "asc" ],
322
+ render: function(_data, type, row) {
323
+ var averageLength = row.length / row.general_all_no | 0;
324
+ if (averageLength && type === "display") {
325
+ return averageLength.toLocaleString();
326
+ }
327
+ return averageLength || null;
328
+ }
329
+ },
285
330
  // 状態
286
331
  { title: "状態",
287
332
  data: "status", visible: cell_visible.status },
@@ -850,13 +895,16 @@ $(document).ready(function() {
850
895
  $("#action-view-all").on(click_event_name, function(e) {
851
896
  e.preventDefault();
852
897
  slideNavbar.slide();
853
- var aoColumns = table.settings()[0].aoColumns;
854
- table.columns().eq(0).each(function(index) {
855
- if (!aoColumns[index].system_column) {
856
- table.column(index).visible(true);
857
- }
898
+ bootbox.confirm("全ての項目を表示します。よろしいですか?", function(result) {
899
+ if (!result) return;
900
+ var aoColumns = table.settings()[0].aoColumns;
901
+ table.columns().eq(0).each(function(index) {
902
+ if (!aoColumns[index].system_column) {
903
+ table.column(index).visible(true);
904
+ }
905
+ });
906
+ table.draw(false);
858
907
  });
859
- table.draw(false);
860
908
  });
861
909
 
862
910
  /*
@@ -968,8 +1016,14 @@ $(document).ready(function() {
968
1016
  * 表示>表示設定を全てリセット
969
1017
  */
970
1018
  $("#action-view-reset").on(click_event_name, function(e) {
971
- // localStorageを全て消してリロードする
972
- localStorage.clear();
1019
+ e.preventDefault();
1020
+ slideNavbar.slide();
1021
+ bootbox.confirm("表示に関わる全ての設定がリセットされます。よろしいですか?", function(result) {
1022
+ if (!result) return;
1023
+ // localStorageを全て消してリロードする
1024
+ localStorage.clear();
1025
+ location.href = "/";
1026
+ });
973
1027
  });
974
1028
 
975
1029
  /*