narou 1.4.6 → 1.5.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.

@@ -108,7 +108,7 @@ module Helper
108
108
  # エンティティ復号
109
109
  #
110
110
  def restor_entity(str)
111
- result = str.dup
111
+ result = str.encode("UTF-16BE", "UTF-8", :invalid => :replace, :undef => :replace, :replace => "?").encode("UTF-8")
112
112
  ENTITIES.each do |key, value|
113
113
  result.gsub!("&#{key};", value)
114
114
  end
@@ -0,0 +1,83 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright 2013 whiteleaf. All rights reserved.
4
+ #
5
+
6
+ class HTML
7
+ attr_accessor :string
8
+
9
+ def initialize(string = "")
10
+ @string = string
11
+ @illust_current_url = nil
12
+ @illust_grep_pattern = /<img.+?src=\"(?<src>.+?)\".*?>/i
13
+ end
14
+
15
+ #
16
+ # 挿絵を置換するための設定を変更する
17
+ #
18
+ def set_illust_setting(options)
19
+ unless options.kind_of?(Hash)
20
+ raise ArgumentError, "invalid parameter(s), need Hash"
21
+ end
22
+ @illust_current_url = options[:current_url] if options[:current_url]
23
+ if grep_pattern = options[:grep_pattern]
24
+ @illust_grep_pattern = grep_pattern.kind_of?(Regexp) ? grep_pattern : /#{grep_pattern}/m
25
+ end
26
+ end
27
+
28
+ #
29
+ # 青空文庫形式に変換
30
+ #
31
+ def to_aozora
32
+ @string = br_to_aozora
33
+ @string = ruby_to_aozora
34
+ @string = b_to_aozora
35
+ @string = i_to_aozora
36
+ @string = s_to_aozora
37
+ @string = img_to_aozora
38
+ @string = delete_tag
39
+ @string
40
+ end
41
+
42
+ def delete_tag(text = @string)
43
+ text.gsub(/<.+?>/, "")
44
+ end
45
+
46
+ def br_to_aozora(text = @string)
47
+ text.gsub(/[\r\n]+/, "").gsub(/<br.*?>/i, "\n")
48
+ end
49
+
50
+ def ruby_to_aozora(text = @string)
51
+ text.tr("《》", "≪≫")
52
+ .gsub(/<ruby>(.+?)<\/ruby>/i) do
53
+ splited_ruby = $1.split(/<rt>/i)
54
+ next delete_tag(splited_ruby[0]) unless splited_ruby[1]
55
+ ruby_base = delete_tag(splited_ruby[0].split(/<rp>/i)[0])
56
+ ruby_text = delete_tag(splited_ruby[1].split(/<rp>/i)[0])
57
+ "|#{ruby_base}《#{ruby_text}》"
58
+ end
59
+ end
60
+
61
+ def b_to_aozora(text = @string)
62
+ text.gsub(/<b>/i, "[#太字]").gsub(/<\/b>/i, "[#太字終わり]")
63
+ end
64
+
65
+ def i_to_aozora(text = @string)
66
+ text.gsub(/<i>/i, "[#斜体]").gsub(/<\/i>/i, "[#斜体終わり]")
67
+ end
68
+
69
+ def s_to_aozora(text = @string)
70
+ text.gsub(/<s>/i, "[#取消線]").gsub(/<\/s>/i, "[#取消線終わり]")
71
+ end
72
+
73
+ def img_to_aozora(text = @string)
74
+ if @illust_grep_pattern
75
+ text.gsub(@illust_grep_pattern) do
76
+ url = @illust_current_url ? URI.join(@illust_current_url, $~[:src]) : $~[:src]
77
+ "[#挿絵(#{url})入る]"
78
+ end
79
+ else
80
+ text
81
+ end
82
+ end
83
+ end
@@ -10,7 +10,8 @@ require "open-uri"
10
10
  #
11
11
  class Illustration
12
12
  ILLUST_DIR = "挿絵/"
13
- ILLUST_URL = "http://%s.mitemin.net/userpageimage/viewimage/icode/%s/"
13
+ NAROU_ILLUST_URL = "http://%s.mitemin.net/userpageimage/viewimage/icode/%s/"
14
+ NAROU_ILLUST_TAG_PATTERN = /^<(i[0-9]+)\|([0-9]+)>\n?/m
14
15
 
