narou 3.1.11 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of narou might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.rubocop.yml +12 -9
- data/ChangeLog.md +42 -2
- data/README.md +32 -17
- data/lib/command/convert.rb +22 -7
- data/lib/command/init.rb +65 -29
- data/lib/command/mail.rb +24 -22
- data/lib/command/send.rb +26 -23
- data/lib/command/setting.rb +20 -5
- data/lib/command/update.rb +1 -1
- data/lib/command/update/general_lastup_updater.rb +4 -3
- data/lib/converterbase.rb +0 -33
- data/lib/downloader.rb +25 -4
- data/lib/helper.rb +19 -2
- data/lib/html.rb +4 -0
- data/lib/narou.rb +42 -10
- data/lib/novelconverter.rb +87 -38
- data/lib/novelinfo.rb +2 -1
- data/lib/novelsetting.rb +25 -14
- data/lib/version.rb +1 -1
- data/lib/web/appserver.rb +25 -122
- data/lib/web/public/resources/narou.library.js +34 -1
- data/lib/web/public/resources/narou.ui.js +66 -12
- data/lib/web/server_helpers.rb +121 -0
- data/lib/web/views/settings.haml +1 -1
- data/lib/web/views/style.scss +12 -0
- data/narou.gemspec +1 -0
- data/narou.rb +3 -7
- data/preset/ncode.syosetu.com/n9463br/setting.ini +0 -4
- data/preset/vertical_font.css +1 -1
- data/preset/vertical_font_with_dakuten.css +1 -1
- data/webnovel/kakuyomu.jp.yaml +12 -7
- data/webnovel/ncode.syosetu.com.yaml +5 -0
- data/webnovel/novel18.syosetu.com.yaml +5 -0
- data/webnovel/syosetu.org.yaml +3 -0
- metadata +17 -4
- data/preset/doubledash.png +0 -0
- data/preset/singledash.png +0 -0
    
        data/lib/command/send.rb
    CHANGED
    
    | @@ -113,20 +113,21 @@ module Command | |
| 113 113 | 
             
                      next
         | 
| 114 114 | 
             
                    end
         | 
| 115 115 | 
             
                    if target == "hotentry"
         | 
| 116 | 
            -
                       | 
| 116 | 
            +
                      ebook_paths = [Update.get_newest_hotentry_file_path(device)]
         | 
| 117 117 | 
             
                    else
         | 
| 118 | 
            -
                       | 
| 118 | 
            +
                      ebook_paths = Narou.get_ebook_file_paths(target, device.ebook_file_ext)
         | 
| 119 119 | 
             
                    end
         | 
| 120 | 
            -
                    unless  | 
| 120 | 
            +
                    unless ebook_paths[0]
         | 
| 121 121 | 
             
                      error "#{target} は存在しません"
         | 
| 122 122 | 
             
                      next
         | 
| 123 123 | 
             
                    end
         | 