15
16
  MIME = { "image/jpeg" => "jpg", "image/png" => "png", "image/gif" => "gif", "image/bmp" => "bmp" }
16
17
 
@@ -21,60 +22,63 @@ class Illustration
21
22
  @inspector = inspector
22
23
  end
23
24
 
25
+ def scanner(source, &block)
26
+ source.gsub!(/[#挿絵((.+?))入る]/) do
27
+ url = $1
28
+ path = url =~ URI.regexp ? download_image(url) : url
29
+ path ? block.call(make_illust_chuki(path)) : ""
30
+ end
31
+ source.gsub!(NAROU_ILLUST_TAG_PATTERN) do
32
+ id1, id2 = $1, $2
33
+ basename = "#{id1},#{id2}.*"
34
+ url = NAROU_ILLUST_URL % [id2, id1]
35
+ path = download_image(url, basename)
36
+ path ? block.call(make_illust_chuki(path)) : ""
37
+ end
38
+ source
39
+ end
40
+
24
41
  #
25
- # 小説家になろうのイラストタグから挿絵データを取得する
42
+ # 画像のURLからデータを保存して、保存したファイルの絶対パスを返す
26
43
  #
27
- def get(illust_tag)
28
- if illust_tag =~ /<(.+)\|(.+)>/
29
- id1, id2 = $1, $2
30
- illust_path = make_illust_path(id1, id2)
31
- unless illust_path
32
- # 挿絵画像をサイトからダウンロードして保存する
33
- open(make_url(id1, id2)) do |read_fp|
34
- read_fp.autoclose = true
35
- content_type = read_fp.meta["content-type"]
36
- ext = MIME[content_type] or raise UnknownMIMEType, content_type
37
- illust_path = make_illust_path(id1, id2, ext, false)
38
- illust_dir = File.dirname(illust_path)
39
- Dir.mkdir(illust_dir) unless File.exists?(illust_dir)
40
- open(illust_path, "wb") do |write_fp|
41
- write_fp.write(read_fp.read)
42
- end
43
- @inspector.info("挿絵「#{File.basename(illust_path)}」を保存しました。")
44
- end
44
+ def download_image(url, basename = nil)
45
+ basename = File.basename(basename ? basename : url, ".*")
46
+ if path = search_image(basename)
47
+ return path
48
+ end
49
+ open(url) do |fp|
50
+ content_type = fp.meta["content-type"]
51
+ ext = MIME[content_type] or raise UnknownMIMEType, content_type
52
+ illust_abs_path = create_illust_path(basename) + "." + ext
53
+ open(illust_abs_path, "wb") do |write_fp|
54
+ write_fp.write(fp.read)
45
55
  end
46
- chuki = make_illust_chuki(illust_path)
47
- chuki
48
- else
49
- # 有効なイラストタグではなかった
50
- @inspector.error("Illustration#get: #{illust_tag} は有効なイラストタグではありません。")
51
- nil
56
+ @inspector.info("挿絵「#{File.basename(illust_abs_path)}」を保存しました。")
57
+ return illust_abs_path
52
58
  end
53
59
  rescue UnknownMIMEType => e
54
- @inspector.error("Illustration#get: イラストタグ #{illust_tag} は対応した画像フォーマットではありません" + \
60
+ @inspector.error("Illustration#download_image: #{url} は未対応の画像フォーマットです" +
55
61
  "(content-type: #{e.message})")
56
62
  nil
57
63
  rescue StandardError => e
58
- @inspector.error("Illustration#get: イラストタグ #{illust_tag} を処理中に例外が発生しました(#{e})")
64
+ @inspector.error("Illustration#download_image: #{url} を処理中に例外が発生しました(#{e})")
59
65
  nil
60
66
  end
61
67
 
62
- def make_illust_chuki(illust_path)
63
- rel_illust_path = to_rel(@setting.archive_path, illust_path)
64
- "[#挿絵(#{rel_illust_path})入る]"
68
+ def search_image(basename)
69
+ path = create_illust_path(basename) + ".*"
70
+ Dir.glob(path)[0]
65
71
  end
66
72
 
67
- def make_illust_path(id1, id2, ext = "*", check = true)
68
- path = File.join(@setting.archive_path, ILLUST_DIR, "#{id1},#{id2}.#{ext}")
69
- if check
70
- Dir.glob(path)[0]
71
- else
72
- path
73
- end
73
+ def create_illust_path(basename)
74
+ illust_abs_dir = File.join(@setting.archive_path, ILLUST_DIR)
75
+ Dir.mkdir(illust_abs_dir) unless File.exists?(illust_abs_dir)
76
+ File.join(illust_abs_dir, basename)
74
77
  end
75
78
 
76
- def make_url(id1, id2)
77
- ILLUST_URL % [id2, id1]
79
+ def make_illust_chuki(illust_path)
80
+ rel_illust_path = to_rel(@setting.archive_path, illust_path)
81
+ "[#挿絵(#{rel_illust_path})入る]"
78
82
  end
79
83
 
80
84
  #
@@ -5,19 +5,13 @@
5
5
 
6
6
  require "open-uri"
7
7
  require "yaml"
8
- require "time"
9
- require_relative "../sitesetting"
8
+ require_relative "../novelinfo"
10
9
 
11
10
  module Narou
12
11
  #
13
12
  # 小説家になろうデベロッパーAPI操作クラス
14
13
  #
15
14
  class API
16
- INFO_SETTING_FILE = "narou_novel_info.yaml"
17
- NOVEL_TYPE = { "連載中" => 1, "完結済" => 1, "短編" => 2 }
18
-
19
- @@novel_info_parameters = {}
20
-
21
15
  #
22
16
  # なろうデベロッパーAPIから情報を取得
23
17
  #
@@ -49,7 +43,7 @@ module Narou
49
43
  else
50
44
  # なろうAPIからデータを取得出来なかった
51
45
  # 開示設定が検索から除外に設定されるとAPIからはアクセスできなくなる
52
- result = parse_novel_info
46
+ result = NovelInfo.load(@setting)
53
47
  unless result
54
48
  error "小説家になろうからデータを取得出来ませんでした"
55
49
  exit
@@ -58,32 +52,5 @@ module Narou
58
52
  end
59
53
  end
60
54
  end
61
-
62
- #
63
- # 小説情報ページをパースして必要な情報を取り出す
64
- #
65
- def parse_novel_info
66
- return @@novel_info_parameters[@ncode] if @@novel_info_parameters[@ncode]
67
- result = {}
68
- of = "nt-s-gf-nu-gl"
69
- request_output_parameters = of.split("-")
70
- info_source = ""
71
- open(@setting["narou_info_url"]) do |fp|
72
- info_source = Helper.pretreatment_source(fp.read)
73
- end
74
- info_setting = SiteSetting.load_file(File.join(Narou.get_preset_dir, INFO_SETTING_FILE))
75
- info_setting.multi_match(info_source, *request_output_parameters)
76
- result["novel_type"] = NOVEL_TYPE[info_setting["novel_type"]]
77
- result["story"] = info_setting["story"].gsub("<br />", "")
78
- %w(general_firstup novelupdated_at general_lastup).each do |elm|
79
- result[elm] = date_string_to_time(info_setting[elm])
80
- end
81
- @@novel_info_parameters[@ncode] = result
82
- result
83
- end
84
-
85
- def date_string_to_time(date)
86
- date ? Time.parse(date.tr("年月日時分秒", "///:::")) : nil
87
- end
88
55
  end
89
56
  end
@@ -14,6 +14,7 @@ require_relative "template"
14
14
  require_relative "progressbar"
15
15
  require_relative "helper"
16
16
  require_relative "localsetting"
17
+ require_relative "html"
17
18
 
18
19
  class NovelConverter
19
20
  NOVEL_TEXT_TEMPLATE_NAME = "novel.txt"
@@ -21,6 +22,10 @@ class NovelConverter
21
22
 
22
23
  attr_reader :use_dakuten_font
23
24
 
25
+ if Narou.already_init?
26
+ @@site_settings = Downloader.load_settings
27
+ end
28
+
24
29
  #
25
30
  # 指定の小説を整形・変換する
26
31
  #
@@ -234,6 +239,8 @@ class NovelConverter
234
239
  toc = @toc
235
240
  cover_chuki = @cover_chuki
236
241
  device = Narou.get_device
242
+ # タイトルがルビ化されてしまうのを抑制
243
+ toc["title"] = toc["title"].gsub("《", "※[#始め二重山括弧]").gsub("》", "※[#終わり二重山括弧]")
237
244
  tempalte_name = (device && device.ibunko? ? NOVEL_TEXT_TEMPLATE_NAME_FOR_IBUNKO : NOVEL_TEXT_TEMPLATE_NAME)
238
245
  Template.get(tempalte_name, binding)
239
246
  end
@@ -264,6 +271,10 @@ class NovelConverter
264
271
  end
265
272
  end
266
273
 
274
+ def find_site_setting
275
+ @@site_settings.find { |s| s.multi_match(@toc["toc_url"], "url") }
276
+ end
277
+
267
278
  #
268
279
  # 変換処理メイン
269
280
  #
@@ -286,6 +297,10 @@ class NovelConverter
286
297
  @section_save_dir = Downloader.get_novel_section_save_dir(@setting.archive_path)
287
298
  @toc = Downloader.get_toc_data(@setting.archive_path)
288
299
  @toc["story"] = conv.convert(@toc["story"], "story")
300
+ html = HTML.new
301
+ site_setting = find_site_setting
302
+ html.set_illust_setting(current_url: site_setting["illust_current_url"],
303
+ grep_pattern: site_setting["illust_grep_pattern"])
289
304
  progressbar = ProgressBar.new(@toc["subtitles"].count)
290
305
  @toc["subtitles"].each_with_index do |subinfo, i|
291
306
  progressbar.output(i)
@@ -295,7 +310,12 @@ class NovelConverter
295
310
  end
296
311
  @inspector.subtitle = section["subtitle"]
297
312
  element = section["element"]
313
+ data_type = element.delete("data_type") || "text"
298
314
  element.each do |text_type, elm_text|
315
+ if data_type == "html"
316
+ html.string = elm_text
317
+ elm_text = html.to_aozora
318
+ end
299
319
  element[text_type] = conv.convert(elm_text, text_type)
300
320
  end
301
321
  section["subtitle"] = conv.convert(section["subtitle"], "subtitle")
@@ -0,0 +1,48 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright 2013 whiteleaf. All rights reserved.
4
+ #
5
+
6
+ require "open-uri"
7
+ require "time"
8
+ require_relative "sitesetting"
9
+ require_relative "html"
10
+
11
+ class NovelInfo
12
+ @@novel_info_parameters = {}
13
+
14
+ def self.load(setting)
15
+ info = new(setting)
16
+ info.parse_novel_info
17
+ end
18
+
19
+ def initialize(setting)
20
+ @setting = setting
21
+ @ncode = @setting["ncode"]
22
+ @@novel_info_parameters[@setting["name"]] ||= {}
23
+ end
24
+
25
+ def parse_novel_info
26
+ info_url = @setting["novel_info_url"] or return nil
27
+ result = @@novel_info_parameters[@setting["name"]][@ncode] ||= {}
28
+ return result unless result.empty?
29
+ of = "nt-s-gf-nu-gl-w"
30
+ request_output_parameters = of.split("-")
31
+ info_source = ""
32
+ open(info_url) do |fp|
33
+ info_source = Helper.pretreatment_source(fp.read, @setting["encoding"])
34
+ end
35
+ @setting.multi_match(info_source, *request_output_parameters)
36
+ result["novel_type"] = @setting["novel_type_string"][@setting["novel_type"]] || 1
37
+ result["story"] = @setting["story"]
38
+ result["writer"] = @setting["writer"]
39
+ %w(general_firstup novelupdated_at general_lastup).each do |elm|
40
+ result[elm] = date_string_to_time(@setting[elm])
41
+ end
42
+ result
43
+ end
44
+
45
+ def date_string_to_time(date)
46
+ date ? Time.parse(date.sub(/[\((].+?[\))]/, "").tr("年月日時分秒", "///:::")) : nil
47
+ end
48
+ end
@@ -182,9 +182,9 @@ class NovelSetting
182
182
  help: "ルビ処理を有効に"
183
183
  },
184
184
  {
185
- name: "enable_narou_illust",
185
+ name: "enable_illust",
186
186
  value: true,
187
- help: "小説家になろうの挿絵タグを有効にする(false なら削除)"
187
+ help: "挿絵タグを有効にする(false なら削除)"
188
188
  },
189
189
  {
190
190
  name: "enable_transform_fraction",
@@ -51,8 +51,13 @@ class SiteSetting
51
51
  end
52
52
  end
53
53
 
54
+ def is_container?(value)
55
+ value.kind_of?(Hash) || value.kind_of?(Array)
56
+ end
57
+
54
58
  def replace_group_values(key, option_values = {})
55
59
  dest = option_values[key] || @match_values[key] || @yaml_setting[key]
60
+ return dest if is_container?(dest)
56
61
  begin
57
62
  result = dest.dup
58
63
  rescue TypeError
@@ -3,7 +3,7 @@
3
3
  # Copyright 2013 whiteleaf. All rights reserved.
4
4
  #
5
5
 
6
- Version = "1.4.6"
6
+ Version = "1.5.0"
7
7
 
8
8
  cv_path = File.expand_path(File.join(File.dirname(__FILE__), "../commitversion"))
9
9
  if File.exists?(cv_path)
@@ -46,11 +46,19 @@ narou コマンドのインストール or アップデートが完了しまし
46
46
  初めてこのアプリケーションを使う場合、小説管理用のフォルダを初期化する必要があります。
47
47
  任意のフォルダで `narou init' を実行して下さい。
48
48
 
49
- 2014/02/28 : **1.4.6**
49
+ 2014/03/06 : **1.5.0**
50
50
  * 追加機能もしくは仕様変更
51
- - `narou diff` コマンドに(凍結済を除く)全ての小説の差分データを削除する `--all-clean` オプションが追加されました
51
+ - 小説投稿サイト ハーメルン (http://syosetu.org/) に対応しました
52
+ - 小説投稿サイト Arcadia (http://www.mai-net.net/) に対応しました
53
+ + Arcadiaは `narou d "http://www.mai-net.net/bbs/sst/sst.php?~略~&n=0&count=1"` のように
54
+ URLを " で囲まないとコマンドがきちんと通りませんのでご注意下さい
55
+ - このバージョン以降ダウンロードした小説の保存フォルダ名には、タイトルの前にIDが付加されるようになりました
56
+ - アラビア数字を漢数字に変換しないパターンを追加(%や単位系)
57
+ - `setting.ini` の項目、 `enable_narou_illust` が `enable_illust` に変更になりました
52
58
  * Bug Fix
53
- - 傍点化の処理が特殊な状況下で Index Error を吐いて止まっていた問題を修正
59
+ - `narou browser --vote` コマンドがなろうのレイアウト変更に対応していなかったので修正
60
+ - 半角カナを全角カナに変換するように修正 #36
61
+ - 小説のタイトルにはルビをふれないように修正 #37
54
62
 
55
63
  #{"*" * 79}
56
64
  EOS
data/narou.rb CHANGED
@@ -7,11 +7,11 @@
7
7
  #
8
8
 
9
9
  $debug = File.exists?(File.join(File.expand_path(File.dirname($0)), "debug"))
10
- watch = File.exists?(File.join(File.expand_path(File.dirname($0)), "watch"))
11
10
  Encoding.default_external = Encoding::UTF_8
12
11
 
13
12
  require_relative "lib/globalsetting"
14
13
 
14
+ display_time = ARGV.delete("--time")
15
15
  display_backtrace = ARGV.delete("--backtrace")
16
16
  display_backtrace ||= $debug
17
17
  $disable_color = ARGV.delete("--no-color")
@@ -23,11 +23,11 @@ require_relative "lib/commandline"
23
23
 
24
24
  rescue_level = $debug ? Exception : StandardError
25
25
 
26
- if watch
27
- require_relative "lib/watcher"
28
- Watcher.__regist__ Helper, LocalSetting, Device, WinAPI, Narou, GlobalSetting, Command,
29
- Database, SiteSetting, Template, Downloader, Ini, NovelSetting, Inspector,
30
- Illustration, ProgressBar, ConverterBase, NovelConverter, CommandLine
26
+ if display_time
27
+ now = Time.now
28
+ at_exit do
29
+ puts "実行時間 #{Time.now - now}秒"
30
+ end
31
31
  end
32
32
 
33
33
  begin
@@ -44,3 +44,4 @@ rescue rescue_level => e
44
44
  end
45
45
  exit 1
46
46
  end
47
+