| 124 | 
            -
                    unless File.exist?( | 
| 125 | 
            -
                      error "まだファイル(#{File.basename( | 
| 124 | 
            +
                    unless File.exist?(ebook_paths[0])
         | 
| 125 | 
            +
                      error "まだファイル(#{File.basename(ebook_paths[0])})が無いようです" unless send_all
         | 
| 126 126 | 
             
                      next
         | 
| 127 127 | 
             
                    end
         | 
| 128 128 |  | 
| 129 | 
            -
                     | 
| 129 | 
            +
                    # TODO: should check all items in ebook_paths
         | 
| 130 | 
            +
                    if !@options["force"] && !device.ebook_file_old?(ebook_paths[0])
         | 
| 130 131 | 
             
                      next
         | 
| 131 132 | 
             
                    end
         | 
| 132 133 | 
             
                    display_target =
         | 
| @@ -138,23 +139,25 @@ module Command | |
| 138 139 | 
             
                    puts "<bold><green>#{display_target}</green></bold>".termcolor
         | 
| 139 140 |  | 
| 140 141 | 
             
                    print "#{device.name}へ送信しています"
         | 
| 141 | 
            -
                     | 
| 142 | 
            -
             | 
| 143 | 
            -
             | 
| 144 | 
            -
             | 
| 145 | 
            -
                       | 
| 146 | 
            -
             | 
| 147 | 
            -
             | 
| 148 | 
            -
             | 
| 149 | 
            -
                       | 
| 150 | 
            -
             | 
| 151 | 
            -
             | 
| 152 | 
            -
             | 
| 153 | 
            -
             | 
| 154 | 
            -
                       | 
| 155 | 
            -
             | 
| 156 | 
            -
                       | 
| 157 | 
            -
             | 
| 142 | 
            +
                    ebook_paths.each do |ebook_path|
         | 
| 143 | 
            +
                      exit_copy = false
         | 
| 144 | 
            +
                      copy_to_path = nil
         | 
| 145 | 
            +
                      Thread.abort_on_exception = true
         | 
| 146 | 
            +
                      Thread.new do
         | 
| 147 | 
            +
                        copy_to_path = device.copy_to_documents(ebook_path)
         | 
| 148 | 
            +
                        exit_copy = true
         | 
| 149 | 
            +
                      end
         | 
| 150 | 
            +
                      until exit_copy
         | 
| 151 | 
            +
                        print "."
         | 
| 152 | 
            +
                        sleep(0.5)
         | 
| 153 | 
            +
                      end
         | 
| 154 | 
            +
                      puts
         | 
| 155 | 
            +
                      if copy_to_path
         | 
| 156 | 
            +
                        puts copy_to_path + " へコピーしました"
         | 
| 157 | 
            +
                      else
         | 
| 158 | 
            +
                        error "#{device.name}が見つからなかったためコピー出来ませんでした"
         | 
| 159 | 
            +
                        exit Narou::EXIT_ERROR_CODE # next しても次も失敗すると分かりきっているためここで終了する
         | 
| 160 | 
            +
                      end
         | 
| 158 161 | 
             
                    end
         | 
| 159 162 | 
             
                  end
         | 
| 160 163 | 
             
                  if send_all && @options["backup-bookmark"]
         | 
    
        data/lib/command/setting.rb
    CHANGED
    
    | @@ -319,6 +319,7 @@ module Command | |
| 319 319 | 
             
                SETTING_TAB_NAMES = {
         | 
| 320 320 | 
             
                  general: "一般",
         | 
| 321 321 | 
             
                  detail: "詳細",
         | 
| 322 | 
            +
                  webui: "WEB UI",
         | 
| 322 323 | 
             
                  global: "Global",
         | 
| 323 324 | 
             
                  default: "default.*",
         | 
| 324 325 | 
             
                  force: "force.*",
         | 
| @@ -326,6 +327,7 @@ module Command | |
| 326 327 | 
             
                }
         | 
| 327 328 |  | 
| 328 329 | 
             
                SETTING_TAB_INFO = {
         | 
| 330 | 
            +
                  webui: "WEB UI 専用の設定です",
         | 
| 329 331 | 
             
                  global: "Global な設定はユーザープロファイルに保存され、すべての narou コマンドで使われます",
         | 
| 330 332 | 
             
                  default: "default.* 系の設定は個別の変換設定で未設定の項目の挙動を指定することが出来ます",
         | 
| 331 333 | 
             
                  force: "force.* 系の設定は個別設定、default.* 等の設定を無視して反映されるようになります",
         | 
| @@ -478,22 +480,35 @@ module Command | |
| 478 480 | 
             
                      help: "ネタバレ防止機能。ダウンロード時の各話タイトルを伏せ字で表示する",
         | 
| 479 481 | 
             
                      tab: :detail
         | 
| 480 482 | 
             
                    },
         | 
| 481 | 
            -
                    " | 
| 483 | 
            +
                    "normalize-filename" => {
         | 
| 484 | 
            +
                      type: :boolean, help: "ファイル名の文字列をNFCで正規化する。※既存データとの互換性が無くなる可能性があるので、バックアップを取った上で機能を理解の上有効にして下さい",
         | 
| 485 | 
            +
                      tab: :detail,
         | 
| 486 | 
            +
                    },
         | 
| 487 | 
            +
                    "webui.theme" => {
         | 
| 482 488 | 
             
                      type: :select, help: "WEB UI 用テーマ選択",
         | 
| 483 489 | 
             
                      invisible: true,
         | 
| 484 490 | 
             
                      select_keys: Narou.get_theme_names,
         | 
| 485 491 | 
             
                      select_summaries: Narou.get_theme_names,
         | 
| 486 | 
            -
                      tab: : | 
| 492 | 
            +
                      tab: :webui
         | 
| 487 493 | 
             
                    },
         | 
| 488 | 
            -
                    " | 
| 489 | 
            -
                      type: : | 
| 490 | 
            -
                       | 
| 494 | 
            +
                    "webui.table.reload-timing" => {
         | 
| 495 | 
            +
                      type: :select, help: "小説リストの更新タイミングを選択。未設定時は1作品ごとに更新",
         | 
| 496 | 
            +
                      invisible: true,
         | 
| 497 | 
            +
                      select_keys: %w(every queue),
         | 
| 498 | 
            +
                      select_summaries: %w(
         | 
| 499 | 
            +
                        1作品ごとに更新
         | 
| 500 | 
            +
                        キューごとに更新
         | 
| 501 | 
            +
                      ),
         | 
| 502 | 
            +
                      tab: :webui
         | 
| 491 503 | 
             
                    },
         | 
| 492 504 | 
             
                  },
         | 
| 493 505 | 
             
                  global: {
         | 
| 494 506 | 
             
                    "aozoraepub3dir" => {
         | 
| 495 507 | 
             
                      type: :directory, help: "AozoraEpub3のあるフォルダを指定", invisible: true
         | 
| 496 508 | 
             
                    },
         | 
| 509 | 
            +
                    "line-height" => {
         | 
| 510 | 
            +
                      type: :float, help: "行間サイズ(narou init から指定しないと反映されません)", invisible: true
         | 
| 511 | 
            +
                    },
         | 
| 497 512 | 
             
                    "difftool" => {
         | 
| 498 513 | 
             
                      type: :string, help: "Diffで使うツールのパスを指定する",
         | 
| 499 514 | 
             
                      tab: :global
         | 
    
        data/lib/command/update.rb
    CHANGED
    
    
| @@ -35,7 +35,7 @@ module Command | |
| 35 35 |  | 
| 36 36 | 
             
                  def update_narou_novels
         | 
| 37 37 | 
             
                    @narou_novels.each do |api_url, ncodes|
         | 
| 38 | 
            -
                      api = Narou::API.new(api_url: api_url, ncodes: ncodes, of: "nu-gl")
         | 
| 38 | 
            +
                      api = Narou::API.new(api_url: api_url, ncodes: ncodes, of: "nu-gl-l")
         | 
| 39 39 | 
             
                      api.request.each do |result|
         | 
| 40 40 | 
             
                        ncode = result["ncode"]
         | 
| 41 41 | 
             
                        data = Downloader.get_data_by_target(ncode)
         | 
| @@ -43,6 +43,7 @@ module Command | |
| 43 43 | 
             
                        if result["novelupdated_at"] > last_check_date
         | 
| 44 44 | 
             
                          data["novelupdated_at"] = result["novelupdated_at"]
         | 
| 45 45 | 
             
                          data["general_lastup"] = result["general_lastup"]
         | 
| 46 | 
            +
                          data["length"] = result["length"]
         | 
| 46 47 | 
             
                          tags = data["tags"] ||= []
         | 
| 47 48 | 
             
                          tags << Narou::MODIFIED_TAG unless tags.include?(Narou::MODIFIED_TAG)
         | 
| 48 49 | 
             
                        end
         | 
| @@ -64,10 +65,10 @@ module Command | |
| 64 65 | 
             
                        next unless downloader.get_latest_table_of_contents(through_error: true)
         | 
| 65 66 | 
             
                        dates = {
         | 
| 66 67 | 
             
                          "novelupdated_at" => downloader.get_novelupdated_at,
         | 
| 67 | 
            -
                          "general_lastup" => downloader.get_general_lastup
         | 
| 68 | 
            +
                          "general_lastup" => downloader.get_general_lastup,
         | 
| 69 | 
            +
                          "length" => downloader.novel_length
         | 
| 68 70 | 
             
                        }
         | 
| 69 71 | 
             
                      rescue OpenURI::HTTPError, Errno::ECONNRESET
         | 
| 70 | 
            -
                        downloader.setting.clear
         | 
| 71 72 | 
             
                        next
         | 
| 72 73 | 
             
                      end
         | 
| 73 74 | 
             
                      data = @database[id]
         | 
    
        data/lib/converterbase.rb
    CHANGED
    
    | @@ -1314,7 +1314,6 @@ class ConverterBase | |
| 1314 1314 | 
             
                (io = after_convert(io)).rewind
         | 
| 1315 1315 | 
             
                data = replace_by_replace_txt(io.read)
         | 
| 1316 1316 | 
             
                data = insert_separator_for_selection(data)
         | 
| 1317 | 
            -
                data = double_dash_to_image(data, output_text_dir)
         | 
| 1318 1317 | 
             
                return data
         | 
| 1319 1318 | 
             
              end
         | 
| 1320 1319 |  | 
| @@ -1430,38 +1429,6 @@ class ConverterBase | |
| 1430 1429 | 
             
                result
         | 
| 1431 1430 | 
             
              end
         | 
| 1432 1431 |  | 
| 1433 | 
            -
              DASH_FILES = %w(singledash.png doubledash.png)
         | 
| 1434 | 
            -
             | 
| 1435 | 
            -
              def double_dash_to_image(text, output_text_dir)
         | 
| 1436 | 
            -
                return text unless @setting.enable_double_dash_to_image
         | 
| 1437 | 
            -
                # サブタイトルの中の場合は無視する
         | 
| 1438 | 
            -
                # (サブタイトルは文字を大きくしているので、画像の位置がずれてしまうため)
         | 
| 1439 | 
            -
                return text if @text_type == "subtitle"
         | 
| 1440 | 
            -
             | 
| 1441 | 
            -
                begin
         | 
| 1442 | 
            -
                  # AozoraEpub3 は相対パスじゃないとエラーになるので相対パスに変換
         | 
| 1443 | 
            -
                  dash_paths = dash_image_relative_paths(Narou.get_preset_dir, output_text_dir)
         | 
| 1444 | 
            -
                rescue ArgumentError => e
         | 
| 1445 | 
            -
                  if e.message =~ /^different prefix/
         | 
| 1446 | 
            -
                    # Windowsにおいて、スクリプト本体のあるドライブと小説フォルダがあるドライブが
         | 
| 1447 | 
            -
                    # 違う場合、相対パスを計算できなくなる。そのための対処として、.narou ディレクトリ
         | 
| 1448 | 
            -
                    # に画像データをコピーし、同一ドライブ内で相対パスを取れるようにする
         | 
| 1449 | 
            -
                    copy_dash_images_to_local_setting_dir
         | 
| 1450 | 
            -
                    dash_paths = dash_image_relative_paths(Narou.local_setting_dir, output_text_dir)
         | 
| 1451 | 
            -
                  else
         | 
| 1452 | 
            -
                    raise
         | 
| 1453 | 
            -
                  end
         | 
| 1454 | 
            -
                end
         | 
| 1455 | 
            -
                text.gsub(/―{2,}/) do |match|
         | 
| 1456 | 
            -
                  len = match.length
         | 
| 1457 | 
            -
                  result = "※[#(#{dash_paths[1]})]" * (len / 2)
         | 
| 1458 | 
            -
                  if len.odd?
         | 
| 1459 | 
            -
                    result += "※[#(#{dash_paths[0]})]"
         | 
| 1460 | 
            -
                  end
         | 
| 1461 | 
            -
                  result
         | 
| 1462 | 
            -
                end
         | 
| 1463 | 
            -
              end
         | 
| 1464 | 
            -
             | 
| 1465 1432 | 
             
              def dash_image_relative_paths(base_dir, output_text_dir)
         | 
| 1466 1433 | 
             
                DASH_FILES.map do |name|
         | 
| 1467 1434 | 
             
                  pathname = Pathname(File.join(base_dir, name))
         | 
    
        data/lib/downloader.rb
    CHANGED
    
    | @@ -579,6 +579,17 @@ class Downloader | |
| 579 579 | 
             
                end
         | 
| 580 580 | 
             
              end
         | 
| 581 581 |  | 
| 582 | 
            +
              #
         | 
| 583 | 
            +
              # 小説の文字数
         | 
| 584 | 
            +
              #
         | 
| 585 | 
            +
              # 小説情報から取得するため、実際に計算するわけではない。
         | 
| 586 | 
            +
              # 情報から取得出来ない(記載がない)場合は無視する
         | 
| 587 | 
            +
              #
         | 
| 588 | 
            +
              def novel_length
         | 
| 589 | 
            +
                info = @setting["info"] || {}
         | 
| 590 | 
            +
                info["length"]
         | 
| 591 | 
            +
              end
         | 
| 592 | 
            +
             | 
| 582 593 | 
             
              #
         | 
| 583 594 | 
             
              # データベース更新
         | 
| 584 595 | 
             
              #
         | 
| @@ -599,6 +610,7 @@ class Downloader | |
| 599 610 | 
             
                  "general_firstup" => info["general_firstup"],
         | 
| 600 611 | 
             
                  "novelupdated_at" => get_novelupdated_at,
         | 
| 601 612 | 
             
                  "general_lastup" => get_general_lastup,
         | 
| 613 | 
            +
                  "length" => novel_length,
         | 
| 602 614 | 
             
                }
         | 
| 603 615 | 
             
                if @@database[@id]
         | 
| 604 616 | 
             
                  @@database[@id].merge!(data)
         | 
| @@ -884,7 +896,11 @@ class Downloader | |
| 884 896 | 
             
              end
         | 
| 885 897 |  | 
| 886 898 | 
             
              def title_to_filename(title)
         | 
| 887 | 
            -
                Helper.replace_filename_special_chars( | 
| 899 | 
            +
                Helper.replace_filename_special_chars(
         | 
| 900 | 
            +
                  Helper.truncate_path(
         | 
| 901 | 
            +
                    HTML.new(title).delete_ruby_tag
         | 
| 902 | 
            +
                  )
         | 
| 903 | 
            +
                )
         | 
| 888 904 | 
             
              end
         | 
| 889 905 |  | 
| 890 906 | 
             
              #
         | 
| @@ -916,7 +932,7 @@ class Downloader | |
| 916 932 | 
             
                    "href" => @setting["href"],
         | 
| 917 933 | 
             
                    "chapter" => @setting["chapter"].to_s,
         | 
| 918 934 | 
             
                    "subchapter" => @setting["subchapter"].to_s,
         | 
| 919 | 
            -
                    "subtitle" => @setting["subtitle"] | 
| 935 | 
            +
                    "subtitle" => slim_subtitle(@setting["subtitle"]),
         | 
| 920 936 | 
             
                    "file_subtitle" => title_to_filename(@setting["subtitle"]),
         | 
| 921 937 | 
             
                    "subdate" => subdate,
         | 
| 922 938 | 
             
                    "subupdate" => @setting["subupdate"]
         | 
| @@ -933,7 +949,7 @@ class Downloader | |
| 933 949 | 
             
                  "index" => "1",
         | 
| 934 950 | 
             
                  "href" => @setting.replace_group_values("href", "index" => "1"),
         | 
| 935 951 | 
             
                  "chapter" => "",
         | 
| 936 | 
            -
                  "subtitle" => @setting["title"],
         | 
| 952 | 
            +
                  "subtitle" => slim_subtitle(@setting["title"]),
         | 
| 937 953 | 
             
                  "file_subtitle" => title_to_filename(@setting["title"]),
         | 
| 938 954 | 
             
                  "subdate" => info["general_firstup"],
         | 
| 939 955 | 
             
                  "subupdate" => info["novelupdated_at"] || info["general_lastup"] || info["general_firstup"]
         | 
| @@ -941,6 +957,11 @@ class Downloader | |
| 941 957 | 
             
                [subtitle]
         | 
| 942 958 | 
             
              end
         | 
| 943 959 |  | 
| 960 | 
            +
              def slim_subtitle(string)
         | 
| 961 | 
            +
                # HTML.new(string).delete_ruby_tag.delete("\n")
         | 
| 962 | 
            +
                HTML.new(string).delete_ruby_tag.delete("\n")
         | 
| 963 | 
            +
              end
         | 
| 964 | 
            +
             | 
| 944 965 | 
             
              #
         | 
| 945 966 | 
             
              # 小説本文をまとめてダウンロードして保存
         | 
| 946 967 | 
             
              #
         | 
| @@ -971,7 +992,7 @@ class Downloader | |
| 971 992 | 
             
                    @stream.print "短編 "
         | 
| 972 993 | 
             
                  end
         | 
| 973 994 | 
             
                  printable_subtitle = @gurad_spoiler ? Helper.to_unprintable_words(subtitle) : subtitle
         | 
| 974 | 
            -
                  @stream.print "#{printable_subtitle} (#{i+1}/#{max})"
         | 
| 995 | 
            +
                  @stream.print "#{HTML.new(printable_subtitle).delete_ruby_tag} (#{i + 1}/#{max})"
         | 
| 975 996 |  | 
| 976 997 | 
             
                  section_file_name = "#{index} #{file_subtitle}.yaml"
         | 
| 977 998 | 
             
                  section_file_relative_path = File.join(SECTION_SAVE_DIR_NAME, section_file_name)
         | 
    
        data/lib/helper.rb
    CHANGED
    
    | @@ -294,7 +294,7 @@ module Helper | |
| 294 294 | 
             
                when Time
         | 
| 295 295 | 
             
                  date
         | 
| 296 296 | 
             
                when String
         | 
| 297 | 
            -
                  Time.parse(date.sub(/[\((].+?[\))]/, "").tr("年月日時分秒@;", "///::: :"))
         | 
| 297 | 
            +
                  Time.parse(date.sub(/[\((].+?[\))]/, "").tr("年月日時分秒@;", "///::: :")).getlocal
         | 
| 298 298 | 
             
                end
         | 
| 299 299 | 
             
              rescue ArgumentError
         | 
| 300 300 | 
             
                nil
         | 
| @@ -325,7 +325,7 @@ module Helper | |
| 325 325 | 
             
              # 数字やスペース、句読点、感嘆符はそのままにする
         | 
| 326 326 | 
             
              #
         | 
| 327 327 | 
             
              def to_unprintable_words(string, mask = "●")
         | 
| 328 | 
            -
                result = ""
         | 
| 328 | 
            +
                result = "".dup
         | 
| 329 329 | 
             
                string.each_char do |char|
         | 
| 330 330 | 
             
                  result += case char
         | 
| 331 331 | 
             
                            when /[0-90-9  、。!?!?]/
         | 
| @@ -354,6 +354,23 @@ module Helper | |
| 354 354 | 
             
                end
         | 
| 355 355 | 
             
              end
         | 
| 356 356 |  | 
| 357 | 
            +
              #
         | 
| 358 | 
            +
              # src をERBとして読み込んでから dst に書き出す
         | 
| 359 | 
            +
              #
         | 
| 360 | 
            +
              def erb_copy(src, dst, _binding)
         | 
| 361 | 
            +
                data = File.read(src, mode: "r:BOM|UTF-8")
         | 
| 362 | 
            +
                result = ERB.new(data, nil, "-").result(_binding)
         | 
| 363 | 
            +
                File.write(dst, result)
         | 
| 364 | 
            +
              end
         | 
| 365 | 
            +
             | 
| 366 | 
            +
              #
         | 
| 367 | 
            +
              # カンマ付き数字列を数値に変換
         | 
| 368 | 
            +
              #
         | 
| 369 | 
            +
              def numeric_length(len)
         | 
| 370 | 
            +
                return len unless len.is_a?(String)
         | 
| 371 | 
            +
                len.delete(",").to_i
         | 
| 372 | 
            +
              end
         | 
| 373 | 
            +
             | 
| 357 374 | 
             
              #
         | 
| 358 375 | 
             
              # 外部コマンド実行中の待機ループの処理を書けるクラス
         | 
| 359 376 | 
             
              #
         | 
    
        data/lib/html.rb
    CHANGED
    
    
    
        data/lib/narou.rb
    CHANGED
    
    | @@ -5,6 +5,7 @@ | |
| 5 5 |  | 
| 6 6 | 
             
            require "fileutils"
         | 
| 7 7 | 
             
            require "memoist"
         | 
| 8 | 
            +
            require "active_support/core_ext/object/blank"
         | 
| 8 9 | 
             
            require_relative "helper"
         | 
| 9 10 | 
             
            require_relative "inventory"
         | 
| 10 11 | 
             
            if Helper.engine_jruby?
         | 
| @@ -23,6 +24,7 @@ module Narou | |
| 23 24 | 
             
              EXIT_INTERRUPT = 126
         | 
| 24 25 | 
             
              EXIT_REQUEST_REBOOT = 125
         | 
| 25 26 | 
             
              MODIFIED_TAG = "modified"
         | 
| 27 | 
            +
              LINE_HEIGHT_DEFAULT = 1.6 # 単位em
         | 
| 26 28 |  | 
| 27 29 | 
             
              UPDATE_SORT_KEYS = {
         | 
| 28 30 | 
             
                "id" => "ID", "last_update" => "更新日", "title" => "タイトル", "author" => "作者名",
         | 
| @@ -35,7 +37,7 @@ module Narou | |
| 35 37 | 
             
              @@is_web = false
         | 
| 36 38 |  | 
| 37 39 | 
             
              def last_commit_year
         | 
| 38 | 
            -
                 | 
| 40 | 
            +
                2018
         | 
| 39 41 | 
             
              end
         | 
| 40 42 |  | 
| 41 43 | 
             
              def get_root_dir
         | 
| @@ -199,7 +201,15 @@ module Narou | |
| 199 201 | 
             
              #
         | 
| 200 202 | 
             
              def create_novel_filename(novel_data, ext = "")
         | 
| 201 203 | 
             
                filename_to_ncode = Inventory.load("local_setting")["convert.filename-to-ncode"]
         | 
| 202 | 
            -
                 | 
| 204 | 
            +
                novel_setting =
         | 
| 205 | 
            +
                  if novel_data["id"]
         | 
| 206 | 
            +
                    NovelSetting.load(novel_data["id"])
         | 
| 207 | 
            +
                  else
         | 
| 208 | 
            +
                    OpenStruct.new
         | 
| 209 | 
            +
                  end
         | 
| 210 | 
            +
                if novel_setting.output_filename.present?
         | 
| 211 | 
            +
                  %!#{novel_setting.output_filename}#{ext}!
         | 
| 212 | 
            +
                elsif filename_to_ncode
         | 
| 203 213 | 
             
                  ncode, domain = novel_data["ncode"], novel_data["domain"]
         | 
| 204 214 | 
             
                  if !ncode || !domain
         | 
| 205 215 | 
             
                    id = novel_data["id"]
         | 
| @@ -213,22 +223,39 @@ module Narou | |
| 213 223 | 
             
                  serialized_domain = domain.to_s.gsub(".", "_")
         | 
| 214 224 | 
             
                  %!#{serialized_domain}_#{ncode}#{ext}!
         | 
| 215 225 | 
             
                else
         | 
| 216 | 
            -
                  author | 
| 217 | 
            -
                     | 
| 218 | 
            -
             | 
| 226 | 
            +
                  author = Helper.replace_filename_special_chars(
         | 
| 227 | 
            +
                    novel_setting.novel_author.presence || novel_data["author"],
         | 
| 228 | 
            +
                    true
         | 
| 229 | 
            +
                  )
         | 
| 230 | 
            +
                  title = Helper.replace_filename_special_chars(
         | 
| 231 | 
            +
                    novel_setting.novel_title.presence || novel_data["title"],
         | 
| 232 | 
            +
                    true
         | 
| 233 | 
            +
                  )
         | 
| 219 234 | 
             
                  "[#{author}] #{title}#{ext}"
         | 
| 220 235 | 
             
                end
         | 
| 221 236 | 
             
              end
         | 
| 222 237 |  | 
| 223 | 
            -
              def  | 
| 224 | 
            -
                 | 
| 238 | 
            +
              def get_mobi_paths(target)
         | 
| 239 | 
            +
                get_ebook_file_paths(target, ".mobi")
         | 
| 225 240 | 
             
              end
         | 
| 226 241 |  | 
| 227 | 
            -
              def  | 
| 242 | 
            +
              def get_ebook_file_paths(target, ext)
         | 
| 228 243 | 
             
                data = Downloader.get_data_by_target(target)
         | 
| 229 244 | 
             
                return nil unless data
         | 
| 230 245 | 
             
                dir = Downloader.get_novel_data_dir_by_target(target)
         | 
| 231 | 
            -
                 | 
| 246 | 
            +
                fname = create_novel_filename(data, ext)
         | 
| 247 | 
            +
                base = File.basename(fname, ext)
         | 
| 248 | 
            +
                get_ebook_file_paths_from_components(dir, base, ext)
         | 
| 249 | 
            +
              end
         | 
| 250 | 
            +
             | 
| 251 | 
            +
              def get_ebook_file_paths_from_components(dir, base, ext)
         | 
| 252 | 
            +
                paths = [File.join(dir, "#{base}#{ext}")]
         | 
| 253 | 
            +
                index = 2
         | 
| 254 | 
            +
                while File.exist?(path = File.join(dir, "#{base}_#{index}#{ext}"))
         | 
| 255 | 
            +
                  paths.push(path)
         | 
| 256 | 
            +
                  index += 1
         | 
| 257 | 
            +
                end
         | 
| 258 | 
            +
                paths
         | 
| 232 259 | 
             
              end
         | 
| 233 260 |  | 
| 234 261 | 
             
              def get_misc_dir
         | 
| @@ -262,7 +289,7 @@ module Narou | |
| 262 289 | 
             
              end
         | 
| 263 290 |  | 
| 264 291 | 
             
              def get_theme
         | 
| 265 | 
            -
                Inventory.load("local_setting")["theme"]
         | 
| 292 | 
            +
                Inventory.load("local_setting")["webui.theme"]
         | 
| 266 293 | 
             
              end
         | 
| 267 294 |  | 
| 268 295 | 
             
              def get_theme_dir(name = nil)
         | 
| @@ -311,5 +338,10 @@ module Narou | |
| 311 338 | 
             
              end
         | 
| 312 339 | 
             
              memoize :kindlegen_path
         | 
| 313 340 |  | 
| 341 | 
            +
              def line_height
         | 
| 342 | 
            +
                global_setting = Inventory.load("global_setting", :global)
         | 
| 343 | 
            +
                global_setting["line-height"] || LINE_HEIGHT_DEFAULT
         | 
| 344 | 
            +
              end
         | 
| 345 | 
            +
             | 
| 314 346 | 
             
             end
         | 
| 315 347 | 
             
            end
         | 
    
        data/lib/novelconverter.rb
    CHANGED
    
    | @@ -25,6 +25,16 @@ class NovelConverter | |
| 25 25 |  | 
| 26 26 | 
             
              attr_reader :use_dakuten_font
         | 
| 27 27 |  | 
| 28 | 
            +
              def self.extensions_of_converted_files(device)
         | 
| 29 | 
            +
                exts = [".txt"]
         | 
| 30 | 
            +
                if device && device.kobo?
         | 
| 31 | 
            +
                  exts.push(device.ebook_file_ext)
         | 
| 32 | 
            +
                else
         | 
| 33 | 
            +
                  exts.push(".epub", device.ebook_file_ext)
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
                exts
         | 
| 36 | 
            +
              end
         | 
| 37 | 
            +
             | 
| 28 38 | 
             
              #
         | 
| 29 39 | 
             
              # 指定の小説を整形・変換する
         | 
| 30 40 | 
             
              #
         | 
| @@ -38,7 +48,7 @@ class NovelConverter | |
| 38 48 | 
             
                if setting
         | 
| 39 49 | 
             
                  novel_converter = new(setting, options[:output_filename], options[:display_inspector])
         | 
| 40 50 | 
             
                  return {
         | 
| 41 | 
            -
                     | 
| 51 | 
            +
                    converted_txt_paths: novel_converter.convert_main,
         | 
| 42 52 | 
             
                    use_dakuten_font: novel_converter.use_dakuten_font
         | 
| 43 53 | 
             
                  }
         | 
| 44 54 | 
             
                end
         | 
| @@ -69,22 +79,28 @@ class NovelConverter | |
| 69 79 | 
             
                  text.force_encoding(options[:encoding]).encode!(Encoding::UTF_8)
         | 
| 70 80 | 
             
                end
         | 
| 71 81 | 
             
                {
         | 
| 72 | 
            -
                   | 
| 82 | 
            +
                  converted_txt_paths: novel_converter.convert_main(text),
         | 
| 73 83 | 
             
                  use_dakuten_font: novel_converter.use_dakuten_font
         | 
| 74 84 | 
             
                }
         | 
| 75 85 | 
             
              end
         | 
| 76 86 |  | 
| 77 87 | 
             
              DAKUTEN_FROM = ["vertical_font_with_dakuten.css", "DMincho.ttf"]
         | 
| 78 88 | 
             
              DAKUTEN_TO = ["template/OPS/css_custom/vertical_font.css", "template/OPS/fonts/DMincho.ttf"]
         | 
| 89 | 
            +
              DAKUTEN_ERB = [true, false]
         | 
| 79 90 |  | 
| 80 91 | 
             
              def self.activate_dakuten_font_files
         | 
| 81 92 | 
             
                preset_dir = Narou.get_preset_dir
         | 
| 82 93 | 
             
                aozora_dir = File.dirname(Narou.get_aozoraepub3_path)
         | 
| 94 | 
            +
                line_height = Narou.line_height
         | 
| 83 95 |  | 
| 84 96 | 
             
                DAKUTEN_FROM.each_with_index do |name, i|
         | 
| 85 97 | 
             
                  src = File.join(preset_dir, name)
         | 
| 86 98 | 
             
                  dst = File.join(aozora_dir, DAKUTEN_TO[i])
         | 
| 87 | 
            -
                   | 
| 99 | 
            +
                  if DAKUTEN_ERB[i]
         | 
| 100 | 
            +
                    Helper.erb_copy(src, dst, binding)
         | 
| 101 | 
            +
                  else
         | 
| 102 | 
            +
                    FileUtils.copy(src, dst)
         | 
| 103 | 
            +
                  end
         | 
| 88 104 | 
             
                end
         | 
| 89 105 | 
             
              end
         | 
| 90 106 |  | 
| @@ -92,8 +108,9 @@ class NovelConverter | |
| 92 108 | 
             
                preset_dir = Narou.get_preset_dir
         | 
| 93 109 | 
             
                aozora_dir = File.dirname(Narou.get_aozoraepub3_path)
         | 
| 94 110 | 
             
                path_normal_vertical_css = File.join(preset_dir, "vertical_font.css")
         | 
| 111 | 
            +
                line_height = Narou.line_height
         | 
| 95 112 |  | 
| 96 | 
            -
                 | 
| 113 | 
            +
                Helper.erb_copy(path_normal_vertical_css, File.join(aozora_dir, DAKUTEN_TO[0]), binding)
         | 
| 97 114 | 
             
                FileUtils.remove(File.join(aozora_dir, DAKUTEN_TO[1]))
         | 
| 98 115 | 
             
              end
         | 
| 99 116 |  | 
| @@ -305,7 +322,7 @@ class NovelConverter | |
| 305 322 | 
             
                else
         | 
| 306 323 | 
             
                  epub_ext = ".epub"
         | 
| 307 324 | 
             
                end
         | 
| 308 | 
            -
                epub_path = txt_path.sub( | 
| 325 | 
            +
                epub_path = txt_path.sub(/\.txt$/, epub_ext)
         | 
| 309 326 |  | 
| 310 327 | 
             
                if !device || !device.kindle? || options[:no_mobi]
         | 
| 311 328 | 
             
                  puts File.basename(epub_path) + " を出力しました"
         | 
| @@ -348,9 +365,10 @@ class NovelConverter | |
| 348 365 | 
             
              def initialize(setting, output_filename = nil, display_inspector = false, output_text_dir = nil)
         | 
| 349 366 | 
             
                @setting = setting
         | 
| 350 367 | 
             
                @novel_id = setting.id
         | 
| 351 | 
            -
                @novel_author = setting. | 
| 352 | 
            -
                @novel_title = setting. | 
| 368 | 
            +
                @novel_author = setting.novel_author.empty? ? setting.author : setting.novel_author
         | 
| 369 | 
            +
                @novel_title = setting.novel_title.empty? ? setting.title : setting.novel_title
         | 
| 353 370 | 
             
                @output_filename = (output_filename || setting.output_filename)
         | 
| 371 | 
            +
                @output_filename = nil if @output_filename.empty?
         | 
| 354 372 | 
             
                @inspector = Inspector.new(@setting)
         | 
| 355 373 | 
             
                @illustration = Illustration.new(@setting, @inspector)
         | 
| 356 374 | 
             
                @display_inspector = display_inspector
         | 
| @@ -368,32 +386,35 @@ class NovelConverter | |
| 368 386 | 
             
                initialize_event
         | 
| 369 387 |  | 
| 370 388 | 
             
                if text
         | 
| 371 | 
            -
                   | 
| 389 | 
            +
                  array_of_converted_text = convert_main_for_text(text)
         | 
| 372 390 | 
             
                else
         | 
| 373 | 
            -
                   | 
| 391 | 
            +
                  array_of_converted_text = convert_main_for_novel
         | 
| 374 392 | 
             
                  update_latest_convert_novel
         | 
| 375 393 | 
             
                end
         | 
| 394 | 
            +
                inspect_novel(array_of_converted_text)
         | 
| 376 395 |  | 
| 377 | 
            -
                 | 
| 378 | 
            -
             | 
| 379 | 
            -
             | 
| 380 | 
            -
             | 
| 396 | 
            +
                array_of_output_path = []
         | 
| 397 | 
            +
                array_of_converted_text.each_with_index do |converted_text, i|
         | 
| 398 | 
            +
                  output_path = create_output_path(text, converted_text, i + 1)
         | 
| 399 | 
            +
                  File.write(output_path, converted_text)
         | 
| 400 | 
            +
                  array_of_output_path.push(output_path)
         | 
| 401 | 
            +
                end
         | 
| 381 402 |  | 
| 382 403 | 
             
                display_footer
         | 
| 383 404 |  | 
| 384 | 
            -
                 | 
| 405 | 
            +
                array_of_output_path
         | 
| 385 406 | 
             
              end
         | 
| 386 407 |  | 
| 387 408 | 
             
              def initialize_event
         | 
| 388 409 | 
             
                progressbar = nil
         | 
| 389 410 |  | 
| 390 | 
            -
                 | 
| 411 | 
            +
                on(:"convert_main.init") do |subtitles|
         | 
| 391 412 | 
             
                  progressbar = ProgressBar.new(subtitles.size)
         | 
| 392 413 | 
             
                end
         | 
| 393 414 | 
             
                on(:"convert_main.loop") do |i|
         | 
| 394 415 | 
             
                  progressbar.output(i) if progressbar
         | 
| 395 416 | 
             
                end
         | 
| 396 | 
            -
                 | 
| 417 | 
            +
                on(:"convert_main.finish") do
         | 
| 397 418 | 
             
                  progressbar.clear if progressbar
         | 
| 398 419 | 
             
                end
         | 
| 399 420 | 
             
              end
         | 
| @@ -419,13 +440,15 @@ class NovelConverter | |
| 419 440 |  | 
| 420 441 | 
             
              # is_hotentry を有効にすると、テンプレートで作成するテキストファイルに
         | 
| 421 442 | 
             
              # あらすじ、作品タイトル、本の読み終わり表示が付与されなくなる
         | 
| 422 | 
            -
              def create_novel_text_by_template(sections, toc, is_hotentry = false)
         | 
| 443 | 
            +
              def create_novel_text_by_template(sections, toc, is_hotentry = false, index = nil)
         | 
| 423 444 | 
             
                cover_chuki = create_cover_chuki
         | 
| 424 445 | 
             
                device = Narou.get_device
         | 
| 425 446 | 
             
                setting = @setting
         | 
| 426 | 
            -
                toc["title"] = setting | 
| 427 | 
            -
                toc["author"] = setting | 
| 428 | 
            -
                 | 
| 447 | 
            +
                toc["title"] = setting.novel_title unless setting.novel_title.empty?
         | 
| 448 | 
            +
                toc["author"] = setting.novel_author unless setting.novel_author.empty?
         | 
| 449 | 
            +
                processing_title = toc["title"]
         | 
| 450 | 
            +
                processing_title += "_#{index}" if index
         | 
| 451 | 
            +
                processed_title = decorate_title(processing_title)
         | 
| 429 452 | 
             
                tempalte_name = (device && device.ibunko? ? NOVEL_TEXT_TEMPLATE_NAME_FOR_IBUNKO : NOVEL_TEXT_TEMPLATE_NAME)
         | 
| 430 453 | 
             
                Template.get(tempalte_name, binding, 1.1)
         | 
| 431 454 | 
             
              end
         | 
| @@ -543,7 +566,7 @@ class NovelConverter | |
| 543 566 | 
             
              #
         | 
| 544 567 | 
             
              # 最終的に出力するパスを生成
         | 
| 545 568 | 
             
              #
         | 
| 546 | 
            -
              def create_output_path(is_text_file_mode, converted_text)
         | 
| 569 | 
            +
              def create_output_path(is_text_file_mode, converted_text, index)
         | 
| 547 570 | 
             
                output_path = ""
         | 
| 548 571 | 
             
                if @output_filename
         | 
| 549 572 | 
             
                  output_path = File.join(@setting.archive_path, File.basename(@output_filename))
         | 
| @@ -560,9 +583,15 @@ class NovelConverter | |
| 560 583 | 
             
                  end
         | 
| 561 584 | 
             
                  filename = Narou.create_novel_filename(info)
         | 
| 562 585 | 
             
                  output_path = File.join(@setting.archive_path, filename)
         | 
| 563 | 
            -
             | 
| 564 | 
            -
             | 
| 565 | 
            -
                   | 
| 586 | 
            +
                end
         | 
| 587 | 
            +
                if output_path !~ /\.\w+$/
         | 
| 588 | 
            +
                  output_path += ".txt"
         | 
| 589 | 
            +
                end
         | 
| 590 | 
            +
                # change output_path to "basename_#{index}.ext" if index is greater than 1
         | 
| 591 | 
            +
                if index > 1
         | 
| 592 | 
            +
                  ext = File.extname(output_path)
         | 
| 593 | 
            +
                  output_path = File.join(File.dirname(output_path), File.basename(output_path, ext))
         | 
| 594 | 
            +
                  output_path += "_#{index}#{ext}"
         | 
| 566 595 | 
             
                end
         | 
| 567 596 | 
             
                output_path
         | 
| 568 597 | 
             
              end
         | 
| @@ -582,7 +611,7 @@ class NovelConverter | |
| 582 611 |  | 
| 583 612 | 
             
                @use_dakuten_font = @converter.use_dakuten_font
         | 
| 584 613 |  | 
| 585 | 
            -
                converted_text
         | 
| 614 | 
            +
                [converted_text]
         | 
| 586 615 | 
             
              end
         | 
| 587 616 |  | 
| 588 617 | 
             
              #
         | 
| @@ -590,24 +619,43 @@ class NovelConverter | |
| 590 619 | 
             
              #
         | 
| 591 620 | 
             
              # 引数 subtitles にデータを渡した場合はそれを直接使う
         | 
| 592 621 | 
             
              # is_hotentry を有効にすると出力されるテキストファイルにあらすじや作品タイトル等が含まれなくなる
         | 
| 622 | 
            +
              # また、 is_hotentry を有効にすると分割も行われなくなる
         | 
| 593 623 | 
             
              #
         | 
| 594 624 | 
             
              def convert_main_for_novel(subtitles = nil, is_hotentry = false)
         | 
| 595 625 | 
             
                toc = Downloader.get_toc_data(@setting.archive_path)
         | 
| 596 626 | 
             
                unless subtitles
         | 
| 597 627 | 
             
                  subtitles = cut_subtitles(toc["subtitles"])
         | 
| 598 628 | 
             
                end
         | 
| 599 | 
            -
                @ | 
| 600 | 
            -
             | 
| 601 | 
            -
             | 
| 602 | 
            -
                 | 
| 603 | 
            -
             | 
| 604 | 
            -
                 | 
| 605 | 
            -
             | 
| 606 | 
            -
             | 
| 607 | 
            -
             | 
| 608 | 
            -
             | 
| 609 | 
            -
             | 
| 610 | 
            -
             | 
| 629 | 
            +
                if is_hotentry == false && @setting.slice_size > 0 && subtitles.length > @setting.slice_size
         | 
| 630 | 
            +
                  puts "#{@setting.slice_size}話ごとに分割して変換します"
         | 
| 631 | 
            +
                  array_of_subtitles = subtitles.each_slice(@setting.slice_size).to_a
         | 
| 632 | 
            +
                else
         | 
| 633 | 
            +
                  array_of_subtitles = [subtitles]
         | 
| 634 | 
            +
                end
         | 
| 635 | 
            +
                array_of_converted_text = []
         | 
| 636 | 
            +
                array_of_subtitles.each_with_index do |sliced_subtitles, index|
         | 
| 637 | 
            +
                  @converter.subtitles = sliced_subtitles
         | 
| 638 | 
            +
                  toc["story"] = @converter.convert(toc["story"], "story")
         | 
| 639 | 
            +
                  html = HTML.new
         | 
| 640 | 
            +
                  html.strip_decoration_tag = @setting.enable_strip_decoration_tag
         | 
| 641 | 
            +
                  site_setting = SiteSetting.find(toc["toc_url"])
         | 
| 642 | 
            +
                  html.set_illust_setting({ current_url: site_setting["illust_current_url"],
         | 
| 643 | 
            +
                                            grep_pattern: site_setting["illust_grep_pattern"] })
         | 
| 644 | 
            +
             | 
| 645 | 
            +
                  sections = subtitles_to_sections(sliced_subtitles, html)
         | 
| 646 | 
            +
                  array_of_converted_text.push(
         | 
| 647 | 
            +
                    create_novel_text_by_template(
         | 
| 648 | 
            +
                      sections, toc, is_hotentry,
         | 
| 649 | 
            +
                      array_of_subtitles.length == 1 ? nil : index + 1
         | 
| 650 | 
            +
                    )
         | 
| 651 | 
            +
                  )
         | 
| 652 | 
            +
                end
         | 
| 653 | 
            +
             | 
| 654 | 
            +
                if is_hotentry
         | 
| 655 | 
            +
                  array_of_converted_text[0]
         | 
| 656 | 
            +
                else
         | 
| 657 | 
            +
                  array_of_converted_text
         | 
| 658 | 
            +
                end
         | 
| 611 659 | 
             
              end
         | 
| 612 660 |  | 
| 613 661 | 
             
              def cut_subtitles(subtitles)
         | 
| @@ -668,8 +716,9 @@ class NovelConverter | |
| 668 716 | 
             
                { "title" => title, "author" => author }
         | 
| 669 717 | 
             
              end
         | 
| 670 718 |  | 
| 671 | 
            -
              def inspect_novel( | 
| 719 | 
            +
              def inspect_novel(array_of_text)
         | 
| 672 720 | 
             
                if @setting.enable_inspect
         | 
| 721 | 
            +
                  text = array_of_text.flatten
         | 
| 673 722 | 
             
                  @inspector.inspect_end_touten_conditions(text)   # 行末読点の現在状況を調査する
         | 
| 674 723 | 
             
                  @inspector.countup_return_in_brackets(text)      # カギ括弧内の改行状況を調査する
         | 
| 675 724 | 
             
                end
         |